PostgreSQL không sử dụng cơ chế cập nhật IN-PLACE, vì vậy theo cách lệnh DELETE và UPDATE được thiết kế,

  • Bất cứ khi nào các hoạt động DELETE được thực hiện, nó đánh dấu bộ giá trị hiện có là DEAD thay vì xóa các bộ giá trị vật lý đó.
  • Tương tự, bất cứ khi nào thao tác UPDATE được thực hiện, nó sẽ đánh dấu bộ giá hiện có tương ứng là DEAD và chèn một bộ giá trị mới (tức là thao tác UPDATE = DELETE + INSERT).

Vì vậy, mỗi lệnh DELETE và UPDATE sẽ dẫn đến một bộ mã DEAD, sẽ không bao giờ được sử dụng (trừ khi có các transaction song song). Những bộ giá trị đã chết này sẽ dẫn đến việc sử dụng thêm dung lượng không cần thiết mặc dù số lượng bản ghi hiệu quả như nhau hoặc ít hơn. Đây còn được gọi là space bloating trong PostgreSQL. Vì PostgreSQL được sử dụng rộng rãi như một loại hệ thống cơ sở dữ liệu quan hệ OLTP, nơi thường xuyên thực hiện các thao tác INSERT, UPDATE và DELETE, nên sẽ có nhiều bộ giá DEAD và do đó dẫn đến hậu quả tương ứng. Vì vậy, PostgreSQL yêu cầu một cơ chế bảo trì mạnh mẽ để đối phó với các bộ giá DEAD này. VACUUM là quá trình bảo trì xử lý bộ tuple DEAD cùng với một số hoạt động khác hữu ích để tối ưu hóa hoạt động của VACUUM. 

VACUUM

Công việc chính của VACUUM là lấy lại không gian lưu trữ bị chiếm bởi các bộ giá trị DEAD. Không gian lưu trữ đã lấy lại không được trả lại cho hệ điều hành mà chúng chỉ được chống phân mảnh trong cùng một trang, vì vậy chúng chỉ có sẵn để sử dụng lại bằng cách chèn dữ liệu trong tương lai trong cùng một bảng. Trong khi thao tác VACUUM diễn ra trên một bảng cụ thể, đồng thời thao tác ĐỌC / VIẾT khác có thể được thực hiện trên cùng một bảng vì Exclusive Locks không được thực hiện trên bảng cụ thể. Trong trường hợp tên bảng không được chỉ định, VACUUM sẽ được thực hiện trên tất cả các bảng của cơ sở dữ liệu. Thao tác VACUUM thực hiện bên dưới một loạt thao tác trong ShareUpdateExclusive lock:

  • Quét tất cả các trang của tất cả các bảng (hoặc bảng được chỉ định) của cơ sở dữ liệu để lấy tất cả các bộ giá trị đã chết.
  • Đóng băng các tuples cũ nếu cần.
  • Loại bỏ bộ chỉ mục trỏ đến bộ giá trị DEAD tương ứng.
  • Xóa các bộ giá trị DEAD của một trang tương ứng với một bảng cụ thể và phân bổ lại các bộ giá trị trực tiếp trong trang.
  • Cập nhật lại Free space Map (FSM) và Visibility Map (VM).
  • Cắt bớt trang cuối cùng nếu có thể (nếu có bộ giá trị DEAD đã được giải phóng).
  • Cập nhật tất cả các bảng hệ thống tương ứng.

Như chúng ta có thể thấy từ các bước công việc trên của VACUUM, rõ ràng đây là một hoạt động rất tốn kém vì nó cần xử lý tất cả các trang của mối quan hệ. Vì vậy, rất cần thiết để bỏ qua các trang có thể không yêu cầu dọn dẹp. Vì Visibility Map (VM) cung cấp thông tin của trang mà nếu không có dung lượng trống, có thể giả định rằng không yêu cầu khoảng trống trang tương ứng và do đó trang này có thể được bỏ qua một cách an toàn.

Vì VACUUM dù sao cũng đi qua tất cả các trang và tất cả các bộ giá trị của chúng, nên nó sẽ có cơ hội thực hiện nhiệm vụ quan trọng khác là đóng băng các bộ giá trị.

Full VACUUM

Như đã thảo luận trong phần trước, mặc dù VACUUM loại bỏ tất cả các bộ giá trị DEAD và chống phân mảnh trang để sử dụng trong tương lai, nó không giúp giảm dung lượng lưu trữ tổng thể của bảng vì không gian thực sự không được giải phóng cho hệ điều hành. Giả sử một bảng tbl1 mà tổng bộ nhớ đã đạt đến 1,5GB và trong số 1GB này bị chiếm bởi bộ chết, thì sau khi VACUUM, khoảng 1GB khác sẽ có sẵn để chèn thêm bộ nhưng vẫn còn, tổng bộ nhớ sẽ vẫn là 1,5GB.

Full VACUUM giải quyết vấn đề này bằng cách thực sự giải phóng dung lượng và đưa nó trở lại hệ điều hành. Nhưng điều này phải trả giá đắt. Không giống như VACUUM, FULL VACUUM không cho phép hoạt động song song vì nó có một exclusive lock với mối quan hệ nhận được FULL VACUUM. Dưới đây là các bước:

  • Có exclusive lock về mối quan hệ.
  • Tạo một tệp lưu trữ trống song song.
  • Sao chép tất cả các bộ giá trị trực tiếp từ bộ nhớ hiện tại sang bộ nhớ mới được cấp phát.
  • Sau đó, giải phóng bộ nhớ gốc.
  • Giải phóng lock.

Vì vậy, cũng rõ ràng từ các bước, nó sẽ chỉ có bộ nhớ cần thiết cho dữ liệu còn lại.

Auto-VACUUM

Thay vì thực hiện VACUUM theo cách thủ công, PostgreSQL hỗ trợ một trigger tự động kích hoạt VACUUM theo định kỳ. Mỗi khi VACUUM thức dậy (theo mặc định là 1 phút), nó sẽ gọi ra nhiều hoạt động (tùy thuộc vào cấu hình các quy trình autovacuum_worker).

Worker của VACUUM tự động thực hiện đồng thời các quy trình VACUUM cho các bảng được chỉ định tương ứng. Vì VACUUM không có bất kỳ exclusive lock nào trên các bảng, nó không (hoặc tối thiểu) ảnh hưởng đến hoạt động của cơ sở dữ liệu khác.

Việc cấu hình Auto-VACUUM phải được thực hiện dựa trên kiểu sử dụng của cơ sở dữ liệu. Nó không nên quá thường xuyên (vì nó sẽ lãng phí worker bật lên vì có thể không có hoặc quá ít bộ chết) hoặc quá nhiều chậm trễ (nó sẽ gây ra nhiều bộ chết với nhau và do đó bàn bị phồng lên).

VACUUM hoặc Full VACUUM

Lý tưởng nhất là ứng dụng cơ sở dữ liệu nên được thiết kế theo cách không cần Full VACUUM. Như đã giải thích ở trên, FULL VACUUM tái tạo không gian lưu trữ và đưa dữ liệu trở lại, vì vậy nếu chỉ có ít bộ dữ liệu chết hơn, thì ngay lập tức không gian lưu trữ sẽ được tạo lại để khôi phục tất cả dữ liệu ban đầu. Cũng vì FULL VACUUM có exclusive lock trên bảng, nó sẽ chặn tất cả các hoạt động trên bảng tương ứng. Vì vậy, thực hiện FULL VACUUM đôi khi có thể làm chậm cơ sở dữ liệu tổng thể.

Tóm lại, nên tránh Full VACUUM  trừ khi biết rằng phần lớn dung lượng lưu trữ là do các bộ giá trị đã chết. Phần mở rộng PostgreSQL pg_freespacemap có thể được sử dụng để có gợi ý hợp lý về không gian trống.

Hãy xem một ví dụ về quy trình VACUUM được giải thích.

Đầu tiên, hãy tạo một bảng demo1:

postgres=# create table demo1(id int, id2 int);
 
CREATE TABLE

Và chèn một số dữ liệu vào đó:

postgres=# insert into demo1 values(generate_series(1,10000), generate_series(1,
 
10000));
 
INSERT 0 10000
 
postgres=# SELECT count(*) as npages, round(100 * avg(avail)/8192 ,2) as average_freespace_ratio FROM pg_freespace('demo1');
 
 npages | average_freespace_ratio
 
--------+-------------------------
 
  45 |                0.00
 
(1 row)

Bây giờ, hãy xóa dữ liệu:

postgres=# delete from demo1 where id%2=0;
 
DELETE 5000

Và chạy vacuum thủ công:

postgres=# vacuum demo1;
 
VACUUM
 
postgres=# SELECT count(*) as npages, round(100 * avg(avail)/8192 ,2) as average_freespace_ratio FROM pg_freespace('demo1');
 
 npages | average_freespace_ratio
 
--------+-------------------------
 
  45 |               45.07
 
(1 row)

Không gian tự do này hiện có sẵn để được sử dụng lại bởi PostgreSQL, nhưng nếu bạn muốn giải phóng không gian đó cho hệ điều hành, hãy chạy:

postgres=# vacuum full demo1;
 
VACUUM
 
postgres=# SELECT count(*) as npages, round(100 * avg(avail)/8192 ,2) as average_freespace_ratio FROM pg_freespace('demo1');
 
 npages | average_freespace_ratio
 
--------+-------------------------
 
  23 |                0.00
 
(1 row)

Phần kết luận

Và đây là một ví dụ ngắn gọn về cách thức hoạt động của quá trình VACUUM. May mắn thay, nhờ vào quy trình VACUUM tự động, hầu hết thời gian và trong môi trường PostgreSQL thông thường , bạn không cần phải suy nghĩ về điều này vì nó được quản lý bởi chính engine postgres.

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments