亿级分区表的全局索引失效与 SQL 执行计划出错

一个按 city_id做 Hash 分区的订单表 orders,拥有 16 个分区,每个分区 ~5000 万行。该表存在一个全局唯一索引 uk_order_no。某次业务变更后,应用加入了一个新字段channel作为查询条件,但未建立对应索引。结果原本走 uk_order_no` 的高并发点查变成了全表扫描,DB 严重拖慢。DBA 紧急加建了局部索引,但执行计划仍然走错,甚至在部分节点上产生了不正确的并行计划,导致 CPU 飙高。
不修改 SQL 文本,利用 OceanBase 的 Outline/SQL Plan Management (SPM) 固定该 SQL 的执行计划为强制走特定索引并关闭并行

1 个赞

在生产环境中,首先找出那个变慢的 SQL。

查询最近慢查询的 SQL_ID:
记下问题 SQL 的SQL_ID ,然后查看它当前的执行计划,确认全表扫描和并行问题:
如果看到TABLE FULL SCAN和 PX BLOCK ITERATOR 等并行算子,就说明计划错误并开启了并行。
在不修改原始 SQL 的前提下,我们需要一个正确的计划:

使用全局唯一索引 uk_order_no,因为 order_no的唯一性能让查询直接定位到一行,然后只对那一行做channel的过滤(回表过滤)。关闭并行 ,将该 SQL 的并行度固定为 1(串行)
确认计划是 TABLE SCAN (INDEX uk_order_no)并且无PX算子。

然后获取这个带 Hint 的 SQL 的 SQL_ID我们称之为target_sql_id:
OceanBase 的 Outline能将某个 SQL 的执行计划,强制替换成另一个 SQL 的计划。语法有两种常用方式:再次执行原始查询(不带 Hint)并查看执行计划:
确保稳定性:处理合并和 Leader 切换
合并 (Major Compaction)不会清空计划缓存,但可能触发统计信息更新。OceanBase 的 Outline 绑定在 SQL 文本解析阶段即生效,与统计信息变化无关,因此固定计划在合并后依然稳定。
Leader 切换:执行计划在各 OBServer 节点间各自缓存,但 Outline 定义存储在内部表中,所有节点都能读取并应用。新 Leader 执行该 SQL 时同样会强制使用绑定的索引和并行度。
*如果担心基线失效,可以使用DBMS_SPM将计划固化到 SPM 基线,但 Outline 已经足够可靠。
若希望对该 SQL 做更细致的资源限制(比如限制 IO),可在 Hint 中加入并行度 1 已经禁用了并行查询。如果还想防止此类 SQL 在未来因某种原因再次开启并行,可以在租户级别设置变量(但不修改 SQL 的情况下 Outline 是最精准的) 如果需要批量禁止所有高并发 SQL 的并行,也可以配合parallel_degree_policy设置为 MANUAL ,但会影响整个租户

2 个赞