Thứ Năm, 17 tháng 11, 2022

Bài 11 - PostgreSQL multi-version concurrency control

Việc concurrent read/write data tưởng chừng đơn giản nhưng với programming thì không ez tí nào. Chém thế chứ cũng không quá phức tạp khi sử dụng các cơ chế sync/lock, tuy nhiên nó làm giảm performance.

Vậy có cách nào không cần lock mà vẫn concurrent read/write không?

Let’s begin.

1) Multi-version concurrency control

Với phần trước, ta biết rằng database sử dụng shared lock và explicit lock trong concurrent read/write để đảm bảo data integrity. Nhược điểm của nó là việc read/write không thể diễn ra cùng lúc vì có thể dẫn đến half-written.

Phần trước mình đã nói qua về half-written nhưng lần này sẽ giải thích kĩ hơn.

Chúng ta biết rằng database lưu trữ data trên disk (HDD, SSD). Có thể hiểu data là một chồng gạch to khổng lồ. Việc lưu trữ data giống như việc xếp gạch vào kho chứa. Bạn không thể chuyển cả 1000 viên gạch vào kho trong một lần, mà phải chuyển từ từ từng block một, mỗi block 10 - 20 viên tùy theo sức của mình.

Nếu đang chuyển gạch mà có người khác kiểm đếm gạch trong kho thì khả năng sẽ không chính xác. Chỉ đếm được một phần số gạch đã chuyển. Nó là ví dụ của half-written.

Công việc check lock, acquire lock, release lock mỗi khi read/write data tốn kha khá thời gian và công sức. Túm cái váy lại, thấy được 2 vấn đề:

  • Read data phải check và acquire shared lock. Nếu quá trình read chưa hoàn thành thì không thể write.
  • Write data cũng cần check và acquire explicit lock. Nếu write chưa xong thì không thể read.

Có vấn đề thì phải giải quyết. Vừa read/write lên một record khá vất vả. Nếu có 2 record A B giống y hệt nhau, record A để read, record B để write, chả phải là mọi chuyện được giải quyết êm đẹp. Thích read thì read ở A, muốn write thì write tại B. Cả read và write có thể diễn ra đồng thời mà không cần lock, chả ảnh hưởng gì đến hòa bình thế giới. Hợp lý :100:.

Đó là idea của multi-version concurrency control, cụ thể là PostgreSQL multi-version concurrency control. Nói vậy không có nghĩa là các loại database khác không có MVCC, mà cách tiếp cận của nó sẽ khác.

Mục đích chính của multi-version concurrency control để quản lý các truy cập read/write đồng thời đến database mà không cần locking. Reading không block writing và ngược lại. Có 2 phương pháp chính để tiếp cận MVVC:

  • Thứ nhất, duy trì multi-version of record trong table, tất nhiên chỉ dùng latest record thôi. Các record cũ sẽ được Garbage Collector thu dọn sau. Đây chính là cách mà PostgreSQL tạo ra và vẫn đang áp dụng. SQL Server cũng áp dụng hình thức tương tự, tuy nhiên có chút khác biệt là các record cũ được lưu tại tempdb - database tạm, khác với database chính.
  • Phương pháp thứ hai được Oracle và MySQL/InnoDB sử dụng, chỉ giữ duy nhất version mới nhất của record trong database. Các version cũ được lưu trữ tại rollback segments, hay còn gọi là undo log.

Cùng tìm hiểu cụ thể PostgreSQL apply multi-version concurrency control như thế nào. Các database khác thì… để dành cho Expert DBA.

2) MVCC với PostgreSQL

Trong PosgreSQL, khi một row được update data, một version mới (tuple) của row sẽ được thêm vào table. Version cũ được đánh dấu expire và có pointer trỏ sang version mới. Lưu ý là version cũ vẫn nằm ở database và chờ thu dọn sau. Idea là vậy, còn cụ thể PostgreSQL implement thế nào?

Để support multi-version, mỗi record có thêm 2 column là xmin và xmax. Khi query không explicit khai báo thì chúng không xuất hiện.

  • xmin: transaction id đã thực hiện insert/update record.
  • xmax: transaction id đã thực hiện delete record. Default khi init một record sẽ có giá trị = 0.

Tại sao lại là transaction id, có phải query nào cũng nằm trong transaction đâu nhỉ?

Mình đã giải thích ở phần trước, tất cả các DML đều được thực thi như một transaction dù có explicit begin transaction và commit hay không. Nếu để transaction mode là manual, bạn có thể thử update một record và query record đấy với một transaction khác. Hai giá trị nhận được không giống nhau.

Muốn biết giá trị của 2 column này, chỉ cần thêm vào SELECT query:

SELECT *, xmin, xmax FROM COUNTRY WHERE id = 1;

PostgreSQL lưu transaction id dưới dạng số nguyên integer 32-bit, tức là đâu đấy có khoảng 2^32 ~ hơn 4 tỉ giá trị. Nghe có vẻ cũng lớn, nhưng nếu số lượng transaction vượt quá con số đó thì chuyện gì xảy ra? Tạm để đây đã.

Transaction bao gồm các state: in-progresscommittedaborted và được quản lý trong table CLOG với file pg_clog. Nếu một transaction commit hoặc rollback, nó sẽ chuyển state tương ứng để chúng ta biết đâu là version mới nhất của record, hoặc rollback thì rollback về version nào.

Ok, lý thuyết đi đôi với thực hành, cùng xem MVCC hoạt động thế nào trong PostgreSQL nhé.

Mình sử dụng database cũ từ bài trước. Tạo 2 session connect đến database, select data trước ở bất kì session nào:

SELECT *, xmin, xmax FROM country WHERE id = 1;
IDCOUNTRY_NAMECREATEDXMINXMAX
1Afghanistan2021-07-19 03:08:46.67074981190

Bắt đầu với session 1, start transaction và update data:

BEGIN TRANSACTION; UPDATE country SET country_name = 'Updated record' WHERE id = 1; SELECT *, xmin, xmax FROM country WHERE id = 1;
IDCOUNTRY_NAMECREATEDXMINXMAX
1Updated record2021-07-19 03:08:46.67074981200

Sang session 2, thực hiện query data:

SELECT *, xmin, xmax FROM country WHERE id = 1;
IDCOUNTRY_NAMECREATEDXMINXMAX
1Afghanistan2021-07-19 03:08:46.67074981198120

Có thêm vài thông tin hay ho rồi.

Với session 2 thực hiện read data, nó vẫn read data cũ, column xmax được gán giá trị = transaction id của transaction đã update data.

Tiếp theo, COMMIT ở session 1 và select lại ở session 2:

SELECT *, xmin, xmax FROM country WHERE id = 1;
IDCOUNTRY_NAMECREATEDXMINXMAX
1Updated record2021-07-19 03:08:46.67074981200

Nếu thay vì COMMIT ở session 1, ta thực hiện ROLLBACK và select lại ở session 2 thì data sẽ thế nào? Nó sẽ giữ nguyên kết quả và xmax không bị reset về giá trị 0.

IDCOUNTRY_NAMECREATEDXMINXMAX
1Afghanistan2021-07-19 03:08:46.67074981198120

PostgreSQL cần giữ lại xmax vì nó đúng là những gì đã xảy ra. Ngoài xmin và xmax, cần dựa thêm vào transaction state để quyết định version nào là mới nhất.

Có một điều cần chú ý là vì sao session 1 update data mà session 2 khi query không thấy sự thay đổi đó? Nó liên quan đến tính isolation, một trong 4 tính chất của relational database: ACID. Việc không nhìn thấy sự thay đổi trên là default isolation level của PostgreSQL và một vài database khác. Tuy nhiên ta hoàn toàn có thể khiến session 2 query được sự thay đổi đó. Chủ đề này sẽ bàn kĩ hơn ở bài sau.

Quay lại vấn đề, mặc dù PostgreSQL multi-version concurrency control giúp giải quyết concurrent read/write nhưng cũng có nhược điểm đã thấy phía trên:

  • Nếu số lượng transaction vượt quá 2^32 thì chuyện gì xảy ra. PostgreSQL handle thế nào?
  • UPDATE/INSERT/DELETE nhiều dẫn tới dư thừa số lượng lớn các ddead tuple (old record version) thì xử lý ra sao?

Theo dõi bài sau để tìm câu trả lời nhé.

=============================
* KHOÁ HỌC ORACLE DATABASE A-Z ENTERPRISE trực tiếp từ tôi giúp bạn bước đầu trở thành những chuyên gia DBA, đủ kinh nghiệm đi thi chứng chỉ OA/OCP, đặc biệt là rất nhiều kinh nghiệm, bí kíp thực chiến trên các hệ thống Core tại VN chỉ sau 1 khoá học.
* CÁCH ĐĂNG KÝ: Gõ (.) hoặc để lại số điện thoại hoặc inbox https://m.me/tranvanbinh.vn hoặc Hotline/Zalo 090.29.12.888
* Chi tiết tham khảo:
https://bit.ly/oaz_w
=============================
KẾT NỐI VỚI CHUYÊN GIA TRẦN VĂN BÌNH:
📧 Mail: binhoracle@gmail.com
☎️ Mobile/Zalo: 0902912888
👨 Facebook: https://www.facebook.com/BinhOracleMaster
👨 Inbox Messenger: https://m.me/101036604657441 (profile)
👨 Fanpage: https://www.facebook.com/tranvanbinh.vn
👨 Inbox Fanpage: https://m.me/tranvanbinh.vn
👨👩 Group FB: https://www.facebook.com/groups/DBAVietNam
👨 Website: https://www.tranvanbinh.vn
👨 Blogger: https://tranvanbinhmaster.blogspot.com
🎬 Youtube: http://bit.ly/ytb_binhoraclemaster
👨 Tiktok: https://www.tiktok.com/@binhoraclemaster?lang=vi
👨 Linkin: https://www.linkedin.com/in/binhoracle
👨 Twitter: https://twitter.com/binhoracle
👨 Địa chỉ: Tòa nhà Sun Square - 21 Lê Đức Thọ - Phường Mỹ Đình 1 - Quận Nam Từ Liêm - TP.Hà Nội

=============================
oracle tutorial, học oracle database, Tự học Oracle, Tài liệu Oracle 12c tiếng Việt, Hướng dẫn sử dụng Oracle Database, Oracle SQL cơ bản, Oracle SQL là gì, Khóa học Oracle Hà Nội, Học chứng chỉ Oracle ở đầu, Khóa học Oracle online,sql tutorial, khóa học pl/sql tutorial, học dba, học dba ở việt nam, khóa học dba, khóa học dba sql, tài liệu học dba oracle, Khóa học Oracle online, học oracle sql, học oracle ở đâu tphcm, học oracle bắt đầu từ đâu, học oracle ở hà nội, oracle database tutorial, oracle database 12c, oracle database là gì, oracle database 11g, oracle download, oracle database 19c, oracle dba tutorial, oracle tunning, sql tunning , oracle 12c, oracle multitenant, Container Databases (CDB), Pluggable Databases (PDB), oracle cloud, oracle security, oracle fga, audit_trail,oracle RAC, oracle dataguard, oracle goldengate, mview, oracle exadata, oracle oca, oracle ocp, oracle ocm , oracle weblogic, postgresql tutorial, mysql tutorial, mariadb tutorial, sql server tutorial, nosql, mongodb tutorial, oci, cloud, middleware tutorial, hoc solaris tutorial, hoc linux tutorial, hoc aix tutorial, unix tutorial, securecrt, xshell, mobaxterm, putty

ĐỌC NHIỀU

Trần Văn Bình - Oracle Database Master