@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
Post a Comment