透过“锁”事看InnoDB对并发的处理?

共 2358字,需浏览 5分钟

 ·

2021-05-28 10:59

点击上方蓝色字体,选择“标星公众号”

优质文章,第一时间送达

  作者 |  机械公敌

来源 |  urlify.cn/QneMza

一. 并发场景下的问题

相对于串行处理方式,并发的事务处理可显著提升数据库的事务吞吐量、提高资源利用率。在MySQL实际应用中,根据场景的不同,可以分为以下几类:

  • 读读并发

  • 读写并发

  • 写写并发

在这些场景下,可能会出现更新丢失、脏读、不可重复度、幻读的问题。

  • 更新丢失:当多个事务同时更新某1/n行数据时,最后提交的事务会将之前提交的更新覆盖。

  • 脏读:一个事务正在插入/更新一行数据,在该事务提交之前,这条数据处于“不一致”状态。其他事务读取到这条“脏数据”并据此做进一步处理,就会产生对未提交数据的依赖关系,这种现象称为脏读。

  • 不可重复读:一个事务在查询某条数据的一定时间后再次查询该数据,却发现该条数据已经发生了更新或被删除,这种现象称为不可重复读。

  • 幻读:一个事务以相同的查询条件先后两次查询数据,第二次查询结果出现了第一次查询没有的新数据,这种情况称为幻读。

其中,在各类并发场景下会出现的问题如下:

  • 读读并发场景不会导致数据不一致问题,因此无需特殊处理。

  • 读写并发场景可能会出现脏读、不可重复读、幻读的问题。

  • 写写并发场景可能会出现更新丢失的问题。

二.事务隔离级别

针对上述描述的问题,MySQL采用不同的事务隔离级别分别用于解决上述的部分问题,具体如下:

隔离级别含义读数据一致性更新丢失脏读不可重复读幻读
读未提交(Read Uncommitted)事务中的修改,即使没有提交,对其他事务依然可见。最低级别,只能保证不读取物理上损坏的数据。 否 是  是 是 
读已提交(Read Committed)只有已提交事务所做的修改才对其他事务可见。语句级 否
可重复读(Repeatable Read )同一事务中多次读取相同的记录结果时一致的。事务级 否
串行化(Serializable)在读取的每一行数据上加锁,强制事务串行执行,不支持并发。事务级 否 否 

 

在上述隔离级别中,从上到下并发性能依次降低,安全性依次提高。InnoDB存储引擎下默认的事务隔离级别是RR,可通过如下SQL查询事务隔离级别。

select @@global.tx_isolation;

三.事务隔离级别的实现

 InnoDB对事务隔离级别的实现,基本可分为如下两种或两种的组合:

1.多版本并发控制(MVCC,Multiversion Currency Control)

MVCC可用来解决读写并发场景下的脏读、不可重复读问题。具体在不同隔离级别下的应用如下:

  • RC:可解决脏读问题。

  • RR:可解决脏读、不可重复读问题。

具体实现可参考这里,此处针对链接中的文章做简单补充:

相较于RC,RR之所以能够解决不可重复读问题,原因在于RR隔离级别下读取Read-View是事务级别的,RC是语句级别的。即:RR只在事务开始时读取一次Read-View,因此一个事务中的多次查询依赖同一个Read-View,能实现可重复读。RC级别下每次查询均读取     最新的Read-View,无法保证同一事务中先后读取到的Read-View是相同的,这也是先后读取到的数据有差异的原因。

2.锁

锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据了而言显得尤为重要。

锁可以解决上述描述读写并发场景下的幻读问题、写写并发场景下的更新丢失问题。下面针对锁、锁的类型及其应用场景展开详细描述。

2.1锁的基本原理

http://mysql.taobao.org/monthly/2016/01/01/

https://mp.weixin.qq.com/s/yq5Erdv5Dft3foJEVE2dxA

https://i6448038.github.io/2019/02/23/mysql-lock/

https://www.163.com/dy/article/G0S7FSGG05319WXB.html

 

duplicate key check检查时需要在二级唯一索引上加gap lock(版本:5.6之前,5.7.26-29之后加record lock)

https://juejin.cn/post/6844903597029720072

https://my.oschina.net/hebaodan/blog/3045246

 

死锁发生场景:

  • 事务1,2均获取某一行数据的s锁后,均想获取x锁。

  • 事务1,2均获取到某一间隙锁后,希望进行插入操作,即增加插入意向锁。

  • 事务1,2操作同一条数据。由于检索条件不同,事务1,2分别锁定了聚簇索引和二级索引,然后事务1无法操作二级索引(先删除,再插入),事务2无法操作聚簇索引,发生死锁。(参考http://mysql.taobao.org/monthly/2016/01/01/)







浏览 34
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报