Điều khiển đồng thời trong cơ sở dữ liệu (Concurrency Control)
1. Tổng quan
A. Định nghĩa và sự cần thiết
Điều khiển đồng thời (Concurrency Control) là kỹ thuật kiểm soát sự can thiệp lẫn nhau phát sinh khi nhiều giao dịch truy cập đồng thời cùng một dữ liệu, nhằm bảo đảm tính nhất quán (Consistency) của dữ liệu và tính cô lập (Isolation) của giao dịch trong khi vẫn tận dụng tối đa lợi ích của tính đồng thời (Concurrency).
Lý do căn bản cần điều khiển đồng thời là tính đồng thời khiến hai giá trị 'hiệu năng' và 'tính đúng đắn' xung đột trực diện. Nếu cho phép nhiều người dùng·ứng dụng truy cập đồng thời vào một DBMS, thời gian rảnh của CPU·đĩa giảm, thông lượng (throughput) và khả năng phản hồi tăng mạnh. Nhưng nếu trộn lẫn các phép toán mà không có kiểm soát, các giao dịch xâm phạm kết quả trung gian của nhau và tính đúng đắn của dữ liệu bị sụp đổ. Tức là mục tiêu của điều khiển đồng thời là tìm điểm cân bằng "xử lý đồng thời nhiều giao dịch nhất có thể, nhưng bảo đảm kết quả giống như thực thi lần lượt từng cái một (tính khả tuần tự, Serializability)".
Ví dụ tiêu biểu là mất cập nhật ở tài khoản ngân hàng. Giả sử với tài khoản có số dư 1 triệu won, hai quầy giao dịch (giao dịch T1·T2) đồng thời thực hiện "đọc số dư (100) → rút 500 nghìn won → cập nhật thành 50". Nếu cả hai giao dịch đều đọc số dư gốc 100 rồi mỗi bên cập nhật thành 50, thì dù thực tế đã rút 1 triệu won, số dư cuối cùng lại là 500 nghìn won, và một lần rút tiền biến mất hoàn toàn. Như vậy, nếu bỏ mặc tính đồng thời thì sẽ dẫn ngay đến lỗi tiền bạc, nên điều khiển đồng thời là cơ chế bắt buộc bảo vệ an toàn cho hệ thống xử lý giao dịch.
B. Quan hệ với ACID của giao dịch
Điều khiển đồng thời là cơ chế hiện thực hóa đặc biệt tính cô lập (Isolation) trong bốn tính chất của giao dịch ACID (tính nguyên tử·tính nhất quán·tính cô lập·tính bền vững). Nếu tính nguyên tử được bảo đảm bằng log·phục hồi, tính bền vững bằng ghi xuống đĩa·log, thì tính cô lập do điều khiển đồng thời đảm nhận. Khi tính cô lập được giữ một cách lý tưởng, mỗi giao dịch hoạt động như thể độc chiếm hệ thống, và nhờ đó tính nhất quán được duy trì. Do đó, điều khiển đồng thời là trục cốt lõi của ACID, nâng đỡ tính nhất quán thông qua tính cô lập.
C. Các hiện tượng bất thường khi thiếu điều khiển đồng thời
Thực thi đồng thời không kiểm soát sinh ra bốn hiện tượng bất thường (anomaly) tiêu biểu. Phải hiểu các hiện tượng này mới thấy rõ vì sao cần điều khiển đồng thời và mức cô lập. Mất cập nhật, như đã thấy, là một cập nhật bị cập nhật khác ghi đè và biến mất; đọc bẩn là đọc giá trị chưa được commit, có thể bị hủy bất cứ lúc nào, rồi dựa vào đó đưa ra phán đoán sai. Đọc không lặp lại·đọc bóng ma là hiện tượng trong một giao dịch, đọc hai lần với cùng điều kiện nhưng giữa chừng giao dịch khác thay đổi giá trị hoặc dòng làm kết quả khác đi; còn rollback dây chuyền là vấn đề rollback của một giao dịch gây ra rollback dây chuyền của các giao dịch khác đã đọc giá trị trung gian đó, làm tăng lãng phí.
| Hiện tượng bất thường | Nội dung | Tình huống ví dụ |
|---|---|---|
| Mất cập nhật (Lost Update) | Cập nhật của một giao dịch bị giao dịch khác ghi đè và biến mất | Rút tiền đồng thời·trừ tồn kho |
| Đọc bẩn (Dirty Read) | Đọc dữ liệu chưa được commit (có thể bị hủy) | Phê duyệt dựa trên số dư chưa xác định |
| Đọc không lặp lại·bóng ma | Lặp lại cùng truy vấn nhưng giữa chừng giá trị·dòng thay đổi | Dữ liệu thay đổi trong khi tổng hợp |
| Rollback dây chuyền (Cascading Rollback) | Một rollback gây ra rollback dây chuyền của giao dịch khác | Tham chiếu giá trị chưa commit rồi bị hủy |
2. Cấu trúc tổng thể của các kỹ thuật điều khiển đồng thời
Điều khiển đồng thời có nhiều kỹ thuật với triết lý tiếp cận khác nhau. Nhìn tổng quát, chúng chia thành cách tiếp cận bi quan (pessimistic) ngăn xung đột từ trước (khóa·nhãn thời gian), cách tiếp cận lạc quan (optimistic) cứ tiến hành rồi kiểm tra sau, và cách tiếp cận đa phiên bản (MVCC) tách đọc và ghi bằng cách chia phiên bản. Sơ đồ cấu trúc dưới đây thể hiện cách phân loại này.
flowchart TB
C["Điều khiển đồng thời (Concurrency Control)"] --> P["Kỹ thuật bi quan (chặn trước)"]
C --> O["Kỹ thuật lạc quan (kiểm chứng sau)"]
C --> M["MVCC (đa phiên bản)"]
P --> L["Khóa (Locking·2PL)"]
P --> T["Sắp thứ tự theo nhãn thời gian (Timestamp Ordering)"]
O --> V["Dựa trên kiểm chứng (Validation·giai đoạn đọc-kiểm chứng-ghi)"]
M --> S["Đọc snapshot (đọc không chặn ghi)"]
style C fill:#e8f0fe,stroke:#2f6fed,stroke-width:2px
style P fill:#fce8e6,stroke:#c5221f
style O fill:#e6f4ea,stroke:#188038
style M fill:#fef7e0,stroke:#f9ab00
A. Khóa (Locking) và khóa hai pha (2PL)
Khóa là cách trực quan nhất, đặt khóa trước khi truy cập dữ liệu để kiểm soát truy cập của giao dịch khác. Phân biệt khóa chia sẻ (Shared Lock) dùng cho đọc, nhiều giao dịch có thể cùng đặt, và khóa độc quyền (Exclusive Lock) dùng cho ghi, chỉ một giao dịch được đặt. Các khóa chia sẻ tương thích với nhau, nhưng khóa độc quyền không tương thích với bất kỳ khóa nào, nên trong khi ghi các truy cập khác phải chờ.
Chỉ đặt và mở khóa đơn thuần không bảo đảm tính khả tuần tự, nên dùng giao thức khóa hai pha (Two-Phase Locking, 2PL). 2PL chia giao dịch thành 'pha mở rộng (growing phase)' chỉ lấy khóa và 'pha thu hẹp (shrinking phase)' chỉ nhả khóa, với quy tắc một khi đã bắt đầu nhả khóa thì không thể lấy khóa mới. Quy tắc này bảo đảm tính khả tuần tự. Tuy nhiên, nếu nhả khóa sớm trước khi commit thì có thể phát sinh rollback dây chuyền, nên trong thực tế chọn 2PL nghiêm ngặt (Strict 2PL) giữ mọi khóa cho đến thời điểm commit·rollback làm tiêu chuẩn.
Tác dụng phụ tiêu biểu của khóa là bế tắc (Deadlock). Đó là tình huống hai giao dịch chờ khóa mà bên kia đang giữ và dừng mãi mãi; chẳng hạn T1 khóa A và chờ B trong khi T2 khóa B và chờ A thì cả hai đều không tiến được. Ngoài ra còn có đánh đổi: tùy theo đặt phạm vi khóa ở dòng·trang·bảng (đơn vị khóa, granularity) mà tính đồng thời và chi phí khác nhau. Đơn vị khóa nhỏ thì tính đồng thời cao nhưng gánh nặng quản lý lớn.
Đánh đổi về đơn vị khóa là vấn đề thường gặp trong thực tế. Khóa mức dòng có tính đồng thời cao vì các giao dịch xử lý các dòng khác nhau không cản trở nhau, nhưng nếu batch cập nhật hàng trăm nghìn dòng giữ khóa cho mỗi dòng thì chi phí quản lý khóa trở nên lớn. Khi đó, nếu số khóa vượt ngưỡng, DBMS thực hiện leo thang khóa (lock escalation) nâng nhiều khóa dòng thành một khóa bảng, giảm chi phí quản lý nhưng làm giảm tính đồng thời. Vì vậy, khi batch cập nhật lớn và giao dịch online tranh chấp cùng một bảng, thiết kế chia nhỏ batch để commit hoặc tách khung giờ thực thi nhằm giảm xung đột khóa là rất quan trọng.
B. Sắp thứ tự theo nhãn thời gian (Timestamp Ordering)
Kỹ thuật nhãn thời gian gán cho mỗi giao dịch thời điểm bắt đầu (nhãn thời gian) và cưỡng chế việc truy cập dữ liệu luôn tuân theo thứ tự thời gian đó. Mỗi dữ liệu ghi lại thời điểm của giao dịch đọc cuối cùng (read-TS) và giao dịch ghi cuối cùng (write-TS), và nếu giao dịch muộn hơn cố đi ngược thứ tự đã được xử lý thì giao dịch đó bị rollback (abort) rồi khởi động lại.
Ưu điểm của cách này là không dùng khóa nên về nguyên tắc không phát sinh bế tắc. Vì các giao dịch không cần chờ nhau. Tuy nhiên, giao dịch vi phạm thứ tự bị hoàn tác ngay, nên trong môi trường tranh chấp cao có nhược điểm là rollback và khởi động lại thường xuyên gây lãng phí lớn. Ngoài ra, cần quản lý để giao dịch đã tiến hành dựa trên giá trị đã commit không bị hủy muộn (ngăn rollback dây chuyền).
C. Kiểm chứng lạc quan (Optimistic Validation)
Kỹ thuật lạc quan xuất phát từ giả định "xung đột hiếm khi xảy ra". Giao dịch được thực thi tự do trước (giai đoạn đọc), không phản ánh ngay vào DB thực mà làm việc trên bản sao cục bộ, rồi ngay trước khi commit kiểm tra có xung đột với giao dịch khác không (giai đoạn kiểm chứng), và chỉ khi không có xung đột mới thực sự phản ánh kết quả (giai đoạn ghi). Nếu phát hiện xung đột khi kiểm chứng thì rollback giao dịch đó.
Kỹ thuật lạc quan có ưu điểm cho tính đồng thời cao mà không có chi phí khóa trong môi trường chủ yếu đọc và tần suất xung đột thấp. Ngược lại, trong môi trường tranh chấp ghi thường xuyên, việc thực thi lại do kiểm chứng thất bại bùng nổ nên lại kém hiệu quả. 'Khóa lạc quan (optimistic lock) phát hiện xung đột bằng số phiên bản' thường dùng trong web·NoSQL·hệ thống phân tán là hiện thực thực tiễn của tư tưởng này.
Cụ thể, khóa lạc quan đặt cột phiên bản (ví dụ: version) cho mỗi dòng, và khi cập nhật thì gắn phiên bản đã đọc vào điều kiện dưới dạng UPDATE ... SET version=version+1 WHERE id=? AND version=?. Nếu giao dịch khác đã cập nhật trước làm phiên bản tăng lên, số dòng bị ảnh hưởng của UPDATE này sẽ là 0, phát hiện xung đột ngay lập tức, và ứng dụng đọc lại giá trị mới nhất để thử lại. @Version của JPA·Hibernate là hiện thực tiêu biểu, đặc biệt hữu ích trong những tình huống khó giữ khóa dài cho giao dịch như môi trường web nơi người dùng mở màn hình lâu rồi mới lưu. Đây là cách xử lý xung đột một cách logic ở tầng ứng dụng thay vì khóa vật lý của engine DB.
D. MVCC (điều khiển đồng thời đa phiên bản)
MVCC (Multi-Version Concurrency Control) là kỹ thuật khi sửa dữ liệu không ghi đè phiên bản cũ mà giữ lại và tạo phiên bản mới, để giao dịch đọc thấy snapshot nhất quán (phiên bản quá khứ) tại thời điểm nó bắt đầu. Kết quả là đọc không chặn ghi, và ghi không chặn đọc. Ngày nay phần lớn DBMS thương mại·mã nguồn mở như Oracle·PostgreSQL·MySQL InnoDB đều lấy MVCC làm nền tảng.
Giá trị cốt lõi của MVCC là tính đồng thời vượt trội trong workload chủ yếu đọc. Tuy nhiên, vì liên tục tích lũy phiên bản cũ nên phát sinh chi phí dọn dẹp chúng. Ví dụ, PostgreSQL cần tác vụ VACUUM thu hồi các tuple chết (dead tuple) không còn được tham chiếu, còn Oracle phải quản lý segment undo chứa ảnh cũ. Chi phí lưu trữ·dọn dẹp này là cái giá mà MVCC phải trả.
3. MVCC và mức cô lập giao dịch
Điều khiển đồng thời không hoạt động đơn lẻ mà được điều phối cùng với mức cô lập giao dịch (Isolation Level). Mức cô lập là chính sách quyết định "cho phép hiện tượng bất thường đến mức nào", và SQL chuẩn định nghĩa bốn mức. Sơ đồ tuần tự dưới đây cho thấy quá trình giao dịch đọc trong môi trường MVCC đọc snapshot mà không bị giao dịch ghi chặn.
sequenceDiagram
participant R as Giao dịch đọc T1
participant DB as DBMS (MVCC)
participant W as Giao dịch ghi T2
R->>DB: "SELECT số dư (yêu cầu snapshot lúc T1 bắt đầu)"
DB-->>R: "Trả về phiên bản v1 (số dư 100)"
W->>DB: "UPDATE số dư=150 (tạo phiên bản mới v2)"
DB-->>W: "Ghi v2 (giữ nguyên v1)"
R->>DB: "SELECT số dư, truy vấn lại"
DB-->>R: "Vẫn trả về v1 (100) — đọc nhất quán"
W->>DB: "COMMIT"
Note over R,DB: "T1 thấy snapshot của mình nên không bị ghi chặn"
Mức cô lập càng thấp thì tính đồng thời càng cao và cho phép nhiều hiện tượng bất thường hơn, càng cao thì tính đúng đắn càng mạnh nhưng tính đồng thời giảm. Read Uncommitted là mức lỏng nhất cho phép cả đọc bẩn, Read Committed là mức chỉ đọc giá trị đã commit để ngăn đọc bẩn, là mặc định của Oracle·PostgreSQL. Repeatable Read bảo đảm tính nhất quán của việc đọc lặp lại trong một giao dịch và là mặc định của MySQL InnoDB, còn Serializable cung cấp cô lập hoàn toàn như thể thực thi tuần tự nhưng tính đồng thời thấp nhất.
| Mức cô lập | Đọc bẩn | Đọc không lặp lại | Đọc bóng ma | Đặc điểm |
|---|---|---|---|---|
| Read Uncommitted | Cho phép | Cho phép | Cho phép | Lỏng nhất·đồng thời cao nhất |
| Read Committed | Ngăn | Cho phép | Cho phép | Mặc định thực tế (Oracle·PG) |
| Repeatable Read | Ngăn | Ngăn | Cho phép (tùy engine có thể ngăn) | Mặc định của InnoDB |
| Serializable | Ngăn | Ngăn | Ngăn | Đúng đắn mạnh nhất·đồng thời thấp nhất |
4. Quản lý bế tắc (Deadlock)
Trong điều khiển đồng thời dựa trên khóa, bế tắc là bài toán cốt lõi phải xử lý bằng chiến lược riêng. Theo chuẩn, tiếp cận theo bốn hướng phòng ngừa (Prevention)·tránh (Avoidance)·phát hiện (Detection)·phục hồi (Recovery). Phòng ngừa là cách định trước thứ tự yêu cầu tài nguyên (sắp thứ tự tài nguyên) hoặc buộc lấy mọi khóa cùng lúc để chặn tận gốc việc chờ vòng tròn, còn tránh là cách dùng nhãn thời gian như Wait-Die·Wound-Wait để quyết định cho phép chờ hay rollback, ngăn hình thành vòng.
Phát hiện là cách tìm chu trình trong đồ thị chờ (Wait-for Graph) biểu diễn quan hệ chờ giữa các giao dịch để phát hiện bế tắc, và nếu có chu trình thì chọn giao dịch có chi phí rollback nhỏ nhất làm nạn nhân (victim) để hoàn tác (phục hồi). DBMS thực tế kiểm tra đồ thị chờ định kỳ, hoặc đơn giản hơn là dùng kèm hết thời gian chờ khóa (lock timeout) tự động hủy giao dịch không lấy được khóa sau một khoảng thời gian. Chẳng hạn, nếu batch lớn và giao dịch online cập nhật cùng một bảng theo thứ tự ngược nhau và bế tắc lặp lại, giải quyết tận gốc bằng cách thống nhất thứ tự truy cập hoặc tách khung giờ batch.
5. Chuyên sâu: điều khiển đồng thời trong môi trường phân tán·đám mây
Nếu điều khiển đồng thời truyền thống lấy DBMS một nút làm tiền đề, thì các cơ sở dữ liệu phân tán·đám mây ngày nay phải xử lý tính đồng thời vượt qua ranh giới các nút, nên các kỹ thuật mới đã nổi lên. Google Spanner gán nhãn thời gian toàn cục trên nền đồng bộ đồng hồ gọi là TrueTime (bao gồm khoảng bất định), hiện thực tính khả tuần tự có tính nhất quán bên ngoài (external consistency) ngay cả giữa các nút phân tán về địa lý. Đây là ví dụ mở rộng tư tưởng sắp thứ tự theo nhãn thời gian lên quy mô toàn cầu.
Một xu hướng khác là SSI (Serializable Snapshot Isolation). Cô lập snapshot (dựa trên MVCC) có hiệu năng tốt nhưng cho phép một bất thường tinh vi gọi là 'lệch ghi (write skew)', và PostgreSQL cung cấp Serializable hoàn toàn với chi phí thấp bằng SSI — cô lập snapshot cộng thêm phát hiện xung đột. Trong giao dịch phân tán, bảo đảm commit nguyên tử bằng commit hai pha (2PC), đồng thời dùng các biến thể·giao thức đồng thuận (Paxos·Raft) để giảm vấn đề chặn khi bộ điều phối gặp sự cố. Nhận thức quan trọng từ góc nhìn Kỹ sư chuyên nghiệp (Professional Engineer) là trong môi trường phân tán, như lý thuyết CAP·PACELC chỉ ra, đánh đổi giữa tính nhất quán và tính sẵn sàng·độ trễ là không thể tránh khỏi, và vì vậy nhiều hệ thống chủ ý chọn mô hình nhất quán nới lỏng phù hợp với mục đích thay vì tính khả tuần tự mạnh.
6. Các điểm cần lưu ý và hàm ý
Bản chất là cân bằng giữa tính đồng thời và tính đúng đắn. Mục tiêu của điều khiển đồng thời không phải là cô lập mạnh vô điều kiện, mà là tối đa hóa tính đồng thời trong phạm vi đáp ứng mức đúng đắn mà nghiệp vụ yêu cầu. Nếu cần nhất quán mạnh như thanh toán·kế toán thì chọn Serializable, còn nếu hiệu năng quan trọng như tra cứu·thống kê thì chọn mức cô lập thấp như Read Committed để điều chỉnh đánh đổi.
Quản lý bế tắc chủ động ở cấp thiết kế·vận hành. Giải pháp căn bản là kết hợp phòng ngừa·tránh·phát hiện·phục hồi và timeout phù hợp tình huống, thống nhất thứ tự truy cập tài nguyên trong ứng dụng và giữ giao dịch ngắn để giảm thời gian giữ khóa. Bế tắc không chỉ được giảm bằng thuật toán mà bằng thiết kế giao dịch.
Hiểu vì sao MVCC trở thành dòng chủ đạo của DBMS hiện đại. Đó là vì trong workload chủ yếu đọc, đọc và ghi không chặn nhau nên tính đồng thời vượt trội, và cái giá phải trả là chi phí dọn dẹp phiên bản cũ (VACUUM·quản lý undo). Trong môi trường MVCC, giao dịch dài có thể gây tích lũy phiên bản cũ làm giảm hiệu năng, nên quản lý vòng đời giao dịch là quan trọng.
Áp dụng mức cô lập khác nhau theo workload. Ngay trong một hệ thống, chiến lược đặt mức cô lập khác nhau theo tính chất giao dịch — chỉ bảo vệ bằng cô lập cao số ít giao dịch mà tính đúng đắn mang tính quyết định, còn lại xử lý với cô lập thấp để bảo đảm thông lượng — là hiệu quả trong thực tế.
Tính đến việc mở rộng sang phân tán·đám mây. Không thể áp dụng nguyên vẹn kỹ thuật một nút cho môi trường phân tán, mà phải thiết kế có ý thức mô hình nhất quán mục tiêu có xét đến TrueTime·SSI·giao thức đồng thuận và đánh đổi CAP·PACELC. Nên định nghĩa đồng thời yêu cầu điều khiển đồng thời·tính nhất quán ngay ở giai đoạn lựa chọn kiến trúc.
Tài liệu tham khảo
- PostgreSQL Documentation — Transaction Isolation / Concurrency Control: https://www.postgresql.org/docs/current/mvcc.html
- Oracle Database Concepts — Data Concurrency and Consistency: https://docs.oracle.com/en/database/oracle/oracle-database/
- Tổng quan các mức cô lập chuẩn ANSI/ISO SQL (Read Committed·Repeatable Read·Serializable): https://en.wikipedia.org/wiki/Isolation_(database_systems)
Tóm tắt một câu: Điều khiển đồng thời là kỹ thuật ngăn các hiện tượng bất thường (mất cập nhật·đọc bẩn, v.v.) do sự can thiệp của các giao dịch đồng thời để bảo đảm tính cô lập·nhất quán, cân bằng tính đồng thời và tính đúng đắn bằng khóa (2PL)·nhãn thời gian·kiểm chứng lạc quan·MVCC, đồng thời phải thiết kế cùng quản lý bế tắc, điều chỉnh mức cô lập và cả mô hình nhất quán của môi trường phân tán.