当SQL查询中包含冗余的GROUP BY或ORDER 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_mode和loose_orderby_elimination_mode参数来控制此优化功能的行为。
PolarDB集群参数在控制台与会话中修改方式存在差异,详细区别如下:
在PolarDB控制台上修改:
兼容性说明:部分集群参数在PolarDB控制台上均已添加MySQL配置文件的兼容性前缀loose_。
操作方法:找到并修改这些带
loose_前缀的参数。
在数据库会话中修改(使用命令行或客户端):
操作方法:当您连接到数据库,使用
SET命令修改参数时,请去掉loose_前缀,直接使用参数的原始名称进行修改。
参数名称 | 级别 | 描述 |
| Global/Session | 控制
|
| 控制
|
工作原理
GROUP BY/ORDER BY消除功能的核心是利用函数依赖(Functional Dependency,FD)。了解其工作原理有助于您更好地构造SQL,并预判优化是否能够生效。
什么是函数依赖:如果A列(或多列)的值能唯一确定B列的值,则称B列函数依赖于A列,记为
A -> B。例如,在用户表中,主键用户ID能唯一确定用户昵称,因此存在函数依赖:用户ID -> 用户昵称。优化器如何发现函数依赖:PolarDB的优化器主要通过以下元数据和查询条件来自动推导函数依赖关系:
主键/唯一键:表结构中定义的
PRIMARY KEY或UNIQUE 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 BY或ORDER 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的列a和b在WHERE子句中已被指定为常量,因此会直接消除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;优化分析:
主键依赖:在
customer表中,c_custkey是主键,因此它唯一决定了c_name、c_acctbal、c_phone、c_address、c_comment、c_nationkey等所有其他列。依赖传导:由于
JOIN条件c_nationkey = n_nationkey,并且n_nationkey是nation表的主键(决定n_name),优化器可以推导出c_custkey也唯一决定了n_name。最终简化:综上,
GROUP BY子句中的所有列(c_name、c_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是由a和b计算得出的(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的所有要求。
以下数据仅为特定场景下的测试结果,实际性能提升效果可能因查询复杂度、数据分布和集群规格等因素而异。
测试场景 | 开启 | 不开启(执行时间) | 性能提升 |
使用列存索引(IMCI)在1 DOP(生成数据后1天内) | 48秒 | 68秒 | 29% |
使用列存索引(IMCI)在32 DOP(生成数据后32天内) | 1.9秒 | 2.6秒 | 27% |