InnoDB多版本并发控制
InnoDB 是一个数据多版本的存储引擎。它保留有关已更改行的旧版本的信息,以支持并发性和回滚等事务性特性。此信息存储在系统表空间中,或在undo 表空间中称为回滚段的数据结构中。InnoDB使用回滚段中的信息来执行事务回滚中所需的撤消操作,它还会使用这些信息来构建具有一致性读的行的早期版本(即多版本快照读)。
InnoDB默认会为每行数据添加三个隐式字段
- 一个6字节的DB_TRX_ID字段表示插入或更新该行的最后一个事务的事务标识符。此外,删除在内部被视为一个更新,其中行中的一个特殊位被设置为将其标记为已删除。
- 一个7字节的DB_ROLL_PTR字段,称为回滚指针。回滚指针指向undo log中的回滚段记录。如果行被更新,则undo log日志将记录更新行之前重建行内容所需的信息。(实际就是通过此指针,将最新行记录和旧的版本记录链接在一起,成为一个链表,MVCC机制的快照读用到了此链表)
- 一个6字节的DB_ROW_ID字段包含一个行ID,它随着插入新行而单调地增加。如果InnoDB自动生成一个聚簇索引,则该索引包含行ID值。否则,DB_ROW_ID列将不会出现在任何索引中。
回滚段中的undo log分为insert日志和update日志。Insert undo logs仅在事务回滚时需要,一旦事务提交,可以立刻丢弃。只有在没有事务为InnoDB分配了一个快照,在快照读中可能需要Update undo logs中的信息来构建数据库行的早期版本时,它们才能被丢弃。 (即当前非最新的update undo log没有任何事务用它来构建快照读视图时可以丢弃)。
建议定期提交事务,包括只有快照读的事务,否则,InnoDB不能丢弃update undo log中的旧记录数据, rollback segment可能会越来越大,从而填充增大它所在的表空间。
回滚段的undo log记录的物理大小通常小于相应的插入或更新的行,你可以使用此信息来计算回滚段所需的空间。
在InnoDB多版本控制方案中,当你使用sql语句删除行时,不会立即从数据库中物理删除该行。InnoDB只有在丢弃为删除写入的update undo log记录时,才会物理删除相应的行及其索引记录 。这个删除操作称为清除,它的清除速度非常快,通常与执行删除的sql语句相同。
如果你在表中以几乎相同的速率插入和删除行,清除线程可能会开始滞后,并且因“dead”行表越来越大,使得所有行都被磁盘绑定,速度非常慢。在这种情况下,请限制新的行操作,并通过调整innodb_max_purge_lag系统变量来为清除线程分配更多的资源 。
多版本并发控制和辅助索引
InnoDB多版本并发控制(mvcc)对辅助索引的处理不同于聚簇索引。聚簇索引中的记录将就地更新,它们隐藏的系统列指向undo log日志项,可以从中重建早期版本的记录。 不同于聚簇索引,辅助索引记录不包含隐藏的系统列,也不进行就地更新。
当辅助索引列更新时,将对旧的辅助索引记录进行删除标记,插入新记录,并最终清除有删除标记的记录。 当辅助索引记录被标记删除或辅助索引页被新的事务更新时,innodb将在聚簇索引中查找数据库记录。在聚簇索引中,检查记录的DB_TRX_ID,如果在启动读取事务后修改了记录,则从undo log中检索记录的正确版本。
如果辅助索引记录被标记需要删除或辅助索引页被新的事务更新,InnoDB在聚簇索引结构中查找记录,而不是从索引结构中返回值。
但是,如果启用了索引条件下推(icp)优化,并且其中的where条件只能使用索引中的字段进行评估,mysql服务器仍然将这部分的where条件下推到存储引擎,在那里使用索引对其进行评估。如果没有找到匹配的记录,则会避免进行聚簇索引查找 。如果找到匹配的记录,即使是在有删除标记的记录中,InnoDB也会在聚簇索引中查找该记录。
MVCC实现原理小结
通过上面官方的概述,我们知道,MVCC是为了提高数据库读写并发而采取的一种数据多版本控制机制。数据库并发除了读写并发还有写写并发,写写并发只能通过程序控制,悲观锁或乐观锁