sql查询慢的关键在于理解explain输出,而非盲目加索引;常见原因包括回表过多、索引覆盖不全、联合索引顺序不当、隐式类型转换及深分页扫描开销大。

SQL查询慢,八成问题出在索引没建对、执行计划没看懂——加索引不等于快,看懂EXPLAIN输出才是关键。
为什么EXPLAIN显示用了索引但还是慢
常见现象是EXPLAIN里type字段为ref或range,key列也显示命中了索引,但实际执行耗时仍高。根本原因往往不是“没走索引”,而是“走了索引但回表太多”或“索引覆盖不全”。
- 如果
SELECT *且索引只包含WHERE条件字段,MySQL必须回到聚簇索引(主键)捞出所有列,I/O放大严重 -
Extra列出现Using filesort或Using temporary,说明排序/分组没走索引,临时表+磁盘排序开销巨大 - 联合索引中,
WHERE只用了后缀列(如索引是(a,b,c),却只查WHERE c = 1),实际无法使用该索引 - MySQL 8.0以下版本对
IN子查询优化弱,EXPLAIN可能显示type=ALL,实则是优化器放弃重写导致
CREATE INDEX时哪些参数真正影响性能
建索引不是CREATE INDEX idx ON t(a,b)一写了之。字段顺序、长度、类型选择都会直接影响查询效率和存储开销。
- 联合索引顺序必须匹配查询模式:高频等值过滤字段放最左,范围查询字段(如
BETWEEN、>)放右,ORDER BY字段尽量靠右并保持方向一致 -
VARCHAR字段建前缀索引要谨慎:INDEX idx_name (name(16))比全字段索引省空间,但若实际查询常需区分前20字符,则命中率骤降 - 枚举型或低选择性字段(如
status只有3个值)单独建索引几乎无效;可通过SELECT COUNT(DISTINCT status)/COUNT(*)验证,结果低于0.05就别单建 - MySQL 5.7+支持不可见索引:
CREATE INDEX idx ON t(a) INVISIBLE,上线前可先建为不可见,用SET optimizer_switch='use_invisible_indexes=on'测试效果
如何识别隐式类型转换导致的索引失效
这是线上最隐蔽也最高频的索引失效原因——SQL看着没问题,但数据库内部悄悄把索引字段转成了字符串或浮点数,强制全表扫描。
- 典型错误:
WHERE user_id = '123'(user_id是INT),MySQL会把每行user_id转成字符串再比对,索引失效 -
WHERE create_time > '2025-01-01'看似正常,但如果create_time是TIMESTAMP而传入字符串,部分MySQL版本会触发时区隐式转换 - 检查方法:在
EXPLAIN输出中看possible_keys是否为空,或Extra出现Using where而非Using index - 修复动作:统一参数类型,应用层传
123而非"123";时间字段用STR_TO_DATE()显式转换,或直接传UNIX_TIMESTAMP整数
分页查询从LIMIT 10000,20到毫秒级的关键跳变点
当偏移量超过几万行,LIMIT offset, size本质是让MySQL扫描并丢弃前offset行,I/O和CPU成本线性增长。游标分页不是“更高级的写法”,而是绕过这个底层限制的必要手段。
- 核心逻辑:用上一页最后一条记录的排序字段值作为下一页起点,例如
WHERE id > 12345 ORDER BY id LIMIT 20 - 必须有唯一、非空、有序的字段作游标锚点,推荐主键或带唯一约束的时间戳+ID组合
- 不能混用
ORDER BY create_time DESC, id DESC和WHERE create_time ,方向错位会导致漏数据 - 注意
NULL值陷阱:如果游标字段允许NULL,WHERE cursor_field > ?会跳过所有NULL行,需额外处理
真正难的不是写出游标SQL,而是让业务代码接受“不能跳页”“不能按任意页码随机访问”的约束——这往往是架构层面最先要对齐的认知。










