翻车案例:用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)、查看慢查询日志、甚至考虑分库分表------毕竟数据库加班到凌晨三点,它也需要个按摩师(读写分离)!"


相关推荐
Ruihong9 小时前
Vue withDefaults 转 React:VuReact 怎么处理?
vue.js·react.js·面试
kyriewen10 小时前
别再这样写 async/await 了:我在 Code Review 中见过最多的 8 个错误
前端·javascript·面试
烬羽15 小时前
字符串算法入门:从反转字符串到回文判断,面试不再慌
算法·面试
云技纵横15 小时前
一个 @Async,把 @Transactional 的事务边界打穿了
后端·面试
想要成为糕糕手16 小时前
Harness Engineering:大模型时代的“马鞍”——从记忆层开始,让AI真正为你所用
面试·ai编程·claude
kyriewen1 天前
我手写了一个 EventEmitter,面试官追问了 6 个问题——第 4 个我没答上来
前端·javascript·面试
她的男孩1 天前
后台接口加密别只会 HTTPS,ForgeAdmin 的 RSA + SM4/AES 源码拆解
后端·面试·开源
Randyliu1 天前
20260508-Agent搭建记录以及对ReAct框架的理解
面试·agent
ZzT1 天前
公司用 AI 筛简历,他写了个 AI 帮你挑公司
面试·aigc·ai编程
PBitW1 天前
GPT训练我的第四天,被打惨了!!!😭😭😭
前端·javascript·面试