问题15:达梦数据库的并行参数,结合例子分析
并行查询是指数据库将一个大查询任务分解为多个子任务,由多个工作线程同时执行,最后将结果汇总返回。并行查询适用于大表扫描、大表连接、大表分组聚合、索引创建等操作。本问题要求理解达梦数据库的并行参数及其对查询性能的影响。
并行查询的核心思想是“分而治之”:将一个大任务拆分成多个小任务,由多个工作线程并行处理,最后合并结果。并行查询适用于:
不适用场景:
TEST.T_PARALLEL_DEMO(50 万行数据)执行命令:
SELECT PARA_NAME, PARA_VALUE, PARA_TYPE
FROM V$DM_INI
WHERE PARA_NAME LIKE '%PARALLEL%';
实际输出:
行号 PARA_NAME PARA_VALUE PARA_TYPE
----- --------------------------------- ----------- ---------
1 HAGR_PARALLEL_OPT_FLAG 4 SESSION
2 MAX_PARALLEL_DEGREE 1 SESSION
3 PARALLEL_POLICY 0 SESSION
4 PARALLEL_THRD_NUM 10 IN FILE
5 PARALLEL_MODE_COMMON_DEGREE 1 SESSION
6 HFINS_PARALLEL_FLAG 0 SYS
7 RLOG_PARALLEL_ENABLE 1 IN FILE
8 REDOS_PARALLEL_NUM 1 IN FILE
9 PARALLEL_PURGE_FLAG 0 IN FILE
10 INDEX_PARALLEL_FILL 0 SYS
11 PARALLEL_THRD_TARGET 0 SYS
12 COST_PARALLEL_FACTOR 1.000000 SESSION
13 RLOG_PARALLEL_NUM 16 IN FILE
14 REDO_PARALLEL_NUM 1 IN FILE
15 DPRTSK_PARALLEL_NUM 32 SESSION
16 HNSW_SHARD_SEARCH_PARALLEL_DEGREE 1 SESSION
17 PARALLEL_DML 0 SESSION
18 VECTOR_INDEX_BUILD_PARALLEL_DEGREE 4 SESSION
关键参数说明:
| 参数名 | 当前值 | 参数类型 | 说明 |
|---|---|---|---|
MAX_PARALLEL_DEGREE |
1 | SESSION | 最大并行度,限制单条查询可使用的最大并行线程数 |
PARALLEL_POLICY |
0 | SESSION | 并行策略:0=关闭,1=自动并行,2=手动HINT |
PARALLEL_THRD_NUM |
10 | IN FILE | 并行线程池大小,静态参数需重启生效 |
PARALLEL_DML |
0 | SESSION | 是否允许并行DML操作 |
解读:当前 MAX_PARALLEL_DEGREE=1 且 PARALLEL_POLICY=0,表示并行功能处于关闭状态。需要先修改参数才能演示并行效果。
执行命令:
CALL SP_SET_PARA_VALUE(0, 'PARALLEL_POLICY', 1);
CALL SP_SET_PARA_VALUE(0, 'MAX_PARALLEL_DEGREE', 4);
参数说明:
SCOPE=0:仅修改当前会话内存中的值(立即生效)PARALLEL_POLICY=1:启用自动并行模式MAX_PARALLEL_DEGREE=4:设置最大并行度为 4输出:
DMSQL 过程已成功完成
DMSQL 过程已成功完成
验证修改生效:
SELECT PARA_NAME, PARA_VALUE, PARA_TYPE
FROM V$DM_INI
WHERE PARA_NAME IN ('PARALLEL_POLICY', 'MAX_PARALLEL_DEGREE');
实际输出:
行号 PARA_NAME PARA_VALUE PARA_TYPE
----- ------------------- ----------- ---------
1 MAX_PARALLEL_DEGREE 4 SESSION
2 PARALLEL_POLICY 1 SESSION
执行命令:
-- 创建测试表
CREATE TABLE TEST.T_PARALLEL_DEMO (
ID INT,
COL1 VARCHAR(100),
COL2 VARCHAR(100),
COL3 INT,
COL4 DATE
);
-- 插入 50 万行数据
INSERT INTO TEST.T_PARALLEL_DEMO
SELECT LEVEL,
'COL1_' || LEVEL,
'COL2_' || MOD(LEVEL, 1000),
MOD(LEVEL, 100),
DATE '2026-01-01' + MOD(LEVEL, 365)
FROM DUAL CONNECT BY LEVEL <= 500000;
COMMIT;
-- 验证数据量
SELECT COUNT(*) FROM TEST.T_PARALLEL_DEMO;
实际输出:
影响行数 500000
操作已执行
行号 COUNT(*)
----- --------
1 500000
执行命令:
EXPLAIN SELECT COUNT(*) FROM TEST.T_PARALLEL_DEMO;
实际输出:
1 #NSET2: [52, 1, 0]
2 #PRJT2: [52, 1, 0]; exp_num(1), is_atom(FALSE)
3 #FAGR2: [52, 1, 0]; sfun_num(1)
逐层解读:
| 层级 | 操作符 | 说明 |
|---|---|---|
| 3 | #FAGR2 |
快速聚集(Fast Aggregation),直接利用表或索引的元数据统计信息计算行数,无需扫描数据页 |
| 2 | #PRJT2 |
投影操作,选择返回列 |
| 1 | #NSET2 |
结果集,返回最终结果 |
分析:执行计划为 NSET2 → PRJT2 → FAGR2,没有出现 #PARALLEL 节点。原因在于 FAGR2 是快速聚集操作,直接从元数据中读取统计信息,不涉及数据扫描,因此无法并行化,且优化器判断串行执行成本已经很低。
执行命令:
EXPLAIN SELECT /*+ PARALLEL(T_PARALLEL_DEMO, 4) */ COUNT(*) FROM TEST.T_PARALLEL_DEMO;
实际输出:
1 #NSET2: [52, 1, 0]
2 #PRJT2: [52, 1, 0]; exp_num(1), is_atom(FALSE)
3 #FAGR2: [52, 1, 0]; sfun_num(1)
分析:即使添加了 /*+ PARALLEL(...) */ HINT,执行计划仍为 FAGR2。这是因为 FAGR2 操作本身无法并行化——快速聚集是从元数据中读取统计信息,不涉及数据扫描,因此没有并行扫描的必要。这验证了并非所有查询类型都能从并行中获益。
执行命令:
EXPLAIN SELECT /*+ PARALLEL(A, 4) */ COL3, COUNT(*)
FROM TEST.T_PARALLEL_DEMO A
GROUP BY COL3;
实际输出:
1 #NSET2: [72, 5000, 4]
2 #LOCAL COLLECT: [72, 5000, 4]; op_id(2) n_grp_by(0) n_cols(0) n_keys(0)
3 #PRJT2: [72, 5000, 4]; exp_num(2), is_atom(FALSE)
4 #HAGR2: [72, 5000, 4]; grp_num(1), sfun_num(1), distinct_flag[0]; keys(A.COL3)
5 #LOCAL DISTRIBUTE: [52, 500000, 4]; op_id(1) n_keys(0) n_grp(1) KEY(A.COL3)
6 #CSCN2: [52, 500000, 4]; INDEX33555579(T_PARALLEL_DEMO as A); btr_scan(1)
逐层解读:
| 层级 | 操作符 | 说明 |
|---|---|---|
| 6 | #CSCN2 |
聚集索引全扫描,扫描整张表(50 万行) |
| 5 | #LOCAL DISTRIBUTE |
数据分发节点,将数据按 COL3 键值分发到多个并行工作线程 |
| 4 | #HAGR2 |
哈希分组聚合,各线程独立计算分组结果 |
| 3 | #PRJT2 |
投影操作 |
| 2 | #LOCAL COLLECT |
局部结果收集节点,将各线程的聚合结果汇总 |
| 1 | #NSET2 |
结果集 |
关键观察:
#LOCAL DISTRIBUTE 的出现表示查询已启用并行执行,数据被分发到多个工作线程处理#LOCAL COLLECT 表示将各线程的局部结果收集汇总[72, 5000, 4] 表示:代价 72、估算行数 5000、估算字节数 4执行命令:
SELECT NAME, COUNT(*)
FROM V$THREADS
WHERE NAME LIKE '%pthd%'
GROUP BY NAME;
实际输出:
行号 NAME COUNT(*)
----- ------------ --------
1 dm_pthd_thd 16
说明:dm_pthd_thd 是并行工作线程,当前有 16 个可用。这些线程由 PARALLEL_THRD_NUM 参数控制,当查询启用并行时,系统从线程池中分配空闲线程执行子任务。
执行命令:
SELECT PARA_NAME, PARA_VALUE, PARA_TYPE
FROM V$DM_INI
WHERE PARA_NAME = 'PARALLEL_THRD_NUM';
实际输出:
行号 PARA_NAME PARA_VALUE PARA_TYPE
----- ----------------- ----------- ---------
1 PARALLEL_THRD_NUM 10 IN FILE
说明:PARALLEL_THRD_NUM=10 是系统并行线程池大小,属于静态参数(IN FILE),修改后需重启实例生效。当前可用的 16 个 dm_pthd_thd 线程可能受其他参数影响。
| 查询类型 | 操作符链 | 是否并行 | 原因 |
|---|---|---|---|
SELECT COUNT(*) |
FAGR2 → PRJT2 → NSET2 |
否 | 快速聚集从元数据读取,不扫描数据页 |
SELECT COUNT(*) WITH HINT |
FAGR2 → PRJT2 → NSET2 |
否 | 同上,该操作本身无法并行化 |
SELECT COL3, COUNT(*) GROUP BY COL3 |
CSCN2 → LOCAL DISTRIBUTE → HAGR2 → LOCAL COLLECT → PRJT2 → NSET2 |
是 | 大数据量分组聚合,#LOCAL DISTRIBUTE 出现 |
| 参数名 | 当前值 | 参数类型 | 作用 |
|---|---|---|---|
MAX_PARALLEL_DEGREE |
4 | SESSION | 最大并行度,控制单条查询的最大并行线程数 |
PARALLEL_POLICY |
1 | SESSION | 并行策略:0=关闭,1=自动,2=手动HINT |
PARALLEL_THRD_NUM |
10 | IN FILE | 并行线程池总大小,静态参数需重启生效 |
PARALLEL_DML |
0 | SESSION | 是否允许并行DML操作 |
PARALLEL_MODE_COMMON_DEGREE |
1 | SESSION | 并行模式通用度 |
PARALLEL_THRD_TARGET |
0 | SYS | 并行线程目标值 |
COST_PARALLEL_FACTOR |
1.000000 | SESSION | 并行代价因子,影响优化器决策 |
主要并行参数:
| 参数 | 作用 | 调大影响 | 调小影响 |
|---|---|---|---|
MAX_PARALLEL_DEGREE |
控制单查询最大并行线程数 | 提升大查询性能,但占用更多资源 | 节省资源,大查询性能下降 |
PARALLEL_POLICY |
并行策略开关 | 1=自动并行,2=手动HINT | 0=完全关闭并行 |
PARALLEL_THRD_NUM |
并行线程池总大小 | 支持更高并发并行 | 并行资源受限 |
PARALLEL_DML |
是否允许并行DML | 1=允许并行写入 | 0=禁用并行DML |
参数调整影响:
MAX_PARALLEL_DEGREE 增大:可提升大表扫描、分组聚合等查询性能,但会占用更多 CPU 和内存资源MAX_PARALLEL_DEGREE 减小:节省资源,适合高并发 OLTP 场景PARALLEL_POLICY=0:完全禁用并行,适合并发高、资源紧张的场景PARALLEL_POLICY=1:自动并行,适合混合负载场景,优化器根据代价自动决策PARALLEL_POLICY=2:手动并行,适合需要精确控制并行度的场景实操结论:
并非所有查询都适合并行:COUNT(*) 使用快速聚集(FAGR2),直接从元数据读取统计信息,无需扫描数据页,即使添加 HINT 也不会触发并行。
分组聚合查询可触发并行:GROUP BY 查询需要扫描大量数据,优化器选择启用并行,执行计划中出现 #LOCAL DISTRIBUTE 节点。
并行 HINT 语法:达梦支持 /*+ PARALLEL(table_alias, degree) */ 格式的 HINT,但最终是否启用并行由优化器根据代价决定。
并行线程池:系统通过 PARALLEL_THRD_NUM 控制并行线程池大小,当前环境有 16 个 dm_pthd_thd 并行工作线程。
-- 1. 查询所有并行参数
SELECT PARA_NAME, PARA_VALUE, PARA_TYPE
FROM V$DM_INI
WHERE PARA_NAME LIKE '%PARALLEL%';
-- 2. 修改并行参数(会话级)
CALL SP_SET_PARA_VALUE(0, 'PARALLEL_POLICY', 1);
CALL SP_SET_PARA_VALUE(0, 'MAX_PARALLEL_DEGREE', 4);
-- 3. 创建测试表并插入数据
CREATE TABLE TEST.T_PARALLEL_DEMO (ID INT, COL1 VARCHAR(100), COL2 VARCHAR(100), COL3 INT, COL4 DATE);
INSERT INTO TEST.T_PARALLEL_DEMO
SELECT LEVEL, 'COL1_'||LEVEL, 'COL2_'||MOD(LEVEL,1000), MOD(LEVEL,100), DATE'2026-01-01'+MOD(LEVEL,365)
FROM DUAL CONNECT BY LEVEL <= 500000;
COMMIT;
-- 4. 查看数据量
SELECT COUNT(*) FROM TEST.T_PARALLEL_DEMO;
-- 5. 简单 COUNT 查询执行计划
EXPLAIN SELECT COUNT(*) FROM TEST.T_PARALLEL_DEMO;
-- 6. 带 HINT 的 COUNT 查询执行计划
EXPLAIN SELECT /*+ PARALLEL(T_PARALLEL_DEMO, 4) */ COUNT(*) FROM TEST.T_PARALLEL_DEMO;
-- 7. 分组聚合查询执行计划(触发并行)
EXPLAIN SELECT /*+ PARALLEL(A, 4) */ COL3, COUNT(*)
FROM TEST.T_PARALLEL_DEMO A
GROUP BY COL3;
-- 8. 查看并行线程
SELECT NAME, COUNT(*) FROM V$THREADS WHERE NAME LIKE '%pthd%' GROUP BY NAME;
-- 9. 查看并行线程池大小
SELECT PARA_NAME, PARA_VALUE, PARA_TYPE FROM V$DM_INI WHERE PARA_NAME = 'PARALLEL_THRD_NUM';
-- 10. 恢复原始设置(可选)
CALL SP_SET_PARA_VALUE(0, 'PARALLEL_POLICY', 0);
CALL SP_SET_PARA_VALUE(0, 'MAX_PARALLEL_DEGREE', 1);
文章
阅读量
获赞
