前言
InnoDB维护旧版本数据来支持事务并发和回滚。旧版本信息会存储在系统表空间或者undo表空间里,以回滚段(rollback segment)的数据结构存储。当事务需要回滚的时候就会使用到这些回滚段来回滚,同时一致性读的时候也会用到这些回滚段。
注:
1、系统表空间:system tablespace,是InnoDB存储数据的空间,存储了doublewrite buffer、change buffer、undo logs,同时可能会存储表数据和索引数据。我们常见的ibdata1这种文件就存储在这个空间。
2、undo表空间:undo tablespaces,这个空间里存储了undo logs,能够节约其他表空间如system tablespace的使用,同时针对SSD存储该空间有做IO优化。
InnoDB架构图
实现原理
1、InnoDB向表里的每行增加三个字段
- DB_TRX_ID,记录了上一次插入或者更新该行的事务id,删除操作也被视为更新操作
- DB_ROLL_PTR,回滚指针,指向回滚段,如果一行被更新,那么更新之前undo log会记录这一行的信息。
- DB_ROW_ID,如果是自增主键,这个值则包含自增主键,如果不是,那么该ID会给每个新增的行存储一个单调递增的ID。
2、Undo logs在回滚段里被划分为insert undo logs和update undo logs,前者事务提交之后就可以清除了;后者主要记录更新的版本数据,并且可以用于一致性读。
3、聚簇索引数据在原位置更新,且这些行的隐藏列会指向undo logs;辅助索引数据的更新则是先删除再插入,同时没有隐藏列,如果涉及事务一致性读或者回滚等操作则会找到该索引对应的聚簇索引,取出DB_TRX_ID,从undo log里取出正确版本信息。
InnoDB是如何利用一致性读(快照读)的
0、快照读和当前读
a、快照读简单理解为是事务里的select。快照读利用mvcc实现。
b、当前读简单理解为是事务里的update、delete。当前读加锁实现。
1、Repeatable Read可重复读隔离级别下
- 同一事务里的所有快照读都读的是第一次读时创建的快照
If the transaction isolation level is
REPEATABLE READ(the default level), all consistent reads within the same transaction read the snapshot established by the first such read in that transaction.
- 此隔离级别下通过快照读和next-key lock(当前读)解决了幻读问题
a、快照读解决幻读就不解释了
b、当前读的时候会给读取到的行加上next-key lock,如果是范围读就给范围加锁,如果是读一些行就给行和行前的间隙加锁。这样当前读的时候就不会有新的数据插入或者被删除
- 考虑RR级别下一致性读的原理
begin
# first read
select * from table where id = 21
.....
# second read
select * from table where id = 21
commit
a、第一次读的时候会创建快照,此时select读取的行里有隐藏了DB_TRX_ID,同时DB_ROLL_PTR指向undo logs里的回滚段。
b、假设此时innodb里有多个活跃的事务887、889、890,和已经提交的事务885、886,则创建快照过程如下:
首先,如果DB_TRX_ID小于当前活跃的最小事务min_active_trx_id,那么说明该行数据是被已提交的事务修改的,直接返回该快照
否则,如果DB_TRX_ID大于等于当前最大事务id max_trx_id,则说明其他新事务修改了这行数据,此时需要从undo log里取已提交的数据创建快照并返回
如果,DB_TRX_ID 大于等于当前活跃的最小事务ID,并且小于当前最大事务ID。
情况1、DB_TRX_ID 是非活跃事务ID,说明修改这行数据的事务已经提交了,直接返回该快照
情况2、如果DB_TRX_ID是活跃事务ID,则说明修改这行数据的事务当时还没提交,这时候就要判断create_trx_id是不是等于DB_TRX_ID,如果是,说明当前事务之前操作了这行数据,则直接返回快照。否则,说明数据是其它活跃事务修改的,那么数据不可见,需要从undo log 里获取已提交的数据创建快照并返回。
d、第二次读的时候直接读取相同快照即可
2、Read Commited 读已提交隔离级别下
- 同一事务里的所有快照读都自己创建快照,并读取这个快照
- 可以看出此隔离级别下快照读无法解决幻读问题,因为每次读都创建快照,不保证多个读操作的一致性
