前置文章:
一、MySQL-存储引擎
零、本文纲要
- 一、事务
- 二、MySQL事务原理
- 三、redo log(重做日志)
- 四、undo log(回滚日志)
- 五、MVCC
1、当前读
2、快照读
3、MVCC实现
4、MVCC原理分析
tips:Ctrl + F快速定位所需内容阅读吧。
一、事务
1、事务介绍
事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作的请求,即这些操作要么同时成功,要么同时失败。
2、事务特性
- 原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败;
- 一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态;
- 隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行;
- 持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。
二、MySQL事务原理
1、redo log & undo log
InnoDB中使用redo log
和undo log
来保证事务的原子性、一致性、持久性。
2、锁 & MVCC
InnoDB中使用锁
和MVCC
来保证事务的隔离性。
三、redo log(重做日志)
1、redo log介绍
redo log(重做日志),记录的是事务提交时数据页的物理修改,是用来实现事务的持久性。
2、redo log作用
该日志文件由两部分组成:重做日志缓冲(redo log buffer
)以及重做日志文件(redo log file
),前者是在内存中,后者在磁盘中。
当事务提交之后会把所有修改信息都存到该日志文件中,用于在刷新脏页到磁盘,发生错误时,进行数据恢复使用。
3、对比redo log有无情形
- 有redo log:
① 当对缓冲区的数据进行增删改之后,会首先将操作的数据页的变化,记录在redo log buffer中;
② 在事务提交时,会将redo log buffer中的数据刷新到redo log磁盘文件中;
③ 过一段时间之后,如果刷新缓冲区的脏页到磁盘时,发生错误,此时就可以借助于redo log进行数据恢复,这样就保证了事务的持久性;
④ 而如果脏页成功刷新到磁盘 或 或者涉及到的数据已经落盘,此时redolog就没有作用了,就可以删除了,所以存在的两个redolog文件是循环写的。
#ib_logfile0
#ib_logfile1
4、WAL
在业务操作中,我们操作数据一般都是随机读写磁盘
的,而不是顺序读写磁盘。 而redo log在往磁盘文件中写入数据,由于是日志文件,所以都是顺序写
的。顺序写的效率,要远大于随机写。这种先写日志的方式,称之为 WAL(Write-Ahead Logging)。
5、刷盘时机
innodb_flush_log_at_trx_commit:
0: 每秒将日志写入并刷新到磁盘一次;
1: 日志在每次事务提交时写入并刷新到磁盘,默认值;
2: 日志在每次事务提交后写入,并每秒刷新到磁盘一次。
四、undo log(回滚日志)
1、undo log介绍
回滚日志(也称撤销日志
),用于记录数据被修改前的信息 , 作用包含两个 : 提供回滚(保证事务的原子性)
和MVCC(多版本并发控制)
。
undo log记录的是逻辑日志,可以认为记录的是与我们执行的update、insert、delete相反的逻辑语句。
2、undo log销毁
undo log在事务执行时产生,事务提交时,并不会立即删除undo log,因为这些日志可能还用于MVCC。
3、undo log存储
undo log采用段的方式进行管理和记录,存放在rollback segment回滚段中,内部包含1024个undo log segment。
五、MVCC
1、当前读
读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。
- ① 对应SQL
- select ... lock in share mode(共享锁);
- select ...for update(排它锁);
- update、insert、delete(排它锁)。
- ② 演示示例
Ⅰ 事务B提交后,事务A当前读,可以读到最新数据,如下:
Ⅱ 事务B提交后,事务A快照读,不能读到最新数据,如下:
2、快照读
简单的select(不加锁)就是快照读,快照读,读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读。
- ① 快照读的不同情形
- Read Committed:每次select,都生成一个快照读;
- Repeatable Read:开启事务后第一个select语句才是快照读的地方;
- Serializable:快照读会退化为当前读。
- ② 演示示例
具体示例见上。
3、MVCC实现
MVCC全称 Multi-Version Concurrency Control,多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突,快照读为MySQL实现MVCC提供了一个非阻塞读功能。MVCC的具体实现,还需要依赖于数据库记录中的三个隐式字段
、undo log日志
、readView
。
- ① 三个隐式字段
Ⅰ DB_TRX_ID:最近修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID;
Ⅱ DB_ROLL_PTR:回滚指针,指向这条记录的上一个版本,用于配合undo log,指向上一个版本;
Ⅲ DB_ROW_ID:隐藏主键,如果表结构没有指定主键,将会生成该隐藏字段。
查看.ibd文件存储位置的命令:show global variables like '%datadir%';
,位置为/var/lib/mysql/
,如下:
show global variables like '%datadir%';
+---------------+-----------------+
| Variable_name | Value |
+---------------+-----------------+
| datadir | /var/lib/mysql/ |
+---------------+-----------------+
查看表结构信息的指令,如下:
ibd2sdi ***.ibd
演示示例:
-- 创建未设置主键的tb_test02表格
create table tb_test02 (id int , name varchar(10));
-- 查看tb_test02表格的各列信息
[root@localhost test]# ibd2sdi tb_test02.ibd
-- 截取指令输出的关键内容
"columns": [
{
"name": "id",
},
{
"name": "name",
},
{
"name": "DB_ROW_ID",
},
{
"name": "DB_TRX_ID",
},
{
"name": "DB_ROLL_PTR",
}
],
- ② undo log(回滚日志)
回滚日志(又称撤销日志),在insert、update、delete的时候产生的便于数据回滚的日志。
Ⅰ 当insert
的时候,产生的undo log日志只在回滚时需要,在事务提交后
,可被立即删除
;
Ⅱ 而update
、delete
的时候,产生的undo log日志不仅在回滚时需要,在快照读时也需要,不会立即被删除
。
注意:insert对快照读已存在的数据不产生影响,而update和delete可能产生影响,所以此处会有区别。
版本链
:不同事务或相同事务对同一条记录进行修改,会导致该记录的undolog生成一条记录版本链表,链表的头部
是最新
的旧记录,链表尾部
是最早
的旧记录。
- ③ readview(读视图)
ReadView(读视图)是 快照读 SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交的)id。
四个核心字段:
Ⅰ m_ids
:当前活跃的事务ID集合;
Ⅱ min_trx_id
:最小活跃事务ID;
Ⅲ max_trx_id
:预分配事务ID,当前最大事务ID+1(因为事务ID是自增的);
Ⅳ creator_trx_id
:ReadView创建者的事务ID。
版本链数据的访问规则(trx_id是当前undolog版本链对应事务ID):
生成ReadView的时机:
Ⅰ READ COMMITTED :在事务中每一次执行快照读时
生成ReadView;
Ⅱ REPEATABLE READ:仅在事务中第一次执行快照读时
生成ReadView,后续复用该ReadView。
4、MVCC原理分析
- ① RC(READ COMMITTED)隔离级别
RC隔离级别下,在事务中每一次执行快照读时生成ReadView。
示例解读:
此情形中,当我们对比到版本链中trx_id = 2的时候,满足条件②,此时该版本就是版本链中事务5可以读取到的数据。
- ② RR(REPEATABLE READ)隔离级别
RR隔离级别下,仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。RR 是可重复读,在一个事务中,执行两次相同的select语句,查询到的结果是一样的。
六、结尾
以上即为事务和MVCC部分的内容了,感谢阅读。