在 SQLite 中,EXPLAIN 和 EXPLAIN QUERY PLAN 是两个强大的 调试工具,帮助你 理解 SQL 是如何被执行的。
一、EXPLAIN vs EXPLAIN QUERY PLAN
| 命令 | 作用 | 输出内容 |
|---|---|---|
EXPLAIN | 显示 底层虚拟机指令(VM bytecode) | 每步操作的 opcode |
EXPLAIN QUERY PLAN | 显示 查询执行计划(更直观) | 扫描/搜索方式、索引使用 |
日常使用推荐
EXPLAIN QUERY PLAN
二、EXPLAIN QUERY PLAN —— 快速判断性能
基本语法
EXPLAIN QUERY PLAN
SELECT * FROM users WHERE email = 'alice@example.com';
输出解读(关键字段)
| 输出关键词 | 含义 | 是否高效 |
|---|---|---|
SCAN TABLE | 全表扫描 | 慢 |
SEARCH TABLE ... USING INDEX | 使用索引查找 | 快 |
SEARCH TABLE ... USING PRIMARY KEY | 使用主键 | 快 |
USING COVERING INDEX | 覆盖索引(无需回表) | 最快 |
TEMP B-TREE | 临时排序/分组 | 可优化 |
示例分析
EXPLAIN QUERY PLAN
SELECT name FROM users WHERE email = 'alice@example.com';
情况 1:命中索引(理想)
SEARCH TABLE users USING INDEX idx_users_email (email=?)
高效!只查索引
情况 2:全表扫描(需优化)
SCAN TABLE users
慢!建议建索引:
CREATE INDEX idx_users_email ON users(email);
情况 3:覆盖索引(最优)
SEARCH TABLE users USING COVERING INDEX idx_cover (email=?)
不读表,直接从索引取数据
三、EXPLAIN —— 深入虚拟机指令
EXPLAIN
SELECT name FROM users WHERE id = 5;
输出示例
addr opcode p1 p2 p3 p4 p5 comment
---- ------------- ---- ---- ---- ------------- -- -------
0 Init 0 10 0 00 Start at 10
1 Integer 5 1 0 00 r[1]=5
2 OpenRead 0 3 0 2 00 root=3
3 SeekRowid 0 8 1 00 Seek to rowid=r[1]
4 Column 0 1 2 00 r[2]=name
5 ResultRow 2 1 0 00 Output r[2]
6 Halt 0 0 0 00
常见 opcode 含义
| opcode | 含义 |
|---|---|
OpenRead | 打开表/索引 |
SeekRowid | 用主键查找 |
Column | 读取列值 |
ResultRow | 输出一行结果 |
Copy / SCopy | 复制寄存器 |
Goto | 跳转 |
适合研究 SQLite 内部机制
四、实战:用 EXPLAIN QUERY PLAN 优化查询
原始慢查询
SELECT * FROM orders WHERE user_id = 123 AND status = 'completed' ORDER BY created_at DESC;
执行计划(差)
SCAN TABLE orders
全表扫描 + 临时排序 → 慢!
优化步骤
1. 建复合索引
CREATE INDEX idx_orders_user_status_date
ON orders(user_id, status, created_at DESC);
2. 再看执行计划(理想)
SEARCH TABLE orders USING INDEX idx_orders_user_status_date (user_id=? AND status=?)
直接定位 + 索引已有序 → 无需排序!
五、常见优化模式
| 查询类型 | 推荐索引 | 执行计划目标 |
|---|---|---|
WHERE a = ? | INDEX ON t(a) | USING INDEX ... (a=?) |
WHERE a = ? AND b = ? | INDEX ON t(a, b) | USING INDEX ... (a=? AND b=?) |
ORDER BY x DESC | INDEX ON t(x DESC) | 避免 TEMP B-TREE |
WHERE a > ? ORDER BY a | INDEX ON t(a) | 范围扫描 + 顺序 |
六、查看索引是否生效(黄金命令)
-- 1. 查看所有索引
PRAGMA index_list(users);
-- 2. 查看索引结构
PRAGMA index_info(idx_users_email);
-- 3. 分析查询计划
EXPLAIN QUERY PLAN SELECT ...;
-- 4. 更新统计信息(帮助优化器)
ANALYZE;
七、图形化工具(推荐)
| 工具 | 功能 |
|---|---|
| DB Browser for SQLite | 一键 EXPLAIN QUERY PLAN |
| DBeaver | 可视化执行计划树 |
| SQLite Expert | 索引建议 |
八、快速诊断清单
-- 1. 执行计划
EXPLAIN QUERY PLAN SELECT ...;
-- 2. 是否全表扫描?
-- → 有 SCAN?建索引!
-- 3. 是否临时排序?
-- → 有 TEMP B-TREE?加 ORDER BY 列到索引
-- 4. 是否回表?
-- → 无 COVERING?加 SELECT 列到索引
九、真实案例:从 2s → 5ms
-- 慢查询(2秒)
SELECT * FROM logs WHERE app = 'web' AND level = 'ERROR' ORDER BY timestamp DESC LIMIT 100;
-- 执行计划
SCAN TABLE logs
TEMP B-TREE FOR ORDER BY
-- 优化:建复合索引
CREATE INDEX idx_logs_app_level_time
ON logs(app, level, timestamp DESC);
-- 新计划
SEARCH TABLE logs USING INDEX idx_logs_app_level_time (app=? AND level=?)
-- → 5ms!
总结:EXPLAIN 使用流程
graph TD
A[写 SQL] --> B{EXPLAIN QUERY PLAN}
B --> C{有 SCAN?}
C -->|是| D[建索引]
C -->|否| E{有 TEMP B-TREE?}
E -->|是| F[索引包含 ORDER BY 列]
E -->|否| G[是否 COVERING?]
G -->|否| H[索引包含 SELECT 列]
G -->|是| I[完美!]
快速参考卡
-- 查看执行计划(推荐)
EXPLAIN QUERY PLAN SELECT ...;
-- 查看虚拟机指令(高级)
EXPLAIN SELECT ...;
-- 更新优化器统计
ANALYZE;
-- 查看索引
PRAGMA index_list(表名);
需要我帮你:
- 分析一段 SQL 的
EXPLAIN QUERY PLAN输出? - 为你的查询设计最优索引?
- 把慢查询优化到毫秒级?
把你的 SQL 和表结构发我!