MySQL事务的四大特性的实现



基本概念


  • 事务的四大特性​ACID :

  • 原子性Atomic :​ 事务的所有的​SQL​操作作为一个原子工作单元执行.要么全部执行,要么全部不执行
  • 一致性Consistent :​ 事务完成后,所有数据状态都是一致的
  • 隔离性Isolation :​ 如果有多个事务并发执行,那么每个事务做出的修改必须和其余的事务隔离
  • 持久性Duration :​ 事务完成后,对数据库中数据的修改会持久化存储

  • 事务的四种隔离级别:

隔离级别

脏读

不可重复读

幻读

未提交读Read Uncommitted

可能

可能

可能

已提交读Read Committed

不可能

可能

可能

可重复读Repeatable Read

不可能

不可能

可能

序列化Serializable

不可能

不可能

不可能

原子性实现

  • MySQL​数据库事务的原子性通过​undo log​实现

  • 事务的所有的增删改的修改操作的相反操作都会写入​undo log​中.比如事务执行一条​insert​语句,那么​undo log​就会记录一条对应的​delete​语句
  • undo log​是一个逻辑文件,记录的是相对应的​SQL​语句.如果事务执行发生异常,导致事务无法成功提交,系统就会执行​undo log​中相对应的撤销操作,达到事务回滚的目的
  • undo log​可以实现​MVCC​的多个版本并发控制

隔离性实现

已提交读


  • 已提交读​Read Committed​允许可重复读的实现策略:


    • 数据的读取不加锁,数据的写入,修改,删除需要加锁
    • 可以解决脏读的问题,无法避免不可重复读的问题
      MySQL中事务四大特性的实现详解_数据


  • 使用加锁策略后,不存在读取到脏数据的情况:


    • T1​写数据​x​时,首先获取​x​的锁,导致​T2​的读操作等待
    • T1​进行数据回滚后,释放锁 ​,T2​可以继续读取原来的数据



可重复读


  • 可重复读​Repeatable Read​允许幻读的实现策略 ​: MVCC​多个版本行控制策略
    MySQL中事务四大特性的实现详解_版本号_02
  • 这种情况下会出现​T1​的更新操作后,导致​T2​两次读取的数据不一致:

    • 通过加行级锁无法解决读取数据不一致的问题:


      • T2​首先读取​x​值
      • T1​经过加锁,解锁的步骤,更新​x​的值,提交事务
      • T2​继续读取,读取出来的值是​T1​更新后的值
      • 这样​T2​两次读取的结果不一致



  • MVCC​多版本行控制:


    • 行级锁是一个悲观锁 ​,MVCC​是一个乐观锁.乐观锁可以在一定程度上避免加锁操作,开销更低
    • InnoDB​的​MVCC​实现是通过保存数据在某个时间点的快照来实现的
    • 一个事务,不管执行多长时间,内部看到的数据是一致的.也就是说,事务在执行过程中不会相互影响


  • MVCC​多版本行控制的具体实现:


    • 通过在每行记录后面保存两个隐藏的列来实现多版本行控制
    • 一个列保存了行的创建时的系统版本号
    • 一个列保存的行的过期时的系统版本号,也就是删除时的系统版本号
    • 每次开始一个新的事务,系统版本号就会递增


      • SELECT操作:


        • 只会查找版本早于或者等于当前事务版本的数据行.这样可以保证事务读取的行,要么是事务开始前就存在的行,要么是事务自身插入或者修改的行
        • 行的删除版本要么是未定义版本号,要么大于当前事务版本号.这样可以保证事务读取的行,在事务开始之前未删除


      • INSERT操作:

        • 将新插入的行保存当前版本号为行版本号

      • DELETE操作:

        • 将删除的行保存当前版本号为删除标识

      • UPDATE操作:

        • UPDATE​操作作为​DELETE​操作和​INSERT​操作的组合


          • DELETE​操作保存当前版本号到原来的行作为删除标识
          • INSERT​操作的行保存当前版本号作为行版本号







  • MVCC多版本行控制策略可以确保一个事务中读取的是同一个数据库版本快照

持久性实现


  • MySQL​数据库事务的持久性是通过​redo log​实现的:

  • 事务的所有增删改的修改操作,数据库都会生成一条​redo​日志到​redo log​中
  • redo log​区别于​undo log​中记录的​SQL​语句 ​,redo log​记录的是事务对数据库的哪个数据页做了什么样的修改,属于物理日志

  • redo​日志的使用场景:

  • 数据库系统直接崩溃,需要进行恢复
  • 通常情况下,数据库都会按时间点备份的策略,首先将数据库恢复到最近备份的时间点状态,然后读取这个时间点之后的redo log记录,重新执行相应记录,达到最终恢复的目的


日志文件刷新策略


  • 日志文件undo log和redo log不是直接写入到磁盘中,而是写入log buffer,然后等待合适的时机同步到OS buffer,然后由操作系统决定刷新到磁盘的时间
    MySQL中事务四大特性的实现详解_数据库_03
  • MySQL​主要有三种日志刷新策略:


    • 每次事务提交写入​OS buffer,​ 并调用​fsync​刷新到磁盘中
    • 每秒写入​OS buffer,​ 并调用​fsync​刷新到磁盘中
    • 每秒提交写入​OS buffer,​ 然后每秒调用​fsync​刷新到磁盘中


  • MySQL​默认使用第一种日志刷新策略,安全性依次下降,效率依次上升