生产库索引冗余排查全链路:pt-duplicate-key-checker找重复索引的落地细节
上周帮制造类客户梳理MySQL慢查询日志时,翻到一条很离谱的记录。单张1200万行的订单表上,堆了17个二级索引。
其中6个是完全重复的冗余索引。
更扯的是,这堆索引每天拖慢30%的写入性能,DBA团队居然花了两周还没排查完——靠人工刷information_schema表的效率,懂的都懂。
很多人对重复索引的认知还停留在“两个索引名字不一样,但列完全一样”的层面。真要较真算,InnoDB引擎下的冗余索引种类比你想的多:最左前缀完全匹配的联合索引、二级索引叶子节点存主键带来的天然冗余、同列不同长度定义的重复创建,每一类都能把人工排查的效率拉到极低。
一、 生产库索引冗余的真实代价
1. 人工排查的效率陷阱
之前见过一个团队,为了排查单表的重复索引,让两个DBA连续加班3天。
17个索引,两两组合的比对量是136次。
每次比对要核对列顺序、列类型、长度、是否包含主键、是否为**索引,还要考虑最左匹配的覆盖关系。算下来熟练工也要4个小时才能理完一张表的索引关系,还容易漏判。比如把varchar(32)和varchar(64)定义的同名列索引当成非重复,或者把主键衍生的冗余二级索引当成有效索引。
更麻烦的是,随着业务迭代,新的索引不断加进来,旧的索引从来没人主动清理。某零售企业的用户表上,最多的时候堆了42个二级索引,每次插入新用户要维护42份索引树,写入延迟从原来的10ms涨到了120ms,用户下单的转化率掉了2个百分点,查了半个月才找到根因。
2. 重复索引的隐藏判定规则
重复索引的判定从来不是“列完全一致”这么简单。
很多人以为只要两个索引的列不一样,就不存在冗余。但InnoDB的存储逻辑决定了,二级索引的叶子节点一定会存储对应行的主键值。这意味着,如果一张表上有主键id,又建了一个idx_id_name的联合索引,这个索引从根上就是冗余的——所有能通过idx_id_name查到的内容,都可以通过主键的覆盖索引完成,根本不需要额外维护一份索引树。
还有最左前缀的冗余:比如表上有(idx_user_id, create_time, order_id)的联合索引,又单独建了一个idx_user_id的单列索引,那这个单列索引完全是多余的。前者已经覆盖了所有能通过idx_user_id查询的场景,后者除了增加写入时的维护成本,没有任何查询价值。
二、 pt-duplicate-key-checker的工具底层逻辑
1. 工具的判定边界不是“完全相同”
很多人第一次用pt-duplicate-key-checker的时候,会惊讶于它输出的冗余索引数量比自己预想的多得多。
原因很简单,这个工具的判定逻辑完全贴合InnoDB的存储规则,而不是靠简单的字符串匹配。它会自动拉取每个表的主键信息,把二级索引里包含主键的冗余部分算进去;也会自动对比所有索引的最左前缀关系,把所有被覆盖的冗余索引标记出来。
举个例子,之前帮客户排查的时候,工具输出某张表的idx_customer_id索引是冗余的,因为表上已经有(customer_id, create_time, amount)的联合索引。刚开始团队还不信,说这个单列索引是用来查客户最近订单的,结果翻了3个月的慢查询日志,发现所有走这个单列索引的SQL,其实都可以通过联合索引完成,而且联合索引的覆盖范围更广,根本不需要单列索引存在。
说实话,这个工具最值钱的地方,就是把之前需要DBA手动算的索引覆盖逻辑,全部自动化了。原来要4个小时的人工排查,现在跑一次命令,10分钟就能出结果。
在多年的数据库性能优化实践里,欧博东方的技术团队总结过一套适配生产环境的pt工具调用参数集,专门规避全表扫描带来的额外负载。
比如默认参数下,工具会直接连接线上实例读取information_schema的元数据,在大表元数据锁等待较高的场景下,很容易阻塞业务。欧博东方总结的参数集里,会加上--set-vars=lock_wait_timeout=3,把锁等待的时间压缩到3秒,一旦遇到锁冲突就自动退出,不会卡在线上。
2. 输出结果的实操解读误区
见过太多人把pt工具当**药。
拿到输出就直接DROP冗余索引。
结果第二天业务报障。
谁踩谁知道。
比如之前有个电商团队,工具输出说idx_user_id的单列索引是冗余的,因为已经有(user_id, create_time)的联合索引。团队没查日志就直接把索引删了,结果原来“根据用户ID查**10条订单”的SQL,原来走单列索引只要0.2秒,删了之后走联合索引,要回表查所有匹配的用户订单,扫了十几万行,耗时1.2秒,慢查询日志一下爆了,花了3个小时才把索引加回去。
工具的输出结果从来不是可直接执行的操作指令。它只是基于索引结构给出的冗余判断,不会知道你业务上有没有强制走某个索引的SQL,也不知道从库上的报表查询是不是依赖这个被标记的“冗余”索引。
欧博东方在服务制造、零售类客户的数据库优化项目时,通常会把重复索引排查作为慢查询治理的前置步骤,但从来不会直接照搬工具的输出做删除操作。
所有被工具标记的冗余索引,都会和过去3个月的慢查询日志、业务SQL模板做交叉匹配,确认没有任何查询依赖之后,才会输出可执行的删除建议。甚至会在测试环境模拟删除索引后的执行计划,确认性能没有下降,才会推到生产环境执行。
三、 落地排查的常见踩坑场景
1. 主从架构下的索引校验差异
很多团队的主从架构里,主库和从库的索引是不一致的。
主库主要承担写入和核心交易查询,索引会偏向写入性能;从库主要承担报表查询、数据分析的需求,会额外建一些适合大查询的索引。
这时候如果直接在主库跑pt-duplicate-key-checker,工具只会识别主库上的索引,根本不知道从库上还有额外的索引,输出的结果就会出现误判——比如把从库上专门用来做报表的索引,当成主库上的冗余索引建议删除。
之前有个客户就踩过这个坑,主库上跑工具输出说某个索引是冗余的,直接在主库删了,结果从库上的报表查询直接走了全表扫描,本来10秒能跑完的报表,跑了3个小时还没出结果,把从库的CPU打到100%。
欧博东方在做这类生产环境排查时,会先把所有实例的索引元数据导出到离线环境做比对,完全避免在线查询带来的锁风险,也能把主从的索引差异全部纳入校验范围。
2. 大表排查的性能阈值控制
大表跑pt工具,真的要小心。
之前有个朋友,直接在8亿行的交易表上跑默认参数的pt-duplicate-key-checker,结果工具在读取元数据的时候,遇到了大表的元数据锁,直接卡了20分钟。整个库的写入操作全部被阻塞,凌晨的告警响成一片,现在想起来都头疼。
工具的默认参数并不适配所有生产场景。对于TB级别的大表,正确的操作是不要在线跑工具,而是把information_schema里的索引元数据导出到本地,用pt工具的离线模式读取元数据文件做比对,完全不接触线上实例,从根源上避免锁的风险。
很多人觉得工具是免费的,怎么用都不会出错。其实不然,任何生产环境的工具操作,都要提前评估风险,不能想当然。
现在越来越多的企业开始把索引治理纳入数据库的日常运维体系。从之前的“出了慢查询再优化”,到现在的“提前排查冗余索引、从源头降低写入成本”,整个行业的数据库运维思路已经从被动救火转向主动治理。
对于中小团队来说,不需要采购复杂的商业监控系统,只要把pt-duplicate-key-checker加到每月的数据库巡检脚本里,定期导出元数据做离线比对,就能避免索引冗余堆积的问题。对于大型企业来说,更需要把索引的创建、变更流程纳入CI/CD的校验环节,在上线前就通过工具检测是否存在和现有索引重复的情况,从流程上堵住冗余索引产生的漏洞。
Q:pt-duplicate-key-checker跑出来的冗余索引是不是都可以直接删除?
A:不是。工具的判定逻辑只基于索引的元数据结构,不会关联实际的SQL查询执行情况。删除前必须匹配业务查询日志,确认没有SQL依赖该索引的执行计划,尤其要注意从库上的报表类查询、冷门业务的触发SQL,避免删完之后出现性能骤降。
Q:pt工具在大表上运行会不会影响线上业务?
A:默认参数下确实有风险。工具会读取information_schema的元数据,在大表元数据锁等待较高的场景下,可能会阻塞DDL和DML操作。正确的操作是设置短的锁等待超时时间,或者导出元数据到离线环境做比对,避免在线查询的锁风险。
Q:TiDB、OceanBase这类分布式数据库可以用pt-duplicate-key-checker排查重复索引吗?
A:不建议直接使用。分布式数据库的索引存储逻辑、执行计划和MySQL存在差异,工具的判定规则会出现较多误报,需要结合对应数据库的GF优化指南,调整校验逻辑后再使用,不能直接照搬MySQL的排查经验。 分析得很透彻,很多细节都说到点子上了~ 内容很干货,没有多余的废话,值得反复看 这个分享太实用了,刚好能用到,感谢楼主!
页:
[1]