Hiểu về Deadlock trong MySQL
Khi nhiều giao dịch (transaction) cùng truy cập vào một tập dữ liệu, việc tranh chấp khóa (lock) có thể xảy ra. Nếu không được xử lý, các giao dịch này có thể rơi vào trạng thái chờ đợi lẫn nhau mãi mãi—đây chính là hiện tượng deadlock. Trong MySQL, đặc biệt với InnoDB, deadlock không phải là chuyện hiếm, nhất là khi ứng dụng có nhiều luồng giao dịch đồng thời.
Nguyên nhân gây ra Deadlock
Có nhiều nguyên nhân dẫn đến deadlock, nhưng chủ yếu xuất phát từ cách các giao dịch truy cập và khóa dữ liệu. Một số tình huống phổ biến:
– Khóa chéo (Circular Lock): Giao dịch A khóa dòng 1 rồi muốn khóa dòng 2; đồng thời, giao dịch B khóa dòng 2 rồi muốn khóa dòng 1. Cả hai đều chờ nhau, tạo thành vòng lặp.
– Thứ tự truy cập không nhất quán: Nếu các ứng dụng không thống nhất thứ tự truy cập bảng hay dòng, khả năng xảy ra deadlock tăng cao.
– Thời gian giữ khóa quá lâu: Giao dịch mở nhiều khóa và giữ chúng trong thời gian dài, làm tăng nguy cơ xung đột.
– Isolation level cao: Mức độ cô lập (isolation level) cao như REPEATABLE READ hay SERIALIZABLE làm tăng khả năng khóa dữ liệu, từ đó tăng nguy cơ deadlock.
Cách xử lý Deadlock khi xảy ra
1. Bật log và phân tích
MySQL cung cấp tính năng ghi lại thông tin deadlock vào error log. Bật tính năng này giúp bạn biết được giao dịch nào bị hủy, nguyên nhân, và cách khắc phục.
SET GLOBAL innodb_print_all_deadlocks = ON;2. Bắt lỗi và retry
Khi giao dịch bị hủy do deadlock, InnoDB tự động rollback một trong các giao dịch và trả về lỗi 1213 (SQLSTATE: 40001). Ứng dụng cần bắt lỗi này và retry giao dịch.
try {
// thực hiện giao dịch
} catch (PDOException $e) {
if ($e->getCode() == '40001') {
// retry giao dịch
}
}