目录

第一类更新丢失(回滚丢失)

第二类更新丢失(覆盖丢失)

如何解决第二类更新丢失

快照读

解决快照读

悲观锁

乐观锁


第一类更新丢失(回滚丢失)

开启事物A

开启事物B

查询id = 1的数据age = 500

更新id为1的数据保存age= 100

更新id为1的数据保存age = 200

提交事物

回滚事物

age恢复500,更新丢失

在MySQL数据库,任何隔离级别不允许第一类更新丢失

第二类更新丢失(覆盖丢失)

开启事物A

开启事物B

查询id = 1的数据age = 500

更新id为1的数据保存age= 100

更新id为1的数据保存age = 200

提交事物

提交事物

age被修改为200,更新丢失

如何解决第二类更新丢失

为了解决不可重复读的问题,MySQL提出了可重复读隔离级别

MySQL可重复读默认采用的是一致性非锁定读,也就是快照读。

快照读

快照读的一个问题也就是没有办法获取最新的数据。

开启事物A

开启事物B

查询id为1的数据 money = 100

更新id为1的数据设置money = 200

提交事物B

查询id为1的数据 money = 100

快照读是第二类更新丢失的一个主要原因。

解决快照读

悲观锁

将一致性非锁定读替换为一致性锁定读

这样能够保证读到的数据都是最新的数据,并且会将记录锁住,其他事物如果想要获取锁会阻塞

所以整体业务流程变成:在开启事物后要获取锁,然后根据业务场景不同进行不同操作

开启事物A

开启事物B

select * from money where id = 1 for update

查到数据 :money = 100

select * from money where id = 1 for update

update money set money = money +100 where id = 1;

阻塞...

commit

阻塞...

查到数据 :money = 200

其他操作....

乐观锁

添加version字段,记录每条记录的更新版本,每次更新后版本号加一。

在每次事物开启之前获取行的版本号,在更新的时候带上版本号进行判断。update money set money = money + 100 where id = 1 and version = 查到的版本号

select version from money where id = 1;

select version from money where id = 1;

获取版本号为0

获取版本号为0

开启事物A

开启事物B

update money set money = money +100 , version = verison +1 where id = 1 and version = 0;

更新成功,更新条数为1

update money set money = money +100 , version = verison +1 where id = 1 and version = 0;

阻塞...

commit

更新成功,更新条数为0(因为另一个事物修改了版本号现在版本号为1)

rollback;

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐