欢迎来到窥视未来的博客

https://github.com/lwx57280 https://gitee.com/li_VillageHead

MySQL索引设计原则:从离散度到覆盖索引,打造高效查询的黄金法则

MySQL索引设计原则:从离散度到覆盖索引,打造高效查询的黄金法则

索引设计得好,查询飞起;设计得差,写入拖垮、查询慢如蜗牛。本文从索引的离散度、最左前缀、覆盖索引等核心概念出发,结合底层原理和实战案例,总结8条索引设计黄金法则,助你避开常见误区,让SQL性能发挥到极致。

一、引言:索引是把“双刃剑”

索引能加速查询,但并非越多越好。每个索引都会占用磁盘空间,并降低INSERT、UPDATE、DELETE的性能(因为需要同步维护索引)。因此,索引设计的核心是在查询效率与写入成本之间找到最佳平衡。

那么,什么样的索引才是“好”索引?我们从“离散度”这个最基础的概念说起。

 

二、原则一:选择离散度高的列

离散度公式:

 

                           image

 

  • 离散度越接近1,列的值越不重复,索引效果越好。

  • 离散度越低(如性别:'男','女'),索引几乎没有区分度,优化器宁愿全表扫描。

示例:

  • 手机号、身份证号 → 离散度 ≈ 1 → 非常适合建索引。

  • 性别、状态(0/1) → 离散度极低 → 不适合建独立索引。

image

注意:如果查询条件总是同时包含低离散度列和其他列,可以考虑联合索引。

三、原则二:索引列应出现在WHERE、JOIN、ORDER BY、GROUP BY中

索引只对那些真正用于过滤、连接、排序或分组的列才有意义。如果某列从未出现在这些子句中,为其建索引只会浪费空间和写性能。

正确做法:

  • 分析业务SQL,找出最常用的过滤条件。

  • 优先为高频且离散度高的列建索引。

反例:为address列建索引,但所有查询都是根据id和name进行的。

四、原则三:避免在索引列上使用函数或表达式

MySQL默认无法对索引列应用函数后再进行查找。例如:

-- 索引失效
SELECT * FROM user WHERE YEAR(create_time) = 2024;

-- 改写为范围查询,索引有效
SELECT * FROM user WHERE create_time >= '2024-01-01' AND create_time < '2025-01-01';

例外:MySQL 8.0支持函数索引,可以直接对函数结果建索引,但需谨慎使用。

五、原则四:覆盖索引——让索引“包办”所有查询字段

覆盖索引:一个索引包含了查询所需的所有列,只需扫描索引即可返回结果,无需回表,性能极高。EXPLAIN中Extra列显示Using index。

-- 假设有联合索引 idx_name_phone (name, phone)
SELECT name, phone FROM user WHERE name = '张三';   -- 覆盖索引
SELECT * FROM user WHERE name = '张三';             -- 需要回表

建议:

  • 尽量只查询必要字段,避免SELECT *。

  • 设计联合索引时,将查询常用字段放入索引中,实现覆盖。

image

 

六、原则五:联合索引遵循最左前缀原则

联合索引(a, b, c)在B+Tree中按a排序,再按b,再按c。查询时必须从索引的最左列开始连续匹配,否则无法使用索引。

有效查询:

  • WHERE a = 1

  • WHERE a = 1 AND b = 2

  • WHERE a = 1 AND b = 2 AND c = 3

无效查询:

  • WHERE b = 2(缺少a)

  • WHERE a = 1 AND c = 3(部分命中索引、部分失效s 部分命中索引(仅a有效,c无效)跳过b,破坏前缀连续性)

优化器自动重排:WHERE b = 2 AND a = 1 会被优化器调换顺序,索引生效。

实战建议:将区分度高的列放在最左边,因为索引会先按最左列筛选,区分度高能快速缩小范围。

 

image

 

 

七、原则六:控制索引数量,避免滥用

每增加一个索引,INSERT、UPDATE、DELETE都要额外维护该索引(排序、页分裂等),写入性能会下降。

经验值:

  • 单表索引数量建议不超过5~7个(视写负载而定)。

  • 删除重复或冗余的索引。例如已有(a, b),再建(a)就是冗余;但(b, a)不是冗余,作用不同。

八、原则七:用合适的数据类型建立索引

  • 整型比字符串更快,占用空间小,B+Tree节点能存更多键值。

  • 自增整型主键:插入时总是在最右端,避免页分裂;UUID等随机值会导致频繁页分裂和碎片。

  • 字符串过长:使用前缀索引,只取前N个字符(如name(10)),既节省空间又保有一定区分度。

-- 前缀索引示例
CREATE INDEX idx_name_prefix ON user(name(10));

计算合适的前缀长度:

SELECT COUNT(DISTINCT LEFT(name, 10)) / COUNT(*) AS selectivity FROM user;

选择使选择性接近1的最小长度。

九、原则八:频繁更新的列不宜做主键或索引

每次更新索引列,都需要在B+Tree中删除旧键并插入新键,可能引发页分裂,造成大量磁盘I/O。对于频繁更新的列(如状态、最后登录时间),应慎重建索引。

 

十、总结:索引设计checklist

 
原则核心要点
离散度 选择COUNT(DISTINCT) / COUNT(*) 接近1的列
使用场景 索引列必须出现在WHERE/JOIN/ORDER BY/GROUP BY中
避免函数 不在索引列上用函数,或使用8.0函数索引
覆盖索引 让索引包含所有查询字段,避免回表
最左前缀 联合索引从最左列开始连续匹配,高离散度列放左边
控制数量 单表索引不宜过多,删除冗余索引
数据类型 整型优于字符串;长字段用前缀索引;主键用自增整型
更新频率 频繁更新的列不宜建索引

一句话总结:优秀的索引设计,是离散度、顺序、覆盖、数量、数据类型五者的精妙平衡。

如果觉得本文对你有帮助,欢迎点赞、收藏、转发!
关注我,获取更多MySQL性能优化干货。

posted on 2026-05-07 19:28  k8s-Mango  阅读(43)  评论(0)    收藏  举报

导航