1. innoDB的主要日志介绍
binlog:服务层生产日志,数据恢复、数据库复制
redo log:数据在物理层面的修改,断电恢复、保证一致性和持久性,顺序 :写入数据->redo log buffer->file system buffer->fsync操作->redo log file ->commit
undo log:事务回滚、实现MVCC
2. innoDB MVCC
2.1 隐含字段
6-byte DB_TRX_ID:最新更新这行记录的事务ID,每处理一个事务,其值自动+1
7-byte DB_ROLL_PTR:指向rollback segment 的undo log记录的指针
6-byte DB_ROW_ID(若没有设置主键MySQL则隐式的创建一个主键,因为InnoDB会自动创建一个主键索引,所以必须要一个主键)
DELETE BIT位用于标识该记录是否被删除,这里的不是真正的删除数据,而是标志出来的删除。真正意义的删除是在commit的时候
2.2 实现原理
innodb MVCC主要是为Repeatable-Read事务隔离级别做的。在此隔离级别下,A、B客户端所示的数据相互隔离,互相更新不可见
了解innodb的行结构、Read-View的结构对于理解innodb mvcc的实现有重要意义
当事务更改该行的值时,会进行如下操作:
用排他锁锁定该行
记录redo log
把该行修改前的值Copy到undo log,即上图中下面的行
修改当前行的值,填写事务编号,使回滚指针指向undo log中的修改前的行
若回滚,根据回滚指针从undo log找出事务修改前的版本并恢复。
若提交,更改事务状态为COMMIT,其他什么都不做(见下面的具体操作)
靠 readView (事务视图) 来实现的。多个 readView 组成 undo log(回滚日志)。
innoDB的mvcc主要是为了Repeatable-Read隔离级别实现的。
2.3 具体操作执行过程
begin->用排他锁锁定该行->记录redo log->记录undo log->修改当前行的值,写事务编号,回滚指针指向undo log中的修改前的行
上述过程确切地说是描述了UPDATE的事务过程,其实undo log分insert和update undo log,因为insert时,原始的数据并不存在,所以回滚时把insert undo log丢弃即可,而update undo log则必须遵守上述过程
下面分别以select、delete、 insert、 update语句来说明过程
SELECT
Innodb检查每行数据,确保他们符合两个标准:
1、InnoDB只查找版本早于当前事务版本的数据行(也就是数据行的版本必须小于等于事务的版本),这确保当前事务读取的行都是事务之前已经存在的,或者是由当前事务创建或修改的行
2、行的删除操作的版本一定是未定义的或者大于当前事务的版本号,确定了当前事务开始之前,行没有被删除
符合了以上两点则返回查询结果。
INSERT
InnoDB为每个新增行记录当前系统版本号作为创建ID。
DELETE
InnoDB为每个删除行的记录当前系统版本号作为行的删除ID。
UPDATE
InnoDB复制了一行。这个新行的版本号使用了系统版本号。它也把系统版本号作为了删除行的版本。
说明
insert操作时 “创建时间”=DB_ROW_ID,这时,“删除时间 ”是未定义的;
update时,复制新增行的“创建时间”=DB_ROW_ID,删除时间未定义,旧数据行“创建时间”不变,删除时间=该事务的DB_ROW_ID;
delete操作,相应数据行的“创建时间”不变,删除时间=该事务的DB_ROW_ID;
select操作对两者都不修改,只读相应的数据
3. ReadView
MVCC 中维护了一个 ReadView 结构,主要包含了当前系统未提交的事务列表 TRX_IDs(事务ID) {TRX_ID_1, TRX_ID_2, ...},还有该列表的最小值 TRX_ID_MIN 和 最大值TRX_ID_MAX。
在进行 SELECT 操作时,根据数据行快照的 TRX_ID 与 TRX_ID_MIN 和 TRX_ID_MAX 之间的关系,从而判断数据行快照是否可以使用:
TRX_ID < TRX_ID_MIN,表示该数据行快照时在当前所有未提交事务之前进行更改的,因此可以使用。
TRX_ID > TRX_ID_MAX,表示该数据行快照是在事务启动之后被更改的,因此不可使用。
TRX_ID_MIN <= TRX_ID <= TRX_ID_MAX,需要根据隔离级别再进行判断:
提交读:如果要读取的数据还存在于TRX_ID事务列表中,表示该数据行快照对应的事务还未提交,则该快照不可使用。否则表示已经提交,可以使用。
可重复读:都不可以使用。因为如果可以使用的话,那么其它事务也可以读到这个数据行快照并进行修改,那么当前事务再去读这个数据行得到的值就会发生改变,也就是出现了不可重复读问题。
可重复读隔离级别则在第一次读的时候会生成一个ReadView,之后的读都复用之前的ReadView。(会通过ROLL_PTR去查询初始的数据行结果,来保证数据相同)