说实话,我干数据库这行也有七八年了,天天看各种大神说索引碎片怎么怎么重要,动不动就建议重建索引。 上个月我们生产库有个表,大概2亿行,查询突然慢了十倍。 我一看碎片率,某个非聚集索引碎片率到了79%,当时第一反应就是赶紧重建。 结果你猜怎么着?重建花了4个小时,业务直接受影响,被领导骂了个狗血淋头。
后来我复盘了一下,发现我犯了个大错。 那个表是典型的只读报表表,每天夜里批量更新一次,白天全是查询。 按理说碎片率高确实会影响扫描,但这个库是SQL Server 2016企业版,支持在线重建。 我直接用了ALTER INDEX ... REBUILD WITH (ONLINE = ON),但忽略了并行度。 默认的MAXDOP是0,结果就是所有CPU一起上,把IO和CPU瞬间打满,业务能不卡吗? 而且碎片率79%的大索引,在线重建本身就会产生额外日志,日志文件还差点爆了。
这事让我开始重新思考碎片维护这个问题。 网上那些"碎片率超过30%就要重建"的说法,其实误导性很强。 碎片率对查询性能的影响,取决于扫描范围。 如果查询总是走窄范围的seek,碎片率再高也无所谓。 相反,如果总是大范围scan,那么碎片率确实致命。 但现代SSD硬件,顺序读和随机读差距没那么大了,碎片影响被硬件进步掩盖了不少。
而且,重建索引真的比先DROP再CREATE好? 我做过对比,对于非聚集索引,很多情况下DROP+CREATE反而更快,因为重建索引时,引擎要做大量分配和日志记录,而DROP+CREATE只需要修改元数据和重新组织数据,虽然会短暂失去索引,但在维护窗口内完全可以接受。 更关键的是,重建索引会重新生成统计信息,有时候统计信息更新触发了错误计划,性能反而变差。 我甚至碰到过重建之后查询计划从哈希连接变成嵌套循环,慢得离谱。
那到底该怎么办? 我现在有自己的套路:先看等待类型,如果是PAGEIOLATCH_SH或者CXPACKET,再看具体查询。 碎片率超过50%的大索引,如果表是OLTP型的,而且有明确维护窗口,我会考虑离线重建,加上MAXDOP=8限制并发,而不是盲目用在线。 如果是OLAP型的,碎片真不重要,重要是分区表对齐和列存储索引。 另外,我习惯重建之前先跑一下DBCC SHOW_STATISTICS,看看密度和直方图,有时候老统计信息比新统计信息更接近真实分布。 总之,索引碎片不是万恶之源,也不是一重建就灵。 关键是要搞清楚你的查询到底在干什么。 别把数据库日常维护当成念经,每念一次就有功德。 我见过太多同行拿着网上抄来的脚本,每个周日凌晨4点跑全库重建,结果服务器累死,业务一点没快。 与其迷信碎片率,不如多看看执行计划和等待统计,那才是真正告诉你问题在哪的东西。 这算是我用一块新硬盘的钱换来的教训吧,希望看到的人别再踩了。