InnoDB是多版本存储引擎:它可以保留数据行在变化之前的版本,支持事务功能,例如并发性和回滚。这个信息存放在表空间中的一种叫做回滚段(类似于Oralce)的数据结构中。InnoDB使用回滚段中的信息来对需要回滚的事务执行撤销操作。也会使用回滚段中较早版本数据行来实现一致读(查询结果在执行查询的时候已经确定,无论在查询的期间数据会如何变化)。
InnoDB会给数据库中的每行添加三个区域。一个6字节的DB_TRX_ID
区域,表示最仅一次入或更新事物的事物ID。另外,删除的本质是将一行的某个位更新为了删除标记。每行还包含一个7字节的DB_ROLL_PTR
区域,叫做roll pointer
滚动指针。滚动指针指向回滚段中的一个UNDO日志记录。如果行被更新,UNDO日志记录里的信息可以让数据行回到更新之前的版本。一个6字节的DB_ROW_ID
区域用于存放新写入行的行标记(row id),这个行标记用于唯一标识每一行并且是单调递增的。如果InnoDB自动生成了聚集索引(clustered index),则索引里会有行标记值。否则,DB_ROW_ID
列不会出现在任何索引中。
回滚段中的UNDO日志分成了insert和update两种UNDO日志记录。Insert的UNDO日志只需要用于事务回滚,事务提交之后就可以清除掉。Update的UNDO日志还可以用于一致读,只有当当前没有事务需要使用Update UNDO日志生成较早版本数据行来实现一致读快照时,才可以清除。
要养成提交事务的习惯,尤其是对于那些只用于一致读的事务。否则,InnoDB无法释放Undo日志,回滚段会变得很大,占满表空间。
回滚段中UNDO日志记录的物理大小通常比对应写入或更新的行要小。可以使用这个信息来计算回滚段的空间大小需求。
在InnoDB多版本的设计上,当使用SQL delete一行时,不会立刻被物理地清除,只会被标记为删除。删除一行时,InnoDB只会物理的移除对应行的索引记录。这种移除操作叫做purge
,非常快,通常和SQL语句delete操作的时间顺序相同。
如果你按照差不多相同的频率写入和删除小批量数据行,purge线程会很滞后,表会变得越来越大,因为存在很多‘死’行,占用大量空间并且很慢。在这种情况下,应该调整innodb_max_purge_lag
系统变量,给purge
线程分配更多资源。
多版本和二级索引
InnoDB多版本并发控制(MVCC
)对待二级索引和对待聚集索引有所不同。聚集索引中的记录是在原地(IN-PLACE
)更新,含有的隐藏系统列指向用于回退到较早版本行的UNDO日志记录,二级索引记录没有隐藏系统列也不是在原地更新。
当二级索引列被更新,旧的二级索引记录会被标记为删除,然后写入新的记录,删除标记过的行最终会被purge
掉。当二级索引记录被标记为删除或者二级索引页被新的事务更新,InnoDB会查找聚集索引里的记录。在聚集索引中,如果读操作执行之后记录被更改(但未提交),会检查记录的DB_TRX_ID,从UNDO日志中获取记录的正确版本。
如果二级索引记录被标记为了删除,或者二级索引页被新的事务更新,covering index
技术不会被使用。不会从二级索引结构中返回值,而是从聚集索引中查找记录。
covering index:
索引里包含所有查询需要返回的结果列。而不会使用索引值作为指针去在全表中查找行,查询返回的值全部来自于索引。
但是,如果index condition pushdown(ICP)
优化选项被启用,并且部分where条件可以仅使用索引中的字段,Mysql服务器会将这一部分条件下推到InnoDB引擎,然后存储引擎会使用索引通过下推的条件从基表中筛选出满足条件的行。如果没有发现匹配的记录,就不用扫描聚集索引了。如果匹配到了一些记录,即使是删除标记的行,InnDB仍然会在聚集索引中查找.
index condition pushdown(ICP):
ICP是让Mysql使用索引来获取行的优化选项。没有ICP,存储引擎会遍历索引来定位基表里的行然后将满足where条件的行返回给Mysql服务器。启用ICP时,如果部分where条件只需要使用索引中的列,Mysql服务器会将这一部分条件下推到InnoDB引擎。然后存储引擎会使用索引通过下推的条件从基表中筛选出满足条件的行。ICP可以减少存储引擎对基表的访问时间以及Mysql服务器访问存储引擎的时间。
网友评论