mysql 事务与隔离级别

发布时间 2023-05-17 15:37:08作者: 黄光跃

ACID

  • Atomicity:原子性(UNDO LOG实现),一组操作要么都成功,要么都失败
  • Consistency:一致性(UNDO LOG实现),从一个合法状态变为另一个合法状态(语义上不是语法上)。比如转账之后余额为负数,虽然也能守恒,但是明显不合法。或者转账时A账户钱少了,B账户钱没多,也是不合法的
  • Isolation:隔离性(锁机制实现),一个事务执行不受其他事务干扰(并发时事务是隔离的,互不干扰)
  • Durability:持久性(REDO LOG实现),事务一旦提交不能再回滚

数据并发问题

  • 脏写(Dirty Write),也叫修改丢失
    • 事务A修改了事务B回滚的数据
    • A 把 name 改成 张三,B把 name 改成李四,A 先提交,这时 name 就是张三,然后B回滚,name又改成最初的值了
  • 脏读(Dirty Read)
    • 事务A读取了事务B回滚的数据
    • B 修改 name 为李四还没提交,此时A去读就会得到 name 是李四,等A读完,B回滚
  • 不可重复读(Non-repeatable Read)
    • 事务A读取了 name,事务B修改了 name(提交了),事务A再次读发现 name 不一样了。每次读取值都会不一样
    • 这个讲道理是合法的,因为修改了值所以别人读到的值就不一样。但是这也确实是一个问题:每次读都不一样
  • 幻读(Phantom)
    • 事务A第一次读有1行数据,事务B插入了几行,事务A再次读,发现行数变多了
    • 和不可重复度类似,讲道理是合法的,区别是一个是更新一个是插入导致

事务隔离级别

用来解决数据并发产生的问题,如果都要解决性能会受影响,所以需要平衡数据准确性和并发能力选择合适的级别
数据问题严重排序:脏写 > 脏读 > 不可重复读 > 幻读

隔离级别 脏读可能性 不可重复读可能性 幻读 加锁读
READ UNCOMMITED(读未提交) yes yes yes no
READ COMMITED(读已提交 oracle 默认) no yes yes no
REPEATABLE READ(可重复读 mysql 默认) no no yes no
SERIALIZABLE(串行化) no no no yes
  • 四种级别都能解决脏写
  • SERIALIZABLE 串行化就把并行变成了串行所以能解决所有问题,并发能力最低
  • 查看隔离级别
    • 5.7.20 (包括)之前 : SHOW VARIABLES LIKE 'tx_isolation'
    • 5.7.20之后: SHOW VARIABLES LIKE 'transaction_isolation'
    • 所有版本都适用: SELECT @@transaction_isolation;
  • 设置隔离级别
    • set global transaction isolation level repeatable read;
    • GLOBAL 和 SESSION 区别:GLOBAL 对新的连接生效,当前是无效的

事务日志

分为 redo 和 undo 日志,分别保证【持久性】和【原子性、一致性】
每次服务启动都会申请一片连续的内存空间作为 redo buffer,redo buffer 会刷到 redo log 中

redo

  • 为什么需要 redo log?
    • 保证数据的持久性
    • 数据库和磁盘是以页为交互基本单位,哪怕该一个字段,也要读取一个完整的数据页,然后再刷盘,刷盘成本是很大的
    • 可以不每次事务提交都刷盘,让保存在一个 redo log 中,后面再同步 redo log 和 磁盘(不需要每次dml都更新磁盘)
  • redo log 数据来自于哪?
    • 来自于 redo buffer
  • redo buffer?
    • 每次服务启动都会申请一片连续的内存空间作为 redo buffer
    • show variables like '%innodb_log_buffer_size%' 查看当前 redo buffer 大小,默认16m
  • redo log 特点
    • 是顺序写入的
    • 事务执行过程中 redo log 不断记录(事务中有几条 dml 就记录多少次,bin log 是遇到 commit 记录所有的 dml,只记录一次)
  • redo log 物理文件
    • ib_logfile0 和 ib_logfile1
    • 在 mysql 数据目录下
  • redo log 流程
    • 事务开始
    • 读取数据到内存(将要修改的数据)
    • 更新内存的数据保存在 redo buffer 中(修改后的数据)
    • redo buffer 中的数据刷到 redo log 中(物理文件)
    • 刷盘(有3中策略,通过 innodb_flush_log_at_trx_commit 参数确定,默认是 1 )

undo

  • 为什么需要 undo log?
    • 保证数据的原子性和一致性,回滚数据
  • undo log 数据来自于哪?
    • 来自于原来的数据
    • 当需要删除时,会把删除前的数据写入 undo log;当需要更新时,把更新前的数据写入 undo log