找不到 MySQL 丢失更新问题的解决方案
Cannot find solution to MySQL Lost Update Problem
我正在尝试修复 MySQL 中的丢失更新问题,其中两个会话不会相互更新。基本上,问题如下:
A owes B . Later, A owes B another . In the end A needs to owe both and , where A eventually has 50 and B has 150.
我打开了两个终端会话,但不确定如何解决并发问题。我已经搜索了所有内容以找到解决方案,但没有运气,我怎么能让 A 和 B 数字在两个会话中得到相同的结果。
以下是我从两个终端会话中获取的分步过程。
会话 A:
UPDATE accounts SET balance = 100;
SELECT * FROM accounts;
START TRANSACTION;
SELECT balance INTO @user1_balance from accounts where id = 1;
SELECT balance INTO @user2_balance from accounts where id = 2;
SELECT @user1_balance, @user2_balance;
会话 B:
START TRANSACTION;
SELECT balance INTO @user1_balance from accounts where id = 1;
SELECT balance INTO @user2_balance from accounts where id = 2;
SELECT @user1_balance, @user2_balance;
会话 A:
UPDATE accounts
SET balance = @user1_balance - 30
WHERE id = 1;
UPDATE accounts
SET balance = @user2_balance + 30
WHERE id = 2;
COMMIT;
SELECT * FROM accounts;
会话 B:
SELECT * FROM accounts;
会话 B:
UPDATE accounts
SET balance = @user1_balance - 20
WHERE id = 1;
UPDATE accounts
SET balance = @user2_balance + 20
WHERE id = 2;
COMMIT;
SELECT * FROM accounts;
如果我运行这些
鉴于您的设置方式,丢失更新是不可避免的。问题是将余额选择为变量,然后用于更新 table.
中的余额
这不会发生在生产环境中,因为不需要存储余额供以后使用。
如果您直接将更新作为事务的一部分进行应用,一切都应该保持一致:
A 航站楼:
START TRANSACTION;
UPDATE accounts set balance = balance -30 where id = 1;
UPDATE accounts set balance = balance +30 where id = 2;
B 航站楼:
START TRANSACTION;
UPDATE accounts set balance = balance -20 where id = 1;
-- This update is blocked by the outstanding COMMIT on terminal A. Terminal B waits
A 航站楼:
COMMIT;
B 航站楼:
-- The COMMIT on Terminal A unlocks the rows, so we can complete our update.
UPDATE accounts set balance = balance +20 where id = 2;
COMMIT;
现在两个终端显示相同的结果:
select * from accounts;
+----+---------+
| id | balance |
+----+---------+
| 1 | 50.00 |
| 2 | 150.00 |
+----+---------+
您不必 运行 和 SELECT
分配用户余额。可以在 UPDATE
完成
UPDATE accounts
SET balance = balance - 20
WHERE id = 1;
注意 balance = balance - 20
。
我正在尝试修复 MySQL 中的丢失更新问题,其中两个会话不会相互更新。基本上,问题如下:
A owes B . Later, A owes B another . In the end A needs to owe both and , where A eventually has 50 and B has 150.
我打开了两个终端会话,但不确定如何解决并发问题。我已经搜索了所有内容以找到解决方案,但没有运气,我怎么能让 A 和 B 数字在两个会话中得到相同的结果。
以下是我从两个终端会话中获取的分步过程。
会话 A:
UPDATE accounts SET balance = 100;
SELECT * FROM accounts;
START TRANSACTION;
SELECT balance INTO @user1_balance from accounts where id = 1;
SELECT balance INTO @user2_balance from accounts where id = 2;
SELECT @user1_balance, @user2_balance;
会话 B:
START TRANSACTION;
SELECT balance INTO @user1_balance from accounts where id = 1;
SELECT balance INTO @user2_balance from accounts where id = 2;
SELECT @user1_balance, @user2_balance;
会话 A:
UPDATE accounts
SET balance = @user1_balance - 30
WHERE id = 1;
UPDATE accounts
SET balance = @user2_balance + 30
WHERE id = 2;
COMMIT;
SELECT * FROM accounts;
会话 B:
SELECT * FROM accounts;
会话 B:
UPDATE accounts
SET balance = @user1_balance - 20
WHERE id = 1;
UPDATE accounts
SET balance = @user2_balance + 20
WHERE id = 2;
COMMIT;
SELECT * FROM accounts;
如果我运行这些
鉴于您的设置方式,丢失更新是不可避免的。问题是将余额选择为变量,然后用于更新 table.
中的余额这不会发生在生产环境中,因为不需要存储余额供以后使用。
如果您直接将更新作为事务的一部分进行应用,一切都应该保持一致:
A 航站楼:
START TRANSACTION;
UPDATE accounts set balance = balance -30 where id = 1;
UPDATE accounts set balance = balance +30 where id = 2;
B 航站楼:
START TRANSACTION;
UPDATE accounts set balance = balance -20 where id = 1;
-- This update is blocked by the outstanding COMMIT on terminal A. Terminal B waits
A 航站楼:
COMMIT;
B 航站楼:
-- The COMMIT on Terminal A unlocks the rows, so we can complete our update.
UPDATE accounts set balance = balance +20 where id = 2;
COMMIT;
现在两个终端显示相同的结果:
select * from accounts;
+----+---------+
| id | balance |
+----+---------+
| 1 | 50.00 |
| 2 | 150.00 |
+----+---------+
您不必 运行 和 SELECT
分配用户余额。可以在 UPDATE
UPDATE accounts
SET balance = balance - 20
WHERE id = 1;
注意 balance = balance - 20
。