Skip to main content

Command Palette

Search for a command to run...

Transaction Isolation Level - REPEATABLE READ

Updated
4 min readView as Markdown

Dạo quanh với REPEATABLE READ – cái thằng đọc lì như trâu, đọc cái gì là cố giữ đúng y chang tới cùng.

Vô chuyện!

1. REPEATABLE READ là gì?

REPEATABLE READ có nghĩa là:

  • Khi một transaction đọc một dữ liệu, thì trong suốt cái transaction đó, đọc lại cũng phải thấy y như cũ.

  • Không ai được phép update hay delete cái dữ liệu mình đã đọc trong lúc mình đang làm việc.

→ Đọc lần 1 thế nào → Đọc lần 2 cũng thế → Không đổi.

Tuy nhiên:

  • Cái gì mình chưa đọc tới (ví dụ thêm dòng mới) thì vẫn có thể mọc ra, nên Phantom Read vẫn có thể xảy ra.

2. Vậy REPEATABLE READ bảo vệ được những gì?

Hiện tượngCó xảy ra ở REPEATABLE READ?
Dirty ReadKhông
Non-repeatable ReadKhông
Phantom ReadCó thể (dữ liệu mới mọc ra thêm dòng)

3. Một số tình huống

Bối cảnh:

  • Tôi kiểm tra giá món hàng trong kho.

  • Query 1: SELECT price FROM product WHERE id = 555;

  • Trong lúc tôi đang ngủ gật, thằng sale kia định tăng giá và commit.

→ Nhưng nếu cấu hình ở level REPEATABLE READ, thì:

  • Nó không cập nhật được đâu.

  • Nó phải chờ tui commit hoặc rollback xong nó mới làm được.

Diễn giải:
→ Dữ liệu tui đã đọc thì khóa cứng luôn.
→ Người khác chỉ biết ngồi ngáp chờ tui xong.

Kết quả:

  • Đọc 1 lần hay 10 lần, giá vẫn như ban đầu.

  • An toàn, chắc cú.

4. Một lưu ý cần nhớ

  • REPEATABLE READ giữ giá trị rất chặt, nhưng không bảo vệ hoàn toàn việc mọc thêm dòng (Phantom Read).

  • MySQL (InnoDB) xử lý Phantom Read luôn, nhưng chuẩn SQL thì REPEATABLE READ vẫn dính Phantom Read.

  • Nếu cần đọc mà giữ nguyên mọi thứ mình thấy lần đầu, không cho phép thay đổi gì cả, thì REPEATABLE READ là một chọn khá chuẩn.

  • Nhưng nhớ, vì nó khóa lâu hơn, nên: → Dễ bị deadlock (khoá cứng dữ liệu làm nhau kẹt cứng).
    → Phải xài cẩn thận với mấy transaction kéo dài lê thê.

5. Ví dụ demo SQL cho chắc tay

-- Session 1
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRANSACTION;
SELECT price FROM product WHERE id = 555;
-- Đọc xong, để nguyên session này.

-- Session 2
BEGIN TRANSACTION;
UPDATE product SET price = price + 10 WHERE id = 555;
-- Session 2 sẽ bị kẹt, chờ Session 1 commit hoặc rollback!

-- Quay lại Session 1
COMMIT;
-- Giờ Session 2 mới được chạy.

Kết quả:

  • Trong suốt transaction của Session 1, giá sản phẩm y chang dù thằng khác rình mò update.

  • Session 2 bị khóa chờ.

6. Nâng cao

Trong REPEATABLE READ, cái gọi là Phantom Read chỉ xảy ra khi CÓ THÊM bản ghi mới thỏa điều kiện query thôi, không phải khi bản ghi bị bớt đi.

Giải thích kỹ hơn chút:

Hành độngCó phải Phantom Read không?
Thêm bản ghi mới (insert) vào và nó thỏa điều kiện query ban đầuCó Phantom Read
Xóa bản ghi (delete) làm nó biến mất khỏi kết quả query ban đầuKhông Phantom Read
Update bản ghi làm nó mới thỏa điều kiện query ban đầuCó Phantom Read

Tại sao xóa bản ghi lại không gọi là Phantom Read?

Vì bản chất Phantom Read là:

"Đọc lần 1, thấy 5 bản ghi. Đọc lần 2, lại thấy 6 bản ghi (có thêm 1 bản ghi mới thỏa điều kiện mà hồi nãy không có)."

Mục tiêu của Repeatable Read là cố gắng giữ cho dữ liệu thấy lúc đầu "cố định" trong cả transaction.

  • Nếu bản ghi bị mất đi thì lần sau SELECT vẫn chỉ đọc snapshot cũ (nó giữ nguyên theo Isolation của transaction).

  • Nếu bản ghi mới chèn vào, bản ghi này không nằm trong snapshot cũ → mà lại thỏa điều kiện query, thì mới lòi ra cái gọi là Phantom.

7. Tổng kết

Đặc điểmNội dung
Bảo vệ khỏi Dirty Read
Bảo vệ khỏi Non-repeatable Read
Bảo vệ khỏi Phantom ReadCó thể, tuỳ DBMS
Ứng dụng thích hợpGiao dịch yêu cầu cực kỳ ổn định dữ liệu, như ngân hàng, ví điện tử, e-commerce

Nếu muốn đọc một lần và yên tâm rằng không ai sửa phá gì được tới lúc mình xong việc, thì REPEATABLE READbạn chí cốt. Chỉ cần nhớ là, đừng giữ khóa lâu quá, không thì chết kẹt chung với thiên hạ đó.

More from this blog

Engineer log

62 posts