Nếu bạn từng trả lời phỏng vấn "ACID là gì?" bằng một câu thuộc lòng — bài viết này sẽ giúp bạn hiểu thực sự từng thuộc tính hoạt động như thế nào bên trong InnoDB, kèm ví dụ code Laravel và các tình huống thực tế.
1. Atomicity — "Tất cả hoặc không gì cả"
Hãy tưởng tượng bạn đang chuyển tiền từ tài khoản A sang B. Đây là 2 thao tác: trừ A và cộng B. Atomicity đảm bảo cả 2 phải xảy ra cùng nhau, hoặc không thao tác nào xảy ra — không bao giờ có trạng thái "trừ A rồi nhưng chưa cộng B".
Cơ chế bên trong InnoDB
- InnoDB dùng Undo Log. Trước khi modify bất kỳ row nào, nó ghi lại trạng thái cũ vào undo log.
- Nếu transaction fail hoặc gọi
ROLLBACK→ InnoDB đọc undo log và khôi phục từng row về trạng thái trước đó. - Nếu
COMMITthành công → undo log được đánh dấu có thể thu hồi (purge thread dọn sau).
DB::transaction(function () {
// Bước 1: Trừ tiền tài khoản A
Account::where('id', 1)->decrement('balance', 500);
// Bước 2: Cộng tiền tài khoản B
Account::where('id', 2)->increment('balance', 500);
// Nếu bước 2 throw exception → bước 1 cũng bị rollback
// → Tiền không "biến mất"
});
2. Consistency — "DB luôn ở trạng thái hợp lệ"
Consistency là "luật chơi" của database. Mọi transaction phải đưa DB từ trạng thái hợp lệ này sang trạng thái hợp lệ khác — không bao giờ vi phạm constraints.
Consistency hoạt động ở 2 tầng
Tầng Database (engine enforce):
- Primary key, unique constraints → không trùng
- Foreign key → referential integrity
- NOT NULL, CHECK constraints → data validation
- Data type → không insert string vào cột integer
Tầng Application (code enforce):
- Business rules mà DB không biểu diễn được
- Ví dụ: "Tổng tiền trước/sau chuyển khoản phải bằng nhau" — DB không tự kiểm tra được, code phải đảm bảo logic đúng
Ví dụ vi phạm khi không có Atomicity:
-- Tài khoản A có 1000đ, chuyển 500đ sang B
-- Nếu KHÔNG có atomicity:
UPDATE accounts SET balance = balance - 500 WHERE id = 1; -- A = 500 ✅
-- Server crash ở đây
UPDATE accounts SET balance = balance + 500 WHERE id = 2; -- Không chạy ❌
-- → 500đ "biến mất" khỏi hệ thống → INCONSISTENT
3. Isolation — "Các transaction không ảnh hưởng lẫn nhau"
Mỗi transaction hoạt động như thể nó là transaction duy nhất đang chạy trên DB — không thấy dữ liệu "dở dang" của transaction khác.
Cơ chế bên trong InnoDB
InnoDB dùng 2 cơ chế kết hợp:
1. MVCC (Multi-Version Concurrency Control) — cho READ:
- Mỗi row có hidden columns:
trx_id(transaction nào tạo/sửa) vàroll_pointer(trỏ tới version cũ trong undo log) - Khi transaction B đọc, nó tạo một Read View (snapshot) — liệt kê các transaction đang active
- Nếu row được sửa bởi transaction chưa commit → B đọc version cũ từ undo log
- → Read không cần lock → read và write không block lẫn nhau
2. Locking — cho WRITE:
- Row lock: Lock chính xác row đang modify
- Gap lock: Lock "khoảng trống" giữa các row trong index → ngăn INSERT mới (chống phantom read)
- Next-key lock: Row lock + gap lock kết hợp
4. Durability — "Dữ liệu đã commit không bao giờ mất"
Khi bạn nhận được COMMIT thành công, dù server mất điện ngay lập tức sau đó, dữ liệu vẫn còn nguyên khi restart. Đây là thuộc tính thú vị nhất về mặt cơ chế.
Write-Ahead Logging (WAL)
InnoDB dùng cơ chế WAL — ghi log trước, ghi data sau:
- Trước khi data được ghi vào data file trên disk, InnoDB ghi redo log trước — mô tả "thay đổi gì ở page nào"
- Redo log được flush to disk trước khi COMMIT trả về (
innodb_flush_log_at_trx_commit = 1) - Data pages vẫn nằm trong Buffer Pool (RAM) — được ghi xuống disk sau (lazy write / checkpoint)
Flow khi COMMIT
App gọi COMMIT
→ InnoDB ghi redo log vào disk (fsync) ← ĐIỂM ĐẢM BẢO DURABILITY
→ Trả về "COMMIT OK" cho app
→ Data pages vẫn ở RAM (dirty pages)
→ Checkpoint thread ghi dirty pages xuống disk sau (background)
Flow khi Crash Recovery
Restart → InnoDB đọc redo log
→ Replay các thay đổi đã commit nhưng chưa flush vào data file
→ Rollback các transaction chưa commit (dùng undo log)
→ DB trở lại trạng thái consistent
Config quan trọng — innodb_flush_log_at_trx_commit
| Giá trị | Hành vi | Rủi ro | Use case |
|---|---|---|---|
= 1 (default) |
Flush redo log mỗi COMMIT | Không mất data | Payment, banking |
= 2 |
Ghi OS buffer mỗi commit, flush mỗi giây | Mất tối đa 1s nếu OS crash | E-commerce, booking |
= 0 |
Ghi + flush mỗi giây | Mất tối đa 1s nếu MySQL crash | Logging, analytics |
Tổng kết — ACID như một hệ thống liên kết
| Thuộc tính | Đảm bảo | Cơ chế InnoDB |
|---|---|---|
| Atomicity | All or nothing | Undo Log (rollback khi fail) |
| Consistency | DB luôn hợp lệ | Constraints + Atomicity + Isolation |
| Isolation | Transaction không ảnh hưởng nhau | MVCC (read) + Locking (write) |
| Durability | Commit = vĩnh viễn | Redo Log / WAL (crash-safe) |
Hiểu ACID không chỉ để trả lời phỏng vấn — mà để biết khi nào cần tune innodb_flush_log_at_trx_commit, khi nào chọn isolation level phù hợp, và tại sao transaction design ảnh hưởng trực tiếp đến data safety của hệ thống. 🚀