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 thao tác DELETE được thực hiện, nó sẽ đánh dấu bộ giá trị hiện có là ĐÃ CHẾT thay vì xóa các bộ giá trị vật lý đó.
- Tương tự, bất cứ khi nào thao tác CẬP NHẬT được thực hiện, nó sẽ đánh dấu bộ giá hiện có tương ứng là ĐÃ CHẾT và chèn một bộ mới (tức là thao tác CẬP NHẬT =XÓA + CHÈN).
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 giao dịch 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. Điều này còn được gọi là không gian đầy hơi 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 đó gây ra các 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á trị DEAD này. VACUUM là quy 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. Hãy hiểu một số thuật ngữ sẽ được sử dụng sau này trong blog này.
Bản đồ Hiển thị
Như tên của nó, nó duy trì thông tin hiển thị về các trang chỉ chứa các bộ giá trị được biết là hiển thị cho tất cả các giao dịch đang hoạt động. Đối với mỗi trang, một bit được sử dụng. Nếu bit được đặt thành 1 có nghĩa là tất cả các bộ giá trị của trang tương ứng đều hiển thị. Bit được đặt thành 0 có nghĩa là không có dung lượng trống trên trang nhất định và các bộ giá trị có thể được hiển thị cho tất cả các giao dịch.
Bản đồ hiển thị được duy trì cho mỗi quan hệ (bảng và chỉ mục) và được liên kết cùng với các quan hệ chính, tức là nếu tên nút của tệp quan hệ là 12345, thì tệp hiển thị được lưu trữ trong tệp song song 12345_vm.
Bản đồ Không gian Miễn phí
Nó duy trì thông tin không gian trống chứa các chi tiết về không gian khả dụng trong mối quan hệ. Điều này cũng được lưu trữ trong tệp song song với tệp chính của quan hệ, tức là nếu tên nút của tệp quan hệ là 12345, thì tệp bản đồ dung lượng trống sẽ được lưu trữ trong tệp song song 12345_fsm.
Đóng băng Tuple
PostgreSQL sử dụng 4 byte để lưu trữ id giao dịch, có nghĩa là có thể tạo tối đa 2 tỷ giao dịch trước khi hoàn tất. Bây giờ hãy xem xét vẫn tại thời điểm này một số tuple chứa id giao dịch ban đầu nói là 100, sau đó đối với giao dịch mới (sử dụng giao dịch được bao bọc xung quanh) nói 5, id giao dịch 100 sẽ xem xét trong tương lai và nó sẽ không thể thấy dữ liệu được thêm vào / được sửa đổi bởi nó mặc dù nó thực sự là trong quá khứ. Để tránh giao dịch đặc biệt này, id FrozenTransactionId (bằng 2) được chỉ định. Id giao dịch đặc biệt này luôn được coi là trong quá khứ và sẽ hiển thị cho tất cả các giao dịch.
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ì khóa riêng 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 khóa ShareUpdateExclusive:
- Quét tất cả các trang của tất cả các bảng (hoặc bảng được chỉ định) trong cơ sở dữ liệu để lấy tất cả các bộ giá trị đã chết.
- Cố định các bộ lưu trữ cũ nếu cần.
- Xóa bộ chỉ mục trỏ đến bộ GỬI tương ứng.
- Xóa các bộ giá trị ĐÃ CHẾT 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 Bản đồ không gian trống (FSM) và Bản đồ hiển thị (VM).
- Cắt bớt trang cuối nếu có thể (nếu có các bộ giá ĐÃ CHẾT đượ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 trên của công việc đối với VACUUM, rõ ràng đây là một hoạt động rất tốn kém vì nó cần phải xử lý tất cả các trang của mối quan hệ. Vì vậy, rất cần thiết phải bỏ qua các trang có thể không yêu cầu hút bụi. Vì Bản đồ hiển thị (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 khoảng trống trang tương ứng là không cần thiết và do đó, trang này có thể được bỏ qua một cách an toàn.
Vì VACUUM vẫn đ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ị.
VACUUM đầy đủ
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 ích gì trong việc 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 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 khóa độc quyền đối với mối quan hệ nhận được FULL VACUUM. Dưới đây là các bước:
- Khóa riêng 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ộ dữ liệu trực tiếp từ bộ nhớ hiện tại sang bộ nhớ mới được cấp phát.
- Sau đó, hãy giải phóng bộ nhớ ban đầu.
- Giải phóng khóa.
Vì vậy, cũng rõ ràng từ các bước, nó sẽ chỉ yêu cầu bộ nhớ cho dữ liệu còn lại.
CHÂN KHÔNG TỰ ĐỘNG
Thay vì thực hiện VACUUM theo cách thủ công, PostgreSQL hỗ trợ một con quỷ 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).
Nhân viên hút bụi 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ỳ khóa độc quyền nào trên các bảng, nên 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. Không nên quá thường xuyên (vì nó sẽ lãng phí việc đánh thức của nhân viên vì có thể không có hoặc quá ít bộ chết) hoặc quá chậm (nó sẽ gây ra nhiều bộ chết với nhau và do đó làm phồng bảng).
VACUUM hoặc Full VACUUM
Tốt nhất, ứng dụng cơ sở dữ liệu nên được thiết kế theo cách không cần ĐẦY ĐỦ 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 bị 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ó khóa độc quyền trên bảng, nên nó chặn tất cả các thao tác trên bảng tương ứng. Vì vậy, việc 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 đầy đủ 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ề dung lượng trống.
Hãy xem ví dụ về quy trình VACUUM được giải thích.
Trước tiên, chúng ta hãy tạo bản trình diễn bảng 1:
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 máy hút bằng tay:
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)
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 quy trình VACUUM. May mắn thay, nhờ vào quy trình hút bụi 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 công cụ.