PostgreSQL MVCC là gì: Cách cơ chế đồng thời hoá hoạt động

PostgreSQL MVCC là gì: Cơ chế đồng thời hóa trong CSDL

PostgreSQL MVCC (Multi-Version Concurrency Control) là cơ chế cho phép nhiều giao dịch cùng đọc và ghi một bảng mà không phải khóa chặn lẫn nhau. Nhờ đó, PostgreSQL giữ được mức thông lượng cao khi có hàng trăm kết nối đồng thời, nhưng đổi lại mỗi bản ghi bị cập nhật sẽ để lại một phiên bản cũ cho tới khi được dọn dẹp.

MVCC giải quyết vấn đề gì

Ở mô hình khóa truyền thống, một giao dịch ghi sẽ khóa bản ghi và mọi giao dịch khác phải chờ. Khi nhiều phiên cùng ghi vào cùng một bảng hệ thống sẽ nghẽn cổ chai. MVCC đi theo hướng ngược lại: thay vì khóa, hệ thống tạo ra nhiều phiên bản của cùng một dòng dữ liệu và để mỗi giao dịch nhìn thấy một phiên bản khác nhau tùy theo thời điểm bắt đầu.

Trong PostgreSQL, mỗi phiên bản (tuple version) lưu hai số quan trọng:

  • xmin: ID của giao dịch tạo ra phiên bản này.
  • xmax: ID của giao dịch đã xóa hoặc cập nhật phiên bản, bằng 0 nếu bản ghi còn hiệu lực.

Một giao dịch chỉ thấy dòng dữ liệu khi xmin nhỏ hơn hoặc bằng ID của nó và xmax bằng 0 hoặc lớn hơn ID của nó. Nhờ quy tắc này, mỗi giao dịch luôn thấy một ảnh chụp nhất quán của cơ sở dữ liệu mà không cần khóa.

Sơ đồ MVCC của PostgreSQL với các phiên bản bản ghi và ID giao dịch xmin xmax

Các mức cô lập giao dịch liên quan đến MVCC

MVCC là nền tảng để PostgreSQL hiện thực hóa bốn mức cô lập chuẩn của SQL. Trong thực tế, ứng dụng dùng READ COMMITTED cho phần lớn truy vấn vì nó cân bằng giữa tính nhất quán và hiệu năng.

Mức cô lập Hiện tượng có thể xảy ra Khi nào nên dùng
READ COMMITTED Đọc cùng một hàng hai lần trong một giao dịch có thể thấy hai giá trị khác nhau Mặc định cho web, API, báo cáo
REPEATABLE READ Ảnh chụp cố định trong suốt giao dịch, có thể gặp lỗi serialization Báo cáo tài chính, xử lý đối soát
SERIALIZABLE Chặt chẽ nhất, phát hiện xung đột bằng SSI và phải thử lại giao dịch Chuyển tiền, chống ghi quá số dư
READ UNCOMMITTED Trong PostgreSQL bị chuyển thành READ COMMITTED Không nên dùng

Hệ quả: bản ghi chết và chỉ số phình to

Mỗi lần UPDATE tạo ra một tuple mới thay vì ghi đè. Consequence trực tiếp là cơ sở dữ liệu phình ra và hiệu năng truy vấn giảm dần, đặc biệt khi bảng chỉ nằm trong bộ nhớ đệm mà không đủ cache. Các phiên bản cũ đã bị đánh dấu xóa hoặc không còn nhìn thấy được gọi là dead tuple.

Tiến trình autovacuum của PostgreSQL tự động dọn dead tuple và cập nhật chỉ số. Tuy nhiên trên hệ thống tải nặng, autovacuum có thể không kịp. Kiểm tra bằng câu lệnh:

Ảnh chụp màn hình phiên làm việc PostgreSQL psql hiển thị kết quả truy vấn

SELECT relname, n_live_tup, n_dead_tup, round(100.0 * n_dead_tup / NULLIF(n_live_tup + n_dead_tup, 0), 2) AS dead_pct FROM pg_stat_user_tables ORDER BY n_dead_tup DESC LIMIT 10;

Nếu tỷ lệ dead vượt 20% thì nên cân nhắc giảm autovacuum_vacuum_scale_factor để vacuum chạy thường xuyên hơn, hoặc lên lịch VACUUM thủ công vào giờ thấp điểm.

Sơ đồ cấu trúc chỉ mục GIN của PostgreSQL dùng cho tìm kiếm toàn văn và JSONB

MVCC tương tác với chỉ số như thế nào

Mọi loại chỉ số trong PostgreSQL đều hoạt động trên nền MVCC, chỉ khác ở cách xử lý phiên bản cũ:

  • B-tree: giữ các mục trỏ tới từng phiên bản tuple, xóa mục khi vacuum dọn. Đây là lý do index bloat thường đi kèm bảng phình.
  • Hash: tương tự B-tree nhưng chỉ hỗ trợ so khớp bằng, không hỗ trợ phạm vi.
  • GIN: hỗ trợ toán tử mảng, JSONB, tsvector, phù hợp tìm kiếm toàn văn và truy vấn theo khóa.
  • GiST: dùng cây tìm kiếm tổng quát cho hộp giới, không gian, chuỗi ký tự.
  • BRIN: rất nhỏ vì chỉ lưu thống kê theo khối trang, hợp dữ liệu ghi tuần tự như log, timestamp.

Khi bảng bị REINDEX thường xuyên vì bloat, cần kiểm tra lại tỷ lệ dead tuple trước đó. REINDEX CONCURRENTLY không khóa bảng ghi nhưng chậm hơn và tốn nhiều I/O hơn.

Cấu hình và vận hành thực tế

Ba tham số nên để ý khi vận hành cơ sở dữ liệu dùng nhiều MVCC:

  • default_transaction_isolation: đặt mặc định ứng dụng nếu muốn thay đổi hành vi toàn cục.
  • autovacuum_vacuum_cost_delay: hạ xuống khi bảng bị cập nhật liên tục để vacuum theo kịp.
  • autovacuum_max_workers: tăng trên server có nhiều bảng nóng, giá trị mặc định 3 thường không đủ.

Ngoài ra, hãy giữ giao dịch ngắn gọn. Giao dịch kéo dài giữ snapshot lâu, chặn vacuum dọn các phiên bản cũ và làm bảng phình nhanh hơn. Với tác vụ nhập dữ liệu lớn, cách an toàn là chia thành nhiều lô nhỏ, mỗi lô một giao dịch.

Chi tiết chính thức về MVCC và các mức cô lập nằm trong tài liệu chính thức của PostgreSQL, phần MVCC và Isolation. Bạn cũng có thể xem bảng tham số autovacuum tại cấu hình autovacuum.

Hiểu MVCC giúp bạn tránh phần lớn lỗi hiệu năng âm thầm trên PostgreSQL: bảng phình to, truy vấn chậm dần theo thời gian, hay khoá chết giữa hai ứng dụng cùng ghi vào cùng một bảng.

Tôi là một lập trình viên IOS. Code chính là IOS nhưng thỉnnh thoảng vẫn đá sang Android hoặc web. Mặc dù không quá thông thạo nhưng tôi sẽ chia sẻ những kiến thức mà mình đã tìm hiểu, áp dụng qua.

Bài viết liên quan

Sơ đồ chuỗi luồng HTTP/3 qua QUIC với nhiều stream độc lập chạy song song

QUIC là gì: Giao thức thế hệ mới nhanh hơn TCP cho HTTP/3

QUIC (Quick UDP Internet Connections) là giao thức truyền tải thế hệ mới do Google phát triển, chạy trên lớp UDP thay vì TCP. Nhờ tích hợp bảo mật và…

Xem thêm

CRDT là gì: Cấu trúc dữ liệu cho cộng tác offline và đồng thời thực

CRDT (Conflict-free Replicated Data Type) là một kiểu cấu trúc dữ liệu được nhân bản trên nhiều máy trong cùng một mạng, cho phép mỗi bản sao cập nhật độc…

Xem thêm

SQLite là gì: Cơ sở dữ liệu nhúng và chế độ WAL hiệu quả

SQLite là một hệ quản trị cơ sở dữ liệu quan hệ (RDBMS) nhẹ, không cần máy chủ riêng biệt, được nhúng trực tiếp vào ứng dụng. Khác với MySQL…

Xem thêm
0 0 đánh giá
Article Rating
Theo dõi
Thông báo của
guest
0 Comments
Cũ nhất
Mới nhất Được bỏ phiếu nhiều nhất