图文实录|MySQL技术内幕系列分享:InnoDB事务锁及调度算法




点击上方蓝字关注我们






本文金句
MySQL InnoDB存储引擎的事务系统在通过事务锁系统保证事务完整性和一致性的前提下,通过CATS事务调度算法显著提升了系统的整体性能。


事务锁系统是MySQL InnoDB存储引擎的一个重要组成部分,通过事务锁系统,保证了事务的完整性和一致性。而由于InnoDB采取的是行锁系统,使得数据锁定的粒度很小,从而极大提升了系统在高并发场景下的性能。

另一方面,InnoDB在MySQL8.0版本中又通过采用热点感知的事务调度算法(CATS),实现了系统在某些行锁成为热点资源而被多个事务争抢的场景下性能的大幅度提升。这次的分享我们就来详细了解一下InnoDB存储引擎的事务锁系统及事务调度算法,使大家对两者有更为深入和全面的认识。


 

1

InnoDB事务锁简介



1.1 事务锁系统简介

锁机制是用来管理对共享资源的并发访问的一种机制,通过对共享资源的锁定,保证了共享资源不会被多个用户同时访问并修改,避免了共享资源的错乱。

事务锁是用于协同多个事务同时对共享数据的并发访问,从而保证了事务的一致性和完整性。而事务锁有着很多种类。

下图对于事务锁进行了不同种类的分类。比如按锁加锁机制分可以将事务锁分为乐观锁和悲观锁。按兼容性分可以分为共享锁和排它锁。按锁粒度分可以分为表锁,页锁和行锁。而按锁模式分,则可以分为记录锁,间隙锁(gap lock),next-key锁,意向锁和插入意向锁等。本次的分享我们将着重讨论InnoDB中的行锁。 

在Klustron分布式数据库的存储节点1.2版本,采用的是MySQL8.0.26版本,而且使用的是InnoDB存储引擎,所以,Klustron的存储节点有着和MySQL一致的事务锁系统和事务调度算法。以下是Klustron分布式数据库的整体架构图:

 
1.2 InnoDB事务处理系统简介

如下图所示,InnoDB的事务处理系统主要建立在三个子系统上面:
  • 事务子系统:包含所有事务的总体信息及各个事务自身的数据结构,如:事务ID,事务锁链表,readview信息等。
  • 事务锁子系统:包含所有锁的相关信息,系统中所有锁的总体信息及各个锁对象的数据结构,如:行锁对象及所有行锁对象的hash表等。
  • Readview子系统:包含当前事务所能看到的readview的信息,主要用于判断其他事务对当前事务的可见性。

这三个子系统通过各自内存结构中的指针来连接,在整个事务处理过程中形成了一个有机的整体,支撑了事务处理的主要流程。

图片来源[1]

2

InnoDB行锁详解



InnoDB的行锁是事务锁系统中最重要的一类锁,它通过表ID(space id)+ 页号(page no)和堆号(heap no)来确定某一行数据对应的行锁。值得说明的一点是,为了减少单个事务所持有的行锁对象的个数,对于同一个事务所持有的相同数据页中数据行,InnoDB只创建一个行锁对象,并通过设置其位图中的位来标注数据行的锁定情况。

如下图所示,行锁对象lock_rec_t中包含了行锁的所有相关信息。图中还显示了行锁和事务的关系。
 
图片来源[2]

2.1 行锁加锁过程
  • 如果记录所在的page上没有任何记录锁时,直接创建锁对象,加入rec_hash;
  • 如果记录所在的page上只存在一个记录锁,并且属于当前事务,则直接设置对应位图的bit位;
  • 检查是否存在和当前申请锁模式冲突的锁,如果存在的话,就创建一个锁对象,并加入到等待队列中,并进行死锁检测;

2.2 行锁释放过程
通常是在事务提交时释放行锁,同时,判断别的正在等待的会话是否可以被唤醒。通过遍历相同page上的所有等待的锁,并判断这些锁等待是否可以被唤醒。

2.3 行锁的死锁检测
死锁检测是为了避免多个事务之间由于形成了循环锁的情况而导致相关事务无法继续执行的情况而采用的一种提前检测机制。

死锁检测采用深度优先遍历的方式,通过构造事务等待图进行判断,当最终发现一个锁请求等待闭环时,可以判定发生了死锁。如果检测深度过长(即锁等待的会话形成的检测链路非常长),也会认为发生死锁,最大深度默认为200。

2.4 死锁检测的问题
死锁检测本身是通过持有全局大锁来进行的,代价很大。在特定场景下,比如热点更新,可以考虑关闭它,而通过设置innodb_lock_wait_timeout来加速性能。

MySQL8.0对于行锁系统和死锁检测做了一些优化(锁分区,异步死锁检测等)

下面的图就是我们对于热点更新(单行高并发update)场景下的测试,可以看到随着并发度的增加,性能急剧下降。


而我们的KunlunBase将会采用基于行锁的优化来解决热点行更新问题。


3

InnoDB的事务调度算法



事务调度算法解决的问题就是当多个事务正在等待对同一个对象的锁时,哪一个应首先获得锁。其中有两种算法,FCFS和CATS, MySQL8.0版本之前,InnoDB一直采用的是FCFS算法,之后则开始采用CATS算法。

FCFS(First Come First Served) 先到先得:按事务请求行锁的顺序来调度;

CATS (Contention-Aware Transaction Scheduling) 热点感知事务调度:根据事务阻塞的其他事务的个数来判断调度顺序。

如下图所示,由于事务t1阻塞了4个事务,而t2阻塞了3个,所以t1优先。
图片来源[3]
通过以上的说明我们可以看出,CATS算法在有数据访问热点,也就是多个事务集中等待相同锁资源的情况下能提升整体的事务处理能力。比较形象的比喻就是堵车的场景,我们可以让车上人多的车,比如公交车优先通过,来提升整体的道路通行效率。

CATS的具体实现方法是主要在两个位置进行处理:
  • 在增加每一把行锁时,更新所有相关事务的edge(等待关系)。
  • 在锁释放之后的事务调度时,根据事务的edge排序,edge值大的优先获得调度。并且,更新所有相关事务的edge。

下图为优化后效果
 图片来源[4]
我们可以看到,TPS在采用CATS算法后都有着明显的提升,而事务延迟事务执行时间有着明显下降。


4

Q&A



q1:能简单介绍一下对于热点行更新问题,Klustron准备做的优化吗?
a1:对于热点行更新的问题,由于其主要原因是高并发场景下行锁过多导致死锁检测耗时长的问题,所以,我们打算采取的办法是避免同一行数据存在过多的行锁。也就是说,对于热点行,我们可以通过预先排队的方式来减少创建大量的行锁。形象一点的说法就是让所有热点更新事务都先排队,然后一个个顺序执行,而不是大家一拥而上,导致效率变低。


q2:mysql8.0对于死锁检测有哪些优化?
a2:MySQL8.0一直在对锁系统进行持续优化,比较重要的是两个优化。一个是锁分区优化,通过将行锁进行分区,避免了不同分区的行锁的互相干扰。另外一个是异步死锁检测优化。这个优化主要是把死锁检测的工作交给后台线程来做,使得工作线程不必一直等待死锁检测完成,而是可以去做其他事情,从而提升了工作效率。


图片来源:

[1]图片来源:https://blog.csdn.net/zwleagle

[2]图片来源:https://developer.aliyun.com/article/4270

[3]图片来源:https://dev.mysql.com/blog-archive/contention-aware-transaction-scheduling-arriving-in-innodb-to-boost-performance/
[4]图片来源:https://dev.mysql.com/blog-archive/contention-aware-transaction-scheduling-arriving-in-innodb-to-boost-performance/












现在,我们发起一轮新的投票,希望就以下问题请教大家,收集大家的反馈。为了感谢您的参与,所有参与问卷调查并留下邮箱联系方式的,由于我们的问卷系统不支持在线抽奖,我们将在后续会统一随机抽取 15 名有效填写者作为幸运参与者,送精美纪念礼品一份,感谢大家参加。
👆扫码填写

END
同时欢迎大家扫码👇添加小助手(备注:加入Klustron技术交流群)欢迎大家在交流群共同探讨更多问题及主题。 

 点击👆上方,关注获取源代码及技术信息~

 

推荐阅读

Klustron特性系列分享:Online DDL和Repartitioning


HTAP数据库能力系列分享:读写分离


HTAP数据库能力系列分享:Online DDL能力探秘


InnoDB事务可见性原理及在KunlunBase中并行查询方案的应用


MySQL技术内幕系列分享:JSON相关特性详解


MySQL技术内幕系列分享:深入了解Adaptive Hash Index特性



免责声明:

1、本站资源由自动抓取工具收集整理于网络。

2、本站不承担由于内容的合法性及真实性所引起的一切争议和法律责任。

3、电子书、小说等仅供网友预览使用,书籍版权归作者或出版社所有。

4、如作者、出版社认为资源涉及侵权,请联系本站,本站将在收到通知书后尽快删除您认为侵权的作品。

5、如果您喜欢本资源,请您支持作者,购买正版内容。

6、资源失效,请下方留言,欢迎分享资源链接

文章评论

0条评论