Deadlock là gì? Tại sao query của bạn tự nhiên treo?

Database chạy ngon lành, bỗng một ngày query treo, CPU tăng vọt, log báo lỗi kiểu “Deadlock found when trying to get lock”. Không phải database hư — mà là các query đang… giữ nhau làm con tin. Đây gọi là deadlock. Hiểu được nguyên nhân và cách tránh deadlock sẽ giúp hệ thống ổn định hơn, đặc biệt trong môi trường nhiều transaction.

Deadlock là gì? Tại sao query của bạn tự nhiên treo?

Deadlock là gì

Deadlock xảy ra khi hai (hoặc nhiều) transaction cùng khóa tài nguyên và chờ nhau giải phóng. Không transaction nào nhường, cả hai đều đứng hình.

Ví dụ đơn giản:

  1. Transaction A khóa dòng user A → chờ dòng user B
  2. Transaction B khóa dòng user B → chờ dòng user A

Cả hai cùng chờ nhau vô thời hạn. Database phải chọn một transaction để hủy, trả lỗi deadlock.

Deadlock xảy ra như thế nào (ví dụ trực quan)

Giả sử bảng accounts:

BEGIN;                   -- Transaction A
UPDATE accounts SET balance = balance - 100 WHERE id = 1;

BEGIN;                   -- Transaction B
UPDATE accounts SET balance = balance - 50 WHERE id = 2;

-- A tiếp tục
UPDATE accounts SET balance = balance + 100 WHERE id = 2;

-- B tiếp tục
UPDATE accounts SET balance = balance + 50 WHERE id = 1;

Lúc này:

  • A khóa record id = 1 → muốn update id = 2
  • B khóa record id = 2 → muốn update id = 1

→ Deadlock.

Làm sao biết hệ thống đang bị deadlock?

Database sẽ log lỗi tương tự:

ERROR 1213 (40001): Deadlock found when trying to get lock

Trong MySQL/MariaDB, xem deadlock trong:

SHOW ENGINE INNODB STATUS;

Tại sao deadlock xảy ra?

  • Transaction quá dài
  • Update nhiều dòng theo thứ tự khác nhau
  • Thiếu index → database phải lock nhiều dòng hơn cần thiết
  • Nhiều transaction update cùng một resource theo logic khác nhau

Cách tránh deadlock hiệu quả

1. Luôn update theo một thứ tự cố định

Nếu nhiều transaction update nhiều dòng, phải thống nhất thứ tự:

-- Đúng: update theo thứ tự id tăng dần
UPDATE accounts SET ... WHERE id IN (1, 2) ORDER BY id ASC;

2. Tạo index để giảm số dòng bị lock

Query thiếu index → database lock nhiều record → tăng khả năng deadlock.

CREATE INDEX idx_accounts_user_id ON accounts (user_id);

3. Transaction phải càng ngắn càng tốt

Đừng làm logic nặng trong transaction, ví dụ:

BEGIN;
UPDATE accounts ...
CALL external_service();    -- Sai: gọi API trong transaction!
COMMIT;

4. Dùng SELECT … FOR UPDATE đúng chỗ

Khoá record ngay từ đầu, tránh lock bất ngờ ở cuối query.

SELECT balance FROM accounts
WHERE id = 1
FOR UPDATE;

5. Retry transaction khi gặp deadlock

Deadlock là chuyện bình thường trên hệ thống lớn. Nên retry transaction.

Pseudo-code minh họa:

while true:
    try:
        begin transaction
        execute logic
        commit
        break
    except Deadlock:
        retry

6. Tránh UPDATE/DELETE theo điều kiện quá rộng

Ví dụ cực nguy hiểm:

UPDATE invoices SET status = 'paid' WHERE user_id = 123;

Nếu không có index, DB lock hàng ngàn dòng → rất dễ deadlock.

Checklist chống deadlock

  • Có index đúng trên WHERE / JOIN?
  • Update theo thứ tự cố định?
  • Transaction có ngắn và gọn không?
  • Không logic phức tạp trong transaction?
  • Đã retry transaction khi gặp deadlock chưa?

Kết luận

Deadlock không phải lỗi database, mà là hệ quả của cách bạn viết query và transaction. Chỉ cần tối ưu thứ tự update, thêm index và giữ transaction ngắn, deadlock sẽ biến mất hoặc giảm đáng kể. Database chạy nhanh hơn, hệ thống ổn định hơn, và bạn bớt đau đầu hơn.

Bình luận


  • Không có bình luận.

Init Toolbox

Nhấn Ctrl + \ trên máy tính, hoặc vuốt sang trái ở bất kỳ đâu trên mobile.

Đăng nhập





Đang tải...