mysql中查询优化的基本思路与实践

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

为什么
SELECT *
在大表上特别危险

它强制 MySQL 读取所有列的全部数据,即使你只用其中一两个字段。更关键的是,这会让覆盖索引失效——哪怕

WHERE
条件能走索引,MySQL 仍得回表查完整行,IO 成倍增加。

SELECT *
换成明确列出需要的字段,例如
SELECT id, name, status
如果只做统计或判断存在性,优先用
SELECT COUNT(1)
SELECT 1
配合
LIMIT 1
确认执行计划:用
EXPLAIN
type
是否为
index
range
Extra
是否含
Using index

WHERE
条件里哪些写法会直接让索引失效

不是加了索引就一定走。常见“隐形拒绝”包括对索引列使用函数、隐式类型转换、以及在左侧使用模糊匹配。

WHERE YEAR(create_time) = 2024
→ 改成
WHERE create_time >= '2024-01-01' AND create_time 
WHERE user_id = '123'
user_id
INT
)→ 字符串引号会触发隐式转换,去掉引号
WHERE name LIKE '%abc'
→ 左模糊无法利用 B+ 树索引,考虑全文索引或倒排表替代
联合索引
(a, b, c)
中,
WHERE b = 2
不走索引;必须满足最左前缀,如
WHERE a = 1 AND b = 2

如何判断是否该加索引,而不是盲目堆索引

索引不是越多越好。每多一个索引,写操作(

INSERT
/
UPDATE
/
DELETE
)都要同步更新 B+ 树,同时占用更多内存和磁盘空间。

优先给高频
WHERE
JOIN
ORDER BY
GROUP BY
中出现的字段建索引
SHOW INDEX FROM table_name
查看现有索引,结合
information_schema.STATISTICS
观察
Cardinality
(基数),低基数字段(如
status
只有 0/1)单独建索引收益极小
避免冗余索引:已有
(a, b)
就不必再建
(a)
(a, b, c)
能覆盖
(a, b)
的查询
pt-duplicate-key-checker
sys.schema_unused_indexes
(MySQL 8.0+)识别长期未被使用的索引

ORDER BY
LIMIT
组合为什么容易慢,怎么破

典型场景:

SELECT * FROM orders ORDER BY created_at DESC LIMIT 10
。如果
created_at
没索引,MySQL 得全表排序;即使有索引,若没覆盖查询字段,仍要回表取数据,尤其当偏移量大时(如
LIMIT 10000, 10
)性能断崖下跌。

确保
ORDER BY
字段有索引,且方向一致(
ASC
/
DESC
匹配索引定义)
用游标分页替代偏移分页:记录上一页最后一条的
created_at
值,下一页查
WHERE created_at 
如果必须用
LIMIT m,n
,且
m
很大,先用子查询定位主键再关联:
SELECT o.* FROM orders o
INNER JOIN (
  SELECT id FROM orders ORDER BY created_at DESC LIMIT 10000, 10
) AS tmp ON o.id = tmp.id;

索引策略和执行路径的细节,往往比 SQL 写法本身更影响性能。一个没走索引的

WHERE
,或者一次没意识的全字段查询,可能让响应时间从毫秒级跳到秒级——而这种问题,在测试数据量小时完全暴露不出来。

相关推荐