在了解mysql事务的实现之前我们先介绍一些概念,这些概念会帮助你理解。

概念知识点

  1. undo log(回滚日志)
    事务的原子性,的底层就是通过undo log(回滚日志)实现的,undo log主要记录了数据的逻辑变化。
    比如:一条 INSERT 语句就对应一条 DELETE 的undo log,对于每个 UPDATE 语句,对应一条相反的 UPDATE
    的undo log,当发生错误的时候,就能回滚到事务之前的数据状态。
  2. redo log(重做日志)
    用于记录事务对数据页做了哪些修改(持久性)。
  • 基本概念
    redo log包括两部分:一个是内存中的日志缓冲(redo log buffer),另一个是磁盘上的日志文件(redo logfile)。
    mysql每执行一条DML语句,先将记录写入redo log buffer,后序在某个时间点一次性将多个操作记录写到redo log file。
    这种先写日志,再写磁盘的技术就是mysql里经常说到的WAL(Write-Ahead Logging)技术。
    在计算机操作系统中,用户空间(user space)下的缓冲区数据一般情况下无法直接写入磁盘,中间必须经过操作系统内核空间(kernel space)缓冲区
    (OS Buffer)。
  • mysql回滚机制 mysql回滚原理_java

  • 因此redo log buffer写入redo logfile实际上是先写入OS buffer,然后再通过系统调用fsync()将其刷到redo logfile中。
  • mysql回滚机制 mysql回滚原理_数据_02

  • mysql支持三种将redo log buffer写入redo log filed的时机,可以通过innodb_flush_log_at_trx_commit参数来配置。
  • mysql回滚机制 mysql回滚原理_回滚_03

  • 前面说过,redo log记录数据页的变更,而这种变更记录是没必要全部保存的,因此redo log采用了大小固定,循环写入的方式,当写到结尾的时候,
    会回到开头循环写日志。
  • mysql回滚机制 mysql回滚原理_回滚_04


  • mysql回滚机制 mysql回滚原理_回滚_05

  1. readView
    在我们执行一个事务的时候就会生成一个readView。readView的组成结构如下。注意:每次执行事务的时候都会生成新的readView
    参数说明:
  1. creator_trx_id 当前事务id
  2. m_ids 表示在生成readView时当前系统中活跃的读写事务的事务id列表
  3. min_trx_id m_ids中最小的事务id,用于判断当前事务版本是否能被访问
  4. max_trx_id m_ids中最大的事务id,用于判断当前事务版本是否能被访问
  1. 版本链
    在Innodb中,新建一张表都会默认新建两个字段,trx_id和roll_pointer。
  1. trx_id:事务字段,当一个事务去操作某个行的数据的时候,会将自己的事务id赋值给trx_id字段。
  2. roll_pointer:回滚指针,当一个事务更新了一个字段的时候,并不会直接覆盖掉之前的字段,而是将该指针指向之前的字段存储到undo log
  3. 每当事务中更新一条数据时,会将其添加到undo log中,随着更新的次数增多,数据会逐渐被连成一个链,也就是所说的版本链。
    有了上面的概念理解起来就很容易了。

上正菜

  1. 事务的四大特征(ACID):原子性,一致性,隔离性,持久性。
  1. 原子性的实现:
    undo log,innodb的回滚,靠的是undo log,当事务对数据库进行修改时,innodb会生成相应的undo log;如果事务执行
    失败或调用了rollback,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子。
  2. 一致性的实现:
  3. 持久性的实现:
    持久性的实现用的是redo log
  4. 隔离性的实现:
    **与原子性,持久性侧重于研究事务本身不同,隔离性研究的是不同事务之间的相互影响。**隔离性是指,事务内部的操作与其他
    事务是隔离的,并发执行的各个事务之间不能互相干扰,严格的隔离性,对应了事务隔离级别中的Serializable(可串行化),
    但实际应用出于性能方面的考虑会很少使用串行化。
  1. 隔离性的四个级别:
  • 读未提交:read uncommitted
  • 读已提交:read committed(一般常用)
  • 可重复读:repeatable read(mysql默认)
  • 串行化:serializable
    我们常用的两种隔离级别rc、rr;所以我们就来聊聊这两个隔离级别的实现原理。
    innodb实现rc、rr这两种隔离级别使用的是MVCC(Multi-Version Concurrency Control)多版本的并发控制协议。
    MVCC最大的优点是读不加锁,因此读写不冲突,并发性能好。

在rc(读已提交)的隔离级别下,在每个查询语句(事务)之前都会生成一个readView,用查询出来的版本链中的事务ID和readView
中存储的事务ID进行对比,直到找出符合当前隔离级别事务下的ID然后返回这个ID所对应的数据。
对比规则:
1. 当访问的版本的事务ID小于readView中最小的事务ID,说明在生成readView之前这个事务就已经被提交了,则可以访问
2. 当访问的版本的事务ID大于readView中最大的事务ID,说明当前访问的事务ID是在生成readView之后才创建的,则不可以访问
3. 当访问的版本的事务ID介于readView中最大和最小之间,则需要判断访问的版本的事务ID是否存在于readView中,如果存在
说明这个事务还没有提交,不能访问。如果不存在,说明这个事务已经提交了,可以访问。
在rr(可重复读)的隔离级别下,在一个事务中的第一个查询之前会生成一个readView,并且生成的这个readView会一直沿用到事务提交
以此来保证可重复读。