参数化过程是指把 SQL 查询中的常量变成变量的过程。
同一条 SQL 语句在每次执行时可能会使用不同的参数,将这些参数做参数化处理,可以得到与具体参数无关的 SQL 字符串,并使用该字符串作为计划缓存的键值,用于在计划缓存中获取执行计划,从而达到参数不同的 SQL 能够共用相同的计划目的。
由于传统数据库在进行参数化时一般是对语法树进行参数化,然后使用参数化后的语法树作为键值在计划缓存中获取计划,而 OceanBase 数据库使用的词法分析对文本串直接参数化后作为计划缓存的键值,因此叫做快速参数化。
OceanBase 数据库支持自适应计划共享(Adaptive Cursor Sharing)功能以支持不同参数条件下的计划选择。
基于快速参数化而获取执行计划的流程如下图所示:
示例解析
obclient>SELECT * FROM T1 WHERE c1 = 5 AND c2 ='oceanbase';
上述示例中的 SQL 查询参数化后结果如下所示,常量 5 和 oceanbase 被参数化后变成了变量 @1 和 @2:
obclient>SELECT * FROM T1 WHERE c1 = @1 AND c2 = @2;
但在计划匹配中,不是所有常量都可以被参数化,例如 ORDER BY 后面的常量,表示按照 SELECT 投影列中第几列进行排序,所以不可以被参数化。
如下例所示,表 t1 中含 c1、c2 列,其中 c1 为主键列,SQL 查询的结果按照 c1 列进行排序,由于 c1 作为主键列是有序的,所以使用主键访问可以免去排序。
obclient>CREATE TABLE t1(c1 INT PRIMARY KEY,c2 INT);
Query OK, 0 rows affected (0.06 sec)
obclient>INSERT INTO t1 VALUES (1,2);
Query OK, 1 row affected (0.01 sec)
obclient>INSERT INTO t1 VALUES (2,1);
Query OK, 1 row affected (0.01 sec)
obclient>INSERT INTO t1 VALUES (3,1);
Query OK, 1 row affected (0.01 sec)
obclient>SELECT c1, c2 FROM t1 ORDER BY 1;
+----+------+
| C1 | C2 |
+----+------+
| 1 | 2 |
| 2 | 1 |
| 3 | 1 |
+----+------+
3 rows in set (0.00 sec)
obclient>EXPLAIN SELECT c1, c2 FROM t1 ORDER BY 1G;
*************************** 1. row ***************************
Query Plan:
| ===================================
|ID|OPERATOR |NAME|EST. ROWS|COST|
-----------------------------------
|0 |TABLE SCAN|t1 |1000 |1381|
===================================
Outputs & filters:
-------------------------------------
0 - output([T1.C1], [T1.C2]), filter(nil),
access([T1.C1], [T1.C2]), partitions(p0)
但如果执行如下命令:
obclient>SELECT c1, c2 FROM t1 ORDER BY 2;
+----+------+
| C1 | C2 |
+----+------+
| 2 | 1 |
| 3 | 1 |
| 1 | 2 |
+----+------+
3 rows in set (0.00 sec)
则结果需要对 c2 排序,因此需要执行显示的排序操作,执行计划如下例所示:
obclient>EXPLAIN SELECT c1, c2 FROM t1 ORDER BY 2G;
*************************** 1. row ***************************
Query Plan:
| ====================================
|ID|OPERATOR |NAME|EST. ROWS|COST|
------------------------------------
|0 |SORT | |1000 |1886|
|1 | TABLE SCAN|t1 |1000 |1381|
====================================
Outputs & filters:
-------------------------------------
0 - output([T1.C1], [T1.C2]), filter(nil), sort_keys([T1.C2, ASC])
1 - output([T1.C1], [T1.C2]), filter(nil),
access([T1.C1], [T1.C2]), partitions(p0)
因此,如果将 ORDER BY 后面的常量参数化,不同 ORDER BY 的值具有相同的参数化后的 SQL,从而导致命中错误的计划。除此之外,如下场景中的常量均不能参数化(即参数化的约束条件):
ORDER BY 1,2;
)GROUP BY 1,2;
)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”,指定输入时间戳的同时,隐含指定了函数处理的精度值为毫秒)为了解决上面这种可能存在的误匹配问题,在硬解析生成执行计划过程中会对 SQL 请求使用分析语法树的方法进行参数化,并获取相应的不一致的信息。例如,某语句对应的信息是“快速参数化参数数组的第 3 项必须为数字 3”,可将其称为“约束条件”。
对于下例所示的 Q1 查询:
Q1:
obclient>SELECT c1, c2, c3 FROM t1
WHERE c1 = 1 AND c2 LIKE 'senior%' ORDER BY 3;
经过词法分析,可以得到参数化后的 SQL 语句如下例所示:
obclient>SELECT c1, c2, c3 FROM t1
WHERE c1 = @1 AND c2 LIKE @2 ORDER BY @3 ;
当 ORDER BY 后面的常量不同时,不能共用相同的执行计划,因此在通过分析语法树进行参数化时会获得另一种参数化结果,如下例所示:
obclient>SELECT c1, c2, c3 FROM t1
WHERE c1 = @1 AND c2 LIKE @2 ORDER BY 3 ;
Q1 请求新生成的参数化后的文本及约束条件和执行计划均会存入计划缓存中。
当用户再次发出如下 Q2 请求命令:
Q2:
obclient>SELECT c1, c2, c3 FROM t1
WHERE c1 = 1 AND c2 LIKE 'senior%' ORDER BY 2;
经过快速参数化后结果如下例所示:
obclient>SELECT c1, c2, c3 FROM t1
WHERE c1 = @1 and c2 like @2 ORDER BY @3;
这与 Q1 请求快速参数化后 SQL 结果一样,但由于不满足“快速参数化参数数组的第 3 项必须为数字 3”这个约束条件,无法匹配该计划。此时 Q2 会通过硬解析生成新的执行计划及约束条件(即“快速参数化参数数组的第 3 项必须为数字 2”),并将新的计划和约束条件加入到缓存中,这样在下次执行 Q1 和 Q2 时均可命中对应正确的执行计划。
基于快速参数化的执行计划缓存优点如下:
SocketImplFactorypublic interface SocketImplFactory该接口为套接字实现定义了一个工厂。 Socket 和 ServerSocket 类使用它来...
SocketAddressjava.lang.Object |---java.net.SocketAddresspublic abstract class SocketAddressextends Objectimplements Seri...
URLDecoderjava.lang.Object |---java.net.URLDecoderpublic class URLDecoderextends ObjectHTML 表单解码的实用程序类。 此类...
Collectionsjava.lang.Object |---java.util.Collectionspublic class Collectionsextends Object此类仅由对集合进行操作或返回...
模板WXML提供模板(template),可以在模板中定义代码片段,然后在不同的地方调用。定义模板使用name属性,作为模板的名字。然后...
绘图接口和方法canvasContext.beginPath定义开始创建一个路径,需要调用fill或者stroke才会使用路径进行填充或描边。Tip: 在最开...
wx.openSetting(OBJECT)基础库版本 1.1.0 开始支持,低版本需做兼容处理调起客户端小程序设置界面,返回用户设置的操作结果Objec...
9.30.1. 检查MCV列表 PostgreSQL提供了一个函数来检查使用CREATE STATISTICS命令定义的复杂统计。9.30.1.检查MCV列表pg_mcv_list...
9.16.1. 处理和创建JSON数据9.16.2. SQL/JSON 路径语言本节描述:用于处理和创建JSON数据的函数和运算器SQL/JSON路径语言要了解有...
COMMIT PREPARED — 提交一个早前为两阶段提交预备的事务大纲COMMIT PREPARED transaction_id描述 COMMIT PREPARED提交一个处于...