Chuyển tới nội dung chính

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.

Các Thuộc tính ACID

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 KEY thự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 so với Consistency

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 UncommittedNhanh nhất
Read CommittedKhôngNhanh
Repeatable ReadKhôngKhôngTrung bình
SerializableKhôngKhôngKhôngChậ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ế:

ACIDBASE
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ỗiLuô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ụngHệ thống tài chính, OLTPHệ 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 ACIDConsistency trong CAP
Ý nghĩaMọ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 viCơ sở dữ liệu đơn, giao dịch đơnHệ thống phân tán (Distributed System), nhiều Replica
Cơ chếRàng buộc (Constraints), Trigger, logic ứng dụngGiao thức Replication (ghi đồng bộ, đọc theo đa số/Quorum)
Ví dụRàng buộc CHECK(balance >= 0) ngăn số dư âmSau 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 IsolationDirty ReadNon-Repeatable ReadPhantom ReadCách hoạt động
Read UncommittedCó thể xảy raCó thể xảy raCó thể xảy raKhông khóa khi đọc — thấy dữ liệu chưa commit từ giao dịch khác
Read CommittedNgăn chặnCó thể xảy raCó thể xảy raChỉ đọc dữ liệu đã commit (khóa chia sẻ ngắn khi đọc)
Repeatable ReadNgăn chặnNgăn chặnCó thể xảy raGiữ 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
SerializableNgăn chặnNgăn chặnNgăn chặnCô 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 ReadPhantom Read
Chuyện gì xảy raBạn đọc dữ liệu chưa commit từ giao dịch khác có thể bị hoàn tácBạ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ặnRead 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