- Deadlock là gì
- Deadlock xảy ra như thế nào (ví dụ trực quan)
- Làm sao biết hệ thống đang bị deadlock?
- Tại sao deadlock xảy ra?
- Cách tránh deadlock hiệu quả
- 1. Luôn update theo một thứ tự cố định
- 2. Tạo index để giảm số dòng bị lock
- 3. Transaction phải càng ngắn càng tốt
- 4. Dùng SELECT … FOR UPDATE đúng chỗ
- 5. Retry transaction khi gặp deadlock
- 6. Tránh UPDATE/DELETE theo điều kiện quá rộng
- Checklist chống deadlock
- Kết luận
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:
- Transaction A khóa dòng user A → chờ dòng user B
- 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