事务隔离及传播特性

关系型数据库MySQL事务相关笔记

Posted by 高强 on March 28, 2020

四大特性

  • 原子性(Atomicity)
    • 事务是一个不可分割的单位,事务中的所有操作要么全部成功,要么全部失败
    • 实现方式(Innodb): 事务失败是通过 undo log 撤销所有已经执行的 sql 语句
  • 一致性(Consistency)
    • 事务执行完毕后,数据库的完整性约束没有被破坏,事务执行的前后都是合法的数据状态
    • 数据库的完整性约束包括但不限于:实体完整性(如行的主键存在且唯一)、列完整性(如字段的类型、大小、长度要符合要求)、外键约束、用户自定义完整性(如转账前后,两个账户余额的和应该不变)
    • 实现措施
      • 保证原子性、隔离性、持久性
      • 数据库本身提供保障,例如不允许向整形列插入字符串值、字符串长度不能超过列的限制等
      • 应用层面进行保障,例如如果转账操作只扣除转账者的余额,而没有增加接收者的余额,无论数据库实现的多么完美,也无法保证状态的一致
  • 隔离性(Isolation)
    • 事务内部的操作与其他事务是隔离的,并发执行的各个事务之间不能互相干扰
    • 严格的隔离性,对应数据库隔离级别中的 Serializable,但实际实现很少考虑可串行化
    • A事务写操作对B事务写操作的影响: 锁机制保证隔离性
    • A事务写操作对B事务读操作的影响: MVCC(多版本并发控制)保证隔离性
  • 持久性(Durability)
    • 事务一旦提交,它对数据库的改变是永久的
    • 实现方式(Innodb): 通过 redo log 将Buffer Pool 中修改的数据刷新到磁盘中; MySQL 宕机时通过 redo log 恢复宕机前未刷新到磁盘的数据

数据库并发读写问题

  • 脏读(Dirty Reads)
    • 当前事务读取到其他事务未提交的数据(脏数据 )
  • 不可重复读(Non-Repeatable Read)
    • 同一个事务先后读取同一条记录结果不一致
    • 原因: 多次读取记录之间该记录被其他事务修改过
    • 与脏读区别: 脏读是读取其他事务未提交的数据,不可重复读是读取了其他事务已提交的数据
  • 幻读(Phantom Reads)
    • 同一个事务内按相同的条件先后查询得到的查询结果条数不一致
    • 原因: 多次查询期间其他事务插入或删除了属于该查询条件内的数据
    • 与不可重复读区别: 不可重复读是一个事务内多次读取同一行的数据有变化,幻读是一个事务内多次读取得到的结果条数有帮我

隔离级别

  • 读未提交(Read Uncommitted, RU)
    • 可能出现: 脏读、不可重复读、幻读
  • 读已提交(Read Committed, RC)
    • 可能出现: 不可重复读、幻读
  • 可重复读(Repeatable Read, RR)
    • 可能出现: 幻读
  • 可串行化(Serializable)
    • 不会出现并发读写问题

MySQL 默认隔离级别

  • 默认隔离级别为可重复读
    • MySQL 5.0 前 binlog 只支持 STATEMENT 格式,该格式在 RC 下存在 bug,其后一直默认使用 RR 隔离级别
    • 互联网项目推荐使用 RC 隔离级别,Innodb 主从复制 binlog 默认使用 row 格式

Spring事务传播属性(Propagation)

  • REQUIRED (需要)
    • 如果当前已有事务,则加入事务,否则创建新的事务
    • 一个调用链内,多个同样传播属性的方法共用一个事务,回滚时多个方法都回滚
  • MANDATORY (强制必须)
    • 当前方法必须使用事务,如果不存在事务则抛出异常,否则加入到已存在的事务
  • REQUIRES_NEW( 需要新的)
    • 每次执行方法时开启新事务,如果当前已存在事务,则暂停当前事务并开启新事务
    • 场景: 业务发生异常的时候发送短消息。如果业务发生异常,业务回滚,但是由于发送段消息是新的事务,不会受到业务异常的影响
  • SUPPORTS (支持)
    • 当前方法支持事务,如果当前线程存在事务,就加入到事务中去,如果不存在,不做任何操作
  • NOT_SUPPORTED (不支持)
    • 当前方法不支持事务,如果当前线程存在事务,就挂起当前事务,执行完当前方法,恢复事务
    • 一般情况下在查询的时候使用,如果一个方法只是查询,并且非常耗时,就可以使用 NOT_SUPPORTED,避免事务时间超长
  • NEVER (不允许事务)
    • 当前方法不支持事务,如果当前线程存在事务,则抛出异常
  • NESTED (嵌套事务)
    • 如果当前存在事务,则在嵌套事务内执行,如果当前不存在事务,则创建一个新的事务
    • 嵌套事务使用数据库中的保存点来实现,即嵌套事务回滚不影响外部事务,但外部事务回滚将导致嵌套事务回滚

资料参考


Related Issues not found

Please contact @peiqianggao to initialize the comment