1.InnoDB多版本简介
InnoDB是一个多版本存储引擎:为了支持像并发和回滚等事务特性,InnoDB保存被修改数据行的旧版本信息。这些旧版本信息存储在表空间中称为回滚段的数据结构中(沿用Oracle中类似的数据结构)。InnoDB用回滚段中的信息来进行事务回滚所需的取消操作。同时,它也用这些信息构建数据行的较早版本来完成一致性读。
InnoDB内部,其为数据库中存储的每个数据行增加了三个域。一个6字节DB_TRX_ID域表示最后insert或update该数据行的事务的标识符。同时,delete操作内部也被当做update处理,其中,被删除数据行的一个特定位被设置以标示该数据行已被删除。每个数据行还包含一个叫做滚动指针(roll pointer)的7字节DB_ROLL_PTR域,滚动指针指向一个已写入回滚段的取消日志记录。如果该数据行被修改了,该取消日志记录包含重建该数据行修改前内容的必要信息。一个6字节DB_ROW_ID域包含一个新数据行插入时单调递增的行ID。如果InnoDB自动产生了一个簇索引,则该索引包含该行ID值。否则,DB_ROW_ID列不会出现在任何索引中。
回滚段内的取消日志分为insert和update取消日志。insert取消日志仅在事务回滚时需要,一旦事务提交就会被抛弃。update取消日志也被用于一致性读,其仅在InnoDB不再为任何事务分配因重构数据行较早版本而需要这些update取消日志信息的快照时,才会被抛弃。
定期提交事务,包括那些只发布一致性读的事务。否则,InnoDB因不能抛弃update取消日志,而导致回滚段变的很大,最终将耗光表空间。
回滚段中一条取消日志记录的物理大小通常比相应插入或修改的数据行要小。因此,可以用这个信息计算回滚段所需的空间。
InnoDB多版本机制中,当用SQL语句删除一个数据行时,物理上并非立即从库中将其删除。InnoDB仅在抛弃该删除操作写入的update取消日志记录时,才会物理上移除相应的数据行及其索引记录。这个移除操作称为清除(purge),其运行非常快,通常只需SQL语句执行删除时大致相当的时间。
如果insert和delete按照同等速率小批量地插入和删除表中的数据行,purge线程可能会发生延迟,且表会因为那些“死”数据行而变的越来越大,同时,会使整个系统变得磁盘受限和很慢。这种情况下,通过限制新的数据行操作,并通过调整innodb_max_purge_lag系统变量来为purge线程分配更多的资源。
2.多版本和二级索引
InnoDB多版本并发控制(MVCC)对待二级索引与簇索引不同。簇索引中的记录被原地修改,它们的隐藏系统列指向重构记录较早版本所需的取消日志项。不像簇索引记录,二级索引记录并不包含隐藏系统列,同时,它们也不是被原地修改。
当二级索引列被修改时,旧记录被标识为删除,新记录被插入,被标识为删除的记录最终被清除掉。当二级索引记录被标识为删除或二级索引页被新事务修改后,InnoDB将在簇索引中查找该数据库记录。簇索引中,如果读事务初始化后该记录已被修改,则将检查该记录的DB_TRX_ID,并从相应取消日志获取该记录的正确版本。
如果二级索引记录被标识为删除或二级索引页被新事务修改,则不会使用覆盖索引技术。而是InnoDB从该二级索引中获取数值,然后,从相应簇索引中查找该记录。
然而,如果开启了索引条件下推(index condition pushdown,ICP)优化,部分where条件可以只通过该索引的域就可以进行评估,mysql服务器还是会将这部分where条件下推到存储引擎,并在存储引擎中通过该索引对其进行评估。如果未发现该条件的匹配记录,则避免了簇索引的查找。如果发现了该条件的匹配记录,哪怕是已标识为删除的记录,InnoDB将在其相应簇索引中查找该记录。