Isolation Level là gì? Các mức độ cô lập trong Database
Trong một ứng dụng Backend, nhiều request có thể truy cập và thay đổi dữ liệu trong database cùng một thời điểm. Khi nhiều transaction chạy đồng thời, kết quả của một transaction có thể bị ảnh hưởng bởi những thao tác của transaction khác.
Để kiểm soát việc truy cập dữ liệu đồng thời, database cung cấp Isolation Level (mức độ cô lập). Đây là thuộc tính quy định mức độ các transaction được phép quan sát ảnh hưởng của nhau và những bảo đảm mà database cung cấp khi chúng thực thi đồng thời.
Isolation Level là một phần quan trọng của ACID, giúp kiểm soát những hiện tượng bất thường khi đọc dữ liệu và hỗ trợ duy trì tính nhất quán của hệ thống.
1. Isolation Level là gì?
Isolation Level là mức độ bảo đảm về tính cô lập giữa các transaction đang thực thi đồng thời trong database.
Ví dụ, một tài khoản ngân hàng có số dư là 1.000.000 đồng. Transaction A đang thực hiện giao dịch, trong khi Transaction B cũng đọc hoặc cập nhật số dư của tài khoản đó.
Nếu không kiểm soát việc truy cập đồng thời phù hợp, các transaction có thể đọc những phiên bản dữ liệu khác nhau hoặc tạo ra kết quả không đáp ứng yêu cầu nghiệp vụ.
Isolation Level xác định những hiện tượng nào có thể xảy ra và mức độ nhất quán mà database phải bảo đảm khi các transaction tương tác với nhau.
Cần lưu ý rằng Isolation không nhất thiết ngăn các transaction chạy đồng thời. Mục tiêu của nó là kiểm soát kết quả của việc thực thi đồng thời theo những quy tắc đã xác định.
2. Các hiện tượng bất thường khi đọc dữ liệu
Để hiểu Isolation Level, trước tiên cần nắm ba hiện tượng thường được sử dụng để phân biệt các mức cô lập.
2.1. Dirty Read — Đọc dữ liệu chưa commit
Dirty Read xảy ra khi một transaction đọc dữ liệu đã bị transaction khác thay đổi nhưng chưa commit.
Ví dụ:
- Transaction A cập nhật số dư từ 1.000.000 thành 500.000 đồng nhưng chưa commit.
- Transaction B đọc số dư và nhận được 500.000 đồng.
- Transaction A thực hiện
ROLLBACK, đưa dữ liệu về trạng thái ban đầu.
Transaction B đã đọc một giá trị chưa được xác nhận chính thức. Nếu B sử dụng giá trị này để tính toán hoặc thực hiện nghiệp vụ khác, kết quả có thể không chính xác.
2.2. Non-repeatable Read — Đọc lại thấy giá trị khác
Non-repeatable Read xảy ra khi một transaction đọc cùng một hàng dữ liệu nhiều lần nhưng nhận được các giá trị khác nhau do transaction khác đã cập nhật và commit dữ liệu giữa các lần đọc.
Ví dụ:
- Transaction A đọc số dư là 1.000.000 đồng.
- Transaction B cập nhật số dư thành 800.000 đồng và commit.
- Transaction A đọc lại số dư.
Ở những mức cô lập cho phép hiện tượng này, lần đọc thứ hai của A sẽ trả về 800.000 đồng thay vì 1.000.000 đồng.
2.3. Phantom Read — Xuất hiện hoặc biến mất các hàng phù hợp điều kiện
Phantom Read xảy ra khi một transaction thực hiện lại truy vấn theo cùng một điều kiện nhưng nhận được tập hợp hàng khác do transaction khác thêm hoặc xóa những hàng phù hợp.
Ví dụ:
- Transaction A truy vấn tất cả sản phẩm có giá dưới 100.000 đồng.
- Transaction B thêm một sản phẩm có giá 50.000 đồng và commit.
- Transaction A chạy lại truy vấn tương tự.
Nếu mức cô lập cho phép Phantom Read, truy vấn thứ hai của A có thể trả về thêm sản phẩm vừa được thêm vào.
Khác với Non-repeatable Read, vốn tập trung vào sự thay đổi của một hàng đã đọc, Phantom Read liên quan đến sự thay đổi tập hợp hàng thỏa mãn điều kiện truy vấn.
3. Bốn mức Isolation Level phổ biến
SQL định nghĩa bốn mức Isolation Level chính. Mỗi mức cung cấp những bảo đảm khác nhau về việc đọc và thay đổi dữ liệu khi các transaction chạy đồng thời.
3.1. Read Uncommitted
Read Uncommitted là mức cô lập thấp nhất theo mô hình SQL. Ở mức này, transaction về lý thuyết có thể đọc dữ liệu do transaction khác thay đổi nhưng chưa commit.
Ví dụ, Transaction A cập nhật số dư nhưng chưa commit. Transaction B đọc giá trị mới trước khi A rollback. Khi đó, B đã đọc dữ liệu chưa được commit.
Đây là hiện tượng Dirty Read.
Mức này cung cấp ít bảo đảm về tính nhất quán của việc đọc dữ liệu. Tuy nhiên, hành vi thực tế phụ thuộc vào hệ quản trị database.
Lưu ý: PostgreSQL không cho phép Dirty Read. Nếu cấu hình READ UNCOMMITTED, PostgreSQL xử lý mức này như READ COMMITTED.
3.2. Read Committed
Read Committed bảo đảm transaction chỉ đọc dữ liệu đã được commit, qua đó ngăn chặn Dirty Read.
Ví dụ:
- Transaction A đọc số dư là 1.000.000 đồng.
- Transaction B cập nhật số dư thành 800.000 đồng và commit.
- Transaction A đọc lại số dư.
Trong PostgreSQL, ở mức READ COMMITTED, mỗi câu lệnh đọc thông thường sẽ sử dụng một snapshot dữ liệu mới. Vì vậy, câu lệnh đọc thứ hai của A có thể trả về 800.000 đồng.
Điều này có nghĩa Read Committed không bảo đảm mọi lần đọc trong cùng transaction đều nhìn thấy cùng một trạng thái dữ liệu.
Read Committed thường phù hợp với nhiều thao tác CRUD thông thường và là mức Isolation Level mặc định của PostgreSQL.
3.3. Repeatable Read
Repeatable Read cung cấp mức cô lập cao hơn, bảo đảm các lần đọc trong transaction tuân theo một snapshot dữ liệu nhất quán, theo cách triển khai của hệ quản trị database.
Ví dụ:
- Transaction A đọc số dư là 1.000.000 đồng.
- Transaction B cập nhật số dư thành 800.000 đồng và commit.
- Transaction A đọc lại số dư.
Trong PostgreSQL, Transaction A vẫn nhìn thấy giá trị 1.000.000 đồng theo snapshot của nó.
Điều này giúp các câu lệnh đọc trong cùng transaction không bị ảnh hưởng bởi những thay đổi đã commit sau thời điểm snapshot được thiết lập.
Tuy nhiên, Repeatable Read không có nghĩa mọi transaction đều có thể cập nhật dữ liệu mà không gặp xung đột. Nếu transaction cố cập nhật một hàng đã bị transaction khác thay đổi sau khi snapshot được thiết lập, PostgreSQL có thể hủy transaction đó và yêu cầu ứng dụng xử lý lỗi hoặc thử lại.
Ngoài ra, cần phân biệt hành vi theo hệ quản trị database. Trong PostgreSQL, REPEATABLE READ ngăn chặn Phantom Read theo định nghĩa của tiêu chuẩn SQL, dù điều này không phải là bảo đảm chung của mức Repeatable Read trên mọi hệ quản trị.
3.4. Serializable
Serializable là mức cô lập cao nhất trong bốn mức được SQL định nghĩa.
Mức này bảo đảm kết quả của các transaction chạy đồng thời tương đương với kết quả của một thứ tự thực thi tuần tự hợp lệ.
Ví dụ, một hệ thống chỉ cho phép tối đa một nhân viên đảm nhiệm một vị trí trực trong cùng một khung giờ. Hai transaction có thể cùng kiểm tra rằng vị trí đó chưa có người đảm nhiệm. Nếu cả hai cùng ghi nhận phân công, quy tắc nghiệp vụ sẽ bị vi phạm.
Serializable giúp database phát hiện và ngăn chặn những kết quả không tương đương với việc thực thi tuần tự.
Trong PostgreSQL, SERIALIZABLE sử dụng cơ chế theo dõi các phụ thuộc đọc/ghi để phát hiện những xung đột có thể dẫn đến kết quả không tuần tự hóa được. Khi cần thiết, database sẽ hủy một transaction.
Do đó, ứng dụng cần xử lý lỗi serialization và có thể thử lại toàn bộ transaction nếu thao tác đó an toàn để thực hiện lại.
Serializable cung cấp bảo đảm mạnh hơn, nhưng có thể làm tăng số transaction phải thử lại và ảnh hưởng đến hiệu năng trong những tình huống có nhiều xung đột.
4. So sánh các mức Isolation Level
| Hiện tượng | Read Uncommitted | Read Committed | Repeatable Read | Serializable |
|---|---|---|---|---|
| Dirty Read | Có thể theo mô hình SQL | Không | Không | Không |
| Non-repeatable Read | Có thể | Có thể | Không | Không |
| Phantom Read | Có thể | Có thể | Có thể theo mô hình SQL | Không |
Bảng trên thể hiện các bảo đảm theo mô hình Isolation Level của SQL. Hành vi thực tế có thể khác giữa các hệ quản trị database.
Đối với PostgreSQL, cần lưu ý:
READ UNCOMMITTEDcó hành vi giốngREAD COMMITTED.REPEATABLE READngăn chặn Dirty Read, Non-repeatable Read và Phantom Read.SERIALIZABLEcung cấp bảo đảm tương đương thực thi tuần tự, nhưng transaction có thể bị hủy khi phát hiện xung đột.
Không nên hiểu bảng trên là thước đo tuyệt đối về hiệu năng. Chi phí thực tế phụ thuộc vào workload, cách thiết kế truy vấn, mức độ xung đột và cơ chế triển khai của database.
5. Cách cấu hình Isolation Level trong PostgreSQL
Có thể chỉ định Isolation Level khi bắt đầu transaction bằng câu lệnh BEGIN.
Ví dụ:
BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;
SELECT balance
FROM accounts
WHERE id = 1;
COMMIT;
Trong ví dụ này, transaction sử dụng mức READ COMMITTED.
Có thể thay bằng REPEATABLE READ hoặc SERIALIZABLE khi nghiệp vụ cần những bảo đảm tương ứng.
Ví dụ:
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT balance
FROM accounts
WHERE id = 1;
-- Thực hiện các thao tác nghiệp vụ cần thiết.
COMMIT;
Nếu database phát hiện xung đột serialization, transaction có thể thất bại. Ứng dụng cần xử lý lỗi và có thể thử lại toàn bộ transaction.
Việc retry phải được thiết kế cẩn thận để tránh lặp lại những tác động bên ngoài database hoặc tạo ra giao dịch trùng lặp.
6. Isolation Level khác Locking như thế nào?
Isolation Level và Locking có liên quan chặt chẽ nhưng không phải cùng một khái niệm.
| Khái niệm | Vai trò |
|---|---|
| Transaction | Gom nhiều thao tác thành một đơn vị xử lý. |
| Isolation Level | Quy định những bảo đảm khi các transaction chạy đồng thời. |
| Locking | Cơ chế khóa để kiểm soát những thao tác truy cập xung đột. |
| Deadlock | Tình trạng các transaction chờ khóa của nhau theo một chu trình. |
Database có thể kết hợp locking, MVCC (Multi-Version Concurrency Control) và cơ chế phát hiện xung đột để cung cấp các bảo đảm về Isolation Level.
Ví dụ, PostgreSQL sử dụng MVCC để cho phép truy vấn đọc thông thường đọc phiên bản dữ liệu phù hợp mà không nhất thiết phải chờ khóa ghi của transaction khác.
Trong khi đó, SELECT ... FOR UPDATE cho phép ứng dụng yêu cầu khóa những hàng cần xử lý để ngăn các transaction khác lấy khóa xung đột trên những hàng đó.
Như vậy, Isolation Level không có nghĩa mọi dữ liệu đều bị khóa. Đồng thời, chỉ sử dụng một mức Isolation Level cũng không tự động bảo đảm mọi quy tắc nghiệp vụ đều đúng. Ứng dụng vẫn cần thiết kế truy vấn, điều kiện cập nhật và transaction phù hợp.
7. Khi nào nên sử dụng từng Isolation Level?
Việc lựa chọn Isolation Level phụ thuộc vào yêu cầu nhất quán dữ liệu, mức độ truy cập đồng thời và khả năng xử lý xung đột của ứng dụng.
- Read Committed: Phù hợp với nhiều thao tác CRUD thông thường và là lựa chọn mặc định của PostgreSQL.
- Repeatable Read: Phù hợp khi transaction cần đọc dữ liệu từ một snapshot nhất quán qua nhiều câu lệnh.
- Serializable: Phù hợp với những nghiệp vụ có các điều kiện dữ liệu phụ thuộc lẫn nhau và cần bảo đảm kết quả tương đương thực thi tuần tự.
- Read Uncommitted: Ít được sử dụng trong thực tế; trên PostgreSQL, mức này có hành vi giống Read Committed.
Không nên mặc định rằng mức cô lập càng cao thì ứng dụng luôn càng tốt. Mức cô lập cao hơn có thể tăng chi phí xử lý xung đột hoặc số lần transaction phải thử lại. Ngược lại, mức cô lập thấp hơn đòi hỏi ứng dụng thiết kế các thao tác đọc, kiểm tra và cập nhật dữ liệu cẩn thận hơn.
8. Isolation Level trong dự án Backend
Trong một ứng dụng Backend, nhiều request có thể đồng thời truy cập database để đặt hàng, cập nhật tồn kho, chuyển tiền hoặc xử lý thanh toán.
Backend Developer cần hiểu Isolation Level để xác định những bảo đảm mà transaction cần có và cách xử lý khi xảy ra xung đột.
Ví dụ, API đặt hàng có thể thực hiện các bước:
- Kiểm tra số lượng tồn kho.
- Tạo đơn hàng.
- Cập nhật số lượng sản phẩm.
- Commit khi các thao tác thành công hoặc rollback khi có lỗi.
Việc lựa chọn Isolation Level cần đi cùng thiết kế transaction, câu lệnh cập nhật có điều kiện, khóa tường minh khi cần và cách xử lý lỗi.
Chẳng hạn, để tránh giảm tồn kho xuống số âm, ứng dụng có thể sử dụng một câu lệnh cập nhật có điều kiện:
UPDATE products
SET stock = stock - 1
WHERE id = 1
AND stock > 0;
Sau đó, ứng dụng kiểm tra số hàng bị ảnh hưởng để xác định việc trừ tồn kho có thành công hay không. Tùy nghiệp vụ, thao tác này có thể cần được kết hợp với việc tạo đơn hàng trong cùng transaction.
Ví dụ này cho thấy không phải vấn đề đồng thời nào cũng cần nâng Isolation Level lên Serializable. Đôi khi, một câu lệnh cập nhật được thiết kế đúng đã giải quyết được vấn đề cụ thể một cách đơn giản hơn.
9. Kết luận
Isolation Level là mức độ bảo đảm về tính cô lập giữa các transaction chạy đồng thời trong database. Bốn mức phổ biến là Read Uncommitted, Read Committed, Repeatable Read và Serializable.
Mỗi mức cung cấp những bảo đảm khác nhau về việc đọc và thay đổi dữ liệu. Mức cô lập cao hơn thường cung cấp bảo đảm mạnh hơn, nhưng có thể đi kèm chi phí xử lý xung đột hoặc yêu cầu thử lại transaction.
Đối với Backend Developer, hiểu Isolation Level giúp lựa chọn cách xử lý truy cập đồng thời phù hợp với từng nghiệp vụ, đồng thời hiểu rõ hơn mối quan hệ giữa transaction, locking, MVCC và deadlock.
Mục tiêu không phải là luôn chọn mức cô lập cao nhất, mà là lựa chọn mức phù hợp với yêu cầu nghiệp vụ và thiết kế transaction sao cho dữ liệu được xử lý chính xác khi nhiều request hoạt động đồng thời.







Khoá học lập trình game con rắn cho trẻ em




