8.5.2-优化InnoDB表的存储布局

要想优化InnoDB事务处理,就要找出事务相关的性能损耗和系统负载之间的平衡。例如,如果一个应用每秒钟commit几千次,可能会带来性能问题,如果每2-3小时提交一次,又会有新的问题。

  • AUTOCOMMIT=1是MySQL的默认配置,这对于一个繁忙的数据库服务器来锁可能会带来性能限制。实用的做法是在一个事务中处理多个相关的数据变更。可以使用set autocommit=0或start transaction语句,修改完成后再使用commit提交。 如果事务修改了数据中的内容,每次事务提交的时候,InnoDB必须将日志刷到磁盘上。如果每一个变更都跟着commit(默认配置),存储设备的I/O吞吐量限制了并发操作的数量。

  • 对于只由select组成的事务,打开autocommit后,InnoDB能识别只读事务,并且对其进行优化。见章节 8.5.3,“只读事务优化”。

  • 在插入、更新、或删除大量记录后,尽量避免进行回滚。如果一个大事务降低了服务器的性能,回滚大事务可能会让事情更严重,耗费的时间可能是原始操作时间的数倍。杀死数据库进程没用,因为在数据库服务重启后,回滚会继续。 要想减少这个问题发生的概率:

    • 增加buffer pool的量,这样所有的数据变更都可以被缓存起来,而不是立即写入磁盘。
    • 将innodb_change_buffering设置成all,这样update和delete操作才会和insert一样被缓存起来。
    • 在遇到大的数据变更操作时,可以考虑定期进行commit,或者将一个delete/update操作分割成多个,每个操作处理的记录更少。
    • 要摆脱失控回滚,增加buffer pool量,这样回滚就变成了cpu密集型,运行的会更快,或者是kill掉数据库进程,重启时带上innodb_force_recovery=3参数。 因为innodb_change_buffering=all是默认配置,这个问题出现的概率预计不会很高。这允许更新和删除操作被缓存在内存中,这样执行的更快,会滚时也会很快。在执行大量插入,更新,或删除相关的长事务时,记得使用这个参数。
  • 如果你能接受数据库意外关闭时,损失一部分最近提交的事务,可以将innodb_flush_log_at_trx_commit设置成0。这时,InnoDB会每秒刷一次日志,虽然仍然不能保证。

  • 当记录被修改或删除时,记录数据和相关undo 日志不会马上被物理删除,即使事务被提交了。等到较早启动或同时启动的事务结束,旧的数据才会被删除。因此,一个长事务可以阻止另一个事务修改的数据刷新到磁盘。

  • 如果在一个长事务中修改或删除记录,使用读已提交和可重复读的其它事务可能要花费更多的时间来重构这些记录的老版本数据。

  • 当一个长事务修改一张表时,其它事务对这张表进行查询时不会使用覆盖索引技术。使用覆盖索引可以从二级索引中查询出所有列,而不需要回表。 如果二级索引页的PAGE_MAX_TRX_ID很新,或者二级索引中的记录被标记为删除,InnoDB可能要在聚簇索引上查找记录。