隐式字段
每行记录除了我们自定义的字段外,还有数据库隐式定义的DB_TRX_ID,DB_ROLL_PTR,DB_ROW_ID等字段
- DB_TRX_ID
6byte,最近修改(修改/插入)事务ID:记录创建这条记录/最后一次修改该记录的事务ID
- DB_ROLL_PTR
7byte,回滚指针,指向这条记录上一个版本(存储于rollback segment里)
- RB_ROW_ID
6byte,隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引
- 实际还有一个删除flag隐藏字段, 既记录被更新或删除并不代表真的删除,而是删除flag变了
如上图,DB_ROW_ID是数据库默认为该行记录生成的唯一隐式主键,DB_TRX_ID是当前操作该记录的事务ID,而DB_ROLL_PTR是一个回滚指针,用于配合undo日志,指向上一个旧版本
undo日志
undo log是为回滚而用,具体内容就是copy事务前的数据库内容(行)到undo buffer,在适合的时间把undo buffer中的内容刷新到磁盘。undo buffer与redo buffer一样,也是环形缓冲,但当缓冲满的时候,undo buffer中的内容会也会被刷新到磁盘;与redo log不同的是,磁盘上不存在单独的undo log文件,所有的undo log均存放在主ibd数据文件中(表空间),即使客户端设置了每表一个数据文件也是如此。
undo log主要分为两种:
- insert undo log
代表事务在insert新记录时产生的undo log, 只在事务回滚时需要,并且在事务提交后可以被立即丢弃 - update undo log
事务在进行update或delete时产生的undo log; 不仅在事务回滚时需要,在快照读时也需要;所以不能随便删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被purge线程统一清除
当执行查询sql的时候会生成一致性视图 read-view,由执行查询时候所有未提交事务id数组(数组里最小的id为min_id)和已创建的最大事务id(max_id)(包含已提交和未提交)组成,查询的数据结果需要跟read-view做对比从而的得到快照结果
undo版本链是如何生成的:
- 对
DB_ROW_ID = 1
的这行记录加排他锁 - 把该行原本的值拷贝到
undo log
中,DB_TRX_ID
和DB_ROLL_PTR
都不动 - 修改该行的值这时产生一个新版本,更新
DATA_TRX_ID
为修改记录的事务ID
,将DATA_ROLL_PTR
指向刚刚拷贝到undo log
链中的旧版本记录,这样就能通过DB_ROLL_PTR
找到这条记录的历史版本。如果对同一行记录执行连续的UPDATE
,Undo Log
会组成一个链表,遍历这个链表可以看到这条记录的变迁 - 记录
redo log
,包括undo log
中的修改
按照规则版本链中查询最新的记录
因为默认是可重复读,那么readview 都是可沿用下来的,如果是读已提交那么每次都会更新最新的
当执行第一条查询sql的时候从日志的上到下开始读取发现当前最新的事务id是300,然后根据read-view进行对比,发现落在黄色区域,并且没有在数组中,那么就是可见的 查询出的结果就是lilei300
另外一种情况:
这个时候因为不是一个会话所以无需遵守 可重复读规则,那么 当前未提交数组就变成了了 200 最大事务是300