知识卡片
索引设计的两个真实陷阱:冗余索引与隐式类型转换导致的全表扫描
内容
索引设计里有两类容易被忽视、却真实发生在生产环境的陷阱。第一类是冗余索引:如果已经建立了idx_abc(a,b,c)这个联合索引,再单独建idx_a(a)或idx_ab(a,b)就是冗余的——因为B+Tree索引遵循最左前缀匹配原则,idx_abc本身已经能够高效支持仅按a查询、或按a和b组合查询的场景,额外建立的这些单独索引不会带来任何检索性能上的收益,反而会增加写入时需要维护的索引数量,白白降低写入性能。第二类更隐蔽、也更容易在真实业务代码里踩坑:隐式类型转换导致的全表扫描——案例中一个定义为varchar(50) NOT NULL的字段remark,如果查询条件写成remark=115127(传入的是整型而不是字符串’115127’),MySQL会因为类型不匹配而放弃使用这个字段上的索引,退化成全表扫描,同样的查询把整型改成加引号的字符串remark='115127'后,执行时间从0.14秒直接降到0.005秒——差了近30倍。这个案例背后揭示的问题不是索引设计本身的缺陷,而是应用程序端没有做好类型检查,让一个本该走索引的查询因为传参类型不对而意外走了全表扫描,且这种性能问题往往不会在功能测试阶段暴露出来(因为查询结果依然是对的,只是慢),只有在真实数据量增长后才会显现出明显的性能劣化。这两个陷阱共同提示了一条排查数据库性能问题的经验:索引本身设计得再合理,也可能因为写入端的多余重复(冗余索引拖慢写入)或调用端的类型使用不规范(隐式转换绕开索引)而失效,排查性能问题时不能只盯着索引结构本身,还要检查实际发出的SQL语句里参数类型是否和字段定义精确匹配。
参考来源
- 位置:《高可用架构(第1卷)》第5章《运维保障》"5.3 单表60亿记录等大数据场景的MySQL优化和运维之道"节,"5.3.2 数据库开发规范"(源文件:_epub-src/OEBPS/Text/Chapter5_3_3.xhtml)
- 结论依据:原文举出冗余索引示例("idx_abc(a,b,c)……idx_a(a)冗余。idx_ab(a,b)冗余")以及隐式转换实测对比("字段定义为varchar类型,但传入的值是int类型,就会导致全表扫描,这要求程序端做好类型检查",实测0.14秒 vs 0.005秒),直接支撑本卡片结论。
- 原始内容:冗余索引示例:idx_abc(a,b,c)。idx_a(a)冗余。idx_ab(a,b)冗余……字段定义为varchar类型,但传入的值是int类型,就会导致全表扫描,这要求程序端做好类型检查。