探秘MySQL——全面了解事务及其底层实现(undo log、redo log、binlog、锁、MVCC)

文章目录

  • 〇、基本概念
    • 什么是事务
    • Q.Jdbc同一个事务中,一个ip地址对应多个连接的操作,在事务中会生效吗?
  • 一、MySQL日志
    • 1.redo log(事务日志)
    • 2.undo log(事务回滚日志)
      • Q.同一个事物内的一条记录被多次修改,每次数据修改前的状态都会写入undo log吗?
    • 3.bin log(二进制归档日志)
  • 二、原子性实现(回滚机制)
  • 三、持久性实现
  • 四、隔离性实现
    • 1.并发事务带来的问题
    • 2.标准SQL事务隔离级别
    • 3.MySQL锁
    • 4.事务隔离性实现(锁+MVCC)
    • Q.针对幻读,事务1插入,不提交,事务2,插入前,事务2查询,插入后,再查询,会发生什么情况?
    • Q.死锁问题的产生和解决
  • 参考

〇、基本概念

什么是事务

  • 定义:

事务是一个不可分割的数据库操作序列,也是数据库并发控制的基本单位,其执行的结果必须使数据库从一种一致性状态到另一种一致性状态。事务是逻辑上的一组操作,要么都执行,要么都不执行。

  • ACID特性:
    原子性:事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
    一致性:事务前后数据的完整性必须保持一致。
    隔离性:事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
    持久性:一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。

事务的 具体实现内容 在下文进行更为详细的介绍。

Q.Jdbc同一个事务中,一个ip地址对应多个连接的操作,在事务中会生效吗?

  • JDBC中的事务机制:
    当一个连接对象被创建时,默认情况下是自动提交事务:每次执行一个 SQL 语句时,如果执行成功,就会向数据库自动提交,而不能回滚。
    关闭数据库连接,数据就会自动的提交。
    同一个事务的多个操作必须在同一个连接下:如果多个操作分别使用的是自己单独的连接,则无法保证事务。

一、MySQL日志

鉴于后文与日志息息相关,因此单独整理一个章节来介绍MySQL中三种重要的日志。

1.redo log(事务日志)

  • 假设每次读写都进行磁盘IO:
    设想一下,如果每次读写都进行磁盘IO,我们都知道,频繁IO会导致系统性能急剧下降,尤其是在高并发场景下,更应该去避免这样的问题。

  • 引入缓存Buffer Pool:
    MySQL在引入缓存Buffer Pool之后,有效缓解数据库的磁盘压力。当从数据库读数据时,首先从缓存中读取,如果缓存中没有,则从磁盘读取后放入缓存;当向数据库写入数据时,先向缓存写入,然后将缓存中的脏页定期刷到磁盘中(后台线程异步操作)。是不是效率高了很多?

  • 引入redo log:
    redo log记录的是数据库中每个物理数据页的修改,不过恢复数据时只能恢复到最后一次提交的位置。
    修改数据时,InnoDB引擎会把更新记录先写在redo log中,再修改Buffer Pool中的数据;
    提交事务时,先把redo log刷入磁盘。由后台线程异步将缓存中更新的数据文件定期刷入磁盘。(两阶段事务,先写redo log,再写bin log)

redo log是固定大小的
redo log记录的是物理数据页上的修改,如果Buffer Pool中数据页已经刷磁盘后,redo log的这些记录就没有保留的必要了,因此新记录会将这些失效的记录进行覆盖擦除。

2.undo log(事务回滚日志)

此日志是保证事务 原子性 的关键。记录的是数据修改前的状态,在数据修改的流程中,同时会记录一条与当前操作相反的逻辑日志到undo log中。

undo log负责回滚,redo log负责前滚。

Q.同一个事物内的一条记录被多次修改,每次数据修改前的状态都会写入undo log吗?

undo log只负责记录事务开始前要修改数据的原始版本。

3.bin log(二进制归档日志)

  • 非擦除:
    bin log记录了数据库所有修改操作,它不会像redo log那样循环写擦除之前的记录,而是会一直记录日志。一个bin log日志文件默认最大容量1G,单个日志超过最大值,则会新创建一个文件继续写日志。

  • 记录反向逻辑:
    和undo log类似,bin log记录的是执行SQL命令的反向逻辑,另外,bin log日志文件需要设置过期时间。

  • 作用:
    用处一:主从模式中主从节点间的数据同步;
    用处二:数据还原。

bin log是人工使用,redo log、undo log是事务使用。

二、原子性实现(回滚机制)

undo log: 当一个事务需要回滚时,MySQL会使用undo log将修改的数据恢复到原始状态。

三、持久性实现

redo log: MySQL 的事务日志采用了"写前日志" (write-ahead logging, WAL) 的方式。这种方式的特点是,在将数据写入磁盘之前,先将对数据的修改操作记录到日志中。这样可以确保在系统出现异常情况时,MySQL 可以根据日志中的操作记录进行恢复。

bin log: 在 MySQL 中,事务的持久性不仅仅依赖于事务日志的写入,还与磁盘的写入策略有关。MySQL 会将数据缓存在内存中,并通过一些优化手段减少磁盘 I/O 操作的次数。但是,为了确保事务的持久性,MySQL 在一定条件下仍然需要将数据写入磁盘中。比如,在执行 COMMIT 语句时,MySQL 会将所有未提交的事务操作强制写入磁盘中,确保数据的持久性。

四、隔离性实现

MySQL的事务隔离性是通过 上锁+MVCC 实现的。

1.并发事务带来的问题

  • 脏读: 读取到了其他事务修改了但还没提交的数据。
  • 不可重复读: 在同一个事务中,两次读取的数据不同,因为在此期间被其他事务修改了。
  • 幻读: 在同一个事务中,两次读取的数据行数不同,因为在此期间事务新增/删除了记录。

2.标准SQL事务隔离级别

MySQL事务隔离级别 默认是可重复读。

探秘MySQL——全面了解事务及其底层实现(undo log、redo log、binlog、锁、MVCC)_第1张图片

3.MySQL锁

  • 全局锁: 对整个数据库实例加锁,将进入全局只读状态。
  • 表锁: 对一张表加锁。
  • 行锁: 对符合条件的索引项加锁。
  • 间隙锁: 对一个行范围加锁,在这个范围内不能插入记录,但是不锁定行本身。即,可删除、不可插入。解决了插入幻读 问题。
  • 行锁+间隙锁: 范围内,不可删除、不可插入。解决了插入幻读、删除幻读 问题。

4.事务隔离性实现(锁+MVCC)

MySQL 提前一级 解决并发读写问题。

  • 读未提交: 读不加锁,更新加行级共享锁。有 脏读 问题。
  • 读提交(提前一级): 读不加锁为MVCC快照读,更新加行级排它锁。解决 脏读、不可重复读 问题。
  • 可重复读(提前一级): 读不加锁为MVCC快照读,更新加行级排它锁+间隙锁。解决脏读、不可重复读、幻读 问题。
  • 序列化: 读加表级共享锁,更新加表级排它锁。
快照读: InnoDB 通过 MVCC(多版本控制)将数据库在过去某个时刻的快照应用在查询上,使得:
这次查询 只能看到 别的事务生成快照前提交的数据,而不能看到 别的事务生成快照后提交的数据或者未提交的数据。

Q.针对幻读,事务1插入,不提交,事务2,插入前,事务2查询,插入后,再查询,会发生什么情况?

MySQL默认事务隔离级别是可重复读,在这一隔离级别,已经基于锁+MVCC解决了幻读问题,因此 不会出现幻读现象

具体: 事务1插入前事务2读到的是没有插入的数据(并且是快照读),事务1插入后,事务1 会生成新的快照,但是事务2读的还是自己之前读的那个快照,并不是这个事务1新产生的快照。

特殊情况: 在一些特殊情况下,仍然可能会发生幻读。例如,如果事务1在执行完插入操作后,立即提交并释放锁,而事务2在执行查询操作时,会重新获取锁并读取最新的数据快照版本。这种情况下,事务2就有可能发生幻读现象,因为它读取的数据快照版本已经更新,包含了事务1插入的新数据。

Q.死锁问题的产生和解决

两阶段锁协议: InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,需要等事务结束时才释放,这就是两阶段锁协议。遵循此协议可能带来事务 死锁 问题。

探秘MySQL——全面了解事务及其底层实现(undo log、redo log、binlog、锁、MVCC)_第2张图片

解决: ①超时等待之后事务退出;②设置死锁检测,发现死锁之后主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。

参考

事务实现
日志

你可能感兴趣的:(存储工具,mysql,数据库,java)