Transaction trong Database
- Đầu tiên, ta cần phải hiểu về Transaction trong Database. Một phiên (Transaction) trong database có thể được coi tạo lên bởi 4 tính chất ACID
- Một transaction (phiên) thể hiên một nhóm các câu lệnh được chạy trong database, thông thường chứa nhiều lệnh khác nhau
Các cấp độ isolation trong Database
Read Uncommitted Isolation Level (Cấp độ “đọc chưa hoàn thành”)
- Đây là cấp độ khá lỏng lẻo, khi mà ta sẽ đọc các dữ liệu chưa được commit của row. Tức là bất kì lệnh cập nhật ,thêm vào mà chưa thật sự commit vào database cũng sẽ có luôn trong phiên hiện tại của mình. Cấp độ này thường được dùng trong các hệ thống đặt vé, đặt lịch hẹn khi mà các transaction khác đang cố update tình trạng của 1 vé, kể cả khi phiên ngoài đó chưa thực thi xong, nhưng ta cũng sẽ coi như là nó đã được chạy.
- Cấp độ này thường k đảm bảo tính toàn vẹn của dữ liệu đọc được, tuy nhiên nếu chúng có thể chấp nhận được thì chúng có thể được dùng để giảm bớt tình trạng deadlock, … (Ví dụ như đọc toàn bộ dân số việt nam, ta có thể chấp nhận sai số khoảng 100-200 người chẳng hạn)
Read Committed Isolation Level (cấp độ “đọc phải hoàn thành”)
- Cấp độ này đảm bảo 2 điều sau:
- No Dirty Reads: Database sẽ không được phép đọc các dữ liệu chưa được commit, cập nhật vào database
- No Dirty Writes: Database không cho phép một transaction nào khác ở cùng row đang có transaction chưa chạy xong. Transaction sau sẽ phải đợi transaction đang chạy hoàn thành, sau đó mới được write vào row
- Trong Spring thì mặc định nếu ta để là @Transactional mà không ghi gì thêm, đây được coi là Read Committed
Repeatable Reads Isolation Level (Cấp độ “đọc lại”)
- Giả sử, ta có một bảng chứa số tiền lương của nhân viên, và ta có một transaction chứa 2 lệnh như sau
- Lệnh 1: Đếm số lượng nhân viên
- Lệnh 2: Đếm tổng tiền lương của nhân viên
Lúc này, giả sử một trường hợp mà có một nhân viên mới được thêm vào ngay sau khi Lệnh 1 hoàn thành. Lúc này kết quả của lệnh 2 sẽ bị thay đổi (do có 1 nhân viên mới). Lúc này chúng ta thường sẽ chọn cấp độ Repeatable Read, đó là đảm bảo rằng số lượng row (hàng) nằm trong phiên hiện tại giữ nguyên giá trị cho đến khi hết transaction.
- Điều này đảm bảo số lượng row nhân viên phải được đảm bảo từ đầu phiên, nên nhân viên mới kia (1 row mới) không được count vào, và lệnh 2 đảm bảo giá trị vẫn đúng.
Serializable Isolation Level
- Đây là cấp độ mạnh nhất trong isolation
- Không một transaction nào khác được quyền đọc hoặc ghi cho đến khi transaction này hoàn thành
- Cấp độ này giải quyết đa số vấn đề mà 3 cấp độ kia có, nhưng mà nó thường làm giới hạn, chỉ cho phép 1 query chạy 1 lúc trong hệ thống, tạo thành 1 điểm nghẽn và khó scale hệ thống hơn.
- Ảnh minh hoạ:
- Giả sử ta đang xây dựng một người tuyết, ta thêm từ từ đầu người tuyết, thân, rồi chân
- Với cấp độ Uncommited Read: Ta không có bất kì hạn chế nào, tức là ví dụ ở nguời dùng 1, họ xây 1 cái đầu ở phút thứ 0, rồi phút thứ 10, do có vấn đề về database, transaction bị rollback trở lại (xoá đi lệnh xây cái đầu). Thì ở phút thứ 5, ta vẫn đọc được những lệnh “chưa commit” đó là xây phần đầu kia.
- Với cấp độ Commited Read: Thì khi ta đã thêm phần đầu, và commit nó, hoặc là rollback transaction. Tức là toàn bộ dữ liệu trong phiên đã xong (Theo tính chất A của ACID) thì phiên khác mới đọc được các dữ liệu đã thay đổi ở phiên này
- Với cấp độ Repeatable Read: Giả sử có 2 phiên như sau
- Phiên 1: Phút 0 thì đọc toàn bộ database, phút 10 đọc toàn bộ database lần nữa, phút 11 đóng phiên
- Phiên 2: Phút thứ 5 thì thêm vào database một dữ liệu gì đó, phút thứ 6 đóng phiên
- Ở các cấp độ trước, do phiên 2 đã hoàn thành, nên hiển nhiên, phút thứ 10 của phiên 1 đọc sẽ ra khác so với đọc ở phút 0. Thì ở cấp độ này sẽ đảm bảo, dữ liệu ở vùng ta đọc sẽ luôn đảm bảo giống nhau ở các lần đọc trong phiên.
- Với cấp độ Serializable: Đầu tiên, ta phải hiểu, dữ liệu ở Repeatable Read không bị khoá, tức là nó chỉ đảm bảo các dữ liệu sẽ luôn như nhau khi ta đọc nó ở các thời điểm khác nhau trong 1 phiên, chứ không đảm bảo rằng phiên khác không được thay đổi nó, tức là cho dù bên khác thay đổi dữ liệu, database vẫn cho phép thay đổi, nhưng ở Repeatable Read chỉ đảm bảo là dữ liệu đọc ở phiên không đổi (dù thực sự nó đã đổi) chứ không ngăn chặn việc đó.
- Thì ở cấp độ này, ta sẽ đảm bảo là vùng dữ liệu bị khoá hết, và bên khác không được quyền thay đổi gì nữa cả.
Nghe thì có vẻ Serializable giúp ta thoải mái và đảm bảo dữ liệu đúng, tuy nhiên bản chất, nó ngăn chặn mọi hành động đa luồng trong database vào 1 vùng dữ liệu, và ta chỉ chạy được 1 phiên trên 1 vùng vào 1 thời điểm
Tham khảo: