每天10分钟学会OceanBase系列(Day 9):SQL性能诊断,看懂执行计划不再难

📅 2026/7/4 20:42:02 👁️ 阅读次数 📝 编程学习
每天10分钟学会OceanBase系列(Day 9):SQL性能诊断,看懂执行计划不再难

昨天我们完成了兼容性改造,但很多小伙伴在评论区问:"迁移完成后,怎么判断OceanBase上的SQL跑得比MySQL快还是慢?如果变慢了,该怎么定位问题?"今天我们就来掌握OceanBase性能诊断的核心武器——执行计划分析

核心概念:逻辑计划 vs 实际计划

在OceanBase中,查看执行计划有两种方式,理解它们的区别至关重要:

  • 逻辑执行计划(EXPLAIN):SQL执行前的"预估方案",告诉你优化器打算怎么执行这条SQL。
  • 实际执行计划(Plan Cache):SQL执行后的"真实记录",告诉你优化器实际是怎么执行的。

两者可能不一致!因为逻辑计划基于统计信息估算,而实际执行时数据分布可能已经发生了变化。

10分钟实操:从EXPLAIN看懂执行计划

假设我们有一张订单表t_order,现在执行一条查询:

EXPLAIN SELECT * FROM t_order WHERE order_no = '202606300001';

返回的执行计划中,你需要重点关注以下几个字段:

字段含义怎么看
OPERATOR操作算子TABLE SCAN表示全表扫描(警惕!),TABLE GET表示主键定位(高效)
NAME操作对象显示使用的索引名,如果是表名说明走的是主表扫描
EST.ROWS预估行数估算值与实际值差距过大说明统计信息过期
COST预估成本数值越大表示该算子越"重"
range_cond扫描条件显示索引的过滤条件,判断是否命中索引

💡 快速判断口诀:看到TABLE SCAN+range(MIN,MIN;MAX,MAX)就要警惕了,这几乎等于全表扫描,是性能杀手!

实操:查看SQL的真实执行计划

逻辑计划只是"纸上谈兵",要看到真实执行情况,需要通过系统视图:

-- 第一步:从SQL审计视图中找到目标SQL的plan_id SELECT sql_id, plan_id, elapsed_time, execute_time FROM gv$ob_sql_audit WHERE query_sql LIKE '%t_order%' ORDER BY request_time DESC LIMIT 1; -- 第二步:根据plan_id查看实际执行计划 SELECT plan_depth, plan_line_id, operator, name, rows, cost, property FROM gv$ob_plan_cache_plan_explain WHERE tenant_id = 1002 AND plan_id = 6380340 AND svr_ip = '192.168.1.100';

⚠️ 重要提醒gv$ob_plan_cache_plan_explain视图只能在SQL实际执行的OBServer节点上查询。如果你是通过OBProxy连接的,可能查不到数据,需要直连OBServer。

使用ODC图形化工具(推荐新手)

如果你觉得命令行太复杂,OceanBase开发者中心(ODC)提供了可视化的执行画像功能:

  1. 在ODC的SQL窗口执行你的查询语句
  2. 执行完成后,点击结果上方的"执行画像"图标
  3. 你会看到一个可视化的执行计划树,每个算子的耗时、I/O、行数一目了然
  4. 系统还会自动标出"耗时Top5"的算子,帮你快速定位瓶颈

💡 生产环境慢查询排查思路

当业务反馈"某条SQL很慢"时,按照以下步骤排查:

  1. 定位慢SQL:查询gv$ob_sql_audit视图,按elapsed_time排序,找到最耗时的SQL
  2. 查看执行计划:通过plan_id查询实际执行计划,确认是否走了索引
  3. 分析时间构成:关注queue_time(排队时间)和execute_time(执行时间)
    • 如果queue_time占比高 → 说明系统资源紧张,需要扩容
    • 如果execute_time占比高 → 说明SQL本身需要优化
  4. 检查活跃会话:如果SQL还在执行中(尚未结束),可以通过GV$OB_PROCESSLIST查看当前活跃会话

今日小结

今天我们掌握了OceanBase性能诊断的核心技能。通过EXPLAIN查看逻辑计划,通过gv$ob_plan_cache_plan_explain查看实际计划,再配合ODC的可视化执行画像,你就能像医生看X光片一样,一眼看穿SQL的性能瓶颈。

💡 课后思考

如果通过执行计划发现某条SQL确实走了全表扫描,但你的WHERE条件字段上明明已经建了索引,为什么索引没有生效?(提示:隐式类型转换、函数运算、最左前缀原则都可能是"凶手"。)