基于湖库一体架构,统一管理结构化、半结构化与非结构化等多模态数据,一个系统承载事务处理、实时分析与 AI 工作负载。
查询列包含 UDF 导致 OMA 回放 SQL 执行缓慢的排查与优化方案
更新时间:2026-08-25 06:51
问题现象
在 OceanBase 数据库进行 OMA(流量回放/压测)期间,包含自定义 UDF(如解密函数)的 SQL 执行耗时显著上升(正常约 10 ms,带 UDF 升至 100 ms 甚至数秒)。高并发场景下引发严重的队列积压与重试放大效应,同时伴随客户端接收结果集延迟、系统日志被异常信息频繁打印打满等现象。 问题 SQL 中包含形如 aes_decypt(xx) AS xx 的查询,即查询列包含自定义 UDF。 对应执行的日志中反复出现以下告警信息:
WDIAG decode ... [errcode=-4002] REACH SYSLOG RATE LIMIT [bandwidth]
WDIAG [PL] execute ... [errcode=-4002] Unhandled exception has occurred in PL (ret=-4002)
WDIAG [SQL.ENG] eval_udf ... [errcode=-4002] fail to execute udf (ret=-4002, package_id=310184)
WDIAG [STORAGE] reset ... [errcode=-4389] guard used too much time (guard_used_us=25044556)
从日志可见:PL 层出现未处理异常(errcode=-4002),UDF 执行失败(package_id=310184),单次执行耗时过长(约 25 秒),且异常信息频繁打印触发日志限流(REACH SYSLOG RATE LIMIT)。 日志中的关键信息如下:
expr_type:"T_FUN_UDF" -- UDF 类型,不是系统函数
expr_name:"user_define_function" -- 明确为"用户自定义函数"
package_id=310184 -- 属于某个 PL/SQL package
关键诊断信息
触发条件
- 查询结果列包含自定义 PL/SQL UDF(如解密函数)。
- OMA 回放/压测等高并发场景下执行包含该 UDF 的 SQL。
事前巡检
- 检查业务 SQL 的查询列中是否包含用户自定义函数,尤其是高并发查询场景。
- 可通过以下 SQL 确认函数是否为用户创建(示例以 AES_DECRYPT 为例,请替换为实际函数名;如函数属于指定用户,可增加 owner 过滤条件):
-- 查看 AES_DECRYPT 是否为用户创建的函数/包
SELECT object_name, object_type, status
FROM cdb_objects
WHERE object_name = 'AES_DECRYPT';
-- 查看函数源码
SELECT text
FROM cdb_source
WHERE name = 'AES_DECRYPT'
ORDER BY line;
-- 或查看日志中 package_id 对应的 package 源码(将 310184 替换为日志中实际打印的 package_id)
SELECT object_name, object_type
FROM cdb_objects
WHERE object_id = 310184;
事后诊断
sql_audit视图中的PLSQL_EXEC_TIME字段值与 SQL 实际执行耗时基本一致,可作为定位 PL/UDF 执行耗时的关键诊断指标。- 业务高峰期 SQL 执行慢时,重试次数显著增高,且单次执行时间超过 5 秒即被纳入大查询队列。
- 客户端(如 OMA)可能出现长时间(数十秒)未读取发送缓冲区数据(如 6.7 MB)的情况,导致接收端阻塞。
- 修复前 Syslog/WDIAG 日志因 UDF 解码失败频繁报错被大量打满;修复后日志恢复平稳。
问题原因
该问题由自定义 UDF 实现逻辑缺陷引发,并非数据库内核缺陷。具体原因如下:
- 异常处理机制不当:UDF 内部调用底层编码/解码函数时抛出异常(如 -4002),但使用
EXCEPTION WHEN OTHERS捕获异常后未及时中断,而是继续处理大量数据行后才抛出未处理异常,异常最终冒泡至 SQL 层,导致整批语句执行失败,而非单行跳过。 - 重试与队列放大效应:单次执行因异常处理变慢(超过 5 秒)后被放入大查询队列,反复重试进一步放大执行时间。
- 客户端消费瓶颈:OMA 回放期间模拟高并发压测,数据库侧返回的大体积结果集未能被客户端及时消费,TCP 缓冲区堆积导致客户端成为最终性能瓶颈。 此问题本质是用户自定义 UDF 本身存在问题,导致执行性能不优。 此外,内置函数与 PL/SQL UDF 的执行方式存在差异:
- 内置函数(如 MySQL 兼容模式的 AES_DECRYPT):由数据库内核直接实现,单行执行耗时微秒级,无异常处理开销,无上下文切换开销。
- PL/SQL UDF(Oracle 兼容模式,当前场景):需经过 PL/SQL 解释执行,单行执行耗时毫秒级,比内置函数慢 100~1000 倍;每次调用存在上下文初始化开销,异常处理(
EXCEPTION WHEN OTHERS)有额外开销,每行数据都要经历完整的 PL/SQL 调用栈。
问题的风险及影响
SQL 执行性能严重下降,高并发下引发队列积压与重试风暴;异常处理不当可能导致单条脏数据引发整批 SQL 执行失败;系统日志被异常信息频繁打印,占用磁盘 IO 并干扰日常监控与故障排查。
影响租户
| sys | MySQL | Oracle |
|---|---|---|
| NO | NO | YES |
影响版本
| 影响版本 |
|---|
| 所有版本 |
解决方法
修正 UDF 的定义与实现逻辑,确保在调用底层解码函数前清理输入数据中的非法字符或换行符(如使用 REPLACE 函数),避免异常抛出导致执行阻塞或失败。
规避方式
- 修正 UDF 的定义,完善异常捕获与返回逻辑,防止异常冒泡至 SQL 层。
- 架构层面建议将加密/解密逻辑迁移至应用侧实现,或在应用与数据库之间引入缓存机制以降低数据库 CPU 消耗。
- 合理划分流量路由,高并发业务查询流量优先走主副本,数据迁移等后台流量可路由至 WEAK 读从副本。
- 针对大结果集并发返回场景,可适当调大客户端 TCP 接收缓冲区参数,提升结果集消费能力。