找不到 MySQL 丢失更新问题的解决方案

Posted

技术标签:

【中文标题】找不到 MySQL 丢失更新问题的解决方案【英文标题】:Cannot find solution to MySQL Lost Update Problem 【发布时间】:2021-07-07 02:41:07 【问题描述】:

我正在尝试修复 mysql 中的丢失更新问题,其中两个会话不会相互更新。基本上,问题如下:

A owes B $30. Later, A owes B another $20. In the end A needs to owe both $30 and $20, 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;

如果我运行这些

【问题讨论】:

【参考方案1】:

鉴于您的设置方式,丢失更新是不可避免的。问题是将余额选择为一个变量,然后用于更新表中的余额。

这在生产环境中不会发生,因为不需要为以后存储余额。

如果您将更新作为事务的一部分直接应用,那么一切都应该是一致的:

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 |
+----+---------+

【讨论】:

【参考方案2】:

您不必运行SELECT 来分配用户余额。可以在UPDATE完成

UPDATE accounts 
SET balance = balance  - 20 
WHERE id = 1;

注意balance = balance - 20

【讨论】:

以上是关于找不到 MySQL 丢失更新问题的解决方案的主要内容,如果未能解决你的问题,请参考以下文章

mysql 数据丢失更新的解决方法

消息丢失,而接收者的存在未在 OF 服务器中更新

POM 丢失,没有可用的依赖信息,找不到 cfg.xml

Laravel 分页 数据丢失问题解决

如果找不到,MySQL“好”的方式插入一行,如果找到则更新它

mysql 8.0找不到my.ini配置文件解决方案