Bootstrap

Mysql事务的奥秘:探索InnoDB事务原理与MVCC机制

本文章示例是以mysql8.0版本

事务是一组操作的集合,它是不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或者撤销操作请求。即这些操作要么同时成功,要么同时失败

事务四大特性

特性

描述

原子性

事务是不可分割的最小单元,要么全部成功,要么全部失败

一致性

事务完成时,必须使所有的数据状态一致

隔离性

数据库系统提供隔离机制,保证事务在不受外部并非操作影响的独立环境运行

持久性

事务一旦提交或者回滚,它对数据库中的数据的改变就是永久的

事务并发问题

问题

描述

脏读

一个事务读取到了另一个事务还没提交的数据

不可重复读

一个事务先后读取同一个记录,但两次读取的数据不同

幻读

一个事务在第一次读取时不存在,当插入数据时发现数据存在

事务隔离级别

级别

可能出现问题

默认设置

读未提交

脏读、不可重复读、幻读

读已提交

不可重复读、幻读

Oracle

可重复读

幻读

Mysql

串行化

InnoDB事务原理

redo log

  • 目的:是为了保证事务的持久性
  • 组成:redolog buffer 和redolog file
  • 操作:当事务提交之后会把所有修改信息都存在该日志文件中,用于在刷新脏页到磁盘发生错误时,进行数据恢复使用

为什么不直接将Buffer Pool实时刷到磁盘?

因为一个事务操作是包含很多数据的,数据页是随机的,IO消耗较大

undo log

  • 目的:是为了保证事务的原子性
  • 概述:逻辑日志,记录每一条的操作记录,用于回滚数据
  • insert,事务提交后,可以立即删除,update、delete,产生的undo log日志不仅在回滚时需要,在快照读时也需要,不会被立即删除

mvcc

基本概念

概念

描述

备注

当前读

读取的是最新的记录,保证不会有其他事务修改数据,需要对记录加锁

select...lock in share mode(共享锁)

select ... for update(排它锁)

快照读

select 读取的是记录数据可见版本,有可能是历史数据,不加锁,是非阻塞读

读已提交:每次select都生成快照读

可重复读:开启事务后第一个select语句才是快照读

串行化:快照读会退化为当前读

mvcc

多版本控制

三个隐式字段、undolog、readView

三个隐式字段

隐藏字段

含义

DB_TRX_ID

最近修改事务ID,记录插入这条数据或最后一次需要改记录的事务ID

DB_ROLL_PRE

回滚指针,指向这条记录的上一个版本,用于配合undo log,指向上一个版本

DB_ROW_ID

隐藏主键,如果表结构没有指定主键,将会生成该隐藏主键

示例:

感兴趣的同学,可以看一下

# 查看磁盘文件 一般是在这个目录下,test是数据库名
cd  /var/lib/mysql/test

# 查看文件命令
ibd2sdi t_user.ibd

undo log版本链

不同事务或者相同事务对同一天记录进行修改会导致该记录的undolog生成一条记录版本链表,链表的头部分是最新的记录,尾部是最早的旧记录

  • 示例数据:

事务2

事务3

事务4

事务5

开启事务

开启事务

开启事务

开启事务

修改id为1记录,age修改为3

查询id为1的记录

提交事务

修改id为1,name改为王五

查询ID为1的记录

提交事务

修改id为1记录,age修改为10

查询ID为1的记录

查询ID为1的记录

提交事务

  • 产生的版本链

ReadView

  • 核心字段

字段

含义

m_ids

当前获取的事务ID集合

min_trx_id

最小活跃事务ID

max_trx_id

预分配事务Id,当前最大事务ID+1

creator_trx_id

ReadView创建者的事务ID

  • 判断逻辑顺序

trx_id:代表是当前事务ld。

  1. trx_id=creator_trx_id? 可以访问该版本 成立,说明数据是当前这个事务更改的
  2. trx_id<min_trx_id?可以访问该版本 成立,说明数据已经提交了
  3. trx_id>max_trx_id?不可以访问该版本 成立,说明该事务是在readview生成后才开启
  4. min_trx_id<trx_id<max_trx_id?如果trx_id不在m_ids中是可以访问该版本的成立,说明数据已经提交

实现原理

不同的隔离级别,生成ReadView的时机不同

RC:在事务中的每一次执行快照读生成ReadView

RR:仅在事务中第一次执行快照读生成ReadView,后续复用该ReadView

继续以上面的示例进行示范

  • 并发事务

  • 判断过程

调链

trx_id=creator_trx_id

trx_id<min_trx_id

trx_id>max_trx_id

min_trx_id<trx_id<max_trx_id? trx_id不在m_ids

是否成立

第一个调用链

4!=5

4>3

4<6

4在m_ids中

第二个调用链

3!=5

3=3

3<6

3在m_ids中

第三个调用链

2!=5

2<3

;