登录
首页 >  数据库 >  MySQL

聊一聊事务的隔离级别以及常见的几个误区

来源:SegmentFault

时间:2023-02-24 21:52:29 428浏览 收藏

本篇文章向大家介绍《聊一聊事务的隔离级别以及常见的几个误区》,主要包括MySQL、数据库、spring,具有一定的参考价值,需要的朋友可以参考一下。

作者:折纸
个人博客:https://www.zhezhi.press
记录菜鸡的成长之旅!

本质

从本质上来说,事务的隔离级别是为并发控制服务的。在应用程序的开发中我们经常用锁进行并发控制,确保临界区的资源不会出现被多个线程同时读写的情况,这其实对应的就是数据库的可串行化级别
那为什么应用层可以提供可串行化的隔离级别而数据库不能提供呢?
在我的理解中,这是由于应用层对临界资源的访问都是内存操作,而数据库要保证持久性它需要把临界区的数据flush到磁盘中,这个IO操作是要比内存操作慢好几个数量级的,这导致临界区持有锁的时间变得不可接受、锁冲突的频率变大,数据库的性能会大大降低。

隔离级别

第一个误区:可重复读级别不担保解决丢失更新问题

维基百科上的数据库隔离级别)我认为是有误的

image.png

这里的问题在于事实上Repeatable Read可重复读级别并不担保不出现丢失更新问题,它只担保解决不可重复读问题。

反例证明如下(MySQL RR级别):

image.png

事务A、B同时进行卖出item A的过程,事务A售出4件,事务B售出1件,理论上库存记录应该为原始的10减去4减去1得到5才对,但最后库存的记录显示为9。这是典型的丢失更新问题,即2个事务同时开启read-modify-write操作序列,出现了一个事务覆盖了另外一个事务的写入,但这个过程中没有利用/包含/读取/获取到对方更新后的最新值(这表明事务A已经commit过了,有别于脏写),换句话说也就是事务B没有利用到事务A的更新结果,仿佛事务A的更新被丢失了一样。
下图是数据库隔离级别和可能出现的问题表格,“?”表明这个级别是否发生对应问题取决于数据库的具体实现
image.png

下图是MySQL的隔离级别,我们在前面的例子中已经解释过了MySQL RR不解决丢失更新问题,我们随后会解释为什么它也没有解决幻读问题。

image.png

异常情况

脏写

描述

事务A覆盖了其他事务未提交的写入

解决

通过行级锁即可解决,在任何隔离级别下都不会发生

脏读

描述

事务A读取了其他事务未提交的写入

解决

提供读已提交隔离级别以上的数据库都可以防止,MySQL中是通过RC级别下的MVCC解决的

读倾斜/不可重复读

描述

事务A在执行过程中,对同一个数据行在不同的时间点前后读取的结果不一致

解决

提供读可重复读隔离级别以上的数据库都可以防止,MySQL中是通过RR级别下的MVCC解决的

丢失更新

描述

两个事务同时执行read-modify-write的过程,出现了事务A覆盖了事务B的写入,但并没有包含事务B修改后的最新值(已commit),导致事务B的更新好像丢失了一样的结果。

解决

原子写操作

如果数据库提供了原子写操作,那就使用它以完成read-modify-write操作,这是推荐的最佳方案。例如在多数关系型数据库中

begin
SELECT * FROM gamer FOR UPDATE;
UPDATE gamer SET credit=credit+1 WHERE score>=740;
SELECT * FROM gamer;
commit;

image.png

Reference

[1] Isolation(database systems)-wikipedia)
[2] 對於 MySQL Repeatable Read Isolation 常見的三個誤解
[3] 《数据密集型应用系统设计》第七章

个人能力有限 本文内容如有错误欢迎批评指正!
欢迎访问我的个人博客!

好了,本文到此结束,带大家了解了《聊一聊事务的隔离级别以及常见的几个误区》,希望本文对你有所帮助!关注golang学习网公众号,给大家分享更多数据库知识!

声明:本文转载于:SegmentFault 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>
评论列表