当前位置:网站首页>两类更新丢失及解决办法
两类更新丢失及解决办法
2022-07-07 14:21:00 【fastjson_】
我们首先创建一张user表,id为主键
CREATE TABLE `user` (
`id` int NOT NULL AUTO_INCREMENT,
`money` int DEFAULT NULL,
`version` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
第一类丢失更新(回滚丢失)
A事务撤销时,把已经提交的B事务的更新数据覆盖了。这种错误可能造成很严重的问题,通过下面的账户取款转账就可以看出来:
时间 | 取款事务A | 转账事务B |
T1 | begin; |
|
T2 | begin; 开始事务 | |
T3 | select money from user where id = 1; | |
T4 | select money from user where id = 1; | |
T5 | money = 1000 + 100; | |
T6 | commit; | |
T7 | money = 1000 - 100; | |
T8 | rollback; | |
T9 | 余额恢复为1000 元(丢失更新) |
A事务在回滚时,“不小心”将B事务已经转入账户的金额给抹去了。
在MySQL数据库,任何隔离级别不允许第一类更新丢失
第二类丢失更新(覆盖丢失/两次更新问题)
A事务覆盖B事务已经提交的数据,造成B事务所做操作丢失:
时间 | 转账事务A | 取款事务B |
T1 | begin; | begin; |
T2 | select money from user where id = 1; | |
T3 | select money from user where id = 1; |
|
T4 | money = 1000 + 100; update user set money=1100 where id = 1; 汇入100元 | |
T5 | commit; | |
T6 | select money from user where id = 1; |
|
T7 | money = 1000 - 100; update user set money=900 where id = 1; 取出100元把余额改为900元 | |
T8 | commit; | |
T9 | 把余额改为900 元(丢失更新) |
上面的例子里由于取款覆盖了转账对存款余额所做的更新,导致用户最后损失了100元,相反如果取款事务先提交,那么用银行将损失100元。
第二类丢失更新原因:MySQL可重复读默认采用的是快照读。快照读的一个问题也就是没有办法获取最新的数据。所以快照读是第二类更新丢失的一个主要原因。
解决快照读:基本两种思路,一种是悲观锁,另外一种是乐观锁;
简单的说就是一种假定这样的问题是高概率的,最好一开始就锁住,免得更新老是失败;另外一种假定这样的问题是小概率的,最后一步做更新的时候再锁住,免得锁住时间太长影响其他人做有关操作。
悲观锁
读取数据的时候加锁,这样能够保证读到的数据都是最新的数据,并且会将记录锁住,其他事物如果想要获取锁会阻塞。所以整体业务流程变成:在开启事物后要获取锁,然后根据业务场景不同进行不同操作
| begin; 开启事务 | begin; 开启事务 |
| select money from user where id = 1 for update; 查询账户余额为1000元 | |
| select money from user where id = 1 for update; 阻塞... | |
| money = 1000 - 100; update user set money=900 where id = 1; 取出100元把余额改为900元 | 阻塞... |
| commit; | 阻塞... |
| 查到数据 :money = 900 | |
| 其他操作.... |
乐观锁
添加version字段,记录每条记录的更新版本,每次更新后版本号加一。
在每次事物开启之前获取行的版本号,在更新的时候带上版本号进行判断。update money set money = money + 100 where id = 1 and version = 查到的版本号
select version from user where id = 1; | |
select version from user where id = 1; | |
begin; | begin; |
money = 1000 - 100; | |
| 更新成功,更新条数为1 | money = 1000 + 100; update user set money=1100,version = version +1 where id = 1 and version = 0; 阻塞... |
| 阻塞... | |
| commit | |
| 更新成功,更新条数为0(因为另一个事物修改了版本号现在版本号为1) |
总结:
悲观锁b方法是通过select..for update方式,这个可能会导致其他会话的阻塞,而乐观锁b方法需要多一个版本列的维护。
个人建议:在用户并发数比较少且冲突比较严重的应用系统中选择悲观锁方法,其他情况首先乐观锁版本列法。
边栏推荐
- Personal notes of graphics (1)
- Laravel5.1 路由 -路由分组
- 【C 语言】 题集 of Ⅹ
- 【知识小结】PHP使用svn笔记总结
- How to query the data of a certain day, a certain month, and a certain year in MySQL
- 【DesignMode】模板方法模式(Template method pattern)
- Xcode Revoke certificate
- Excessive dependence on subsidies, difficult collection of key customers, and how strong is the potential to reach the dream of "the first share of domestic databases"?
- Multiplication in pytorch: mul (), multiply (), matmul (), mm (), MV (), dot ()
- Laravel5.1 Routing - routing packets
猜你喜欢

Balanced binary tree (AVL)

Power of leetcode-231-2

Personal notes of graphics (3)

Imitate the choice of enterprise wechat conference room

平衡二叉树(AVL)

Step by step monitoring platform ZABBIX

统计学习方法——感知机

Plate - forme de surveillance par étapes zabbix

谈谈 SAP iRPA Studio 创建的本地项目的云端部署问题

The difference and working principle between compiler and interpreter
随机推荐
AutoLISP series (1): function function 1
二叉搜索树(特性篇)
Balanced binary tree (AVL)
markdown公式编辑教程
目标跟踪常见训练数据集格式
修改配置文件后tidb无法启动
分类模型评价标准(performance measure)
Bidding announcement: Fujian Rural Credit Union database audit system procurement project (re bidding)
Logback logging framework third-party jar package is available for free
Have fun | latest progress of "spacecraft program" activities
Xingruige database was shortlisted as the "typical solution for information technology application and innovation in Fujian Province in 2021"
121. 买卖股票的最佳时机
Asyncio concept and usage
【DesignMode】享元模式(Flyweight Pattern)
预测——灰色预测
URL和URI的关系
js中复选框checkbox如何判定为被选中
AutoLISP series (2): function function 2
PHP实现执行定时任务的几种思路详解
Description of vs common shortcut keys