GoldenDB 慢 SQL 优化指南
相关阅读:EXPLAIN 完全指南 · GoldenDB 数据库开发规范 · GoldenDB 数据库调优
来源:longyu.cool/archives/1777189129607 + 内部运维资料整合
1. 引言
GoldenDB 是一款分布式数据库,在处理大规模数据时,慢 SQL 可能导致:
- 系统性能下降、响应时间变长
- 执行线程阻塞,影响业务正常运行
- DN 节点 CPU 飙高,甚至引发宕机
本指南涵盖慢 SQL 的识别 → 分析 → 优化 → 预防全流程。
2. 慢 SQL 的定义与识别
2.1 什么是慢 SQL
慢 SQL 是指执行时间超过设定阈值的 SQL 语句。在 GoldenDB 中,通常认为执行时间超过 1 秒的 SQL 语句为慢 SQL。
2.2 如何识别慢 SQL
| 方式 | 说明 |
|---|---|
| CN 节点慢日志 | 记录 SQL 的执行总时间、执行计划时间等信息 |
| DN 节点慢日志 | 记录具体的 SQL 语句、执行时间、扫描行数等信息 |
| OMM 页面告警 | "执行线程阻塞"告警、慢 SQL 数量增加告警 |
| 关键指标监控 | PlanTreeExecTime(执行计划树耗时)、DB connection_id duration(DB 执行时间)、Rows_examined(扫描行数) |
3. 慢 SQL 的常见原因
3.1 未使用索引
- 现象:SQL 执行时进行全表扫描,
Rows_examined值极大 - 原因:WHERE 条件中的列没有创建索引
- 影响:需要扫描大量数据,执行时间长
3.2 SQL 写法不当
- 现象:返回大结果集,执行时间长
- 原因:
- 使用
SELECT *查询所有列 - 查询范围过大,没有限制返回行数
- 条件选择性差,过滤效果不好
- 复杂的 JOIN 操作,没有合适的索引
- 子查询嵌套过深
- 使用
3.3 统计信息过时
- 现象:执行计划不合理
- 原因:表的统计信息过时,优化器选择了错误的执行计划
- 影响:即使有索引,也可能选择全表扫描
3.4 锁等待
- 现象:SQL 执行时间主要消耗在锁等待上
- 原因:并发更新同一记录导致锁竞争
- 影响:SQL 执行被阻塞,响应时间变长
3.5 分布式环境特有的问题
- 现象:跨分片查询,执行时间长
- 原因:
- 数据分布不均匀
- 跨分片 JOIN 操作
- 大量数据在网络传输
- 影响:网络开销大,执行时间长
3.6 内存相关问题
- 现象:SQL 执行过程中内存持续上涨
- 原因:复杂子查询(如
NOT EXISTS转LEFT JOIN)中,WHERE 计算结果未复用,每行数据都重新申请内存 - 影响:内存耗尽,甚至导致进程异常
相关 FAQ:SQL-FAQ-内存持续上涨原因
慢日志分析方法
CN 慢日志分析
CN 慢日志包含两部分:
- 整体执行情况:记录 SQL 的总执行时间、各阶段耗时等
- 子语句执行情况:记录执行计划中子语句的执行情况
关键字段
| 字段 | 说明 | 分析要点 |
|---|---|---|
TotalExecTime |
语句执行的总时间 | 了解整体执行耗时 |
PlanTreeExecTime |
执行计划树的总时间 | 占总时间的比例,判断是否为执行计划问题 |
DB connection_id duration |
DB 侧执行时间 | 如果该值较大,说明问题出在 DB 侧 |
sqltoRoute |
语句从执行线程下发到路由线程的耗时 | 超过 100μs 说明路由线程积压 |
TaskWait |
语句加入执行队列到真正开始执行的等待时间 | 时间长说明队列积压 |
workerdelay |
listener 将 task 放入队列,到 worker 开始执行的时间 | 时间长说明有其他语句占用了 worker 线程 |
worker |
空闲 worker 线程被唤醒后处理 task 的时间 | 时间长且 num 大说明结果集大 |
restoExec |
路由线程或 worker 线程将结果处理后给执行线程发消息的时间 | 时间长说明执行线程消息积压 |
快速定位流程
TotalExecTime 过大
├─ PlanTreeExecTime 占比高 → 执行计划问题(见 4.3)
├─ DB connection_id duration 占比高 → DB 侧问题(见 4.2)
├─ TaskWait 过长 → 队列积压,排查并发量
├─ sqltoRoute 过长 → 路由线程积压
└─ worker + num 过大 → 结果集过大,加 LIMIT
4.2 DN 慢日志分析
DN 慢日志记录了 SQL 在数据节点的执行情况:
| 字段 | 说明 | 分析要点 |
|---|---|---|
Query_time |
查询执行时间 | 了解 DB 侧执行耗时 |
Lock_time |
锁等待时间 | 时间长说明存在锁竞争 |
Rows_sent |
返回行数 | 与 Rows_examined 对比,判断查询效率 |
Rows_examined |
扫描行数 | 数值过大说明可能未使用索引 |
效率指标:
Rows_examined / Rows_sent比值越大,说明扫描了大量无关行,索引设计越不合理。
4.3 执行计划分析(EXPLAIN)
使用 EXPLAIN 命令分析 SQL 的执行计划:
EXPLAIN SELECT * FROM order_info WHERE order_status = 2 AND create_time > '2026-04-01';
关键字段解读:
| 字段 | 含义 | 优化方向 |
|---|---|---|
type |
访问类型 | ALL(全表扫描)→ 需优化;range/ref/eq_ref → 较优 |
possible_keys |
可能使用的索引 | 为空说明无可用索引 |
key |
实际使用的索引 | 为 NULL 说明未使用索引 |
rows |
估计扫描行数 | 值越大越需要优化 |
filtered |
过滤后行数占比 | 百分比越低说明过滤效果越好 |
Extra |
额外信息 | Using filesort/Using temporary → 需优化;Using index → 覆盖索引,较优 |
EXPLAIN type 访问类型(从最差到最优)
| 类型 | 说明 | 性能 |
|---|---|---|
ALL |
全表扫描 | 最差 |
index |
全索引扫描 | 差 |
range |
范围扫描 | 中等 |
ref |
索引查找(非唯一) | 较好 |
eq_ref |
唯一索引查找 | 好 |
const |
常量查找 | 最好 |
system |
系统表 | 最好 |
4.4 检查表结构与索引
使用 SHOW CREATE TABLE 命令查看表结构和索引:
SHOW CREATE TABLE order_info\G
检查 WHERE 条件中的列是否有相应的索引,以及索引的设计是否合理。
5. 优化策略
5.1 索引优化
- 创建合适的索引:根据 SQL 的 WHERE 条件创建索引
- 复合索引:对于多个条件的查询,创建复合索引
- 索引顺序:将选择性高的列放在索引前面
- 覆盖索引:将查询的列包含在索引中,避免回表查询
索引设计反模式
| 反模式 | 错误示例 | 正确写法 |
|---|---|---|
| 隐式类型转换 | WHERE id = '123'(id 为 INT) |
WHERE id = 123 |
| 函数操作索引列 | WHERE DATE(create_time) = '2026-04-15' |
WHERE create_time BETWEEN '2026-04-15 00:00:00' AND '2026-04-15 23:59:59' |
| LIKE 左模糊 | WHERE name LIKE '%张' |
WHERE name LIKE '张%' |
| OR 连接不同列 | WHERE a = 1 OR b = 2 |
拆分为 UNION 或分别建索引 |
| 索引列参与运算 | WHERE id + 1 = 10 |
WHERE id = 9 |
| NOT IN / NOT EXISTS | 大量数据时效率低 | 改用 LEFT JOIN + IS NULL |
5.2 SQL 写法优化
- 避免使用
SELECT *,只查询需要的列 - 使用
LIMIT限制返回行数 - 避免在索引列上进行函数操作(会导致索引失效)
- 使用绑定变量,减少 SQL 解析开销
- 避免子查询嵌套过深,优先使用 JOIN
- 合理使用
UNION ALL代替UNION(不需要去重时)
5.3 统计信息更新
定期更新表的统计信息,帮助优化器选择更好的执行计划:
-- 更新表统计信息
ANALYZE TABLE order_info;
-- 优化表(整理碎片)
OPTIMIZE TABLE order_info;
5.4 分布式环境优化
- 合理分片:根据业务特点选择合适的分片键,确保数据分布均匀
- 避免跨分片查询:尽量在单个分片内完成查询操作
- 批量操作:将多个小查询合并为批量操作,减少网络开销
- 使用绑定变量:减少 SQL 解析开销,提高执行效率
5.5 紧急处理措施
当遇到慢 SQL 导致系统性能下降时:
第一步:定位阻塞 SQL
grep "exec sql too long" dbproxy*
第二步:Kill 阻塞链路
kill connection $dialogid;
第三步:临时优化 — 限制返回行数
SELECT * FROM order_info
WHERE order_status = 2 AND create_time > '2026-04-01'
LIMIT 1000;
6. 工具速查
6.1 InsightTool 诊断工具
InsightTool 支持采集和分析 CN/DN 慢日志:
# 采集 CN slow 日志(最近 30 分钟)
python onekey_diag.py --action gdb_log_analysis_tool \
--obj_module cn --collect_type slow --time_before 30min --obj_cluster 1
# 采集 CN slow + general 日志(指定时间区间)
python onekey_diag.py --action gdb_log_analysis_tool \
--obj_module cn --collect_type slow,general \
--time_during '2026-04-01 00:00:00~2026-04-02 00:00:00' \
--model normal --parallel_num 4 --obj_cluster 2
# DN 慢 SQL 分析
python onekey_diag.py --action exec --exec_model=dn_slow_analyze --obj_module=dn
参数说明:
--collect_type:slow(慢日志)、general(通用日志),可逗号分隔--time_before:支持min、hour、week--time_during:格式为年-月-日 时:分:秒~年-月-日 时:分:秒--parallel_num:解析并发数,最大 4
6.2 dbtool SQL 跟踪
# 设置 SQL 跟踪条件
dbtool -pm -trace <tracenum> <clusterId> db.table.field=value
# 跟踪集群 Top 10 SQL
dbtool -pm -trace <tracenum> <clusterId>
7. 最佳实践
7.1 开发规范
索引规范:
- 根据 SQL 编写需求创建合适索引
- 避免创建过多索引,影响写入性能
- 定期检查索引使用情况
SQL 编写规范:
- 避免使用
SELECT * - 使用分页查询限制返回行数
- WHERE 条件列要有索引
- 避免在索引列上进行函数操作
- 使用绑定变量,减少 SQL 解析开销
评审流程:
- DDL 变更前进行 SQL 性能评审
- 新功能上线前进行性能测试
- 定期 Review 慢 SQL 列表
7.2 日常维护
| 频率 | 事项 |
|---|---|
| 每日 | 检查慢 SQL 列表,关注新增慢 SQL |
| 每周 | 分析慢查询日志趋势,识别恶化 SQL |
| 每月 | 执行 ANALYZE TABLE 更新统计信息 |
| 每季度 | 检查索引使用情况,删除冗余索引;根据业务发展调整表结构 |
7.3 监控与告警
| 监控项 | 指标 | 告警阈值建议 |
|---|---|---|
| 慢 SQL 频率 | EXEC process run too long 日志出现频率 |
持续出现 |
| 执行时间 | PlanTreeExecTime 和 DB connection_id duration |
超过 1s |
| 全表扫描 | Rows_examined 过大的 SQL |
超过 10 万行 |
| 锁等待 | 锁等待时间 | 超过 500ms |
| 分布式查询 | 跨分片查询执行情况 | 超过 3s |
8. 案例分析
8.1 案例一:索引缺失导致全表扫描
现象:
- 执行线程阻塞告警触发
- 业务查询响应时间从毫秒级飙升至数十秒
- DN 节点 CPU 使用率飙升至 80%+
分析过程:
- 查看慢日志发现 SQL:
SELECT * FROM order_info WHERE order_status = 2 AND create_time > '2026-04-01' - 执行计划显示全表扫描,
Rows_examined: 5823671 - 检查表结构发现
order_status和create_time列无索引 - CN 慢日志显示
DB connection_id duration: 38450000us(约 38 秒),说明问题出在 DB 侧
解决方案:
-- 1. 创建复合索引
CREATE INDEX idx_order_status_create_time ON order_info(order_status, create_time);
-- 2. 优化 SQL,避免 SELECT *
SELECT id, order_no, amount FROM order_info
WHERE order_status = 2 AND create_time > '2026-04-01';
-- 3. 限制返回行数
SELECT id, order_no, amount FROM order_info
WHERE order_status = 2 AND create_time > '2026-04-01'
LIMIT 1000;
效果:
- 执行计划变为范围查询(
range),仅扫描 15 万行 - SQL 执行时间从 38 秒降至毫秒级
- 系统性能恢复正常
8.2 案例二:跨分片 JOIN 查询优化
现象:
- 跨分片 JOIN 查询执行时间长
- 网络传输数据量大
分析过程:
- SQL:
SELECT o.id, o.order_no, u.username FROM order_info o JOIN user_info u ON o.user_id = u.id WHERE o.create_time > '2026-04-01' - 订单表和用户表分别在不同分片
- 执行计划显示需要在所有分片上执行查询,然后在 CN 节点进行 JOIN
解决方案:
-- 方案一:调整分片策略,将相关表放在同一分片(长期)
-- 方案二:创建本地索引,减少跨分片数据传输(中期)
-- 方案三:优化 SQL,减少返回数据量(临时)
SELECT o.id, o.order_no, u.username
FROM order_info o
JOIN user_info u ON o.user_id = u.id
WHERE o.create_time > '2026-04-01'
LIMIT 100;
效果:
- 执行时间从 10 秒降至 1 秒以内
- 网络传输数据量减少 90%
9. 总结
慢 SQL 优化是 GoldenDB 性能管理的重要组成部分。核心要点:
- 索引先行:WHERE 条件列必须创建索引,避免全表扫描
- 写法规范:避免
SELECT *、过大的查询范围、索引列函数操作 - 统计信息:定期更新,确保优化器选择正确的执行计划
- 分片设计:注意分布式环境的特殊性,合理设计分片策略
- 监控告警:建立完善的监控和告警机制,及时发现和处理慢 SQL
- 开发规范:从源头避免慢 SQL,DDL 变更前做性能评审