参数化过程是指把 SQL 查询中的常量变成变量的过程。如下例所示:
参数化后结果如下例所示:
但在计划匹配中,不是所有常量都可以被参数化,比如 order by 后面的常量,表示按照 select 投影列中第几列进行排序。
如下例所示,表 t1 中含 c1, c2 列,其中c1 为主键列,SQL 意义要求结果按照 c1 列进行排序,由于 c1 作为主键列已是有序的,使用主键访问可以免去排序。
select c1, c2 from t1 order by 1;
OceanBase (root@oceanbase)> explain select c1, c2 from t1 order by 1;
| ===================================
|ID|OPERATOR |NAME|EST. ROWS|COST|
-----------------------------------
|0 |TABLE SCAN|t1 |1000 |1381|
===================================
则结果需要对 c2 排序,因此需要执行显示的排序操作,执行计划如下例所示:
| ====================================
------------------------------------
|0 |SORT | |1000 |1886|
|1 | TABLE SCAN|t1 |1000 |1381|
====================================
因此,不能将 order by 后面的常量参数化,否则会导致不同 order b y的值参数化后具有相同的参数化后的 SQL,从而命中错误的计划。
除此以外,如下场景中的常量均不能参数化(约束条件):
- 所有 ORDER BY 后常量(例如“ORDER BY 1,2;”)
- 所有 GROUP BY 后常量(例如“GROUP BY 1,2;”)
- LIMIT 后常量(例如 “LIMIT 5;”)
- 作为格式串的字符串常量(例如“SELECT DATE_FORMAT(‘2006-06-00’, ‘%d’);“ 里面的 %d)
- 函数输入参数中,影响函数结果并最终影响执行计划的常量(例如“CAST(999.88 as NUMBER(2,1))”中的“NUMBER(2,1)”,或者“SUBSTR(‘abcd’, 1, 2)”中的“1, 2”)
- 函数输入参数中,带有隐含信息并最终影响执行计划的常量(例如“SELECT UNIX_TIMESTAMP(‘2015-11-13 10:20:19.012’); ” 里面的“2015-11-13 10:20:19.012”,指定输入时间戳的同时,隐含指定了函数处理的精度值为毫秒)
为了解决上面这种可能存在的误匹配问题,在硬解析生成执行计划过程中(图1蓝色方框内)会对 SQL 请求使用分析语法树的方法进行参数化,并获取相应的不一致的信息。比如该语句对应的信息是“快速参数化参数数组的第3项必须为数字3”,可将其称为“约束条件”。
对于下例所示的请求 SQL_A 命令:
from t1
经过词法分析,可以得到参数化后的 SQL 如下例所示:
参数化后SQL:select c1, c2, c3
from t1
where c1 = @1 and c2 like @2 order by 3 ;
参数化参数为:{1, ‘senior’}
约束条件:快速参数化参数数组的第3项必须为数字3
SQL_A 请求新生成的参数化后的文本及约束条件和执行计划均会存入计划缓存中。
当用户再次发出如下 SQL_B 请求命令:
select c1, c2, c3
from t1
where c1 = 1 and c2 like ‘senior%’ order by 2;
经过快速参数化后结果如下例所示:
这与 SQL_A 请求快速参数化后 SQL 结果一样,但由于不满足“快速参数化参数数组的第3项必须为数字3”这个约束条件,无法匹配该计划。此时 SQL_B 会通过硬解析生成新的执行计划及约束条件(即快速参数化参数数组的第3项必须为数字2), 并将新的计划和约束条件加入到缓存中, 下次执行 SQL_A 和 SQL_B 时均可命中对应正确的执行计划。
基于快速参数化的执行计划缓存优点如下:
查找 hash map 时,对参数化后语法树的hash和比较操作,可替换为对文本串进行 hash 和 memcmp 操作,效率更高。