mysql查询优化时合理选择索引与存储引擎

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

为什么
WHERE
条件字段没加索引,查询就变慢?

MySQL 在执行

SELECT
时,若
WHERE
中的字段未被索引覆盖,引擎会触发全表扫描(
type: ALL
),数据量越大,I/O 和 CPU 消耗越明显。不是所有字段都适合建索引——重复值高的字段(如
status
只有 '0'/'1')走索引反而可能被优化器放弃,改用全表扫描。

EXPLAIN SELECT ...
key
rows
字段:若
key
NULL
,说明没走索引;
rows
接近表总行数,大概率是全扫
联合索引要注意最左前缀原则:
INDEX (a, b, c)
能加速
WHERE a=1
WHERE a=1 AND b=2
,但对
WHERE b=2
无效
避免在索引字段上使用函数或表达式:
WHERE YEAR(create_time) = 2024
无法命中
create_time
索引,应改写为
WHERE create_time >= '2024-01-01' AND create_time 

InnoDB
MyISAM
在查询优化中怎么选?

绝大多数 OLTP 场景下,

InnoDB
是唯一合理选择。它支持行级锁、事务、外键和崩溃恢复,而
MyISAM
的表级锁在并发更新时会严重阻塞查询,且无事务保障。即使你只读不写,
MyISAM
的缓存机制(仅缓存索引,数据靠 OS 文件缓存)也远不如
InnoDB
buffer_pool
稳定可控。

InnoDB
的主键即聚簇索引,主键设计直接影响查询性能:尽量用自增整型,避免用 UUID 或字符串作主键(导致页分裂、B+ 树深度增加)
MyISAM
COUNT(*)
很快,因为它直接读元数据;但
InnoDB
需要实时统计,带
WHERE
COUNT
两者都得走索引或扫描,别迷信“
MyISAM
更快”
如果真有超大只读历史表(如日志归档),可考虑
ARCHIVE
引擎,但它不支持索引,只适合按主键查单条或全量导出

哪些索引操作实际会拖慢查询?

索引不是越多越好。每多一个索引,INSERT/UPDATE/DELETE 就得多维护一棵 B+ 树,同时占用更多磁盘和内存。更隐蔽的问题是:优化器可能因索引过多而选错执行计划。

重复索引浪费资源:
INDEX (a)
INDEX (a, b)
共存时,前者基本无用
冗余索引干扰优化器:比如已有
INDEX (user_id, status, created_at)
,再加
INDEX (user_id, status)
不仅冗余,还可能让优化器误判索引选择成本
ORDER BY
GROUP BY
字段若不在索引覆盖范围内,会导致
Using filesort
Using temporary
,这两项出现在
EXPLAIN
Extra
列里,就是性能瓶颈信号
EXPLAIN SELECT user_id, COUNT(*) 
FROM orders 
WHERE status = 1 
GROUP BY user_id 
ORDER BY COUNT(*) DESC;

上面这个查询,如果只有

INDEX (status)
,就会触发
Using temporary; Using filesort
;加上
INDEX (status, user_id)
后,
GROUP BY
ORDER BY
都能利用索引有序性,消除临时表和排序。

如何验证索引是否真正生效?

别只看

EXPLAIN
显示用了哪个索引,要结合真实执行时间、扫描行数、以及
SHOW PROFILE
performance_schema
查看 I/O 和 CPU 消耗。有时优化器“选对了索引”,但因统计信息过期或数据倾斜,实际性能仍差。

更新统计信息:
ANALYZE TABLE orders;
,尤其在大批量导入或删除后
强制指定索引(仅调试用):
SELECT * FROM orders USE INDEX (idx_user_status) WHERE user_id = 123;
,对比执行时间变化
注意隐式类型转换:比如
user_id
INT
,但查询写成
WHERE user_id = '123'
,可能导致索引失效(字符串与数字比较时,MySQL 可能转列而非参数)

索引和存储引擎的选择,本质是权衡读写比例、一致性要求和数据规模。很多线上慢查,问题不在 SQL 写得不好,而在建表时没想清楚主键怎么设、用什么引擎、哪些查询路径必须走索引——这些决策一旦固化,后期改造成本远高于初期设计。

相关推荐