为什么 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 写得不好,而在建表时没想清楚主键怎么设、用什么引擎、哪些查询路径必须走索引——这些决策一旦固化,后期改造成本远高于初期设计。
