在ask tom看到一个很精彩的讨论,关于索引的重建,解释了很多问题,值得学习。

索引的“平衡”

贴子中的这句话引起了我的兴趣,因为之前习惯将重建索引安排到数据库定期维护中。

The time lag between index rebuilds should be approximate FOREVER. They would destroy the equilibrium that the system worked so hard to get to.

对于这个结论的解释是:

在对索引列进行反复更新时,这条记录在索引树上的位置也在不断变化移动,在这期间索引会不断的增大:当可移动的空间不够时,数据库会扩展空间(拆分block)。这将造成索引上有许多空闲的blocks(正常情况下这些空闲的Leaf blocks会在freelist上,在需要的时候会被重用),但这并不会造成问题,扩展到一定程度后,即便再插入新的数据,索引树也不需要再继续扩展,因为索引树上空闲空间已经足够多,可以被新数据重复利用了。

引用中的”平衡”就是指这:使用中Freelist上blocks共成的组成的索引。两者比例应该是最适合系统业务运行的(因为是通过实际业务操作一点一点形成的,就像HashMap一样,所占用空间并不能完全被利用,而是通过LoadFactor来控制),或者说是因为系统业务特点而造成了索引离散性,正常情况下这应该是索引的最佳状态

而当重建索引后,所有经过以上过程产生blocks清掉。但随着系统业务模块运行,反复的更新再次导致不断记录在索引的移动,而索引又重新开始尝试扩展到”最佳状态”的过程。


为什么会经常考虑到重建索引?

之前我会把重建索引安排到定期的数据库维护中,甚至有时不会检查是否需要。觉得即便多次重建也不会有影响,因为很久之前听过这么一个规则:

Oracle中,当表中记录被删除后,索引中对应的记录不会删除。

并由此推出重建索引后,能够回收空间,而去除“无用数据”后的索引也应该更加高效。但其实在Oracle中有补偿机制来规避这些问题:

  • 当leaf block的上的数据被删除后,正常情况下这些block会进入freelist中,在需要的时候会被重用。不存在浪费。
  • freelist中的block也是索引的一部分,他是由实际系统操作累积到的。即便重建索引回收这部分空间,随着系统的运行,索引还是会扩展出这部分空间并且一般表而言,通过重建索引回收的空间并不可观。
  • 一些情况会导致被删除数据关联的索引block不能正常进入freelist(如在删除大量数据后,这些block并不能立即被放到freelist中),导致查询时会访问这些dead leaf,降低效率。但针对这种情况,除了rebuild外,coalesce更适合解决这问题。alter index index_name coalesce会重新声明那些dead leaf block到freelist中,而不是把这部分清除掉。
  • 索引碎片问题可以通过将索引创建在local managed的表空间来解决。

只需要在适当的场景下重建索引

综合上面所说,不适当的索引重建会造成:

o 数据库会生成成倍的redo log.
o 系统运行将变慢.
o 消耗更多的资源(CPU,IO,latching,etc).
o 系统或将无法处理与之前同等负载.

不过并不是反对所有的索引重建,而不该将其当作一个定时任务-”无脑”重建。应该分析后再重建,例如,如果能确保某张表的数据不会在继续增大了,可以重建回收一部分空间(这更适合数据仓库的需求);大数据量导入时,为追求效率的情况下,可在导入前禁用索引,导入完成后重建;又或者index的状态为unusable了,重建可以使其恢复正常等。

关于重建,rebuild与re-create可以达到相同的效果,但rebuild更加安全,在原索引健全的情况下,能够直接访问原索引创建出新的索引,而不需要对表做全表访问和排序。但如果原索引信息不完整(如用一段时间后将索引禁用),rebuild也会对原表排序和访问。

总之,这篇帖子中学到了”打脸”的知识点。