当前位置:网站首页>两类更新丢失及解决办法
两类更新丢失及解决办法
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方法需要多一个版本列的维护。
个人建议:在用户并发数比较少且冲突比较严重的应用系统中选择悲观锁方法,其他情况首先乐观锁版本列法。
边栏推荐
- 面试题 01.02. 判定是否互为字符重排-辅助数组算法
- Bidding announcement: Panjin people's Hospital Panjin hospital database maintenance project
- torch.numel作用
- 无法将“pip”项识别为 cmdlet、函数、脚本文件或可运行程序的名称
- Logback日志框架第三方jar包 免费获取
- Rongyun won the 2022 China Xinchuang digital office portal excellence product award!
- iptables只允许指定ip地址访问指定端口
- Enterprise log analysis system elk
- Bidding announcement: Fujian Rural Credit Union database audit system procurement project (re bidding)
- 删除 console 语句引发的惨案
猜你喜欢
Tragedy caused by deleting the console statement
【DesignMode】代理模式(proxy pattern)
torch. Numel action
1亿单身男女“在线相亲”,撑起130亿IPO
pycharm 终端部启用虚拟环境
二叉搜索树(特性篇)
Shandong old age Expo, 2022 China smart elderly care exhibition, smart elderly care and aging technology exhibition
Balanced binary tree (AVL)
Notification uses full resolution
Pycharm terminal enables virtual environment
随机推荐
Laravel constructor and middleware execution order
php 自带过滤和转义函数
Have fun | latest progress of "spacecraft program" activities
【DesignMode】代理模式(proxy pattern)
Three. JS series (1): API structure diagram-1
Rongyun won the 2022 China Xinchuang digital office portal excellence product award!
修改配置文件后tidb无法启动
1亿单身男女“在线相亲”,撑起130亿IPO
01tire+链式前向星+dfs+贪心练习题.1
logback.xml配置不同级别日志,设置彩色输出
删除 console 语句引发的惨案
谈谈 SAP iRPA Studio 创建的本地项目的云端部署问题
一个普通人除了去工厂上班赚钱,还能干什么工作?
【DesignMode】模板方法模式(Template method pattern)
IP地址和物理地址有什么区别
Three. JS series (2): API structure diagram-2
Balanced binary tree (AVL)
iptables只允许指定ip地址访问指定端口
Talk about the cloud deployment of local projects created by SAP IRPA studio
asyncio 概念和用法