Hãy tưởng tượng bạn đang làm việc ở một quán cà phê, nơi một bạn phục vụ kiểm tra số lượng bánh ở bếp, còn bạn khác thì đang ghi nhận order bánh từ khách mới. Trong thế giới lý tưởng, cả hai nên làm việc với cùng một dữ liệu về số lượng bánh để tránh lỗi kiểu "đặt chỗ hai lần". Nhưng thực tế thì có thể gặp vấn đề khi thao tác song song.
Dưới đây là ba rắc rối chính có thể xảy ra:
Dirty Read (đọc bẩn): Một truy vấn nhìn thấy thay đổi do truy vấn khác thực hiện nhưng chưa commit. Nếu thay đổi đó bị rollback, truy vấn đầu tiên sẽ bị ngây thơ như sinh viên đi phỏng vấn lần đầu.
Non-Repeatable Read (đọc không lặp lại): Một truy vấn đọc cùng dữ liệu hai lần, nhưng giữa hai lần đó ai đó đã kịp sửa đổi nó. Giống như bạn ra ga xem lịch tàu, quay lại sau một phút — và phát hiện tàu đã bị huỷ. Hoặc vé của bạn bị mua mất trong lúc bạn đang tìm tiền :)
Phantom Read (đọc ma): Một truy vấn thấy một tập con các dòng, nhưng giữa hai lần thực hiện ai đó thêm dòng mới làm kết quả thay đổi. Kiểu như công ty bạn thua thầu, rồi tất cả hồ sơ trừ của bạn và vợ ông chủ tịch thành phố bị huỷ.
Các mức độ cách ly transaction
Giờ khi đã biết về các vấn đề, cùng xem công cụ mà PostgreSQL cung cấp để giải quyết — mức độ cách ly transaction. Nó giống như đặt ra luật chơi cho các transaction chạy song song. Mức cách ly càng cao thì càng đảm bảo các transaction không làm phiền nhau. Nhưng đổi lại, "tốc độ phục vụ" (hiệu năng) sẽ giảm đi.
Các mức độ cách ly trong PostgreSQL
Read Uncommitted (Đọc dữ liệu chưa commit):
- Cho phép đọc cả những thay đổi chưa commit (đúng kiểu đọc bẩn luôn).
- Trong PostgreSQL, mức này thực ra được implement như
Read Committed, nên không hỗ trợ đúng nghĩa. PostgreSQL từ chối implement vì quá nguy hiểm.
Read Committed (Đọc dữ liệu đã commit):
- Ngăn đọc bẩn.
- Transaction chỉ thấy dữ liệu đã commit tại thời điểm thực hiện lệnh.
- Nhưng vẫn có thể bị
Non-Repeatable ReadvàPhantom Read.
Repeatable Read (Đọc lặp lại):
- Đảm bảo dữ liệu bạn đọc sẽ không thay đổi trong suốt transaction.
- Ngăn đọc bẩn và đọc không lặp lại.
- Nhưng vẫn có thể gặp dòng ma.
Serializable (Tuần tự hoá):
- Đảm bảo các transaction chạy như thể chúng chạy tuần tự, từng cái một.
- Ngăn cả ba vấn đề: đọc bẩn, đọc không lặp lại và dòng ma.
- Mức nghiêm ngặt nhất — và cũng chậm nhất.
Tại sao cách ly transaction lại quan trọng?
Hãy tưởng tượng database của một shop online, nơi cả ngàn user cùng lúc đặt hàng. Nếu không set mức cách ly hợp lý, bạn sẽ gặp đủ loại conflict: từ hàng "bốc hơi" đến đơn hàng bị trùng.
Chọn đúng mức cách ly giúp xử lý transaction song song, cân bằng giữa hiệu năng và tính toàn vẹn dữ liệu. Ví dụ:
- Hệ thống phân tích thường chọn mức cách ly thấp nhất (ví dụ Read Committed), vì độ chính xác không phải lúc nào cũng quan trọng.
- Hệ thống tài chính thì nên chọn Serializable để tránh lỗi tính toán hoặc trùng lặp giao dịch.
Ví dụ về áp dụng các mức cách ly
- Read Committed
Mức này đảm bảo bạn không bao giờ đọc dữ liệu có thể bị rollback.
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
-- Đọc dữ liệu tài khoản.
SELECT balance FROM accounts WHERE account_id = 1;
-- Nếu transaction khác update balance, dữ liệu này sẽ cập nhật ngay.
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
COMMIT;
- Repeatable Read
Mức này đảm bảo nếu bạn đọc dữ liệu, nó sẽ không đổi với bạn trong suốt transaction.
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;
-- Đọc dữ liệu tài khoản.
SELECT balance FROM accounts WHERE account_id = 1;
-- Dù transaction khác update balance này, bạn cũng không thấy thay đổi.
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
COMMIT;
- Serializable
Ở mức này, transaction hoạt động như thể nó là duy nhất trong hệ thống.
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN;
-- Đọc dữ liệu tài khoản.
SELECT balance FROM accounts WHERE account_id = 1;
-- Transaction khác muốn sửa dữ liệu này sẽ bị khoá cho đến khi bạn xong.
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
COMMIT;
Làm sao chọn mức cách ly?
Chọn mức cách ly tuỳ vào yêu cầu của bạn:
- Nếu tốc độ quan trọng hơn độ chính xác, và bạn chịu được dòng ma — chọn
Read Committed. - Nếu cần chính xác nhưng vẫn muốn hiệu năng cao — dùng
Repeatable Read. - Nếu cần đảm bảo dữ liệu đúng 100%, dù chậm — chọn
Serializable.
Cẩn thận nhé! Mức cách ly nghiêm ngặt có thể gây khoá và giảm hiệu năng hệ thống. Giải pháp hợp lý là cân bằng giữa hiệu năng và tính nhất quán dữ liệu.
GO TO FULL VERSION