翻车案例:用Explain解读慢查询的死亡密码


翻车案例:用Explain解读慢查询的死亡密码

🎬 故事开场:当数据库加班到凌晨三点

场景:某电商系统在"双11"当天,查询"用户购买记录"的SQL突然变慢,数据库CPU飙到99%,运维工程师小王被紧急叫醒。经查,罪魁祸首是一条看似无害的SQL:

sql 复制代码
SELECT * FROM orders 
WHERE user_id = 12345 
AND order_time BETWEEN '2023-11-11 00:00:00' AND '2023-11-11 23:59:59';

🔍 用Explain破解慢查询之谜

1. Explain语法与输出字段解读

sql 复制代码
EXPLAIN FORMAT=JSON 
SELECT * FROM orders 
WHERE user_id = 12345 
AND order_time BETWEEN '2023-11-11 00:00:00' AND '2023-11-11 23:59:59';

关键输出解析(以MySQL为例):

json 复制代码
{
  "query_block": {
    "select_id": 1,
    "table": {
      "table_name": "orders",
      "access_type": "ALL",    -- ❌ 全表扫描!
      "possible_keys": null,   -- 索引失踪案!
      "rows": 10000000         -- 扫描1亿行???
    }
  }
}

Mermaid图示(查询执行计划):

graph TD A[全表扫描] -->|扫描1亿行| B[数据库崩溃] C[没有索引] -->|CPU飙车| B

🚨 典型慢查询翻车现场(附修复代码)

案例1:索引失踪案

问题SQL

sql 复制代码
SELECT * FROM orders 
WHERE user_id = 12345 
AND order_time > '2023-01-01';

Explain诊断

json 复制代码
"possible_keys": null

修复方案

sql 复制代码
-- 创建联合索引(注意字段顺序!)
ALTER TABLE orders ADD INDEX idx_user_time (user_id, order_time);

案例2:Like的致命诱惑

问题SQL

sql 复制代码
SELECT * FROM users 
WHERE name LIKE '%张三%';

Explain诊断

json 复制代码
"rows": 5000000, "filtered": 100.00

修复方案

sql 复制代码
-- 全局搜索考虑全文索引(MySQL 5.7+)
ALTER TABLE users ADD FULLTEXT(name);
SELECT * FROM users 
WHERE MATCH(name) AGAINST('+张三*' IN BOOLEAN MODE);

案例3:隐形排序陷阱

问题SQL

sql 复制代码
SELECT order_id, user_id FROM orders 
ORDER BY create_time 
LIMIT 10;

Explain诊断

json 复制代码
"filesort": true

修复方案

sql 复制代码
-- 在ORDER BY字段上添加索引
ALTER TABLE orders ADD INDEX idx_create_time (create_time);

🧠 常见面试题 & 答案

题1:Explain的access_type有哪些类型?优先级如何?

答案

  • 优先级从高到低
    • system(单行) > const(主键单行) > eq_ref(唯一索引) > ref(非唯一索引) > range(范围查询) > index(索引扫描) > ALL(全表扫描)
  • 考场杀手锏 :看到ALL直接喊"优化!"

题2:为什么WHERE id=1 AND name='张三'可能不走索引?

答案

  • 索引合并失效 :若没有联合索引(id,name),单独的单字段索引会被随机选择一个,另一个条件退化为回表查询
  • 实战演示
sql 复制代码
-- 错误写法(单字段索引)
CREATE INDEX idx_id ON users(id);
CREATE INDEX idx_name ON users(name);

-- 正确写法(联合索引)
ALTER TABLE users ADD INDEX idx_id_name (id, name);

题3:Using temporary; Using filesort如何优化?

答案

  • 优化路径
    1. 确保ORDER BY字段有索引
    2. 确保GROUP BY字段有索引
    3. 避免在ORDER BY中使用函数(如ORDER BY LOWER(name)
  • 代码示例
sql 复制代码
-- 原始SQL(触发filesort)
SELECT department, COUNT(*) 
FROM employees 
GROUP BY department 
ORDER BY department DESC;

-- 优化后(添加索引)
ALTER TABLE employees ADD INDEX idx_department (department);

题4:覆盖索引是什么?如何验证?

答案

  • 定义:查询字段全在索引中,避免回表查询
  • 验证方法
sql 复制代码
EXPLAIN SELECT user_id, order_time 
FROM orders 
WHERE user_id = 12345;  -- 若显示"Using index"即为覆盖索引

题5:为什么SELECT *SELECT id慢?

答案

  • 数据量差异SELECT *读取整行数据,而SELECT id仅取单字段
  • 索引覆盖 :若存在覆盖索引(id)SELECT id可直接从索引中获取,无需回表

🛠️ 代码实验室:动手优化慢查询

实验环境搭建(MySQL)

sql 复制代码
-- 创建测试表
CREATE TABLE `orders` (
  `id` bigint PRIMARY KEY AUTO_INCREMENT,
  `user_id` int NOT NULL,
  `order_time` datetime NOT NULL,
  `status` varchar(16) NOT NULL,
  INDEX idx_user_time (user_id, order_time)
) ENGINE=InnoDB;

-- 插入测试数据
INSERT INTO orders (user_id, order_time, status)
VALUES (12345, '2023-11-11 00:00:00', 'PAID'),
       (67890, '2023-11-11 01:00:00', 'CANCELLED');

实验任务:

  1. 执行慢查询
sql 复制代码
EXPLAIN SELECT * FROM orders WHERE status = 'PAID';
  • 预期输出:possible_keys: NULL(因为status无索引)
  1. 优化并验证
sql 复制代码
ALTER TABLE orders ADD INDEX idx_status (status);
EXPLAIN SELECT * FROM orders WHERE status = 'PAID';
-- 应显示key: idx_status

💡 面试官心理战

当面试官问 :"如果优化后的SQL还是慢,怎么办?"
反杀回答:"我会检查索引碎片化(执行ANALYZE TABLE)、查看慢查询日志、甚至考虑分库分表------毕竟数据库加班到凌晨三点,它也需要个按摩师(读写分离)!"


相关推荐
chenyuhao20244 小时前
链表的面试题4之合并有序链表
数据结构·链表·面试·c#
PgSheep7 小时前
深入理解 JVM:StackOverFlow、OOM 与 GC overhead limit exceeded 的本质剖析及 Stack 与 Heap 的差异
jvm·面试
uperficialyu8 小时前
2025年01月10日浙江鑫越系统科技前端面试
前端·科技·面试
HebyH_10 小时前
2025前端面试遇到的问题(vue+uniapp+js+css)
前端·javascript·vue.js·面试·uni-app
蓝婷儿18 小时前
前端面试每日三题 - Day 32
前端·面试·职场和发展
EnticE1521 天前
[高阶数据结构]二叉树经典面试题
数据结构·算法·面试
Attacking-Coder1 天前
前端面试宝典---webpack面试题
前端·面试·webpack
前端小巷子1 天前
CSS3 遮罩
前端·css·面试·css3
什码情况1 天前
星际篮球争霸赛/MVP争夺战 - 华为OD机试真题(A卷、Java题解)
java·数据结构·算法·华为od·面试·机试
chenyuhao20241 天前
链表面试题7之相交链表
数据结构·算法·链表·面试·c#