联合索引不是多个索引,而是一棵B+树
很多人误以为
CREATE INDEX idx_a_b_c ON t(a,b,c)会生成
(a)、
(a,b)、
(a,b,c)三棵树——其实不会。MySQL只建一棵B+树,排序规则是:先按
a升序,
a相同时再按
b升序,
a和
b都相同时再按
c升序。这就决定了查询必须“从左开始连续匹配”,否则无法定位到有序区间。 能用索引:
WHERE a = 1、
WHERE a = 1 AND b = 2、
WHERE a = 1 AND b = 2 AND c > 10不能用索引:
WHERE b = 2(跳过
a)、
WHERE a = 1 AND c = 3(
b中断,
c无法继续走索引) 注意:
WHERE b = 2 AND a = 1看似顺序反了,但MySQL查询优化器会自动重排为
a = 1 AND b = 2,仍可命中索引
范围查询(>、
这是最常踩的坑。一旦联合索引中某列用了范围条件,它右边所有列就**彻底失去索引加速能力**,哪怕你写了等值条件也不行。
SELECT * FROM orders WHERE user_id = 1001 AND status = 1 AND create_time > '2024-01-01';
这句只能用上
(user_id, status),
create_time的范围判断之后,索引就“断”了——即使你后面还加了
AND pay_amount = 99.9,这个
pay_amount也查不到索引。 正确做法:把范围列尽量放在联合索引末尾,如
(user_id, status, create_time)错误设计:若常用
WHERE status = 1 AND create_time BETWEEN ... AND ...,却把
status放在第二位,那这个查询根本用不上索引
LIKE 'abc%'是范围行为,
LIKE '%abc'则完全不走索引(连最左都不满足)
explain 是唯一能验证你是否真用上索引的工具
别靠“我写了 where a=1 and b=2”就以为索引生效了。必须看
EXPLAIN的
key和
key_len字段:
key显示实际使用的索引名 → 确认没走全表扫描
key_len显示用了索引多少字节 → 比如
idx_user_status_time中
user_id是INT(4字节),
status是TINYINT(1字节),那么
key_len = 5表示只用到了前两列;若
key_len = 4,说明只用到了
user_id,
status没参与索引查找 执行
EXPLAIN FORMAT=TRADITIONAL SELECT ...,重点关注
type(最好为
ref或
range),避免出现
ALL
索引字段顺序不是“哪个查得多就放前面”,而是按查询模式定
有人觉得“
status取值少、区分度低,应该放后面”,但现实是:如果你的高频查询是
WHERE status = 'paid' AND user_id IN (1,2,3),那这个索引
(status, user_id)就比
(user_id, status)更合适——因为前者能直接定位到所有
status='paid'的数据块,再在其中快速筛选
user_id;后者却要先遍历每个
user_id对应的索引项,再回表判断
status,效率反而更低。 等值查询多、范围查询少 → 把高选择性(区分度高)字段放前面 固定前缀 + 范围查询 → 把等值列全放左,范围列放右,如
(shop_id, category_id, created_at)存在多个高频查询组合?优先覆盖最耗性能的那条,或拆成两个索引(空间换时间)
真正容易被忽略的是:最左前缀原则不是“语法限制”,而是B+树物理结构决定的刚性约束。写SQL时字段顺序无关紧要,但索引定义的列序一旦定下,就锁死了所有可能的查询路径。改索引成本远高于改SQL,所以建之前就得想清楚主查询模式。
