SQL并行查询优化实践:从执行计划看并行能力的正确使用

前言

随着业务数据规模不断增长,单条SQL的执行时间逐渐成为影响系统性能的重要因素。在多核CPU已经成为服务器标配的今天,如何充分利用硬件资源提升查询效率,是数据库优化过程中经常面对的问题。

KingbaseES提供了完善的并行查询能力,可以将一个大型查询拆分为多个任务,由多个Worker协同完成,从而降低单个CPU核心的计算压力,缩短SQL执行时间。

但在实际工作中,经常会遇到这样的情况:

明明表中已经有数百万甚至上千万数据,CPU资源也比较充足,执行计划却依然选择单线程执行;甚至添加了并行Hint之后,SQL仍然没有走并行计划。

本文结合实际案例,对KingbaseES中的并行机制进行分析,并介绍影响并行执行计划生成的几个关键因素。


一个简单的并行案例

构造1000万行测试数据:

sql 复制代码
CREATE TABLE app_family2(
    family_id varchar(32),
    application_id varchar(32),
    family_number varchar(50),
    household_register_number varchar(50),
    poverty_reason varchar(32)
);

INSERT INTO app_family2
SELECT generate_series(1,10000000),
       generate_series(1,10000000),
       'aaaa',
       'aaa',
       'bbb';

查询全部数据:

sql 复制代码
EXPLAIN ANALYZE
SELECT * FROM app_family2;

执行计划如下:

text 复制代码
Gather
  Workers Planned: 2
  Workers Launched: 2
  -> Parallel Seq Scan

可以看到数据库启动了两个并行Worker参与扫描,每个Worker负责部分数据读取工作,最终由Gather节点汇总结果。

这种执行方式能够有效利用多核CPU资源,在大表扫描场景下通常能够获得较好的性能收益。


为什么有些SQL无法走并行

很多用户会发现,同样的数据量下,有些SQL能够使用并行,而有些SQL始终是单线程执行。

例如:

sql 复制代码
INSERT INTO testu
SELECT COUNT(*)
FROM test;

查看执行计划:

text 复制代码
Insert on testu
  -> Aggregate
       -> Seq Scan on test

整个执行过程只有一个Seq Scan,没有出现Gather和Parallel Seq Scan节点。

即使开启:

sql 复制代码
SET force_parallel_mode=on;

执行计划仍然不会发生变化。

这是因为当前版本中,INSERT INTO ... SELECT语句不会生成并行查询计划。

对于需要利用并行能力完成数据装载的场景,可以采用以下方式改写SQL:

sql 复制代码
SELECT COUNT(*)
INTO testu
FROM test;

或者:

sql 复制代码
CREATE TABLE testu AS
SELECT COUNT(*)
FROM test;

改写后执行计划中即可看到:

text 复制代码
Gather
  Workers Planned: 2
  -> Parallel Seq Scan

实现并行计算。


并行度并不是越大越好

很多DBA在看到并行功能后,第一反应是提高Worker数量。

例如:

sql 复制代码
SET max_parallel_workers_per_gather=4;

再次执行查询:

text 复制代码
Workers Planned: 4
Workers Launched: 4

数据库会启动4个Worker参与计算。

但需要注意的是,并行度增加并不意味着性能一定提升。

随着Worker数量增加,会产生:

  • 任务分发开销
  • 结果汇总开销
  • 进程调度开销

当数据量较小或者CPU资源紧张时,过高的并行度反而可能导致性能下降。

因此并行度设置需要结合:

  • CPU核心数量
  • 数据规模
  • 系统负载

综合评估,而不是简单地无限增大。


优化器为什么放弃并行

实际生产环境中,更常见的问题是:

数据库明明支持并行,却没有选择并行执行。

原因通常来自优化器成本评估。

KingbaseES在生成执行计划时,会比较:

text 复制代码
串行执行成本
VS
并行执行成本

如果优化器认为:

text 复制代码
并行收益 < 并行开销

则不会生成并行计划。

其中影响较大的参数是:

sql 复制代码
parallel_tuple_cost
parallel_setup_cost

例如:

sql 复制代码
SET parallel_tuple_cost=0.001;

重新查看执行计划:

text 复制代码
Gather
  Workers Planned: 2
  -> Parallel Seq Scan

此时优化器会更倾向于采用并行方案。

因此,在一些大表扫描场景下,如果发现执行计划始终无法并行,可以适当调整相关成本参数进行验证。


自定义函数导致并行失效

除了参数因素外,自定义函数也是影响并行执行的重要原因。

例如:

sql 复制代码
SELECT
    family_id,
    fun_getdistance1(
        family_id::numeric,
        application_id::numeric
    )
FROM app_family2;

即使增加Parallel Hint,执行计划仍然可能是单线程。

原因在于函数默认并不一定支持并行执行。

可以通过以下方式查看函数属性:

sql 复制代码
SELECT *
FROM sys_proc
WHERE proname='fun_getdistance1';

如果函数被标记为:

text 复制代码
PARALLEL UNSAFE

则无法参与并行执行。

修改方式:

sql 复制代码
ALTER FUNCTION fun_getdistance1
PARALLEL SAFE;

修改后优化器才会考虑生成并行计划。


CTE场景下的并行限制

另一个容易忽略的问题是WITH语句。

例如:

sql 复制代码
WITH a AS (
    SELECT *
    FROM app_family
)
SELECT *
FROM a;

执行计划通常为:

text 复制代码
Seq Scan on app_family

不会出现并行扫描节点。

因此在需要极致性能的场景下,应关注CTE对执行计划的影响,必要时考虑改写SQL结构。


总结

并行查询本质上是利用多核CPU资源换取更短的SQL执行时间,但并不是所有SQL都能够自动获得并行能力。

从实际案例来看,影响并行执行的因素主要包括:

  • SQL类型是否支持并行
  • Worker数量配置
  • 优化器成本评估参数
  • 自定义函数并行属性
  • CTE等特殊SQL结构

在日常性能优化过程中,建议首先通过执行计划确认SQL是否真正进入并行模式,再结合业务特点调整并行参数,而不是单纯依赖Hint或者提高并行度。只有理解优化器的决策逻辑,才能真正发挥并行查询的性能优势。

相关推荐
鸽芷咕10 小时前
MySQL/PostgreSQL 迁移金仓 KES:LEFT JOIN 丢数据排查与避坑指南
数据库·mysql·postgresql
Csvn19 小时前
📊 SQL 入门 Day 7:子查询 — 查询中的查询
后端·sql
2401_8414956420 小时前
【数据结构】B+树
数据结构·数据库·c++·b+树·概念·结构·操作原理
Hardworking6661 天前
第7章 软硬件系统集成
数据库·软硬件系统集成
名字还没想好☜1 天前
Go 的 time.Ticker 陷阱:定时任务里被忽略的内存泄漏与正确关闭
java·数据库·golang·go·定时器
熊猫钓鱼>_>1 天前
Redis 突发缓存穿透:一次完整的定位复盘
数据库·人工智能·redis·缓存·ai·agent·智能
wenb1n1 天前
MySQL诊断系列(3/6):索引分析——5个SQL揪出“僵尸索引”
数据库·人工智能·编程语言
段一凡-华北理工大学1 天前
向量数据库实战:选型、调优与落地~系列文章03:向量相似度算法全解:余弦、欧氏、内积,到底该用哪个?
大数据·数据库·人工智能·算法·机器学习·向量相似度·高炉炼铁
ClouGence1 天前
CloudDM 数据库管理平台,全新 UI,更清晰、更高效!
数据库·开源
花生了什么事o1 天前
DDD:领域驱动设计的初步认识
java·数据库