mysql索引优化中的数据分布与索引设计

来源:这里教程网 时间:2026-02-28 20:48:16 作者:

为什么
WHERE
条件用了索引,但执行计划还是走全表扫描?

常见错觉是“只要字段有索引,查询就一定用上”。实际 MySQL 优化器会评估

WHERE
条件的 选择性(selectivity):如果数据分布极度倾斜(比如
status
字段 95% 是
'active'
),即使该字段有索引,优化器也可能判定走索引+回表的成本高于直接全表扫描。

SELECT COUNT(*) FROM t WHERE status = 'active';
SELECT COUNT(*) FROM t;
对比,估算选择性是否低于 5%~10%
执行
EXPLAIN
后重点看
type
列:若为
ALL
index
,说明未命中有效索引;再看
rows
是否接近总行数
对低选择性字段,单独建索引意义不大;可考虑与高选择性字段组合成联合索引,把低选择性字段放在后面

联合索引中字段顺序怎么排才不白建?

联合索引

(a, b, c)
实际只等价于三个索引:
(a)
(a,b)
(a,b,c)
,并不支持跳过前导列的查询(如
WHERE b = ?
WHERE b = ? AND c = ?
)。

过滤性最强 的字段放最左:比如用户表中
user_id
(唯一)比
city
(重复多)更适合做首列
把常用于
ORDER BY
GROUP BY
的字段靠右放置,可避免
Using filesort
;例如查询
WHERE category = ? ORDER BY created_at DESC
,索引应为
(category, created_at)
注意
IN
和范围查询(
>
,
BETWEEN
)会截断后续字段的索引使用:在
(a, b, c)
上执行
WHERE a = 1 AND b > 10 AND c = 5
,只有
a
b
能用索引,
c
不会生效

DISTINCT
JOIN
LIKE
查询怎么避开索引失效?

这三类操作极易触发索引退化,不是语法写错,而是语义导致无法利用 B+ 树结构。

DISTINCT
在无
WHERE
时通常需要扫描全部索引或数据页;若仅需去重某几列,确保这些列本身构成覆盖索引,例如
SELECT DISTINCT a, b FROM t
可用
(a, b)
索引避免回表
JOIN
中被驱动表(即
EXPLAIN
type
ref
/
eq_ref
的那张)的关联字段必须有索引;且类型要严格一致——
VARCHAR(50)
关联
VARCHAR(100)
可能因隐式转换失效
LIKE
以通配符开头(
LIKE '%abc'
)必然无法使用索引;若必须前缀模糊,考虑全文索引(
FULLTEXT
)或倒排表;
LIKE 'abc%'
是安全的,前提是字段没参与函数运算,如
UPPER(name) LIKE 'ABC%'
就会失效

什么时候该删掉旧索引?

索引不是越多越好。冗余或长期未被使用的索引会拖慢写入性能,并增加优化器决策负担。

sys.schema_unused_indexes
视图(MySQL 5.7+)查哪些索引从没被
SELECT
使用过;注意它不统计
INSERT/UPDATE/DELETE
的维护开销
检查是否有重复索引:比如已有
(a, b)
,又建了
(a, b, c)
,前者在大多数场景下是冗余的(除非
c
经常参与排序或覆盖查询)
定期用
SHOW INDEX FROM t
结合慢查询日志反查,确认每个索引是否真实支撑了高频查询;一个索引三个月内没出现在任何
EXPLAIN
输出里,大概率可以下线
SELECT 
  object_name AS table_name,
  index_name,
  rows_selected,
  select_latency
FROM sys.schema_index_statistics 
WHERE table_schema = 'your_db'
ORDER BY select_latency DESC LIMIT 5;
索引设计本质是权衡:它服务于具体查询模式,而非数据本身。很多“优化”失败,是因为盯着单条 SQL 的
EXPLAIN
看,却忽略了业务中查询的分布、并发压力、以及写入频次对索引维护成本的真实影响。

相关推荐