Một trong những điểm hấp dẫn nhất của hệ quản trị cơ sở dữ liệu Postgres đó là cách mà nó thực hiện điều khiển tương tranh giữa các transaction, nghĩa là read sẽ không bao giờ chặn write và ngược lại. Nói một cách dễ hình dung hơn, nếu hai transaction thực thi cùng một lúc thì nguyên tắc thực thi là thực thi độc lập, Postgres thực hiện được điều này là nhờ một cơ chế gọi là Multi Version Concurrency Control (MVCC). Kĩ thuật này không phải chỉ riêng Postgres mới có, mà đã có nhiều hệ quản trị cơ sở dữ liệu khác cũng đang thực hiện các cách tương tự với MVCC để điều khiển tương tranh giữa các transaction bao gồm: Oracle, Berkeley DB, CouchDB và các hệ quản trị cơ sở dữ liệu khác nữa. Việc hiểu cách MVCC được triển khai như thế nào trong PostgreSQL rất quan trọng, vì điều này giúp các nhà phát triển phần mềm có thể thiết kế các ứng dụng có tính đồng thời cao mà có sử dụng đến PostgreSQL, hoặc phần nào giúp giải quyết được rất nhiều vấn đề khó có thể gặp phải trong tương lai.
MVCC hoạt động như thế nào?
Mỗi một transaction trong PostgreSQL đều có một transaction id - id này là một số nguyên 32-bit, gọi là XID. Các transaction này bao gồm các câu lệnh đơn như INSERT, UPDATE hoặc DELETE, và một nhóm các câu lệnh tạo thành một khối lệnh được đặt bên trong hai từ khóa BEGIN - COMMIT. Khi bắt đầu một transaction, Postgres tạo ra một XID và gán nó cho transaction hiện tại. Ta có thể nhìn thấy được XID của transaction hiện tại bằng cách gọi hàm txid_current() có sẵn trong PostgreSQL.
SELECT CAST(txid_current() AS TEXT);Postgres lưu tất cả thông tin của một transaction vào bên trong
table data của hệ thống, những thông tin này sẽ được Postgres dùng để xác định một bản ghi sẽ có trạng thái ẩn hay hiện đối với một transaction.
Một điều thú vị nữa trong cơ chế MVCC này của Postgres đó là ngoài các column đã được khai báo trong quá trình tạo bảng thì mỗi bản ghi của một bảng sẽ có thêm hai cột bổ sung:
xmin- xác địnhXIDcủa transaction đãinsertbản ghi nàyxmax- xác địnhXIDcủa transaction đãdeletebản ghi này
Nếu ta không truy vấn tới hai cột này, mặc định chúng sẽ bị ẩn đi. Ta có thể truy vấn giá trị của hai cột này như các cột bình thường khác có trong một bảng.
Ví dụ ta có câu truy vấn DDL sau:
CREATE TABLE numbers (value int);Để lấy được hai giá trị này ta chỉ cần truy vấn:
SELECT *, xmin, xmax FROM numbers;
Quá trình thực hiện chèn một bản ghi
Để hiểu INSERT hoạt động như thế nào trong MVCC, cùng xem xét sơ đồ sau:
- Alice và Bob khởi tạo transaction mới, ta có thể thấy
XIDcủa hai transaction này bằng cách gọi hàmtxid_current()trong PostgreSQL. - Khi Alice chèn một bản ghi mới vào bảng
post, giá trị cộtxminsẽ được gán bằngXIDcủa transaction Alice. - Mặc định PostgreSQL sử dụng mức độ cô lập
READ COMMITTED, vì vậy Bob sẽ không thể nhìn thấy được bản ghi mà Alice đã chèn thêm vào bảngpostcho đến khi Alice thực hiệncommittransaction. - Sau khi Alice đã thực hiện
commit, bây giờ Bob mới có thế nhìn thấy được bản ghi mới mà Alice đã thêm.
Bất kì transaction nào có
XID>xmincủa bản ghi đã được chèn vào, thì đều có thể thấy được bản ghi đó.Nếu transaction bất kì có
XID<xmincủa một bản ghi đã được thêm, thì Postgres sẽ dựa vào mức độ cô lập transaction để quyết định một bản ghi sẽ bị ẩn hay hiện đối với transaction đó. Với mức độREAD COMMITED(mặc định), transaction hiện tại sẽ không thể thấy được sự thay đổi của một bản ghi nếu transaction chèn bản ghi chưa đượccommit. Với mức độREPEATABLE READhoặcSERIALIZABLE, thì transaction hiện tại có thể thấy được sự thay đổi của bản ghi mặc dù transaction chèn dữ liệu chưa đượccommit.
Quá trình thực hiện xóa một bản ghi
Để hiểu cách DELETE hoạt động trong MVCC, cùng xem xét sơ đồ sau:
- Alice và Bob khởi tạo transaction mới, ta có thể thấy
XIDcủa hai transaction này bằng cách gọi hàmtxid_current()trong PostgreSQL. - Khi Bob thực hiện xóa một bản ghi của bảng
post, giá trị của cộtxmaxđược gán bằng với giá trịXIDcủa transaction Bob. - Mặc định PostgreSQL sử dụng mức độ cô lập
READ COMMITTED, vì vậy cho đến khi Bob vẫn chưa thực hiệncommittransaction, thì Alice vẫn sẽ nhìn thấy bản ghi sẽ bị Bob xóa khỏi bảngpost. - Sau khi Bob thực hiện
committransaction thì Alice không còn nhìn thấy bản ghi đã bị Bob xóa khỏi bảngpostnữa.
Thao tác DELETE dữ liệu không trực tiếp xóa ngay lập tức bản ghi khỏi bộ nhớ vật lý, nó chỉ đánh dấu một bản ghi đã sẵn sàng để xóa. Tiến trình VACUUM sẽ thực hiện thu hồi bộ nhớ đã cấp phát cho bản ghi này khi nào nó không còn được sử dụng bởi bất kì transaction nào đang chạy.
Nếu một transaction có
XID>xmaxcủa một bản ghi đã đượccommit, thì transaction này không thể đọc bản ghi này nữa.Nếu một transaction có
XID<xmax, thì PostgreSQL sẽ dựa vào mức độ cô lập để quyết định xem bản ghi bị ẩn hay có thể đọc đối với transaction này. Với mức độREAD COMMITED(mặc định), transaction hiện tại vẫn có thể thấy được bản ghi, nếu thao tácDELETEchưa đượccommit. Với mức độREPEATABLE READhoặcSERIALIZABLE, thì transaction hiện tại không thể thấy được bản ghi mặc dù transaction xóa dữ liệu chưa đượccommit.
Quá trình thực hiện cập nhật một bản ghi
Để hiểu cách UPDATE hoạt động trong MVCC, cùng xem xét sơ đồ sau:
- Alice và Bob khởi tạo transaction mới, ta có thể thấy
XIDcủa hai transaction này bằng cách gọi hàmtxid_current()trong PostgreSQL. Khi Bob thực hiện thao tác
UPDATEmột bản ghi của bảngpost, ta có thể thấy có hai thao tác xảy ra: mộtDELETEvà mộtINSERT.Bản ghi trước đó bị đánh dấu sẵn sàng xóa bằng việc gán giá trị
xmax=XIDcủa transaction Bob, và một bản ghi mới được tạo ra được gán giá trịxmin=XIDcủa transaction Bob.- Mặc định PostgreSQL sử dụng mức độ cô lập transaction là
READ COMMITTED, vì vậy cho đến khi Bob vẫn chưa thực hiệncommittransaction, thì Alice vẫn sẽ nhìn thấy bản ghi trước đó mà chưa bị Bob cập nhật. - Sau khi Bob thực hiện
committransaction, thì Alice đã có thể thấy được bản ghi mới mà Bob cập nhật.



Không có nhận xét nào:
Đăng nhận xét