+2

[Backend Masterclass] Giao tranh trong Database: 4 Mức Độ Cô Lập (Isolation Levels) và những "bóng ma" dữ liệu

chào anh em, làm việc với Database mà chỉ biết SELECT, INSERT, UPDATE thì anh em mới chỉ dạo chơi ở vòng gửi xe thôi

Khi hệ thống của anh em có hàng nghìn user, sẽ có những thời điểm 2, 3 cái Transactions (giao dịch) cùng lao vào đọc và sửa cùng mộ dùng dữ liệu tạt cùng một mili-giây. Nếu Database không có cơ chế phân xử, dữ liệu sẽ nát bét.

Nhưng nếu bắt tất cả phải xếp hàng đợi nhau chạy từng cái một thì hệ thống lại chậm như rùa. Do đó, DB sinh ra các Isolation Levels (Mức độ cô lập) để anh em tự cân bằng giữa Hiệu năng (Performance) và Độ an toàn dữ liệu (Data Consistency).

Trước khi chọn khiên, chúng ta phải biết mặt kẻ thù. Dưới đây là 3 "con ma" thường xuyên ám hệ thống của bạn.

1. Ba cơn ác mộng của Concurrency (Truy cập đồng thời)

Ác mộng 1: Dirty Read (Đọc bẩn)

  • Kịch bản: Vợ bạn lén chuyển 50 triệu cho bồ nhí (Transaction A - đang chạy, chưa Commit). Lúc này, bạn ở nhà mở app ngân hàng lên check số dư (Transaction B).
  • Lỗi: Transaction B đọc được số dư đã bị trừ 50 triệu, bạn tá hỏa tưởng mất tiền. Nhưng 1 giây sau, Vợ bạn hối hận và bấm Rollback Transaction A.
  • Kết quả: Bạn đã đọc phải một dữ liệu ảo, dữ liệu chưa từng tồn tại chính thức trong DB. Đó gọi là Đọc bẩn.

Ác mộng 2: Non-Repeatable Read (Đọc không nhất quán)

  • Kịch bản: Bạn đang code logic mua vé xem phim (Transaction A). Lần 1 bạn SELECT ghế A1, thấy còn trống. Bạn đang bận tính toán logic giảm giá mất 2 giây. Trong 2 giây đó, một khách hàng khác (Transaction B) nhảy vào mua luôn ghế A1 và Commit thành công. Lần 2 bạn SELECT lại ghế A1 để chuẩn bị update thì ghế... đã bị mua.
  • Lỗi: Trong cùng một Transaction, bạn đọc 1 dòng dữ liệu 2 lần nhưng ra 2 kết quả khác nhau (do bị thằng khác UPDATE).

Ác mộng 3: Phantom Read (Đọc bóng ma)

  • Kịch bản: Kế toán (Transaction A) chạy lệnh đếm: SELECT COUNT(*) FROM users WHERE role = 'admin' ra kết quả là 10 người. Cùng lúc đó, ông Sếp (Transaction B) lén INSERT thêm 1 ông admin mới và Commit. Kế toán chạy lại lệnh đếm lần 2, bỗng nhiên lòi ra 11 người.
  • Lỗi: Giống cái số 2, nhưng ở đây không phải do dòng dữ liệu bị sửa, mà là do có dòng dữ liệu mới đột nhiên chui lên từ dưới đất (bị thằng khác INSERT hoặc DELETE). Cảm giác rợn tóc gáy như gặp ma!

2. Tứ Đại Mức Độ Cô Lập (The 4 Isolation Levels)

Để chống lại 3 con ma trên, DB SQL chuẩn (như ANSI SQL) cung cấp 4 mức độ bảo vệ từ lỏng lẻo nhất đến an toàn tuyệt đối.

Level 1: Read Uncommitted (Sống buông thả)

  • Quy tắc: Thằng A chưa Commit, thằng B cũng đọc được luôn.
  • Hiệu năng: Nhanh vô địch, vì chả có cái Lock (khóa) nào được tạo ra cả.
  • Độ an toàn: Nát bét. Dính cả 3 lỗi: Dirty Read, Non-Repeatable Read, Phantom Read. 👉 Thực tế: Éo ai xài.

Level 2: Read Committed (Bảo vệ cơ bản)

  • Quy tắc: Chỉ khi nào thằng A Commit xong, thằng B mới được phép đọc dữ liệu đó.
  • Tác dụng: Chống được Dirty Read.
  • Nhược điểm: Vẫn dính Non-Repeatable Read và Phantom Read. 👉 Thực tế: Đây là mức Default mặc định của PostgreSQL, Oracle và SQL Server. Vì nó cân bằng cực tốt giữa tốc độ và sự an toàn.

Level 3: Repeatable Read (Nhất quán tuyệt đối cho từng dòng)

  • Quy tắc: Khi Transaction A bắt đầu đọc dữ liệu, DB sẽ "chụp" lại một bức ảnh (Snapshot). Trong suốt quá trình A chạy, dù B có nhảy vào sửa và Commit bao nhiêu lần đi nữa, A vẫn chỉ nhìn thấy data từ cái bức ảnh ban đầu.
  • Tác dụng: Chống được Dirty Read và Non-Repeatable Read.
  • Nhược điểm: Sách giáo khoa bảo nó vẫn dính Phantom Read (vì ảnh chụp không chặn được việc thằng khác Insert thêm dòng mới vào DB). 👉 Thực tế: Đây là mức Default mặc định của MySQL (InnoDB).

Level 4: Serializable (Trùm cuối)

  • Quy tắc: Khóa toàn bộ. Các Transaction phải đứng xếp hàng chạy tuần tự từng cái một (như qua trạm thu phí 1 làn). Thằng A đang đọc, thằng B muốn Insert/Update phải đứng chờ A chạy xong.
  • Tác dụng: Tiêu diệt toàn bộ 3 con ma. Data hoàn hảo 100%.
  • Nhược điểm: Chậm cmn nhất thế giới. Server dễ dính Deadlock. Cực kì ngốn tài nguyên.

3. Lời khuyên xương máu cho anh em Backend

Học lý thuyết xong, giờ là lúc thực chiến:

  1. Hiểu rõ DB của mình đang xài cái gì: Đừng code app MySQL mà bê nguyên tư duy của PostgreSQL vào. MySQL mặc định là Repeatable Read, trong khi Postgres mặc định là Read Committed. Behaviour (hành vi) của chúng nó khi chạy song song sẽ khác hẳn nhau.
  2. Sự bá đạo của MySQL (InnoDB): Ở trên mình nói Repeatable Read dính Phantom Read. Nhưng MySQL InnoDB đã "hack" cơ chế này bằng Next-Key Lock và MVCC (Multi-Version Concurrency Control). Nhờ đó, dù ở mức Repeatable Read, MySQL cũng đã chặn đứng luôn cả Phantom Read! Bạn gần như không bao giờ phải tăng level lên Serializable khi dùng MySQL.
  3. Pha xử lý kinh điển: Dù DB có che chắn tốt đến đâu, nếu bạn cần trừ tiền tài khoản hoặc bán vé máy bay/vé xem phim, hãy luôn kết hợp với khoá ở tầng dòng: SELECT ... FOR UPDATE (Pessimistic Locking) hoặc xài field version (Optimistic Locking) để tránh triệt để việc mất tiền oan nhé.

Tổng kết

Tóm lại: Mức cô lập càng cao -> An toàn càng cao -> Chạy càng chậm (và ngược lại). Việc của anh em kĩ sư là phải biết lúc nào cần nhanh (hiển thị bài viết, đếm like), và lúc nào cần an toàn tuyệt đối (giao dịch tiền bạc) để ép DB dùng đúng Isolation Level.

Hệ thống của chúng ta đã đi từ tối ưu Query, Full-text Search (Elasticsearch), xử lý bất đồng bộ (Queue, Kafka), đến cả bảo vệ tính toàn vẹn (Isolation Levels).

Nhưng anh em có để ý không? Giờ đây hệ thống ta có quá nhiều món đồ chơi lắt nhắt. Lúc deploy bằng tay (copy source code lên server rồi chạy lệnh) thì chắc chắn sẽ khóc thét vì config môi trường.

Để giải quyết nỗi đau "Trên máy em chạy bình thường, sao lên server lại chết?", bài tới mình sẽ dắt anh em làm quen với bến đỗ bình yên của mọi Developer: 👉 Bài sau: Docker - Đóng gói cả thế giới vào một chiếc container và vứt đi khái niệm "Lỗi do môi trường".

Anh em thấy sao về chủ đề Database hôm nay? Comment chém gió cùng mình nhé! Đừng quên thả 1 Upvote ủng hộ ông tác giả cày đêm viết bài.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí