MySQL是如何实现事务ACID四个特性的?

码字不易,转载请附原链,搬砖繁忙回复不及时见谅,技术交流请加QQ群:909211071

目录

事务的实现

redolog

undolog

purge

group commit

回归ACID

原子性(Atomicity)

一致性(Consistency)

隔离性(Isolation)

持久性(Durability)


事务的实现

要了解事务是如何实现的,必须要先了解下面几个概念:

  • redolog
  • undolog
  • purge
  • group commit

下面将分别介绍。

redolog

重做日志用来实现事务的原子性和持久性,主要需要关注下面几个概念点:

  • 日志由两部分组成,一部分是内存中的重做日志缓冲(redo log buffer),是容易丢失的,另一个部分是重做日志文件(redo log file),是持久到磁盘的。
  • 为了确保每次日志都写入重做日志文件,在每次将重做日志缓冲写入重做日志文件后,InnoDB引擎都会调用一个fsync刷盘完成持久化,刷盘时机受 innodb_flush_log_at_trx_commit参数影响。

在redolog中有个重要的概念,LSN(Log Sequence Number),代表的是日志序列号,占用8字节,单调递增,表示事务写入重做日志的字节总量,用于意外宕机后重启时的redolog恢复。比如:某个页的LSN为800,而数据库启动时检测到重做日志中的LSN为1000,那么数据库需要进行恢复操作,将重做日志应用到该页中。

可以通过命令查看当前LSN状态:

show engine innodb status\G;

主要关注三个指标:Log sequence number 表示当前的LSN,Log flushed up to表示刷新到重做日志文件的LSN,Last checkpoint at表示刷新到磁盘的LSN。

undolog

在对数据库进行修改时,InnoDB存储引擎会产生一定量的undolog,用于保证数据的原子性和一致性。undolog存放在数据库内部的一个特殊段(undo segment)中,位于共享表空间内。主要有下面两个作用:

  • undolog是逻辑日志,对每个操作记录了相应的反操作。
  • undolog通过指针记录了每行数据的不同版本,用于实现MVCC。

存储:

支持128个回滚段(rollback segment),每个回滚段记录了1024个 undo log segment,也就是一共可以同时支持 128 * 1024个在线事务,受几个参数影响:

MySQL [buy_commission]>  show variables like '%undo%';
+--------------------------+-------------------------------+
| Variable_name            | Value                         |
+--------------------------+-------------------------------+
| innodb_max_undo_log_size | 1073741824                    |
| innodb_undo_directory    | /226442-1/home/mysql/data/log |
| innodb_undo_log_truncate | OFF                           |
| innodb_undo_logs         | 128                           |
| innodb_undo_tablespaces  | 8                             |
+--------------------------+-------------------------------+
5 rows in set (0.00 sec)

MySQL [buy_commission]>
  • innodb_max_undo_log_size:控制最大innodb_undo_tablespaces大小
  • innodb_undo_directory:undolog存储目录, 当DB写压力较大时,可以设置独立undo表空间,从ibdata文件中分离开来,指定 innodb_undo_directory目录存放,制定高速磁盘上,加快undolog的读写性能。
  • innodb_undo_log_truncate:是否开启在线回收undo log日志
  • innodb_undo_logs:回滚段数量
  • innodb_undo_tablespaces:构成回滚段的文件数量

undolog删除时机:

事务提交后并不能马上删除undolog,因为还可能有其他的事务通过undolog来记录之前的版本,用于实现MVCC,在事务提交时将undolog放入history list中,等待purge线程进行最后删除。

增删改操作: 

  • 插入操作产生 TRX_UNDO_INSERT_REC 类型的undolog。
  • 删除操作产生 TRX_UNDO_DEL_MARK_REC 类型的undolog,标记记录的delete flag为1,并不会真实删除,等待purge线程真正执行删除操作。
  • 更新操作对undolog来说,分为删除和插入两步,先记录 TRX_UNDO_DEL_MARK_REC 类型的undolog并标记删除,再插入一条新的undolog TRX_UNDO_INSERT_REC。

purge

        上面提到过delete和update操作并不会直接删除原有数据,只是标记为删除,真正的操作被延时到purge操作中完成。所有操作产生的undolog按照事务提交的顺序,保存在history list中,在执行purge时,会从该链表头部开始找到第一个需要被清理的记录,并根据事务id向后检测该undolog是否被其他事务使用,如果未使用则进行purge操作并删除,否则放弃,继续判断链表的下一个undolog。

        为了提高purge效率,可以通过 innodb_purge_batch_size 设置每次purge操作需要清理的undolog数量,越大则每次回收undo页越多,可供重用的undo页就越多,减少了磁盘存储空间和分配的开销。不过该参数设置得过大,会导致每次需要purge的undo配置越多,从而导致CPU和磁盘IO过于集中使用,使性能下降。

        全局动态参数 innodb_max_purge_lag 用来控制history list的长度,若长度大于该参数时,会延缓DML操作(dalay单位为毫秒),默认值为0,表示不对list大小做任限制。延缓算法:

delay = ((length(history_list) - innodb_max_purge-lag)*10) - 5

innodb_max_purge_lag_delay 用来控制 delay 的最大毫秒数,避免由于purge 操作导致其他SQL无限制等待,默认为0。

2PC

目的:解决单个事务的redo/binlog顺序一致的问题

  1. prepare阶段:redo持久化到磁盘(redo group commit),并将回滚段置为prepared状态,此时binlog不做操作。
  2. commit阶段:innodb释放锁,释放回滚段,设置提交状态,binlog持久化到磁盘,然后存储引擎层提交。

group commit

1、为什么需要group commit

        若每次事务提交时需要进行一次fsync操作,保证重做日志成功刷盘,以便当数据库发生宕机时,可以通过重做日志进行数据恢复。为了提高刷盘效率,数据库提供了group commit功能,在一次fsync时可以刷新多个事务日志写入磁盘。

2、事务提交后做了哪些操作

在开启binlog后,为了保证redolog和binlog中的一致性,事务提交时采取二阶段提交,步骤如下:

  1. InnoDB存储引擎进行prepare操作,将日志写入redolog缓冲
  2. 数据库层写入binlog日志,由sync_binlog控制刷盘策略
  3. 调用fsync将redolog刷入磁盘

在上述步骤中,第二步完成后,就确保了事务提交,为了保证提交顺序一致,在第二步执行之前,会加 prepare_commit_mutex 锁,从而阻止其他事务写入binlog,用于保证事务的一致性,与此同时就导致了group commit失效。

3、用Binary Log Group Commit解决group commit失效问题

MySQL数据库上层进行提交时,首先按顺序将redolog放入一个队列中,队列中第一个事务称为leader,其他事务称为follower,leader控制follower进行group commit,提交步骤如下:

  1. Flush阶段将队列中所有事务的二进制日志写入内存中
  2. Sync阶段将内存中的二进制日志刷新到磁盘(binlog的group commit)
  3. Commit阶段,leader调用InnoDB层执行redolog的group commit操作。

当有一组事务在进行commit阶段时,其他新事务可与进行flush阶段,从而提高效率,同时通过 binlog_max_flush_queue_time 控制flush阶段中等待时间,即使前一组事务完成commit,当前一组事务也不会马上进入sync阶段,而是等待该参数设置的时间,这样可以使下一次group commit的事务数量更多。该参数默认为0,且推荐设置为0,除非当前MySQL有大量的活跃连接进行写入事务,可以调高该参数,用来提高commit效率。

回归ACID

原子性(Atomicity)

Write-ahead logging,undolog记录反向操作,用于回滚:

  • 对于每一个insert,undolog记录一个delete
  • 对于每一个delete,undolog记录一个insert
  • 对于每一个update,undolog记录一个相反的update

redolog group commit 用于提交。



一致性(Consistency)



隔离性(Isolation)

  • 通过隔离模式实现不同级别下的隔离性。
  • 通过锁和MVCC实现隔离模式下的读写并行。

四种隔离模式:关于mysql的事务隔离模式、快照读和当前读、MVCC,并用RC+CAS实现库存处理_程序猿的世界-CSDN博客

加锁原理:MySQL的InnoDB加锁原理和常见死锁_程序猿的世界-CSDN博客



持久性(Durability)

  • 通过redolog实现持久性(Write-ahead logging)
  • sync_binlog和innodb_flush_log_at_trx_commi设置为1,也就是大家常说的双1策略,保证宕机数据不丢失。
  • 宕机重启时通过对比redolog页和数据页的LSN(Log Sequence Number)进行恢复。



 参考:『浅入深出』MySQL 中事务的实现 - 面向信仰编程

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AirGo.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值