龙羽
发布于 2026-04-26 / 119 阅读
0

GoldenDB慢SQL优化指南

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 EXISTSLEFT JOIN)中,WHERE 计算结果未复用,每行数据都重新申请内存
  • 影响:内存耗尽,甚至导致进程异常

相关 FAQSQL-FAQ-内存持续上涨原因


慢日志分析方法

CN 慢日志分析

CN 慢日志包含两部分:

  1. 整体执行情况:记录 SQL 的总执行时间、各阶段耗时等
  2. 子语句执行情况:记录执行计划中子语句的执行情况

关键字段

字段 说明 分析要点
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_typeslow(慢日志)、general(通用日志),可逗号分隔
  • --time_before:支持 minhourweek
  • --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 日志出现频率 持续出现
执行时间 PlanTreeExecTimeDB connection_id duration 超过 1s
全表扫描 Rows_examined 过大的 SQL 超过 10 万行
锁等待 锁等待时间 超过 500ms
分布式查询 跨分片查询执行情况 超过 3s

8. 案例分析

8.1 案例一:索引缺失导致全表扫描

现象

  • 执行线程阻塞告警触发
  • 业务查询响应时间从毫秒级飙升至数十秒
  • DN 节点 CPU 使用率飙升至 80%+

分析过程

  1. 查看慢日志发现 SQL:
    SELECT * FROM order_info WHERE order_status = 2 AND create_time > '2026-04-01'
    
  2. 执行计划显示全表扫描,Rows_examined: 5823671
  3. 检查表结构发现 order_statuscreate_time 列无索引
  4. 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 查询执行时间长
  • 网络传输数据量大

分析过程

  1. 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'
    
  2. 订单表和用户表分别在不同分片
  3. 执行计划显示需要在所有分片上执行查询,然后在 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 性能管理的重要组成部分。核心要点:

  1. 索引先行:WHERE 条件列必须创建索引,避免全表扫描
  2. 写法规范:避免 SELECT *、过大的查询范围、索引列函数操作
  3. 统计信息:定期更新,确保优化器选择正确的执行计划
  4. 分片设计:注意分布式环境的特殊性,合理设计分片策略
  5. 监控告警:建立完善的监控和告警机制,及时发现和处理慢 SQL
  6. 开发规范:从源头避免慢 SQL,DDL 变更前做性能评审