Khái niệm transaction trong database là một khái niệm quan trọng mà tất cả các lập trình viên nên biết bởi vì chúng ta sẽ làm việc rất nhiều với khái niệm này. Trong bài viết này, mình xin trình bày với các bạn về khái niệm này!
Để dễ hình dung, mình xin lấy một ví dụ như sau: chúng ta đang làm việc với một ứng dụng bên ngân hàng, ứng dụng này có một tính năng đó là chuyển tiền giữa các tài khoản ngân hàng với nhau, ví dụ chuyển tiền cho tài khoản A sang tài khoản B. Đây là một tính năng mà quá trình xử lý sẽ gồm 2 bước:
- Thứ nhất là trừ tiền trong tài khoản A
- Và bước thứ hai là cộng tiền vào tài khoản B.
Rõ ràng, như các bạn thấy, đây là một quá trình nếu gọi là thành công thì bắt buộc 2 bước trên phải thành công, hoặc nếu thất bại thì bắt buộc 2 bước trên cũng phải thất bại luôn. Nếu chỉ một trong 2 bước trên thành công hoặc thất bại thì chúng ta sẽ gặp vấn đề ngay. Và chúng ta khi xây dựng ứng dụng cũng phải đảm bảo 2 điều kiện trên.
Đây chính là ý tưởng chính của khái niệm transaction trong database. Trong database, một transaction là một tập hợp các thao tác mà ở đó hoặc là các thao tác đó được thực hiện thành công, hoặc là không một thao tác nào được thực hiện thành công cả. Khi các bạn suy nghĩ về transaction trong database, các bạn hãy hình dung nó là “hoặc là tất cả hoặc là không gì hết”.
Ở đây, chúng ta có một chữ viết tắt ACID (Atomicity, Consistency, Isolation, Durability) định nghĩa các tính chất của một transaction trong database.
Trong đó:
Atomicity
Tính chất này thể hiện khái niệm “hoặc là tất cả hoặc là không gì hết”. Toàn bộ các bước được thực hiện trong một transaction được xử lý như một thực thể không thể chia nhỏ và không thể phân rã, nếu thành công thì phải thành công tất cả, nếu thất bại thì tất cả cũng phải thất bại. Nếu một transaction thành công thì tất cả những thay đổi phải được lưu vào database. Nếu thất bại thì tất cả những thay đổi trong transaction đó phải được rollback về trạng thái ban đầu.
Trong ví dụ của mình, rõ ràng khi đã trừ tiền trong tài khoản A thì bắt buộc việc cộng tiền vào tài khoản B phải xảy ra. Còn không thì không có gì xảy ra hết, đã không trừ tiền trong tài khoản A thì chắc chắn rồi, việc cộng tiền vào tài khoản B cũng không xảy ra.
Consistency
Đảm bảo một transaction move dữ liệu trong database từ một trạng thái valid này sang một trạng thái valid khác, bảo toàn các quy tắc và ràng buộc được định nghĩa bởi database và ứng dụng.
Trong ví dụ của mình thì, nếu đã trừ 10$ trong tài khoản A thì trong tài khoản B phải được cộng 10$.
Isolation
Nếu chúng ta có nhiều transaction cùng một lúc thì phải đảm bảo là các transaction này xảy ra độc lập, không tác động lẫn nhau trên dữ liệu.
Trong ví dụ của mình, giả sử cùng một lúc xảy ra 2 transaction như trên thì rõ ràng chúng ta không xác định được trạng thái của tài khoản A và tài khoản B sau khi thực hiện cùng 1 lúc. Mà điều này thì rất nguy hiểm.
Các lỗi có thể xảy ra là:
- Dirty read
Transaction B chưa commit dữ liệu mà transaction A đã read những dữ liệu này rồi.
- Non-repeatable read
Xảy ra khi một transaction A query 2 lần cùng một row dữ liệu, nhưng mỗi lần lại nhận được các giá trị dữ liệu khác nhau. Hiện tượng bất thường này xảy ra khi một concurrent transaction B khác đã cập nhật hoặc xóa row dữ liệu đó và đã commit các thay đổi của mình giữa hai lần đọc của transaction A.
- Phantom read
Xảy ra khi một transaction A query 2 lần cùng một câu lệnh SQL, nhưng mỗi lần lại nhận được số lượng record khác nhau. Điều này xảy ra khi một concurrent transaction B khác đã thêm mới record hoặc xóa record thỏa mãn câu lệnh SQL của transaction A và đã commit các thay đổi của mình giữa hai lần đọc của transaction A.
Để giải quyết các lỗi có thể xảy ra như trên, chúng ta có thể định nghĩa các isolation level như sau:
| Isolation level | Dirty read | Non-repeatable read | Phantom read | Risks |
|---|---|---|---|---|
| READ UNCOMMITTED | Possible | Possible | Possible | Cho phép dirty reads, non-repeatable reads, và phantom reads |
| READ COMMITTED | No | Possible | Possible | Ngăn chặn dirty reads, nhưng cho phép non-repeatable reads. Đây là cài đặt mặc định trong nhiều cơ sở dữ liệu như PostgreSQL. |
| REPEATABLE READ | No | No | DB-dependent | Ngăn chặn dirty and non-repeatable reads, nhưng vẫn có thể cho phép phantom reads (trong đó các record mới khớp với query vẫn có thể return). |
| SERIALIZABLE | No | No | No | Đảm bảo an toàn dữ liệu tối đa, nhưng có thể ảnh hưởng nghiêm trọng đến hiệu năng và gây ra high locking contention. |
Durability
Điều này có nghĩa là dữ liệu sau khi thực hiện transaction, sẽ vẫn tồn tại nếu chúng ta gặp vấn đề gì đó liên quan đến hệ thống như database crash chẳng hạn.
Ví dụ sau khi chuyển tiền vào tài khoản B thành công thì dù database có gặp bất cứ vấn đề gì thì tài khoản B vẫn đảm bảo đã nhận đủ 10$.



X981
Viết 1bài về transaction trong Spring đi bạn ơi.