适用版本
OceanBase 数据库 V2.x、V3.x 版本。
问题描述
OCP 监控平台告警,OBServer 日志出现 plan cache memory used reach limit 的报错。
2023-02-14T06:56:01+08:00 [OceanBase] ob_cluster=lg_ob_cluster_01:host=3.xx.xx.xx:server_type=observer:ob_error_code=5226 OCP告警通知-多条告警 - 名称:ob_log_alarm:OB日志告警 - 级别:严重 - 告警数量:1 - 聚合分组:ob_log_alarm:5226:lg_ob_cluster_01 - 告警对象:ob_cluster=lg_ob_cluster_01:host=3.xx.xx.xx:server_type=observer:ob_error_code=5226 生成时间:2023-02-14T06:56:01+08:00 概述:[OB日志告警] ob_cluster=lg_ob_cluster_01:host=3.xx.xx.xx:server_type=observer:ob_error_code=5226 详情:集群:lg_ob_cluster_01 主机:3.xx.xx.xx 日志类型:observer 错误码:5226 TraceId:YB42030E5A0C-0005F3BADFD974E4-0-0 日志名称:OB_REACH_MEMORY_LIMIT 日志级别:error 日志详情:[2023-02-14 13:37:57.260504] ERROR [SQL.PC] add_plan (ob_plan_cache.cpp:793) [7777][0][YB42030E5A0C-0005F3BADFD974E4-0-0] [lt=11] [dc=0] plan cache memory used reach limit(tenant_id=1002, get_mem_hold()=859832320, get_mem_limit()=858993455, ret=-5226) BACKTRACE:0xf729d8f 0x53a274f 0x54fa56e 0x53d0eaf 0x54fa2dc 0x5510fde 0xc10fb90 0xc10f771 0xe4ca42b 0x
问题原因
当前系统中大量执行无法重用 Plan 的 SQL,导致同一条 SQL_ID 下存在很多不同的 PLAN_ID。如果 SQL 的执行计划占用的内存较多,比如 30M,一个 SQL_ID 可能会使用的 Plan Cache 内存接近 600M。
当前的设计是,一个 SQL_ID 下最多可以有 200 个 Plan。如果一个 SQL 无法重用执行计划,每次执行都要生成新的 Plan,那么当 SQL_ID 下有 200个 Plan 后,新的 Plan 无法加入到 Plan Cache 中,Plan Cache 也无法进行淘汰,此后每次 SQL 执行都是硬解析。
目前,我们发现有两类 SQL 会导致该问题。
使用
SELECT FROM DUAL的 UNION:可能会在不同位置执行,因而不能共用 plan。obclient> insert into tab_lh (c1,c2,c3) select 1,'a','b' from dual union all select 1,'c','d' from dual;obclient> insert into tab_lh (c1,c2,c3) select 1,'a','' from dual union all select 1,'a','b' from dual;obclient> insert into tab_lh (c1,c2,c3) select 1,'a','b' from dual union all select 1,'','d' from dual;obclient> insert into tab_lh (c1,c2,c3) select 1,'a','b' from dual union all select 1,'','' from dual;使用 IFNULL 的 SQL:当
IFNULL(?, ?)出现在不同的列时不能共用 plan,IFNULL(0, 0)和IFNULL(null, 0)也不能共用 plan。obclient> select 1, IFNULL(0, 0) from dual;obclient> select 1, IFNULL(null, 0) from dual;
解决方法
目前,遇到此类问题的应急处理流程如下。
增加租户内存或者调整参数
ob_plan_cache_percentage。obclient> set global ob_plan_cache_percentage=xx;手工淘汰指定租户的 sql_id。
obclient> alter system flush plan cache sql_id='xxx' tenant='xxx' global;给这些 SQL 使用如下 HINT。
/*+USE_PLAN_CACHE(NONE)*/`