当前位置: 首页 > 科技观察

InnoDB并发这么高,这是为什么呢?

时间:2023-03-14 08:26:03 科技观察

《InnoDB行锁,如何锁住一条不存在的记录?》埋了个坑,没想到评论反响猛烈,大家都想挖深一点。本来打算写InnoDB的锁来结束这个case。既然呼声这么高,我就简单系统的写一下InnoDB的并发控制、锁、事务模型。系统比较宏大,要写的文章肯定不止一篇。我来说说吧,深入浅出地解释一下来龙去脉。一、并发控制(一)为什么要并发控制?并发任务对相同的关键资源进行操作。如果不采取任何措施,可能会导致不一致。因此,必须实现并发控制(ConcurrencyControl)。(2)从技术上讲,并发控制通常是如何进行的?通过并发控制来保证数据一致性的常用手段有:数据加锁多版本(MultiVersioning)2、加锁(1)如何使用普通锁来保证一致性?普通锁使用最多:在操作数据之前,加锁并实现互斥,不允许其他并发任务操作;操作完成后释放锁,让其他任务执行;等等以确保一致性。(2)普通锁有哪些问题?简单的锁太粗糙了,连“读任务”都无法并行,任务执行过程本质上是串行的。于是就有了共享锁和排它锁:共享锁(ShareLocks,记为S锁),读取数据时加S锁排它锁(eXclusiveLocks,记为X锁),修改数据时加X锁共享锁而排他锁的加锁方式是:共享锁不互斥,简写为:读和读可以是并行的排他锁和任何锁都是互斥的,简写为:写和读,写和写不能并行,一旦写入数据的任务没有完成,其他任务就无法读取数据,对并发性影响很大。画外音:对应数据库,可以理解为如果写事务没有提交,读相关数据的select也会被阻塞。(3)是否可以进一步提高并发性?即使写入任务没有完成,其他读取任务也可能并发,从而导致数据的多版本。3.数据多版本数据多版本是一种可以进一步提高并发性的方法。其核心原理是:当有写任务发生时,克隆一份数据,并通过版本号区分;写入任务操作新克隆的数据,直到提交;并发读任务可以不阻塞地继续读取旧版本的数据;如上图:数据的初始版本为V0;T1发起一个写任务,就是克隆一份数据,修改,版本变为V1,但是任务还没有完成;T2时,并发下发读任务,仍然可以读到V0版本的数据;T3时,并发下发了另一个读任务,但仍然没有阻塞;可以看出数据有多个版本,通过“读取”获取旧版本数据可以大大提高任务的并发度。提高并发度的演化思路是这样的:普通锁本质上是串行读写锁,可以实现多版本并发读取和读取数据,可以实现并发读取和写入画外音:这个思路在整篇文章中比其他技术细节更重要,希望大家牢记。那么对应到InnoDB,它具体是如何工作的呢?四、redo、undo、rollback段在进一步介绍InnoDB如何利用“读取旧版本数据”来大幅提高任务的并发性之前,有必要先介绍一下Redolog、undolog,回滚段(rollbacksegment).(1)为什么会有重做日志?数据库事务提交后,更新的数据必须flush到磁盘,保证ACID特性磁盘随机写性能低下。如果每次都刷盘,会大大影响数据库的吞吐量。优化方法是先将修改行为写入redolog(此时变成顺序写),然后周期性的flush数据到磁盘,可以大大提升性能。画外音:这里的架构设计方式是把随机写优化为顺序写,思路比较重要。如果在某个时刻,数据库crash,磁盘上的数据还没有被flush,重启数据库后,redolog中的内容会被重做,以保证committransaction对数据的影响被flush到磁盘。总之,重做日志是用来保证提交事务的ACID特性的。(2)为什么会有undolog?当数据库事务未提交时,事务修改数据的镜像(即修改前的旧版本)将存储在undolog中。当事务回滚或数据库崩溃时,可以使用undolog,即旧版本数据,撤销未提交的事务对数据库的影响。画外音:更详细一点,对于insert操作,undolog记录了新数据的PK(ROW_ID),回滚时直接删除;对于delete/update操作,undolog记录旧数据行,回滚时直接恢复;它们存储在不同的缓冲区中。总之,undolog是用来保证未提交的事务不会影响数据库的ACID特性。(3)什么是回滚段?undolog存放的地方就是回滚段。undolog和rollbacksegment与InnoDB的MVCC密切相关。这是一个例子来解释它。栗子:t(idPK,name)数据为:shenjianzhangsanlisi此时没有未提交的事务,所以回滚段为空。然后启动一个事务:starttrx;delete(1,shenjian);updateset(3,lisi)t??o(3,xxx);insert(4,wangwu),事务处于未提交状态。可以看出:删除前的(1,shenjian)作为旧版本数据进入回滚段;(3、lisi)修改前作为旧版本数据进入回滚段;插入的数据,PK(4)进入回滚段;接下来,如果事务回滚,可以通过回滚段中的undolog来回滚。画外音:假设事务已经提交,可以删除回滚段中的undolog。可以看到:被删除的旧数据恢复了;修改后的旧数据也被恢复;插入的数据被删除;事务回滚成功,一切如故。4.InnoDB是一个基于多版本并发控制的存储引擎《大数据量,高并发量的互联网业务,一定要使用InnoDB》提到InnoDB是互联网高并发场景最推荐的存储引擎。根本原因在于它的多版本并发控制(MultiVersionConcurrencyControl,MVCC)。行锁、并发、事务回滚等各种特性都与MVCC有关。MVCC就是通过“读取旧版本数据”来减少并发事务的锁冲突,提高任务的并发性。(1)核心问题:旧版本数据存储在哪里?存储旧版本数据会对MySQL和InnoDB原有架构产生巨大影响吗?通过上面undolog和回滚段的铺垫,这两个问题就很容易回答了。Already:旧版本数据存放在回滚段;对MySQL、InnoDB原有架构影响不大;InnoDB内核会在所有行数据中添加三个内部属性:DB_TRX_ID,6字节,记录每一行的最新时间修改其事务ID;DB_ROLL_PTR,7字节,记录指向回滚段undolog的指针;DB_ROW_ID,6字节,单调递增的行ID;(2)InnoDB为什么能做到这么高的并发?rollbacksegment中的数据其实是历史数据的快照(snapshot),这些数据不会被修改,select可以肆无忌惮地并发读取它们。快照读(SnapshotRead),这种一致的非锁读(ConsistentNonlockingRead),是InnoDB之所以有如此高并发的核心原因之一。这里的一致性是指一个事务读取的数据要么是事务开始之前就存在的数据(当然是其他提交的事务产生的),要么是事务本身插入或修改的数据。(3)snapshotread是什么样的select?除非显示锁,否则普通select语句为快照读,例如:select*fromtwhereid>2这里显示锁,非快照读指的是:select*fromtwhereid>2lockinsharemodeselect*fromtwhereid>2forupdate问题来了。这些displaylockedreads,什么样的reads?将添加哪些锁?它与事务的隔离级别有什么关系?这一节内容已经够多了,且听下一章。5、总结常用的并发控制方法保证数据一致性:锁、多版本数据;普通锁串行、读写锁、读写并行、数据多版本读写并行;redolog保证了提交事务的ACID特性,设计思路是的,用顺序写代替随机写,提高并发性;undo日志用于回滚未提交的事务,这些事务存储在回滚段中;InnoDB是一个基于MVCC的存储引擎,利用存储在回滚段中的undo日志,即旧版本的数据,提高并发性;InnoDB之所以有高并发,是因为快照读取没有加锁;InnoDB的所有常用选项都是快照读取;画外音:本文知识点基于MySQL5.6。【本文为专栏作者《58神剑》原创稿件,转载请联系原作者】点此阅读更多该作者好文