Các Thuộc tính ACID trong Cơ sở dữ liệu
ACID là gì?
ACID là tập hợp bốn thuộc tính đảm bảo các giao dịch cơ sở dữ liệu (Database Transactions) được xử lý đáng tin cậy. Các thuộc tính này đảm bảo tính toàn vẹn dữ liệu (Data Integrity) ngay cả khi xảy ra lỗi hệ thống, sập hoặc truy cập đồng thời.

Tính Nguyên tử (Atomicity)
Các thao tác ghi (Writes) trong một giao dịch (Transaction) được thực thi tất cả cùng lúc và không thể chia thành các phần nhỏ hơn. Nếu có lỗi khi thực thi giao dịch, các thao tác ghi sẽ được hoàn tác (Rolled Back).
Vì vậy, Atomicity có nghĩa là "tất cả hoặc không có gì" (All or Nothing).
Cách hoạt động
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- Nếu thất bại ở đây, UPDATE đầu tiên sẽ được hoàn tác
COMMIT;
- Cơ sở dữ liệu sử dụng Nhật ký giao dịch (Write-Ahead Log/WAL) để theo dõi các thay đổi
- Nếu giao dịch thất bại hoặc bị hoàn tác, cơ sở dữ liệu sẽ hoàn nguyên tất cả các thay đổi một phần (Partial Changes)
- Client không bao giờ thấy một giao dịch hoàn thành một phần (Partially Completed Transaction)
Tính Nhất quán (Consistency)
Khác với "tính nhất quán" trong Định lý CAP (nghĩa là mọi lần đọc đều nhận được lần ghi gần nhất hoặc một lỗi), ở đây Consistency có nghĩa là bảo toàn các bất biến cơ sở dữ liệu (Database Invariants). Bất kỳ dữ liệu nào được ghi bởi giao dịch phải hợp lệ theo tất cả các quy tắc đã định nghĩa và duy trì cơ sở dữ liệu ở trạng thái tốt.
Cách hoạt động
- Ràng buộc (Constraints) —
NOT NULL,UNIQUE,CHECK,FOREIGN KEYthực thi các quy tắc - Trigger — logic xác thực tùy chỉnh trước/sau khi ghi
- Kiểm tra ở mức ứng dụng (Application-level Checks) — quy tắc nghiệp vụ được xác thực trong giao dịch
-- Ràng buộc CHECK đảm bảo số dư không bao giờ âm
CREATE TABLE accounts (
id INT PRIMARY KEY,
balance DECIMAL(10,2) CHECK (balance >= 0)
);
Nếu một giao dịch vi phạm bất kỳ ràng buộc nào, nó sẽ bị từ chối (Rejected) và hoàn tác.
Atomicity đảm bảo rằng một giao dịch là tất cả hoặc không có gì (All or Nothing). Consistency đảm bảo rằng phần "tất cả" chỉ xảy ra nếu kết quả là hợp lệ. Chúng hoạt động cùng nhau: Atomicity ngăn chặn các trạng thái một phần (Partial States), Consistency ngăn chặn các trạng thái không hợp lệ (Invalid States).
Tính Cô lập (Isolation)
Khi có nhiều thao tác ghi đồng thời (Concurrent Writes) từ hai giao dịch khác nhau, hai giao dịch đó được cô lập (Isolated) với nhau. Mức cô lập nghiêm ngặt nhất là Tính tuần tự hóa (Serializability), trong đó mỗi giao dịch hoạt động như thể nó là giao dịch duy nhất đang chạy trong cơ sở dữ liệu. Tuy nhiên, điều này khó triển khai trong thực tế, nên chúng ta thường áp dụng các mức cô lập (Isolation Level) lỏng hơn.
Các mức Cô lập (từ yếu nhất đến mạnh nhất)
| Mức | Đọc bẩn (Dirty Read) | Đọc không lặp lại (Non-Repeatable Read) | Đọc ma (Phantom Read) | Hiệu năng |
|---|---|---|---|---|
| Read Uncommitted | Có | Có | Có | Nhanh nhất |
| Read Committed | Không | Có | Có | Nhanh |
| Repeatable Read | Không | Không | Có | Trung bình |
| Serializable | Không | Không | Không | Chậm nhất |
Các vấn đề đồng thời phổ biến (Concurrency Problems)
- Đọc bẩn (Dirty Read) — đọc dữ liệu chưa được commit từ giao dịch khác có thể bị hoàn tác
- Đọc không lặp lại (Non-Repeatable Read) — đọc cùng một dòng hai lần và nhận được giá trị khác vì giao dịch khác đã sửa đổi nó ở giữa
- Đọc ma (Phantom Read) — thực hiện lại cùng một truy vấn và nhận được tập hợp dòng khác vì giao dịch khác đã chèn/xóa các dòng phù hợp
Cách cơ sở dữ liệu triển khai Isolation
- Khóa (Locking) — khóa ở mức dòng (Row-level), mức trang (Page-level) hoặc mức bảng (Table-level) để ngăn chặn sửa đổi đồng thời
- MVCC (Multi-Version Concurrency Control - Điều khiển Đồng thời Đa phiên bản) — mỗi giao dịch thấy một ảnh chụp (Snapshot) của cơ sở dữ liệu tại một thời điểm (được PostgreSQL, MySQL InnoDB sử dụng)
- Serializable Isolation — có thể sử dụng Khóa hai giai đoạn (Two-Phase Locking/2PL) hoặc Isolation Ảnh chụp tuần tự hóa (Serializable Snapshot Isolation/SSI)
Tính Bền vững (Durability)
Dữ liệu được lưu trữ vĩnh viễn (Persisted) sau khi giao dịch được commit ngay cả khi xảy ra lỗi hệ thống. Trong một hệ thống phân tán (Distributed System), điều này có nghĩa là dữ liệu được sao chép (Replicated) đến các Node khác.
Cách hoạt động
- Write-Ahead Log (WAL - Nhật ký ghi trước) — các thay đổi được ghi vào nhật ký chỉ thêm (Append-only Log) trên đĩa trước khi áp dụng vào các tệp dữ liệu chính
- Ghi xuống đĩa (Flush to Disk) — cơ sở dữ liệu gọi
fsync()để đảm bảo dữ liệu được ghi vật lý, không chỉ nằm trong bộ đệm trang của HĐH (OS Page Cache) - Replication (Sao chép) — trong hệ thống phân tán, giao dịch không được coi là đã commit cho đến khi đủ số Replica xác nhận ghi
Transaction commits:
1. Write changes to WAL → fsync to disk
2. Apply changes to data pages (can be lazy)
3. Return success to client
If crash happens after step 1:
→ On recovery, replay WAL to restore committed transactions
ACID so với BASE
Không phải tất cả cơ sở dữ liệu đều cung cấp đảm bảo ACID đầy đủ. Các cơ sở dữ liệu NoSQL phân tán thường tuân theo mô hình BASE thay thế:
| ACID | BASE | |
|---|---|---|
| Tính nhất quán (Consistency) | Nhất quán mạnh (Strong Consistency) | Nhất quán cuối cùng (Eventual Consistency) |
| Khả dụng (Availability) | Có thể chặn khi có lỗi | Luôn khả dụng |
| Cô lập (Isolation) | Cô lập nghiêm ngặt (Strict Isolation) | Cô lập lỏng (Loose Isolation) |
| Độ trễ (Latency) | Cao hơn (thao tác đồng bộ) | Thấp hơn (thao tác bất đồng bộ) |
| Trường hợp sử dụng | Hệ thống tài chính, OLTP | Hệ thống thông lượng cao, thời gian thực |
Điểm chính cần nhớ
- Atomicity — giao dịch là tất cả hoặc không có gì; các ghi một phần được hoàn tác
- Consistency — dữ liệu phải thỏa mãn tất cả các ràng buộc và bất biến đã định nghĩa sau mỗi giao dịch
- Isolation — các giao dịch đồng thời không can thiệp lẫn nhau; cô lập mạnh hơn = hiệu năng thấp hơn
- Durability — dữ liệu đã commit tồn tại qua các sự cố; được đảm bảo bởi WAL, fsync và Replication
- ACID là một phổ (Spectrum) — các cơ sở dữ liệu thực tế cung cấp các đánh đổi có thể cấu hình giữa tính nghiêm ngặt và hiệu năng
Câu hỏi Phỏng vấn
1. Mỗi chữ cái trong ACID viết tắt cho điều gì và tại sao nó quan trọng?
- Atomicity (Tính Nguyên tử) — giao dịch là tất cả hoặc không có gì (All or Nothing). Nếu bất kỳ bước nào thất bại, toàn bộ giao dịch được hoàn tác (Rolled Back) để cơ sở dữ liệu không bao giờ ở trạng thái một phần (Partial State).
- Consistency (Tính Nh ất quán) — giao dịch chỉ có thể đưa cơ sở dữ liệu từ một trạng thái hợp lệ (Valid State) sang trạng thái hợp lệ khác. Tất cả ràng buộc (Constraints), Trigger và quy tắc phải được thỏa mãn sau khi giao dịch commit.
- Isolation (Tính Cô lập) — các giao dịch đồng thời (Concurrent Transactions) không can thiệp lẫn nhau. Mỗi giao dịch hoạt động như thể nó là giao dịch duy nhất đang chạy, ngăn chặn các điều kiện tranh chấp (Race Conditions).
- Durability (Tính Bền vững) — khi một giao dịch đã commit, dữ liệu là vĩnh viễn và tồn tại qua các sự cố sập, mất điện và khởi động lại.
Nếu không có ACID, một ứng dụng ngân hàng có thể trừ tiền một tài khoản nhưng không bao giờ cộng vào tài khoản kia, hoặc hai người dùng có thể ghi đè dữ liệu của nhau mà không hề hay biết.
2. Sự khác biệt giữa "Consistency" trong ACID và "Consistency" trong Định lý CAP là gì?
| Consistency trong ACID | Consistency trong CAP | |
|---|---|---|
| Ý nghĩa | Mọi giao dịch bảo toàn các bất biến cơ sở d ữ liệu (Database Invariants — ràng buộc, quy tắc) | Mọi lần đọc đều nhận được lần ghi gần nhất hoặc một lỗi |
| Phạm vi | Cơ sở dữ liệu đơn, giao dịch đơn | Hệ thống phân tán (Distributed System), nhiều Replica |
| Cơ chế | Ràng buộc (Constraints), Trigger, logic ứng dụng | Giao thức Replication (ghi đồng bộ, đọc theo đa số/Quorum) |
| Ví dụ | Ràng buộc CHECK(balance >= 0) ngăn số dư âm | Sau khi người dùng A cập nhật hồ sơ, người dùng B ngay lập tức thấy hồ sơ mới |
Tóm lại: Consistency trong ACID là về tính đúng đắn của dữ liệu (Data Correctness) — trạng thái hợp lệ, Consistency trong CAP là về tính cập nhật của dữ liệu (Data Currency) — đọc được lần ghi gần nhất.
3. Bốn mức Isolation trong SQL là gì? Mỗi mức ngăn chặn vấn đề đồng thời nào?
| Mức Isolation | Dirty Read | Non-Repeatable Read | Phantom Read | Cách hoạt động |
|---|---|---|---|---|
| Read Uncommitted | Có thể xảy ra | Có thể xảy ra | Có thể xảy ra | Không khóa khi đọc — thấy dữ liệu chưa commit từ giao dịch khác |
| Read Committed | Ngăn chặn | Có thể xảy ra | Có thể xảy ra | Chỉ đọc dữ liệu đã commit (khóa chia sẻ ngắn khi đọc) |
| Repeatable Read | Ngăn chặn | Ngăn chặn | Có thể xảy ra | Giữ khóa chia sẻ (Shared Locks) trên tất cả các dòng đã đọc cho đến khi giao dịch kết thúc |
| Serializable | Ngăn chặn | Ngăn chặn | Ngăn chặn | Cô lập đầy đủ — các giao dịch thực thi như thể được tuần tự hóa (Range Locks hoặc SSI) |
Hầu hết cơ sở dữ liệu mặc định sử dụng Read Committed (PostgreSQL, SQL Server) hoặc Repeatable Read (MySQL InnoDB).
4. Write-Ahead Log (WAL) đảm bảo cả Atomicity và Durability như thế nào?
Đối với Atomicity:
- Trước khi sửa đổi dữ liệu, cơ sở dữ liệu ghi thay đổi dự định vào WAL
- Nếu giao dịch thất bại giữa chừng, khi phục hồi cơ sở dữ liệu đọc WAL và hoàn tác (Undo/Rollback) các giao dịch chưa hoàn thành
- Điều này đảm bảo không có ghi một phần (Partial Writes) nào bao giờ hiển thị
Đối với Durability:
- Khi giao dịch commit, mục WAL được fsync xuống đĩa trước khi trả về thành công cho Client
- Ngay cả khi cơ sở dữ liệu sập ngay sau đó, WAL trên đĩa chứa tất cả các thay đổi đã commit
- Khi phục hồi, cơ sở dữ liệu phát lại (Replay/Redo) WAL để khôi phục tất cả các giao dịch đã commit
WAL là nguồn chân lý duy nhất (Single Source of Truth) — nó được ghi trước các trang dữ liệu thực tế, đó là lý do tại sao gọi là "ghi trước" (Write-Ahead).
5. Tại sao không phải tất cả cơ sở dữ liệu đều sử dụng Serializable Isolation mặc định?
- Chi phí hiệu năng (Performance Overhead) — Serializable yêu cầu khóa nghiêm ngặt (Strict Locking) hoặc xác thực ảnh chụp (Snapshot Validation), làm giảm thông lượng (Throughput) đáng kể dưới khối lượng công việc đồng thời
- Nhiều Deadlock hơn — mức cô lập càng nghiêm ngặt, hai giao dịch càng dễ khóa chặt lẫn nhau, gây ra Deadlock cần thử lại (Retries)
- Hầu hết ứng dụng không cần — Read Committed hoặc Repeatable Read là đủ cho phần lớn các trường hợp sử dụng (ví dụ: hiển thị hồ sơ người dùng, xử lý đơn hàng)
- Đánh đổi (Trade-off) — cơ sở dữ liệu cho phép bạn chọn mức Isolation phù hợp với yêu cầu nhất quán so với nhu cầu hiệu năng
PostgreSQL cung cấp Serializable nhưng mặc định là Read Committed. Bạn chỉ nên chọn Serializable khi logic nghiệp vụ thực sự yêu cầu (ví dụ: ngăn đặt trùng phòng, đặt trước tồn kho).
6. Sự khác biệt giữa Dirty Read và Phantom Read là gì?
| Dirty Read | Phantom Read | |
|---|---|---|
| Chuyện gì xảy ra | Bạn đọc dữ liệu chưa commit từ giao dịch khác có thể bị hoàn tác | Bạn chạy lại truy vấn phạm vi (Range Query) và nhận được các dòng khác vì giao dịch khác đã chèn/xóa các dòng phù hợp |
| Ví dụ | Giao dịch A ghi balance = 500 (chưa commit). Giao dịch B đọc 500. Giao dịch A hoàn tác. B đã đọc dữ liệu chưa từng tồn tại. | Giao dịch A truy vấn SELECT * FROM orders WHERE amount > 100 và nhận 5 dòng. Giao dịch B chèn đơn hàng mới với amount = 200 và commit. Giao dịch A chạy lại truy vấn và nhận 6 dòng. |
| Mức Isolation để ngăn chặn | Read Committed (trở lên) | Serializable (Repeatable Read ngăn chặn trong MySQL InnoDB nhưng không ngăn chặn trong SQL chuẩn) |
| Phạm vi | Ảnh hưởng đến giá trị (Value) của các dòng hiện có | Ảnh hưởng đến tập hợp dòng (Set of Rows) được trả về bởi truy vấn |
Tìm hiểu thêm
- Indexing trong Cơ sở dữ liệu — cách Index tăng tốc đọc
- Replication trong Cơ sở dữ liệu — cách dữ liệu được sao chép giữa các Node cho Durability
- Sharding trong Cơ sở dữ liệu — phân chia dữ liệu trên nhiều máy chủ