当前位置:网站首页>两类更新丢失及解决办法
两类更新丢失及解决办法
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方法需要多一个版本列的维护。
个人建议:在用户并发数比较少且冲突比较严重的应用系统中选择悲观锁方法,其他情况首先乐观锁版本列法。
边栏推荐
- 【DesignMode】代理模式(proxy pattern)
- AutoLISP series (2): function function 2
- Laravel changed the session from file saving to database saving
- 华东师大团队提出,具有DNA调控电路的卷积神经网络的系统分子实现
- 全网“追杀”钟薛高
- Prometheus API deletes all data of a specified job
- laravel构造函数和中间件执行顺序问题
- 【知识小结】PHP使用svn笔记总结
- Bidding announcement: Panjin people's Hospital Panjin hospital database maintenance project
- MySQL中, 如何查询某一天, 某一月, 某一年的数据
猜你喜欢
网关Gateway的介绍与使用
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"?
Shandong old age Expo, 2022 China smart elderly care exhibition, smart elderly care and aging technology exhibition
spark调优(三):持久化减少二次查询
Prediction - Grey Prediction
Logback日志框架第三方jar包 免费获取
Tragedy caused by deleting the console statement
【DesignMode】外观模式 (facade patterns)
Strengthen real-time data management, and the British software helps the security construction of the medical insurance platform
Xcode Revoke certificate
随机推荐
统计学习方法——感知机
Description of vs common shortcut keys
How can laravel get the public path
PHP中exit,exit(0),exit(1),exit(‘0’),exit(‘1’),die,return的区别
应用程序和matlab的通信方式
Enterprise log analysis system elk
JS 模块化
平衡二叉树(AVL)
PHP实现微信小程序人脸识别刷脸登录功能
Balanced binary tree (AVL)
【C 语言】 题集 of Ⅹ
PyTorch 中的乘法:mul()、multiply()、matmul()、mm()、mv()、dot()
Cesium(3):ThirdParty/zip. js
Odoo集成Plausible埋码监控平台
asyncio 概念和用法
three.js打造酷炫下雪效果
Laravel 服务提供者实例教程 —— 创建 Service Provider 测试实例
SqlServer2014+: 创建表的同时创建索引
The inevitable trend of the intelligent development of ankerui power grid is that microcomputer protection devices are used in power systems
laravel中将session由文件保存改为数据库保存