Bạn có hai transaction cùng cập nhật dữ liệu.
Transaction A giữ dữ liệu mà B cần.
Transaction B lại giữ dữ liệu mà A cần.
Cả hai cùng chờ nhau.
Đó chính là Deadlock.
Điều đáng chú ý là Deadlock không đơn giản là “database đang bận”. Đây là một vòng chờ (circular wait) khiến các transaction liên quan không thể tiếp tục nếu không có hành động phá vòng chờ.
1. Deadlock xảy ra như thế nào?
Giả sử có hai bản ghi:
Employee 100
Employee 200
Hai transaction hoạt động đồng thời.
Transaction A
UPDATE employees
SET salary = salary * 1.1
WHERE employee_id = 100;
A đã khóa row 100.
Transaction B
UPDATE employees
SET salary = salary * 1.1
WHERE employee_id = 200;
B đã khóa row 200.
Đến đây chưa có vấn đề gì.
Nhưng sau đó:
Transaction A tiếp tục
UPDATE employees
SET salary = salary * 1.1
WHERE employee_id = 200;
A muốn row 200, nhưng row này đang bị B giữ.
→ A phải chờ B.
Trong khi đó B thực hiện:
UPDATE employees
SET salary = salary * 1.1
WHERE employee_id = 100;
B muốn row 100, nhưng row này đang bị A giữ.
→ B phải chờ A.
Ta có:
Transaction A
│
│ đang giữ
▼
Row 100
▲
│ đang chờ
│
Transaction B
Transaction B
│
│ đang giữ
▼
Row 200
▲
│ đang chờ
│
Transaction A
Hay đơn giản hơn:
A giữ 100 → chờ 200
↑ ↓
└──────────┘
B giữ 200 → chờ 100
Đây là Deadlock.
Oracle mô tả chính xác tình huống này: hai session chờ dữ liệu đang bị khóa bởi nhau và không transaction nào có thể tiếp tục.
2. Deadlock khác Lock thông thường thế nào?
Đây là điểm rất dễ nhầm.
Lock thông thường
Transaction A giữ row
↓
Transaction B chờ
↓
A COMMIT
↓
B tiếp tục
Không có Deadlock.
B chỉ đang waiting.
Deadlock
A giữ resource 1
↓
A chờ resource 2
B giữ resource 2
↓
B chờ resource 1
Hai bên tạo thành một vòng chờ.
Nếu không có cơ chế phát hiện và xử lý, chúng sẽ không thể tự giải quyết.
Vì vậy:
Lock có thể khiến transaction phải chờ. Deadlock khiến các transaction chờ lẫn nhau theo một vòng không thể tự phá.
3. Oracle xử lý Deadlock như thế nào?
Oracle có cơ chế tự phát hiện Deadlock.
Khi phát hiện Deadlock, Oracle sẽ rollback statement liên quan trong một transaction để giải phóng một phần row lock, từ đó phá vòng chờ. Transaction nhận lỗi thường thấy:
ORA-00060: deadlock detected while waiting for resource
Điểm rất quan trọng:
Oracle rollback statement gây ra Deadlock, không đồng nghĩa với việc toàn bộ transaction trước đó tự động bị rollback.
Ví dụ:
Transaction A
UPDATE row 100; ← thành công
UPDATE row 200; ← Deadlock
Statement thứ hai có thể bị rollback, nhưng thay đổi của statement đầu tiên không tự động biến mất chỉ vì lỗi Deadlock.
Vì vậy application vẫn cần xử lý transaction một cách rõ ràng sau khi nhận lỗi.
4. Làm thế nào để hạn chế Deadlock?
Không có cách nào đảm bảo mọi hệ thống hoàn toàn không thể gặp Deadlock, nhưng có thể giảm đáng kể khả năng xảy ra.
① Truy cập resource theo cùng một thứ tự
Đây là một nguyên tắc rất quan trọng.
Ví dụ tất cả transaction đều cập nhật theo thứ tự:
Row 100
↓
Row 200
thay vì transaction A:
100 → 200
và transaction B:
200 → 100
Khi các transaction sử dụng cùng một thứ tự truy cập resource, nguy cơ tạo vòng chờ sẽ giảm.
② Transaction nên ngắn
Không nên giữ transaction lâu hơn cần thiết.
Ví dụ:
BEGIN
UPDATE
UPDATE
UPDATE
...
COMMIT
Nếu transaction thực hiện thêm nhiều thao tác không cần thiết trước khi COMMIT, các lock có thể được giữ lâu hơn.
Transaction càng kéo dài → thời gian các transaction khác phải chờ càng tăng.
③ Chỉ lock những gì thực sự cần
Oracle tự động thực hiện locking cần thiết cho các thao tác dữ liệu; explicit locking như SELECT ... FOR UPDATE hoặc LOCK TABLE có thể thay đổi hành vi locking và cần được sử dụng cẩn thận.
Ví dụ:
SELECT *
FROM employees
WHERE employee_id = 100
FOR UPDATE;
Nếu không thực sự cần lock trước khi cập nhật, đừng sử dụng explicit locking một cách tùy tiện.
5. Khi debug Deadlock, nên nhìn vào đâu?
Khi gặp:
ORA-00060
đừng chỉ sửa câu SQL gây lỗi.
Hãy tìm chuỗi transaction nào đang giữ và chờ resource nào.
Có thể tư duy theo mô hình:
Transaction
↓
Đang giữ resource nào?
↓
Đang chờ resource nào?
↓
Transaction khác đang giữ resource đó?
↓
Transaction kia lại đang chờ resource của mình?
↓
Circular Wait → Deadlock
Trong Oracle, có các công cụ/view phục vụ việc theo dõi lock và session đang chờ lock; tài liệu Oracle cũng đề cập đến catblock.sql và utllockt.sql cho việc giám sát tình trạng lock.
Điều quan trọng nhất khi debug không phải là tìm “câu SQL chậm”, mà là tìm quan hệ giữa các transaction.
6. Một số hiểu lầm thường gặp
❌ “Hai transaction cùng update một row là Deadlock”
Không nhất thiết.
Có thể chỉ là:
A giữ row
B chờ A
A COMMIT
B tiếp tục
Đây là lock contention, không phải Deadlock.
❌ “Deadlock = query chạy lâu”
Không hẳn.
Query có thể chạy lâu vì nhiều nguyên nhân khác.
Deadlock có đặc điểm quan trọng là các transaction tạo thành vòng chờ lẫn nhau.
❌ “Oracle gặp Deadlock thì rollback toàn bộ transaction”
Không chính xác.
Oracle rollback statement liên quan để phá Deadlock; các thao tác trước đó trong transaction không tự động bị rollback chỉ vì statement đó thất bại.
❌ “Thấy Deadlock thì cứ retry vô hạn”
Không nên.
Retry có thể là một phần của chiến lược xử lý ở application, nhưng trước tiên phải tìm nguyên nhân tạo Deadlock. Nếu logic transaction vẫn tạo cùng một vòng chờ, retry chỉ có thể khiến vấn đề lặp lại.
Tóm lại
Có thể nhớ Deadlock bằng một câu:
A giữ thứ B cần
B giữ thứ A cần
↓
Cả hai chờ nhau
↓
DEADLOCK
Và khi thiết kế transaction:
Transaction ngắn
+
Thứ tự truy cập nhất quán
+
Lock đúng lúc
+
Xử lý lỗi rõ ràng
↓
Giảm nguy cơ Deadlock
Deadlock không phải vấn đề của riêng Database. Nó là vấn đề của cách ứng dụng quản lý Transaction và truy cập dữ liệu đồng thời.


