Data Race vs. Race Condition
Vì thường sử dụng MySQL nhiều trong công việc nên phạm vi bài này sử dụng chủ yếu ví dụ MySQL, tuy nhiên từ những khái niệm và ví dụ này có thể mở rộng sang các loại CSDL khác.
Data Race trong MySQL
Data Race xảy ra khi nhiều transaction (giao dịch) đồng thời đọc và ghi vào cùng một dữ liệu mà không có cơ chế đồng bộ thích hợp, dẫn đến mất dữ liệu hoặc dữ liệu không chính xác.
Ví dụ Data Race trong MySQL
Giả sử có bảng bank_accounts chứa số dư tài khoản ngân hàng:
CREATE TABLE bank_accounts (
account_id INT PRIMARY KEY,
balance DECIMAL(10,2)
);
INSERT INTO bank_accounts (account_id, balance) VALUES (1001, 5000);
Hai giao dịch T1 và T2 cùng thực hiện rút tiền từ tài khoản 1001.
Transaction 1 (T1) - Rút 1000 VND
START TRANSACTION;
SELECT balance FROM bank_accounts WHERE account_id = 1001; -- balance = 5000
UPDATE bank_accounts SET balance = 5000 - 1000 WHERE account_id = 1001; -- balance = 4000
COMMIT;
Transaction 2 (T2) - Rút 500 VND
START TRANSACTION;
SELECT balance FROM bank_accounts WHERE account_id = 1001; -- balance = 5000
UPDATE bank_accounts SET balance = 5000 - 500 WHERE account_id = 1001; -- balance = 4500
COMMIT;
Kết quả sai do Data Race
Cả
T1vàT2đều đọcbalance = 5000.T1ghibalance = 4000.T2ghibalance = 4500.Giá trị đúng phải là
3500, nhưng cuối cùng lại là4500do mất bản cập nhật của T1.
Cách khắc phục Data Race trong MySQL
1. Dùng khóa hàng (Row Locking) với SELECT … FOR UPDATE
START TRANSACTION;
SELECT balance FROM bank_accounts WHERE account_id = 1001 FOR UPDATE;
UPDATE bank_accounts SET balance = balance - 1000 WHERE account_id = 1001;
COMMIT;
FOR UPDATE khóa hàng, đảm bảo chỉ một transaction cập nhật tại một thời điểm.
2. Dùng Isolation Level cao hơn (REPEATABLE READ hoặc SERIALIZABLE)
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
3. Optimistic Locking (sử dụng cột version)
UPDATE bank_accounts
SET balance = balance - 1000, version = version + 1
WHERE account_id = 1001 AND version = 1;
Race Condition trong MySQL
Race Condition xảy ra khi thứ tự thực thi của các transaction ảnh hưởng đến kết quả cuối cùng.
Ví dụ Race Condition trong MySQL
Giả sử có hệ thống đặt vé máy bay với bảng tickets:
CREATE TABLE tickets (
ticket_id INT PRIMARY KEY,
available INT
);
INSERT INTO tickets (ticket_id, available) VALUES (123, 1);
Hai người dùng A và B cùng đặt vé:
Transaction của A
START TRANSACTION;
SELECT available FROM tickets WHERE ticket_id = 123; -- available = 1
UPDATE tickets SET available = available - 1 WHERE ticket_id = 123;
COMMIT;
Transaction của B (chạy gần như cùng lúc)
START TRANSACTION;
SELECT available FROM tickets WHERE ticket_id = 123; -- available = 1
UPDATE tickets SET available = available - 1 WHERE ticket_id = 123;
COMMIT;
Lỗi xảy ra: Cả A và B đều mua được vé dù chỉ có 1 vé
Cách khắc phục Race Condition trong MySQL
1. Sử dụng Locking (SELECT FOR UPDATE)
START TRANSACTION;
SELECT stock FROM products WHERE id = 1 FOR UPDATE;
IF stock > 0 THEN
UPDATE products SET stock = stock - 1 WHERE id = 1;
INSERT INTO orders (product_id) VALUES (1);
END IF;
COMMIT;
Giữ khóa trên hàng dữ liệu, đảm bảo chỉ một giao dịch cập nhật vé tại một thời điểm.
2. Dùng Atomic Operation với CAS - Compare And Swap
UPDATE tickets
SET available = available - 1
WHERE ticket_id = 123 AND available > 0;
Chỉ cập nhật nếu vé vẫn còn (available > 0), tránh tình trạng overbooking.
3. Sử dụng Queue (Message Queue như Kafka, RabbitMQ)
Gửi yêu cầu đặt vé vào một hàng đợi.
Xử lý từng yêu cầu một cách tuần tự để đảm bảo không có Race Condition.
So sánh Data Race và Race Condition trong MySQL
| Tiêu chí | Data Race | Race Condition |
| Nguyên nhân | Nhiều transaction ghi vào cùng dữ liệu mà không đồng bộ | Thứ tự thực thi ảnh hưởng đến kết quả, lỗi logic nghiệp vụ |
| Hậu quả | Dữ liệu bị ghi sai hoặc mất dữ liệu | Hành vi không mong muốn, có thể không bị lỗi ngay lập tức |
| Gây ra do | Transaction không khóa dữ liệu | Transaction xử lý không đúng thứ tự |
| Ví dụ | Cập nhật số dư tài khoản sai | Đặt vé máy bay quá số lượng thực tế. Bán 1 món hàng cho 2 người |
| Cách khắc phục | Locking, Isolation Level, Optimistic Locking | Locking, Atomic Operation, Queue |