派生表与视图的列裁剪优化

当查询仅从包含大量列的派生表或视图中选取少数几列时,可能会因读取和处理不必要的数据而导致性能下降。此问题在处理宽表或复用大而全的通用视图时尤为突出。PolarDB MySQL的列裁剪(Column Pruning)功能可自动优化此类查询。它在查询解析阶段识别并移除派生表或视图中未被最终引用的列,从而显著减少数据扫描量、网络传输和内存消耗,降低查询延迟,提升系统吞吐。

适用范围

  • 产品系列:集群版标准版

  • 内核版本:MySQL 8.0.2,且修订版本需为8.0.2.2.31.1及以上版本。

开启列裁剪

通过设置loose_derived_table_pruning_mode参数来控制此优化功能的行为。

PolarDB集群参数在控制台与会话中修改方式存在差异,详细区别如下:

  • PolarDB控制台上修改

    • 兼容性说明:部分集群参数在PolarDB控制台上均已添加MySQL配置文件的兼容性前缀loose_

    • 操作方法:找到并修改这些带loose_前缀的参数。

  • 在数据库会话中修改(使用命令行或客户端):

    • 操作方法:当您连接到数据库,使用SET命令修改参数时,请去掉loose_前缀,直接使用参数的原始名称进行修改。

参数名称

级别

描述

loose_derived_table_pruning_mode

Global/Session

控制该功能的主开关。取值范围如下:

  • REPLICA_ON(默认值):仅在只读(RO)节点上开启此功能。

  • ON:开启。

  • OFF:关闭。

工作原理与示例

列裁剪优化的核心是查询改写。在查询的逻辑优化阶段,优化器会分析整个查询语句,识别出派生表或视图中未被最终SELECT列表、WHERE条件、JOIN条件等引用的列。若某列是多余的,优化器会重写内部查询,从源头避免读取和处理该列的数据,从而减少I/OCPU消耗。

示例1:优化派生表查询

此示例展示了列裁剪如何优化对派生表的查询。

-- 优化前
SELECT c1 FROM (SELECT c1 FROM (SELECT c1, c2 FROM t1 GROUP BY c1, c2) v) t;
-- 优化后
SELECT c1 FROM (SELECT c1 FROM t1 GROUP BY c1, c2) v
  1. 准备测试数据:在数据库中创建一张包含多个列的表t1

    CREATE TABLE t1 (id INT PRIMARY KEY, c1 INT, c2 INT, c3 VARCHAR(100));
    INSERT INTO t1 VALUES (1, 10, 100, 'data'), (2, 20, 200, 'data');
  2. 关闭优化进行查询:首先,在会话中关闭列裁剪功能,以观察优化前的执行计划。

    SET derived_table_pruning_mode = 'OFF';
    EXPLAIN SELECT c1 FROM (SELECT c1 FROM (SELECT c1, c2 FROM t1 GROUP BY c1, c2) v) t;
    SHOW warnings;
    
    +-------+------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | Level | Code | Message                                                                                                                                                                                         |
    +-------+------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | Note  | 1003 | /* select#1 */ select `v`.`c1` AS `c1` from (/* select#3 */ select `testdb`.`t1`.`c1` AS `c1`,`testdb`.`t1`.`c2` AS `c2` from `testdb`.`t1` group by `testdb`.`t1`.`c1`,`testdb`.`t1`.`c2`) `v` |
    +-------+------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
  3. 开启优化进行查询:现在,在会话中开启列裁剪功能,并执行相同的查询。

    SET derived_table_pruning_mode = 'ON';
    EXPLAIN SELECT c1 FROM (SELECT c1 FROM (SELECT c1, c2 FROM t1 GROUP BY c1, c2) v) t;
    SHOW warnings;
    
    +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | Level | Code | Message                                                                                                                                                              |
    +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | Note  | 1003 | /* select#1 */ select `v`.`c1` AS `c1` from (/* select#3 */ select `testdb`.`t1`.`c1` AS `c1` from `testdb`.`t1` group by `testdb`.`t1`.`c1`,`testdb`.`t1`.`c2`) `v` |
    +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+

    执行计划解读:优化后的执行计划发生了显著变化。优化器识别到外部查询仅需c1列,因此直接重写了查询。

示例2:优化视图查询

列裁剪对视图的优化与派生表类似,能够避免不必要的计算。

CREATE VIEW v1 AS SELECT COUNT(*) AS a, (SELECT a FROM t2 WHERE a=FLOOR(COUNT(t1.a)/2)) AS s FROM t1;

-- 优化前
SELECT a FROM v1;
-- 优化后
SELECT COUNT(*) AS a FROM t1;

优化限制

只有在保证查询结果与原始SQL语义完全等价的前提下,列裁剪优化才会触发。当派生表或视图中看似未被引用的列实际参与了以下运算时,该列不会被裁剪:

  • 被用于ORDER BYGROUP BYHAVINGDISTINCT子句。

  • 被用于窗口函数(Window Function)的分区键(PARTITION BY)或排序键(ORDER BY)。

  • 如果被裁剪的列是基于一个非确定性函数(如RAND()UUID())生成的,该函数将不会被执行,从而避免了不必要的计算开销。此为优化效果,非限制。

常见问题

修改derived_table_pruning_mode参数需要重启集群吗?

不需要。该参数支持动态修改,修改后可立即生效,无需重启。

为什么在主节点上设置了derived_table_pruning_mode=ON,但查询性能没有提升?

  1. 确认SQL场景:检查您的SQL是否符合列裁剪的适用场景,即外层查询是否确实只使用了内层派生表或视图的部分列。

  2. 查看执行计划:使用EXPLAIN确认优化是否实际发生。如果未发生,请参考优化限制章节,检查是否存在无法裁剪的场景。