问题现象
OceanBase 数据库 V3.x 版本引入静态执行引擎后,涉及类型转换时需要显式的在解析阶段添加 cast 表达式。对一个超长的 inlist 而言,当类型转换的 cast 需要增加在 inlist 上时,需要对 inlist 中的每个元素都添加 cast。添加的数量正比于 inlist 中元素的个数。之后在解析和优化阶段,需要对 inlist 中的 cast 进行类型推导,常量折叠等操作,这会消耗大量的时间。通常长度达到 2-3K、且存在隐式转换的的 SQL 硬解析耗时就会达到秒级。
关键诊断信息
触发条件
存在一个大 inlist,并且 inlist 中的元素和 in 的左值类型不同,需要对 inlist 的元素添加 cast。典型的场景如,其中 c1 的类型为数值型。此外,in 左右两侧字符集不同时也可能出现。
c1 in ('1','2','3' ...,'1000"); // c1 为数值型,数值型和字符串比较时,需要将字符串转换为数值型
事前巡检
巡检带超大 inlist 的 SQL,参考如下语句。
-- OceanBase 数据库 V3.x 版本
SELECT tenant_id,
param_count,
query_sql
FROM
(SELECT tenant_id,
query_sql,
LENGTH(param_infos) - LENGTH(REPLACE(param_infos, '{', '')) AS param_count
FROM oceanbase.gv$plan_cache_plan_stat)
WHERE query_sql LIKE '%in %(%'
AND param_count > 200
ORDER BY param_count DESC LIMIT 5;
-- OceanBase 数据库 V4.x 版本
SELECT tenant_id,
param_count,
query_sql
FROM
(SELECT tenant_id,
query_sql,
LENGTH(param_infos) - LENGTH(REPLACE(param_infos, '{', '')) AS param_count
FROM oceanbase.gv$ob_plan_cache_plan_stat)
WHERE query_sql LIKE '%in %(%'
AND param_count > 200
ORDER BY param_count DESC LIMIT 5;
事后诊断
对查询进行 explain 观察 inlist 上是否存在 cast。如下案例中 16 行的 in 谓词中,inlist 中每个常量都存在一个 cast。当这个 inlist 长度逐步增加后,硬解析耗时会不断增加。
explain select * from t1 where c1 in ('1','2','3');
+--------------------------------------------------------------------------------------------------------------------------------------+
| Query Plan |
+--------------------------------------------------------------------------------------------------------------------------------------+
| =================================================== |
| |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| |
| --------------------------------------------------- |
| |0 |TABLE RANGE SCAN|t1(idx)|1 |17 | |
| =================================================== |
| Outputs & filters: |
| ------------------------------------- |
| 0 - output([t1.c1], [t1.c2]), filter(nil), rowset=16 |
| access([t1.__pk_increment], [t1.c1], [t1.c2]), partitions(p0) |
| is_index_back=true, is_global_index=false, |
| range_key([t1.c1], [t1.__pk_increment]), range(1,MIN ; 1,MAX), (2,MIN ; 2,MAX), (3,MIN ; 3,MAX), |
| range_cond([cast(t1.c1, DECIMAL(11, 0)) IN (cast('1', DECIMAL(1, -1)), cast('2', DECIMAL(1, -1)), cast('3', DECIMAL(1, -1)))]) |
+--------------------------------------------------------------------------------------------------------------------------------------+
问题原因
静态执行引擎需要保证 in 谓词左右两侧的类型是一致的,当类型不一致时,需要在解析阶段添加 cast。大量添加 cast 会导致硬解析整体的耗时和内存占用升高。
问题的风险及影响
SQL 硬解析耗时长,内存消耗高。
影响租户
影响 OceanBase 数据库中的 SYS 租户和 Oracle 租户以及 MySQL 租户。
影响版本
OceanBase 数据库企业版 V3.1.2 GA(oceanbase-3.1.2-20210618150922)之后版本、V3.2.2 GA(oceanbase-3.2.0-20211130225726)之后版本、V3.2.3 GA(oceanbase-3.2.3.0-20220419)、V3.2.4 GA(oceanbase-3.2.4.0-100000072022102819)之后版本、V4.1.0 GA(oceanbase-4.1.0.0-100001122023040322)之后版本、V4.2.0 GA(oceanbase-4.2.0.0-100010082023083014)之后版本、V4.2.1 GA(oceanbase-4.2.1.0-100000182023092722)之后版本。
解决方法
调整 SQL 写法,改变 in 谓词右值的数据类型,避免产生隐式 cast。
OceanBase 数据库企业版 V4.2.x 系列升级至 V4.2.2 GA(oceanbase-4.2.2.0-100000082024011317)及之后版本;V4.3.x 系列升级至 V4.3.2(oceanbase-4.2.3.0-100000052024041220)及之后版本,并调整
_inlist_rewrite_threshold的默认取值。MySQL MODE:
ALTER SYSTEM SET _inlist_rewrite_threshold = 1000;。Oracle MODE:
ALTER SYSTEM SET '_inlist_rewrite_threshold' = 1000;。
规避方式
无。