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

数据库事务的MySQL跨行事务模型

时间:2023-03-16 17:50:21 科技观察

与MySQL有关。毕业后的第一份工作分配到RDS团队,主要负责把MySQL作为数据库服务上云。虽然整天和MySQL打交道,但说实话,那段时间对MySQL核心的理解并不深刻。基本上我做的就是围绕MySQL搭建一个管控系统,比较高级。还好身边有MySQL核心大神。在他们的影响下,对MySQL的一些基础知识有了一些零碎的记录和模糊的理解。这些基础对于今天梳理和理解MySQL跨行事务模型非常重要。更重要的是还有很多不懂的地方,可以向师傅请教。网上也有很多关于MySQL事务模型的介绍。在写这篇文章之前,我也查阅了很多资料作为参考,希望能够让自己的理解更加深入和全面。看了大部分介绍性的文章,发现有些文章并不完整。比如有些只介绍MySQL在几种隔离级别下的性能,并没有从技术角度去解读。有些文章比较全面,但缺少一点条理,读起来不太容易理解。这就是笔者希望能给大家带来不一样的东西,从技术角度解读,方便理解。MySQL事务原子性保证事务原子性要求一个事务中的一系列操作要么全部完成,要么根本不执行,不能执行一半。原子性对于原子操作来说很容易实现,就像HBase中行级事务的原子性实现比较简单一样。但是对于多条语句组成的事务,如果事务执行过程中出现异常,保证原子性的唯一办法就是回滚,回滚到事务开始前的状态,就好像事务根本没有发生过一样.如何实现?MySQL的回滚操作完全依赖于undolog。还有一点,undolog在MySQL中不仅用来实现原子性保证,还用来实现MVCC,后面也会涉及到。使用undo实现原子性在操作任何数据之前,先将修改前的数据记录在undolog中,然后再进行实际的修改。如果发生异常需要回滚,系统可以利用undo中的备份将数据恢复到事务开始前的状态。下图显示了MySQL中表示事务的基本数据结构。undo相关的字段是insert_undo和update_undo,分别指向本次事务产生的undolog。事务回滚,根据update_undo(或insert_undo)找到对应的undolog,进行反向操作。对于已经标记为删除的数据,清理并删除标记,对于更新的数据,直接回滚更新;insert操作稍微复杂一点,不仅需要删除数据,还需要删除相关的聚簇索引和二级索引记录。undolog是MySQL内核中非常重要的一部分。它涉及的知识很多,也很复杂。例如:1.undolog必须在数据修改前持久化。undolog是否持久化记录redo,防止宕机异常?如果有必要那么就涉及到宕机恢复...2.如何通过undolog实现MVCC?3、那些undolog在什么场景下可以被回收清理?如何清理?MySQL事务一致性保证:强一致性事务保证MySQL事务隔离级别ReadUncommitted(RU技术解读:使用X锁实现写并发)ReadUncommitted只实现写写并发控制,不具备有效的读写并发控制,导致当前事务可能读取其他事务中未提交的修改数据。这些数据的准确性是不可靠的(可能会回滚),所以在此基础上做出的所有假设都是不可靠的。很少有企业会在现实场景中选择这种隔离级别。写写并发的实现机制和HBase一样,都是采用两阶段锁协议,给对应的记录加行锁。但是MySQL中的行锁机制比较复杂。根据行记录是主键索引、唯一索引、非唯一索引还是无索引,可以分为多种加锁情况。1、如果id列是主键索引,MySQL只会锁定聚簇索引记录。2.如果id列是唯一的二级索引,MySQL会锁定二级索引的叶子节点和聚簇索引记录。3、如果id列是非唯一索引,MySQL会锁定所有满足条件(id=15)??的二级索引叶子节点和对应的聚簇索引记录。4.如果id列没有索引,SQL会扫描聚集索引全表,并将扫描结果加载到SQLServer层进行过滤,所以InnoDB会先锁住所有扫描到的记录,如果SQLServer层过滤的话不满足条件,InnoDB会释放锁。因此,InnoDB会锁定所有扫描的记录,这很可怕!接下来,无论是RC,RR,还是Serialization,都是通过上面提到的机制来写写并发控制的,这里不再赘述。下面重点分析RC和RR隔离级别下的读写并发控制机制。在详细介绍RC和RR之前,有必要先介绍一下MySQL中的MVCC机制,因为RC和RR都使用了MVCC机制来实现事务间的并发读写。只是两者在实现细节上有些不同,具体的不同会在后面讨论。MySQL中的MVCCMySQL中的MVCC机制要比HBase复杂很多,涉及到的数据结构也更加复杂。为了更清楚的说明,举个栗子作为模板进行说明。比如当前有一行记录如下图:前四列是该行记录的实际列值,需要隐藏的两个列DB_TRX_ID和DB_ROLL_PTR(用户不可见)专注于。其中,DB_TRX_ID表示修改该行事务的事务ID,DB_ROLL_PTR表示指向该行回滚段的指针。rowrecord上的所有版本数据在undo中都是以链表的形式组织起来的,而这个值在undoRecord链表中实际上指向了该行的历史记录。现在假设有一个修改行数据的事务trx2,行记录会变成下图形式,DB_TRX_ID是最近一次修改行事务的事务ID(trx2),DB_ROLL_PTR指向undo历史记录列表:了解了MySQL行记录之后,我们再来看看事务的基本结构。下图就是我们上面提到的MySQL的事务数据结构。事务开启后,会创建一个数据结构来存放事务相关信息,锁信息,undolog,以及非常重要的read_view信息。read_view保存的是当前事务启动时,整个MySQL中所有活跃事务的列表,如下图,当前事务启动时,系统中的活跃事务有trx4、trx6、trx7、trx10。另外,up_trx_id表示当前交易开始时,当前交易链表中最小的交易ID;low_trx_id表示当前交易开始时,当前交易链表中最大的交易ID。read_view是MVCC实现中的一个关键点,用于判断记录的哪个版本对当前事务可见。如果当前事务要读取一条行记录,并且该行记录的版本号(事务ID)为trxid,则:1、如果trxidlow_trx_id,则说明该行的事务是在当前事务创建后才开启的,所以该行记录对当前事务不可见。3、如果up_trx_id1的过滤条件执行了3次查询,2号事务执行了一次insert,插入的记录刚好满足id>1的条件。可以看出,三个查询得到的数据是一致的,这是RR隔离级别的MVCC机制保证的。从这个角度来看,避免了幻读,但是当最后一个1号事务在id=2处插入一条记录时,MySQL会返回Duplicateentry错误,这说明避免幻读是一种错觉。Strictlyavoidphantomreads(技术解读:当前读使用Gap锁来避免幻读)。上面提到的所有RR级别的select语句都称为快照读取。快照读可以保证不可重复读,但是无法避免幻读。于是MySQL提出了“当前读”的概念。目前常见的读语句有:1、selectforupdate2。选择共享模式锁定3。lock-Gap锁,Gap锁不锁定具体的一条记录,而是锁定记录之间的区间,保证在这个区间内不会插入新的其他记录。下图是示意图:上图中1号事务先执行一条currentreadselect语句,会对所有id>0的区间加Gap锁,然后2号事务在id=3处执行insert系统会返回Lockwaittimeoutexeccded异常。当然其他事务在id<=0的条件下也能插入成功,没问题。Serializable(技术解读:S锁(读)+X锁(写))序列化隔离级别是最严格的隔离级别,所有读请求都会加读锁,无论快照读和当前读,所有写都会加写锁。当然,由于锁定开销,这种隔离级别的性能相对最差。MySQL事务持久化保证MySQL事务持久化策略与HBase基本相同,但是涉及的组件相对较多,主要有doublewrite、redolog、binlog:1.MySQL数据持久化(DoubleWrite)其实就是实际数据编写MySQL的组件。两次写,一次写到一个叫DoubleWrite的地方,写成功后,实际上是写到数据所在的磁盘上。为什么要写两次?这是因为MySQL数据页的大小与磁盘上原子操作的大小不一致,可能会出现偏写的情况。例如InnoDB默认数据页大小为16K,而一次原子写入磁盘的大小为512个字Section(扇区大小),这样的数据页写入需要多次IO,所以一旦中间出现异常,数据会发生损失。另外需要注意的是,DoubleWrite的性能不会受到很大的影响,因为写入DoubleWrite是顺序写入,对性能的影响并不大。2、redolog持久化策略(innodb_flush_log_at_trx_commit)redolog是InnoDB的WAL,数据先写入redolog并放到磁盘上,再写入并更新到bufferpool中。redolog的持久化策略与HBase中hlog的持久化策略是一致的。默认为1,表示每次事务commit后日志都会持久化到磁盘;0的值意味着日志将每隔1秒左右由异步线程持久化到磁盘。磁盘,在这种情况下,如果MySQL宕机,可能会丢失一些数据。取值为2表示每次事务提交后,日志都会刷到操作系统缓冲区,然后由操作系统异步刷到磁盘。这样的话,MySQL宕机是不会丢失数据的,但是当机器宕机的时候,可能会丢失一些数据。数据。3、binlog持久化策略(sync_binlog)binlog作为server层的日志系统,主要以事件的形式顺序记录数据库的各种操作,也可以记录每次操作所花费的时间。在MySQL官方文档中,主要介绍了Binlog最基本的两个核心功能:备份和复制,所以binlog的持久化会在一定程度上影响数据备份和复制的完整性。和redo持久化策略一样,可能的值为0、1、N。默认为0,这意味着写入操作系统缓冲区并异步刷新到磁盘。值1表示同步写入磁盘。如果为N,则表示每写入N次操作系统缓冲区,就会进行一次刷新操作。总结本文核心介绍MySQL的单机跨行事务模型,其中对隔离涉及的锁技术和MVCC机制进行了详细的讲解。并对事务原子性和持久性等相关特性进行了简要分析和说明。