@Transactional in SpringBoot

Phần này chúng ta sẽ tìm hiểu sức mạnh một lần nữa của @Transactional trong SpringBoot

Như bạn đã biết khi một hệ thống tra tấn 1 DB đồng nghĩa với việc khả năng tương tranh dữ liệu trong 1 Giao dịch trong DB xảy ra là rất cao. Tuy nhiên trong thực tế các dự án nhỏ chúng ta thường bỏ qua vấn đề cô lập transaction khi code, hoặc một số là do developer không hiểu hoặc không để ý đến tầm ảnh hưởng của việc tương tranh dữ liệu trong DB.

Các bạn nếu là người mới bắt đầu tìm hiểu thì có lẽ bài này sẽ giúp ích phần nào cho bạn hiểu về Cô lập transaction 
Bạn có thể đọc wiki để hiểu rõ về các tình huống có thể xảy ra với một transaction

Isolation (database systems)  là một trong 4 tính chất ACID mà một DB cần có. 

Tôi sẽ đi thẳng vào bốn tình huống 

Dirty reads

Tình huống này giống như kiểu ví dụ sau đây


Bạn có thể thấy hệ thống của bạn đang có 2 transaction cùng truy cập tới một bản ghi có ID=1 

Khi transaction1 đang lấy dữ liệu bản ghi này OK thì transaction2 cũng bắt đầu update dữ liệu bản ghi này

Tiếp theo là transaction1 lại đọc lại dữ liệu bản ghi này và thấy dữ liệu khác lúc lần 1 đọc.
Tôi giả sử Transaction1 đọc được dữ liệu sau đó hiển thị ra màn hình cho người dùng là dữ liệu chưa được commit từ transaction2.

Sau đó transaction2 Rollback dữ liệu

==> Transaction1 đang hiển thị sai dữ liệu không thực tế. 

Điều này là tối kị trong các hệ thống xử lý giao dịch tài chính ngân hàng hoặc một số hệ thống chứng khoán.



Non-repeatable reads



Tình huống này như sau 

Cũng tương tự tình huống trên nhưng dữ liệu đang thao tác ở transaction2 là đã commit, dẫn đến dữ liệu cùng bản ghi đó ở transaction1 bị thay đổi.
Bạn có thể thấy dữ liệu cùng 1 bản ghi ở transaction1 tại 2 thời điểm là khác nhau.

Tôi lấy ví dụ như sau để thể hiện lỗi này
ví du bạn có 1 bản ghi  trong bảng USERS( id, age, name, address)
bạn có 1 bản ghi là    id=1, age = 20, name ="james", address=hanoi

và bên transaction2 đang update và commit id=1 age= 21

ví dụ transaction1 của bạn là trả về danh sách các Nhân viên có quê quán ở Hà nôi, và một danh sách khác là trả về danh sách các nhân viên có tuổi là >=21 

Kết quả hiển thị như sau

Danh sách nhân viên có tuổi >=21
Tên            Tuổi                   địa chỉ
james           21                      hanoi

Danh sach nhân viên có quê quán ở hà nội là 
Tên            Tuổi                   địa chỉ
james          20                        hanoi 



======> Khi Sếp tổng đọc báo cáo thấy tại sao có một ông nhân viên mà lại in ra 2 ông khác nhau trong 1 lúc. Trong khi hệ thống in ra tại 1 thời điểm. Dẫn đến tính không nhất quán dữ liệu.

Nếu chúng ta xử lý dữ liệu tại thời điểm đó cho trường hợp Nhân viên này sẽ dễ dấn đến sai sót, vì điều kiện dữ liệu đã bị thay đổi trong lúc tính toán.


==> Tuy vậy bạn nên cân nhắc trường hợp này nếu xử lý dữ liệu quan trọng


Phantom reads






Trường hợp này nó như thế này.

Cũng tương tự  như trường hợp 2, nhưng lần này là có dữ liệu thêm mới. hoặc xóa mất

Dẫn đến số lượng bản ghi ở Transaction1 là khác nhau tại 2 thời điểm, mặc dù là nó có cùng điều kiện

Tôi cũng lấy ví dụ như này

Bạn có 1 transaction và thông tin cần trả về là :

- Tổng Số lượng nhân viên ở Hà Nội
- Danh sách nhân viên ở Hà nội có tên là Bob:
- Danh sách nhân viên ở Hà nội có tuổi là từ 10 đến 30

Ví dụ Trước khi 2 transaction bắt đầu thì DB đang rỗng
Dẫn đến câu truy vấn thứ nhất ở Transaction1 trả về số lượng bản ghi là 0,
Câu truy vấn thứ 2 của transaction1 lại trả về 1 bản ghi, do transactrion2 đã commited data insert
(Giả sử data là (Tên Bob, tuổi 27, address hà nội))

===> Suy ra báo cáo in ra là :
----------------------------------------------------------------------------
Số lượng Nhân viên ở Hà nội là 0;

Danh sách nhân viên ở Hà nội có tên là Bob
Không có ai

Danh sách nhân viên ở Hà Nội có tuổi  từ 10->30
Tên             Tuổi 
Bob             27
----------------------------------------------------------------------------         
Bạn có thấy báo cáo rất khập khiễng không, trên thì in không có danh sách nhân viên nào là Bob và không có sinh viên ở Hà Nội 

Dưới thì lại có một thằng ở Hà Nội mà lại có tuổi từ 10->30
============================END===================================

Vậy để giải quyết cái này thì làm thế nào ????

Đó là áp dụng isolation trong DB khi  mỗi transaction thực hiện

Có 4 mức mà các DB luôn có là 

Read uncommitted

Mức này cho phép đọc dữ liệu chưa committed, RẤT NGUY HIỂM, bạn không nên áp dụng mức này
Một số DB như PostgreSQL không cho đọc dữ liệu như thế này. Bạn vẫn truyền mức này vào Transaction tuy nhiên nó k lỗi, nhưng nó không thực hiện đc việc đọc dữ liệu chưa commit.

Read committed


Cho phép đọc dữ liệu đã commit, nhưng nó vẫn dễ dàng bị rơi vào 2 tình huống lỗi cuối bên trên 

Repeatable reads


Nếu áp dụng mức này đồng nghĩa với việc đọc dữ liệu nhiều lần trong 1 giao dịch đều nhận được kết quả như khi đọc lần đầu tiên,==> Tuy nhiên tình huống này vẫn có thể xảy ra lỗi phantom

Serializable

Mức này là cao nhất, có nghĩa là không một tình huống lỗi nào bên trên có thể xảy ra được
TUy nhiên nếu dùng level này thì hệ thống cũng hiệu năng kém hơn.

======================Apply trong SpringBoot======================



@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Transactional(isolation = Isolation.READ_COMMITTED)



@Transactional(isolation = Isolation.REPEATABLE_READ)



@Transactional(isolation = Isolation.SERIALIZABLE)




Tôi đã test và hoatj động tốt.
TUy nhiên đối với từng DB khác nhau nó sẽ có cơ chế xử lý khác nhau. Bạn nên tìm hiểu thêm






Comments

Popular posts from this blog

Fixing the DeepSpeed Import Error While Fine-Tuning the Qwen Model

Amazon Linux 2023 - User data configuration for launch templates to connect to the EKS cluster

How to create ISM policy and rotate logs in opensearch