引擎条件下推优化
该优化提高了对非索引列与常量之间直接比较的效率。在这种情况下,条件被"下推"到存储引擎进行评估。这种优化只能由 NDB 存储引擎使用。
对于 NDB 集群,这种优化可以消除在群集的数据节点和发出查询的 MySQL 服务器之间发送不匹配行的需求,并且可以加快使用此优化的查询速度,相比可以使用但未使用条件下推时,速度提高了5到10倍。
假设 NDB 集群表定义如下:
CREATE TABLE t1 ( a INT, b INT, KEY(a) ) ENGINE=NDB;
引擎条件下推可以与如下所示的查询一起使用,该查询包括一个非索引列与常量之间的比较:
SELECT a, b FROM t1 WHERE b = 10;
可以通过 EXPLAIN 的输出来观察是否使用了引擎条件下推优化:
mysql> EXPLAIN SELECT a, b FROM t1 WHERE b = 10\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: t1 type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 10 Extra: Using where with pushed condition
然而,对于以下查询,无法使用引擎条件下推优化:
SELECT a,b FROM t1 WHERE a = 10;
在此情况下,由于列 a 上存在索引,因此无法应用引擎条件下推优化。(索引访问方法更高效,因此会优先选择索引访问方法,而不是条件下推优化。)
当使用 > 或 < 运算符将索引列与常量进行比较时,也可以使用引擎条件下推优化:
mysql> EXPLAIN SELECT a, b FROM t1 WHERE a < 2\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: t1 type: range possible_keys: a key: a key_len: 5 ref: NULL rows: 2 Extra: Using where with pushed condition
引擎条件下推还支持以下比较:
- column [NOT] LIKE pattern
pattern 必须是一个包含要匹配的模式的字符串字面值。
- column IS [NOT] NULL
- column IN (value_list)
value_list 中的每个项必须是一个常量、字面值。
- column BETWEEN constant1 AND constant2
constant1 和 constant2 必须分别是一个常量、字面值。
在上述列表中的所有情况下,都可以将条件转换为一个或多个列与常量之间的直接比较形式。
默认情况下,引擎条件下推是启用的。要在服务器启动时禁用它,请将 optimizer_switch 系统变量的 engine_condition_pushdown 标志设置为 off。例如,在 my.cnf 文件中,使用以下行:
[mysqld] optimizer_switch=engine_condition_pushdown=off
在运行时禁用条件下推,可以这样操作:
SET optimizer_switch='engine_condition_pushdown=off';
引擎条件下推受到以下限制:
- 引擎条件下推仅受 NDB 存储引擎支持。
- 在 NDB 8.0.18 之前,仅可以将列与常量或计算结果为常量值的表达式进行比较。在 NDB 8.0.18 及更高版本中,只要这些适用的列具有完全相同的类型(包括相同的有符号性、长度、字符集、精度和刻度),可以相互比较。
- 用于比较的列不能是任何 BLOB 或 TEXT 类型。此排除范围还包括 JSON、BIT 和 ENUM 列。
- 与列进行比较的字符串值必须使用与列相同的校对规则。
- 不直接支持连接;尽可能地将涉及多个表的条件单独推送。使用扩展的 EXPLAIN 输出来确定实际被推送的条件。
以前,引擎条件下推仅限于引用来自正在推送条件的同一张表的列值。从 NDB 8.0.16 开始,还可以从查询计划中的较早表中引用被推送条件的列值。这减少了在连接处理期间 SQL 节点必须处理的行数。过滤也可以并行在 LDM 线程中执行,而不是在单个 mysqld 进程中执行。这有可能显著提高查询的性能。
从 NDB 8.0.20 开始,如果在相同的连接嵌套中使用的任何表上没有不可推送的条件或者依赖于它的上面连接嵌套中的任何表上没有不可推送的条件,那么使用扫描的外连接可以进行推送。对于半连接也是如此,前提是采用的优化策略是 firstMatch。
在以下两种情况下,无法将连接算法与来自前面表的引用列相结合:
- 当任何被引用的前面表位于连接缓冲区中时。在这种情况下,从扫描过滤表检索到的每一行都与缓冲区中的每一行进行匹配。这意味着在生成扫描过滤器时没有特定的单个行可以从中获取列值。
- 当列来自推送连接中的子操作时。这是因为在生成扫描过滤器时,连接中的祖先操作所引用的行尚未检索到。
从 NDB 8.0.27 开始,可以将连接中的祖先表的列推送下来,前提是满足前面列出的要求。下面是一个使用之前创建的表 t1 的查询示例:
mysql> EXPLAIN -> SELECT * FROM t1 AS x -> LEFT JOIN t1 AS y -> ON x.a=0 AND y.b>=3\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: x partitions: p0,p1 type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 4 filtered: 100.00 Extra: NULL *************************** 2. row *************************** id: 1 select_type: SIMPLE table: y partitions: p0,p1 type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 4 filtered: 100.00 Extra: Using where; Using pushed condition (`test`.`y`.`b` >= 3); Using join buffer (hash join) 2 rows in set, 2 warnings (0.00 sec)