VACUUM và chức năng autovacuum
Tại sao PostgreSQL cần VACUUM ?
-
Dung lượng đĩa cứng có thể full
Khác với các RDBMS khác (như MySQL), khi người dùng chạy lệnh DELETE hay UPDATE, PostgreSQL không xoá dữ liệu cũ đi luôn mà chỉ đánh dấu "đó là dữ liệu đã bị xoá".
Nên nếu liên tục INSERT/DELETE hoặc UPDATE dữ liệu mà không có cơ chế xoá dữ liệu dư thừa thì dung lượng ổ cứng tăng dẫn đến full.
-
Lỗi (dữ liệu bị vô hiệu) khi Wraparound Transaction ID
PostgreSQL sử dụng 32 bit Transaction ID (XID) để quản lý transaction(1). Mỗi một record dữ liệu đều có thông tin về XID. Khi dữ liệu được tham chiếu PostgreSQL sử dụng thông tin XID này so sánh với XID hiện tại để đánh giá dữ liệu này có hữu hiệu không. Dữ liệu đang tham chiếu có XID lớn hơn XID hiện tại là dữ liệu không hữu hiệu. Khi sử dụng hết 32 bit XID (khoảng 4 tỷ transactions), để sử dụng tiếp XID sẽ được reset về ban đầu (0). Nếu không có cơ chế chỉnh lại XID trong data thì mỗi lần reset XID, dữ liệu hiện tại sẽ trống trơn (dữ liệu hiện tại luôn có XID lớn hơn XID đã reset (0)).
Một trong những chức năng của VACUUM là quyết những vấn đề như trên.
(1) transaction(Giao dịch cơ sở dữ liệu): Định nghĩa từ wikipedia: Giao dịch cơ sở dữ liệu (database transaction) là đơn vị tương tác của một hệ quản lý cơ sở dữ liệu hoặc các hệ tương tự, mỗi giao dịch được xử lý một cách nhất quán và tin cậy mà không phụ thuộc vào các giao dịch khác. Một hệ cơ sở dữ liệu lý tưởng sẽ phải bảo đảm toàn bộ các tính chất ACID cho mỗi giao dịch. Trên thực tế, các tính chất này thường được nới lỏng để giúp việc thực thi đạt hiệu quả hơn.
PostgreSQL không thực hiện xóa dữ liệu ngay sau khi DELETE hoặc UPDATE nhằm giải quyết vấn đề cùng một lúc nhiều người sử dụng truy cập một vùng dữ liệu (PostgreSQL gọi cơ chế đó là Multiversion Concurrency Control hay MVCC). Theo thông tin gần đây từ cộng đồng PostgreSQL, từ phiên bản 12(?) PostgreSQL có thể bỏ chức năng VACUUM nhờ sử dụng cơ chế lưu trữ zheap.
VACUUM thực hiện những công việc gì
- Lấy lại dữ liệu dư thừa để tái sử dụng
- Cập nhật thông tin thống kê (statistics)
- Giải quyết vấn đề dữ liệu bị vô hiệu khi Wraparound Transaction ID
Lấy lại dữ liệu dư thừa
Như trên, PostgreSQL chưa xoá dữ liệu cũ khi thực hiện thao tác DELETE/UPDATE. Khi VACUUM, những dữ liệu dư thừa đó sẽ được lấy lại và vị trí dư thừa sẽ được cập nhật lại trong bảng vị trí trống (Free Space Map(FSM)). Ngoài ra những block dữ liệu đã được VACUUM sẽ được đánh dấu là đã VACUUM trên bảng khả thị (Visibility Map(VM)), khi UPDATE/DELETE dữ bảng khả thị sẽ cập nhật lại trạng thái là cần VACUUM.
Free Space Map(FSM): Mỗi bảng dữ liệu (hoặc index) tồn tại tương ứng một FSM. FSM chứa thông tin các vị trí trống trong file dữ liệu. Khi dữ liệu mới được ghi PostgreSQL sẽ nhìn vị trí trống từ FSM trước, việc này giảm thiểu truy cập trực tiếp (sinh I/O disk) vào file dữ liệu. File FSM nằm cùng vị trí với file dữ liệu và có tên = file_dữ_liệu_fsm (như ví dụ dưới).
Visibility Map(VM): Mỗi bảng dữ liệu tồn tại tương ứng một visibility map (VM). Một block dữ liệu tương ứng với 1 bit trên VM. VACUUM xem trước thông tin VM của bảng dữ liệu, và chỉ thực hiện trên những block cần được VACUUM. File VM nằm cùng vị trí với file dữ liệu và có tên = file_dữ_liệu_vm (như ví dụ dưới).
testdb=# create table testtbl(id integer);
CREATE TABLE
testdb=# insert into testtbl select generate_series(1,100);
INSERT 0 100
testdb=# delete from testtbl where id < 90;
DELETE 89
testdb=# checkpoint;
CHECKPOINT
testdb=# \! ls -l $PGDATA/base/16384/24576*
-rw------- 1 bocap staff 8192 Jun 24 18:19 /Users/bocap/Downloads/pg94/data/base/16384/24576
-rw------- 1 bocap staff 24576 Jun 24 18:19 /Users/bocap/Downloads/pg94/data/base/16384/24576_fsm
-rw------- 1 bocap staff 8192 Jun 24 18:19 /Users/bocap/Downloads/pg94/data/base/16384/24576_vm
Cập nhật lại thông tin thống kê
Một số bạn lầm tưởng rằng chỉ có chức năng ANALYZE mới cập nhật lại thông tin thống kê dùng bởi planner, nhưng thực tế VACUUM cũng cập nhật thông tin này. Cụ thể là relpages và reltuples trong system catalog pg_class. Do vậy nên có thể nói VACUUM cũng ảnh hưởng tới việc chọn plan thực thi.
postgres=# select relpages,reltuples from pg_class where relname = 'testtbl';
relpages | reltuples
----------+-----------
0 | 0
(1 row)
postgres=# insert into testtbl select generate_series(1,10),random()::text;
INSERT 0 10
postgres=# select relpages,reltuples from pg_class where relname = 'testtbl';
relpages | reltuples
----------+-----------
0 | 0
(1 row)
postgres=# vacuum testtbl;
VACUUM
postgres=# select relpages,reltuples from pg_class where relname = 'testtbl';
relpages | reltuples
----------+-----------
1 | 10
(1 row)
autovacuum
autovacuum là chức năng tự động thực thi VACUUM hoặc ANALYZE khi cần thiết. Chức năng này hoạt động khi tham số autovacuum và track_counts thiết lập là on. Cả 2 tham số này đều mặc định là on nên autovacuum sẽ tự động hoạt động khi khởi động PostgreSQL. Khi tham số autovacuum là on. Sau khi khởi động PostgreSQL process "autovacuum launcher process" sẽ đảm nhận việc này.
BocapnoMacBook-Pro:postgres bocap$ ps -ef | grep autovacuum | grep -v grep
501 3169 3164 0 13Aug17 ?? 0:03.13 postgres: autovacuum launcher process
Launcher process cứ mỗi autovacuum_naptime sẽ kiểm tra thông tin thống kê, nếu thấy bảng nào cần thiết VACUUM hoặc ANALYZE, launcher process sẽ khởi động các worker processes để thực hiện việc VACUUM hoặc ANALYZE. Số lượng process autovacuum worker hoạt động trong cùng một thời điểm được giới hạn bởi tham số autovacuum_max_workers (mặc định là 3).
BocapnoMacBook-Pro:postgres bocap$ ps -ef | grep autovacuum | grep -v grep
501 26490 26484 0 1:14AM ?? 0:00.01 postgres: autovacuum launcher process
501 26618 26484 0 1:16AM ?? 0:00.03 postgres: autovacuum worker process postgres
Điều kiện cần thiết cho bảng được VACUUM hoặc ANALYZE bởi autovacuum như bên dưới.
Ví dụ: số lượng dòng là 1000, thì mặc định khi số dòng bị xoá hoặc update lớn hơn 0.2*1000 + 50 = 250 bảng sẽ tự động được VACUUM
Ví dụ: số lượng dòng là 1000, thì mặc định khi số dòng bị xoá, update hoặc insert lớn hơn 0.1*1000 + 50 = 250 bảng sẽ tự động được ANALYZE
Khi chạy một câu truy vấn, thông tin thống kê cần phải chính xác để planner chọn đúng plan tối ưu nhất. autovacuum thực thi việc cập nhật thông tin này, nhưng vì một số lý do nào đó (ví dụ: đối tượng bảng đang bị lock trong transaction khác) , autovacuum không thực thi được tốt. Vì vậy, khi thấy câu truy vấn chạy chậm, nên kiểm tra thông tin thống kê xem ANALYZE, VACUUM đã được thực hiện tốt chưa thông qua view pg_stat_all_tables, và thực hiện VACUUM ANALYZE cũng là một giải pháp
Ngoài ra để process autovacuum không ảnh hưởng tới hệ thống, việc thiết lập autovacuum = off và chạy VACUUM ANALYZE thủ công vào thời điểm thích hợp cũng được sử dụng nhiều
Giám sát hoạt động autovacuum
Để biết đối tượng bảng có được VACUUM, ANALYZE hợp lý không ta có thể xem thông qua view pg_stat_all_tables.
-- ví dụ bên dưới cho thấy bảng testtbl đã được:
-- vacuum thủ công vào lúc: 2017-08-26 23:50:28
-- autovacuum vào lúc: 2017-08-27 01:16:04
-- autoanalyze vào lúc: 2017-08-27 01:16:04
postgres=# select last_vacuum,last_autovacuum,last_analyze,last_autoanalyze from pg_stat_all_tables where relid = (select oid from pg_class where relname = 'testtbl');
last_vacuum | last_autovacuum | last_analyze | last_autoanalyze
-------------------------------+-------------------------------+--------------+-------------------------------
2017-08-26 23:50:28.025241+09 | 2017-08-27 01:16:04.664297+09 | | 2017-08-27 01:16:04.665366+09
(1 row)
Ngoài ra, ta có thể theo dõi tình trạng hoạt động của autovacuum thông qua việc thiết lập tham số log_autovacuum_min_duration = 0 (log tất cả các hoạt động autovacuum).
Ví dụ:
2017-08-27 01:16:04.664 JST [26618] LOG: automatic vacuum of table "postgres.public.testtbl": index scans: 0
pages: 1276 removed, 0 remain, 0 skipped due to pins, 0 skipped frozen
tuples: 100000 removed, 0 remain, 0 are dead but not yet removable, oldest xmin: 579
buffer usage: 3851 hits, 0 misses, 0 dirtied
avg read rate: 0.000 MB/s, avg write rate: 0.000 MB/s
system usage: CPU: user: 0.02 s, system: 0.00 s, elapsed: 0.28 s
2017-08-27 01:16:04.665 JST [26618] LOG: automatic analyze of table "postgres.public.testtbl" system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s