文档首页/ 云数据库 TaurusDB/ 内核介绍/ 查询优化/ 范围查询冗余条件消除
更新时间:2026-08-06 GMT+08:00
分享

范围查询冗余条件消除

操作场景

社区MySQL在处理 WHERE col = const 或 WHERE col > const 等谓词时,若列上存在索引且优化器选择 Range scan 访问方式,范围扫描的上下界已隐含保证了谓词成立。然而,SQL 层仍会对每一行重复求值,造成不必要的 CPU 开销。对于大范围扫描场景,这种冗余求值的性能损失尤为突出。

以查询 SELECT * FROM t WHERE a = 5 AND b > 3 为例,若索引为 (a, b),范围扫描的 min_key 为 [5, 3]、max_key 为 [5, +∞],则 a = 5 已被范围下界完全保证,无需逐行求值。但社区 MySQL 仅支持 REF 访问的等值消除(test_if_ref),对 Range scan 场景下的等值及不等式谓词均不做消除。

理论上,可引入类似 REF 访问的等值消除机制来处理 Range scan,但面临以下挑战:

  • 边界信息访问安全:Range scan 的区间边界存储在 QUICK_RANGE 结构中,需安全读取并与谓词常量比较。若比较语义与存储引擎不一致,可能导致谓词被误删,进而产生错误查询结果。
  • 合并区间语义丢失:OR 子句产生的合并区间与原始谓词形式可能不一致。例如 c=3 OR c>2 会被合并为 c>2 的单区间(无上界),此时无法判断 c=3 是否冗余,强行消除会导致语义错误。
  • NDP 下推条件受影响:NDP(Near Data Processing)下推依赖 WHERE 条件,冗余消除后可能导致可下推条件减少,反而降低整体性能。

因此,TaurusDB支持范围查询冗余条件消除功能。当表使用Range scan访问时,优化器自动识别并移除已被范围上下界隐含保证的谓词,减少SQL层的重复求值开销,提升查询性能。

转换过程

原查询:

SELECT * FROM t WHERE a = 5 AND b > 3

若索引为(a, b),Range scan 的 min_key=[5, 3],max_key=[5, +∞]:

  • a = 5:min_key 和 max_key 中 a 的值均为5,被范围隐含保证,无需求值,因此被消除。
  • b > 3:min_key 中 b 的值为 3,但范围下界为包含(>=),谓词为严格大于(>),两者语义不同,无法消除,即被保留。

转换后:

SQL层实际求值的条件(a=5 已被消除)。

SELECT * FROM t WHERE b > 3

前提条件

支持的谓词类型

表1 支持的谓词类型

谓词类型

SQL 示例

消除条件

等值

col = 5

范围的min_key和max_key中该列的值均等于常量。

小于

col < 10

范围的max_key等于常量,且范围上界为严格小于(NEAR_MAX 标记)。

小于等于

col <= 10

范围的max_key等于常量。

大于

col > 3

范围的min_key等于常量,且范围下界为严格大于(NEAR_MIN 标记)。

大于等于

col >= 3

范围的min_key等于常量

BETWEEN

col BETWEEN 3 AND 10

范围的min_key 等于下界常量且 max_key 等于上界常量。

IS NULL

col IS NULL

范围的min_key和max_key的NULL标记位均为1。

支持的查询语句

  • SELECT
  • INSERT ... SELECT
  • REPLACE ... SELECT
  • 支持视图、PREPARED STMT
  • 支持组合索引中的非首列字段
  • 支持 DESC 索引
  • 支持可空字段的 NULL 字节处理

约束限制

  • 仅支持简单的Range scan(单区间扫描),不支持 index merge、skip scan。
  • 仅支持单区间(ranges.size() == 1),不支持 OR 产生的不相交多区间。
  • 不支持前缀索引段(即索引列定义了前缀长度,如col(10))。
  • 不支持BIT类型字段。
  • 不支持动态范围扫描(Dynamic Range,即执行时才确定范围的场景)。
  • 不支持OR子句内部的谓词消除。OR子句会被临时禁用冗余检查,确保正确性。
  • 不支持NOT BETWEEN。
  • 不支持外连接中NULL扩展表的谓词消除。
  • 当 NDP(Near Data Processing)可能启用时,冗余条件消除自动禁用,优先保证NDP下推效果。
  • Hypergraph 优化器的 EXPLAIN 输出尚未适配该特性。

使用方法

该特性通过会话参数rds_empty_redundant_check_in_range_scan控制。

表2 参数说明

参数

级别

默认值

说明

rds_empty_redundant_check_in_range_scan

SESSION / GLOBAL

OFF

索引范围扫描时,SQL层去除冗余的条件并对存储引擎返回的行不再执行冗余条件的检查,扩大了Offset Pushdown的生效范围。

  • ON : 优化生效,SQL层去除冗余检查。
  • OFF : 优化不生效。

示例

  1. 创建测试表并插入数据:
    CREATE TABLE t1 (a INT, b INT, INDEX(a, b));
    INSERT INTO t1 VALUES (1,2),(2,3),(3,3),(4,3),(5,5),(2,5),(3,7);
  2. 关闭冗余条件消除,查看执行计划:
    SET SESSION rds_empty_redundant_check_in_range_scan = OFF;
    EXPLAIN FORMAT=TREE SELECT * FROM t1 WHERE a = 5 AND b > 3;

    执行计划中保留了Filter层,SQL层需要逐行求值a = 5 和 b > 3:

    mysql> EXPLAIN FORMAT=TREE SELECT * FROM t1 WHERE a = 5 AND b > 3;
    +-----------------------------------------------------------------------------------------------------------------------+
    | EXPLAIN                                                                                                               |
    +-----------------------------------------------------------------------------------------------------------------------+
    | -> Filter: ((t1.a = 5) and (t1.b > 3))  (cost=0.46 rows=1)
        -> Index range scan on t1 using a  (cost=0.46 rows=1)
    +-----------------------------------------------------------------------------------------------------------------------+
  3. 开启冗余条件消除,查看执行计划:
    SET SESSION rds_empty_redundant_check_in_range_scan = ON;
    EXPLAIN FORMAT=TREE SELECT * FROM t1 WHERE a = 5 AND b > 3;

    `Filter` 层消失,`a = 5` 和 `b > 3` 均已被范围扫描的上下界隐含保证,无需SQL层重复求值:

    mysql> EXPLAIN FORMAT=TREE SELECT * FROM t1 WHERE a = 5 AND b > 3;
    +--------------------------------------------------------+
    | EXPLAIN                                                |
    +--------------------------------------------------------+
    | -> Index range scan on t1 using a  (cost=0.46 rows=1)
    +--------------------------------------------------------+
  4. 验证查询结果一致性:
    SET SESSION rds_empty_redundant_check_in_range_scan = OFF;
    SELECT * FROM t1 WHERE a = 2 AND b > 2;
    SET SESSION rds_empty_redundant_check_in_range_scan = ON;
    SELECT * FROM t1 WHERE a = 2 AND b > 2;

    两种模式下查询结果完全一致:

    +------+------+
    | a    | b    |
    +------+------+
    |    2 |    3 |
    |    2 |    5 |
    +------+------+

性能测试

以下测试基于1000万行宽表,组合主键上的等值+范围查询场景,验证冗余条件消除对查询性能的提升。

  1. 准备测试数据。
    SET SESSION cte_max_recursion_depth = 10000001;
    DROP TABLE IF EXISTS big_test;
    CREATE TABLE big_test (
    a INT NOT NULL,
    b INT NOT NULL,
    c INT NOT NULL,
    d INT NOT NULL,
    e INT NOT NULL,
    f1 VARCHAR(200),
    f2 VARCHAR(200),
    f3 VARCHAR(200),
    PRIMARY KEY (a, b, c, d, e)
    ) ENGINE=InnoDB ROW_FORMAT=COMPACT;
    INSERT INTO big_test (a, b, c, d, e, f1, f2, f3)
    WITH RECURSIVE seq AS (
    SELECT 1 AS n
    UNION ALL
    SELECT n+1 FROM seq WHERE n < 10000000
    )
    SELECT
    (n-1) DIV 2000000 + 1 AS a,
    (n-1) DIV 200000 MOD 10 + 1 AS b,
    (n-1) DIV 20000 MOD 10 + 1 AS c,
    (n-1) DIV 200 MOD 100 + 1 AS d,
    n AS e,
    REPEAT(CONCAT('val', n MOD 1000), 5) AS f1,
    REPEAT(CONCAT('key', n MOD 500), 5) AS f2,
    REPEAT(CONCAT('dat', n MOD 200), 5) AS f3
    FROM seq;
  2. 执行计划对比。

    查询WHERE a = 3 AND b > 3,匹配约200万行。其中a = 3可被范围下界隐含保证,属于冗余谓词。

    • 关闭冗余条件消除时,EXPLAIN 显示Using where,SQL层需逐行求值a = 3 AND b > 3。
      SET SESSION rds_empty_redundant_check_in_range_scan = OFF;
      EXPLAIN SELECT SUM(LENGTH(f1)) FROM big_test WHERE a = 3 AND b > 3;
      +----+-------------+----------+-------+---------------+---------+---------+------+---------+----------+-------------+
      | id | select_type | table    | type  | possible_keys | key     | key_len | ref  | rows    | filtered | Extra       |
      +----+-------------+----------+-------+---------------+---------+---------+------+---------+----------+-------------+
      |  1 | SIMPLE      | big_test | range | PRIMARY       | PRIMARY | 8       | NULL | 2719004 |   100.00 | Using where |
      +----+-------------+----------+-------+---------------+---------+---------+------+---------+----------+-------------+
    • 开启冗余条件消除时,a = 3被消除,EXPLAIN 的 Extra 列不再显示 Using where。
      SET SESSION rds_empty_redundant_check_in_range_scan = ON;
      EXPLAIN SELECT SUM(LENGTH(f1)) FROM big_test WHERE a = 3 AND b > 3;
      +----+-------------+----------+-------+---------------+---------+---------+------+---------+----------+-------+
      | id | select_type | table    | type  | possible_keys | key     | key_len | ref  | rows    | filtered | Extra |
      +----+-------------+----------+-------+---------------+---------+---------+------+---------+----------+-------+
      |  1 | SIMPLE      | big_test | range | PRIMARY       | PRIMARY | 8       | NULL | 2719004 |   100.00 | NULL  |
      +----+-------------+----------+-------+---------------+---------+---------+------+---------+----------+-------+
  3. 查询耗时对比
    SET SESSION rds_empty_redundant_check_in_range_scan = OFF;
    SELECT SUM(LENGTH(f1)) FROM big_test WHERE a = 3 AND b > 3;
    SET SESSION rds_empty_redundant_check_in_range_scan = ON;
    SELECT SUM(LENGTH(f1)) FROM big_test WHERE a = 3 AND b > 3;
    • 冗余条件消除关闭
      mysql> SELECT SUM(LENGTH(f1)) FROM big_test WHERE a = 3 AND b > 3;
      +-----------------+
      | SUM(LENGTH(f1)) |
      +-----------------+
      |        41230000 |
      +-----------------+
      1 row in set (0.69 sec)
    • 冗余条件消除开启
      mysql> SELECT SUM(LENGTH(f1)) FROM big_test WHERE a = 3 AND b > 3;
      +-----------------+
      | SUM(LENGTH(f1)) |
      +-----------------+
      |        41230000 |
      +-----------------+
      1 row in set (0.51 sec)
    表3 查询耗时对比

    模式

    Extra

    耗时

    说明

    OFF

    Using where

    0.69s

    SQL层对200万行逐行求值 a=3 AND b>3。

    ON

    NULL

    0.51s

    a=3 被消除,仅求值 b>3,性能提升约 26%。

相关文档