全部产品
Search
文档中心

云原生数据库 PolarDB:消除冗余的GroupBy/OrderBy

更新时间:Jan 15, 2026

当SQL查询中包含冗余的GROUP BYORDER BY子句时,数据库需执行不必要的排序或哈希计算,这会消耗大量CPU和内存资源。尤其在处理海量数据时,此类冗余操作会严重影响分析型(AP)查询的性能。PolarDB MySQL版GROUP BY/ORDER BY消除功能,能够基于函数依赖(Functional Dependency)自动识别并消除这类冗余操作。您无需修改任何业务代码,即可在分析型查询场景下获得显著的性能提升(在TPC-H测试中最高可达29%),并降低资源消耗。

适用范围

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

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

开启消除功能

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

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

  • PolarDB控制台上修改

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

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

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

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

参数名称

级别

描述

loose_groupby_elimination_mode

Global/Session

控制GROUP BY消除功能的主开关。取值范围如下:

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

  • ON:开启。

  • OFF:关闭。

loose_orderby_elimination_mode

控制ORDER BY消除功能的主开关。取值范围如下:

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

  • ON:开启。

  • OFF:关闭。

工作原理

GROUP BY/ORDER BY消除功能的核心是利用函数依赖(Functional Dependency,FD)。了解其工作原理有助于您更好地构造SQL,并预判优化是否能够生效。

  • 什么是函数依赖:如果A列(或多列)的值能唯一确定B列的值,则称B列函数依赖于A列,记为A -> B。例如,在用户表中,主键用户ID能唯一确定用户昵称,因此存在函数依赖:用户ID -> 用户昵称

  • 优化器如何发现函数依赖PolarDB的优化器主要通过以下元数据和查询条件来自动推导函数依赖关系:

    • 主键/唯一键:表结构中定义的PRIMARY KEYUNIQUE KEY是最直接的函数依赖来源。

    • 等值连接条件:通过JOIN的等值条件(如 t1.pk = t2.fk),主键的唯一性可以传递到关联的表中。

    • 常量过滤条件WHERE子句中的等值过滤(如 WHERE status = 'active')会使该列在结果集中成为一个常量。

  • 核心优化规则:

    • 消除GROUP BY

      • 如果GROUP BY的列集合已经能唯一确定结果集中的每一行(例如,包含了某张表的主键),则整个GROUP BY算子是冗余的,可以被安全地消除。

      • 如果GROUP BY的列集合均为常量,则可消除整个GROUP BY算子,并添加LIMIT 1

    • 消除ORDER BY:如果ORDER BY的列集合均为常量,则整个ORDER BY算子是冗余的,可以被安全地消除。

    • 简化GROUP BY/ORDER BY:如果GROUP BYORDER BY的列中存在函数依赖关系(如GROUP BY x, y,且已知x -> y),则可以简化为GROUP BY x,减少参与分组或排序的列数。

典型优化场景与示例

场景一:根据主键或唯一键分组

GROUP BY子句中包含了表的主键或唯一键时,由于主键已能唯一确定一行数据,因此对其他列进行分组是多余的。

  • 业务场景:统计每个用户的订单总额,同时展示用户名。

  • 原始SQL

    -- user表的user_id是主键,可以唯一确定user_name
    SELECT
        u.user_id,
        u.user_name,
        COUNT(o.order_id)
    FROM user u
    JOIN orders o ON u.user_id = o.user_id
    GROUP BY
        u.user_id,
        u.user_name;
  • 优化分析:因为user_id是主键,它唯一决定了user_name的值。优化器识别到user_id -> user_name的函数依赖关系,会自动将GROUP BY u.user_id, u.user_name简化为GROUP BY u.user_id,从而消除对user_name的不必要分组操作。

场景二:根据常量分组

GROUP BY子句中的所有列都是常量时,整个分组操作是冗余的,因为结果集最多只有一行。

  • 业务场景:查询某个特定用户的特定状态信息(一种不常见的SQL写法,但优化器能处理)。

  • 原始SQL

    SELECT a, b FROM t1 WHERE a = 1 AND b = 1 GROUP BY a, b;
  • 优化分析:优化器识别到GROUP BY的列abWHERE子句中已被指定为常量,因此会直接消除GROUP BY算子,并添加LIMIT 1

    SELECT a, b FROM t1 WHERE a = 1 AND b = 1 LIMIT 1;

景三:多表连接下的复杂分组(TPC-H Q10

在多表JOIN的复杂查询中,函数依赖关系可以通过连接条件进行传导,优化器能够利用这种传导性来消除更复杂的冗余分组。

  • 原始SQL(TPC-H Q10):

    SELECT
        c_custkey, c_name, c_acctbal, c_phone, n_name, c_address, c_comment,
        SUM(l_extendedprice * (1 - l_discount)) AS revenue
    FROM customer, orders, lineitem, nation
    WHERE c_custkey = o_custkey
      AND l_orderkey = o_orderkey
      AND c_nationkey = n_nationkey
      -- 其他过滤条件...
    GROUP BY
        c_custkey, c_name, c_acctbal, c_phone, n_name, c_address, c_comment
    ORDER BY revenue DESC
    LIMIT 20;
  • 优化分析

    1. 主键依赖:在customer表中,c_custkey是主键,因此它唯一决定了c_name、c_acctbal、c_phone、c_address、c_comment、c_nationkey等所有其他列。

    2. 依赖传导:由于JOIN条件c_nationkey = n_nationkey,并且n_nationkeynation表的主键(决定n_name),优化器可以推导出c_custkey也唯一决定了n_name

    3. 最终简化:综上,GROUP BY子句中的所有列(c_namec_acctbal等)都函数依赖于c_custkey。因此,优化器可以将复杂的GROUP BY列表直接简化为GROUP BY c_custkey,大幅减少了分组计算的复杂度。

    SELECT 
       c_custkey, c_name, c_acctbal, c_phone, n_name, c_address, c_comment,
       SUM(l_extendedprice * (1 - l_discount)) AS revenue
    FROM customer, orders, lineitem, nation
    WHERE c_custkey = o_custkey
      AND l_orderkey = o_orderkey
      AND c_nationkey = n_nationkey
      -- 其他过滤条件...
    GROUP BY c_custkey
    ORDER BY revenue DESC
    LIMIT 20;

场景四:Order By场景

ORDER BY子句中的列存在函数依赖关系时,优化器可以简化排序操作,减少参与排序的列数。

  • 业务场景:对表进行排序,其中某些列由其他列唯一确定。

  • 表结构

    CREATE TABLE t1 (
      a int,
      b int,
      c int as (a + b) unique not null
    );
  • 原始SQL

    EXPLAIN SELECT a, b FROM t1 ORDER BY a,b,c;
  • 优化分析 :由于列c是由ab计算得出的(c = a + b),并且c被定义为unique not null,因此存在函数依赖关系:(a, b) -> c。优化器识别到这一依赖关系后,会自动将ORDER BY a, b, c简化为ORDER BY a, b,从而减少排序操作的复杂度。

    SHOW warnings;
    +-------+------+-------------------------------------------------------------------------------------------------------------------------------+
    | Level | Code | Message                                                                                                                       |
    +-------+------+-------------------------------------------------------------------------------------------------------------------------------+
    | Note  | 1003 | /* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` order by `test`.`t1`.`a`,`test`.`t1`.`b` |
    +-------+------+-------------------------------------------------------------------------------------------------------------------------------+

性能测试

在TPC-H 100 GB标准数据集上进行的测试表明,开启GROUP BY消除功能后,Q10查询的性能得到显著提升。

说明
  • 本文的TPC-H的实现基于TPC-H的基准测试,并不能与已发布的TPC-H基准测试结果相比较,本文中的测试并不完全符合TPC-H的所有要求。

  • 以下数据仅为特定场景下的测试结果,实际性能提升效果可能因查询复杂度、数据分布和集群规格等因素而异。

测试场景

开启GROUP BY消除(执行时间)

不开启(执行时间)

性能提升

使用列存索引(IMCI)在1 DOP(生成数据后1天内)

48秒

68秒

29%

使用列存索引(IMCI)在32 DOP(生成数据后32天内)

1.9秒

2.6秒

27%