当前位置: 首页 > java >正文

达梦分布式集群DPC_DPC的执行计划新增操作符详解_yxy

达梦分布式集群DPC_DPC的执行计划新增操作符详解

  • 1 分布式执行计划是什么?
  • 2 数据交换操作符:ESEND 和 ERECV
    • 2.1 发送操作符 ESEND详解
      • 2.1.1 BY HASH
      • 2.1.2 BY RANGE
      • 2.1.3 BY LIST
      • 2.1.4 BY N_DEST
      • 2.1.5 DIRECT
      • 2.1.6 BROADCAST
    • 2.2 接收操作符ERECV
  • 3 数据迭代操作符 GI
    • 3.1 RANDOM
    • 3.2 PART_UNIT
    • 3.3 LV2_PART_UNIT
    • 3.4 USE_SQC_NO(LV2_SQC_NO)
    • 总结

1 分布式执行计划是什么?

分布式数据在不同机器,肯定就会出现数据在不同节点之间的流动的问题

例如
分区表TEST01,有三个子表(1、2、3),分别存储在机器1、2、3上,那么执行select *from TEST01;
就会从机器1、2、3上分别取数据再汇总后返回结果集;

分布式执行计划就是控制 不同服务器之间如何传递数据 和 怎么从机器上取数据(控制工作线程获取表数据时的粒度)的指令

想象一下,你有一个巨大的任务(比如处理TB级的用户订单数据)。为了加快速度,你把这个任务拆分成很多小任务,分给多个工人(线程/工作节点)同时做。这些工人可能在不同的机器上(不同实例),也可能在同一台机器的不同CPU核心上(不同线程)。分布式执行计划就是用来高效协调这些工人之间如何传递数据(ESEND/ERECV)以及工人如何从仓库(数据库表)里拿数据(GI)的指令

例如

1.单机上创建普通表,并进行查询
create table DPC_TABLE(c int);
EXPLAIN select *from DPC_TABLE;
1   #NSET2: [1, 1, 16] 
2     #PRJT2: [1, 1, 16]; exp_num(2), is_atom(FALSE) 
3       #CSCN2: [1, 1, 16]; INDEX33582147(DPC_TABLE)2.DPC创建一个分区表,并进行查询
create table DPC_T1(c int) PARTITION BY HASH(C) PARTITIONS 20;
explain select *from DPC_T1;
1   #NSET2: [1, 1, 16] 
2     #ERECV: [1, 1, 16]; stask_no(-1), l_stask_no(0), n_key(0), in_turn(0), trig(0)
3       #ESEND: [1, 1, 16]; stask_no(0), type(DIRECT), sites(1:1,2:1,3:1,4:1,5:1,10:1,13:1,11:1,9:1,12:1), sql_invoke(0), pwj_opt(0), table(-); INFO_BITS(0xc)
4         #GI: [1, 1, 16]; policy(PART_UNIT), gi_unit[0..0], scan_type[0](FULL)
5           #PRJT2: [1, 1, 16]; exp_num(2), is_atom(FALSE) 
6             #CSCN2: [1, 1, 16]; INDEX47488784(DPC_T1); btr_scan(1)可以看到单机和分布式的区别就是多了三行分布式操作符,ESEND、ERECV、GI
ESEND操作符把主计划分成了10个子任务,sites(1:1,2:1 .....表示该子计划会在 RAFT_ID 为 1、2 .....的 站点执行,每个站点上并行度为 1,每个子任务只扫描自己所在节点的数据
ERECV负责接受所有节点上查询的数据
GI负责按PART_UNIT策略来访问数据, PART_UNIT表示访问粒度固定为一级分区子表。每个工作线程一次处理一个完整的一级分区子表;

2 数据交换操作符:ESEND 和 ERECV

发送操作符 ESEND 和 接收操作符 ERECV
为了在不同的实例之间或同一实例不同的线程之间进行数据交换

2.1 发送操作符 ESEND详解

ESEND:
ESEND 将孩子操作符的数据按照某种分发规则发送给特定 ERECV 操作符。ESEND 操作符发送的内容为当前子计划的执行结果,有时也会包含一些附加信息。

例如上述的
#ESEND: [1, 1, 16]; stask_no(0), type(DIRECT), sites(1:1,2:1,3:1,4:1,5:1,10:1,13:1,11:1,9:1,12:1), sql_invoke(0), pwj_opt(0), table(-); INFO_BITS(0xc)
扫描10个节点的数据,并将所有节点的数据按DIRECT方式发送给ERECV

ESEND 共有 6 种数据分发方式:
BY HASH、BY RANGE、BY LIST、BY N_DEST、DIRECT 和 BROADCAST

2.1.1 BY HASH

按哈希值分发
按照发送列的 HASH 取值和并行度,决定发送给 ERECV 操作符中的第几个 worker 线程。

例如:
ERECV 所在并行度为 4,发送 key 为 UserID, HASH(UserID)% 4 = ?,等于多少,就将发送给 ERECV 操作中第 几个 worker 线程。

ESEND 按 UserID 进行 BY HASH 分发到 4 个线程。订单 A 的 UserID=100, HASH(100) % 4 = 2 ➔ 发给 ERECV3 (假设线程编号从0开始:0,1,2,3)
订单 B 的 UserID=205, HASH(205) % 4 = 1 ➔ 发给 ERECV2
订单 C 的 UserID=308, HASH(308) % 4 = 0 ➔ 发给 ERECV1

按规则算出来是多少就发送给第几个工作线程

生产示例

select * from "表A" a inner join "表B" b on a.FYID=B.JZLSH;
1   #NSET2: [8933, 63578794, 7326] 
2     #ERECV: [8933, 63578794, 7326]; stask_no(-1), l_stask_no(0), n_key(0), in_turn(0), trig(0)
3       #ESEND: [8933, 63578794, 7326]; stask_no(0), type(DIRECT), sites(2:2,3:2,4:2,5:2,12:2,10:2,13:2,11:2,9:2,1:2), sql_invoke(0), pwj_opt(0), table(-); INFO_BITS(0xc)
4         #PRJT2: [8933, 63578794, 7326]; exp_num(181), is_atom(FALSE) 
5           #HASH2 INNER JOIN: [8933, 63578794, 7326];  KEY_NUM(1); KEY(A.FYID=B.JZLSH) KEY_NULL_EQU(0)
6             #GI: [2522, 3796081, 2090]; policy(USE_SQC_NO), gi_unit[0..0], scan_type[0](FULL)
7               #CSCN2: [2522, 3796081, 2090]; INDEX44188661(表A as A); btr_scan(1)
8             #ERECV: [2790, 1676530, 5236]; stask_no(0), l_stask_no(1), n_key(0), in_turn(0), trig(0)
9               #ESEND: [2790, 1676530, 5236]; stask_no(1), type(HASH), sites(2:2,3:2,4:2,5:2,12:2,10:2,13:2,11:2,9:2,1:2), sql_invoke(0), pwj_opt(1), table(表A) empty_type(PRUNE) ,keys(B.JZLSH) ; INFO_BITS(0xc)
10                #GI: [2790, 1676530, 5236]; policy(PART_UNIT), gi_unit[0..0], scan_type[0](FULL)
11                  #CSCN2: [2790, 1676530, 5236]; INDEX44188997(表B as B); btr_scan(1)

目的
1.均匀分布数据。希望不同用户的数据尽可能平均地分散到所有线程上,避免某个线程过忙或过闲。常用于 GROUP BY、JOIN等需要按某个键聚合或关联的操作。
2.小表可以按大表的数据分布方式,同样进行计算,并按规则将数据分发到大表上,减少网络传输

2.1.2 BY RANGE

按照发送列的范围判断发送给 ERECV 操作符的第几个 worker 线程,每个 worker 线程事先都约定了各自的接收数据范围。

例如:

ESEND 按 OrderID 进行 BY RANGE 分发到 4 个线程,约定:ERECV1: OrderID 在 [1, 1000]
ERECV2: OrderID 在 [1001, 2000]
ERECV3: OrderID 在 [2001, 3000]
ERECV4: OrderID > 3000订单 D 的 OrderID=1500 ➔ 发给 ERECV2
订单 E 的 OrderID=3500 ➔ 发给 ERECV4

目的
范围查询或排序优化。如果后续操作是按 OrderID排序或查询某个范围的订单,这种方式可以让每个线程处理一段连续的数据,便于局部排序或快速定位。也常用于范围分区表的数据分发。

2.1.3 BY LIST

按照发送列的值判断发送给 ERECV 操作符的第几个 worker 线程,每个 worker 线程事先都约定了各自的接收数据值。

例如

ESEND 按 Region 进行 BY LIST 分发到 4 个线程,约定:ERECV1: Region IN ('North', 'Northeast')
ERECV2: Region IN ('East', 'Southeast')
ERECV3: Region IN ('South', 'Southwest')
ERECV4: Region IN ('West', 'Northwest')订单 F 的 Region='East' ➔ 发给 ERECV2
订单 G 的 Region='West' ➔ 发给 ERECV4

目的
离散值分组。当数据天然可以按照某个分类(如地区、产品类型、状态码)清晰地划分时,这种方式非常直观高效。常用于按类别聚合或处理。

2.1.4 BY N_DEST

类似于 BY HASH,都是按照 HASH(key)%N 决定发送给哪一个接收者线程。

区别在于
1.BY HASH 中的 N 取值为 HASH 水平分区子表个数,通常用在分区智能连接( Partition Wise Join ,简称为 PWJ)的一侧,另一侧是 HASH 分区表。
2.BY N_DEST 方式下,连接的两侧按照约定的目标线程个数进行数据划分,N 的取值由并行度决定,一般是成对出现在连接的两侧孩子中。

例如

有两个大表:
Orders 表按 UserID HASH 分区成 6 个子表 (P1-P6)。
Users 表按 UserID HASH 分区成 8 个子表 (U1-U8)。
执行SELECT ... FROM Orders JOIN Users ON Orders.UserID = Users.UserID ;BY N_DEST:
在 Orders、Users表的扫描计划分支上,ESEND 使用BY N_DEST,N的取值由并行度决定结果:
UserID=100 的订单记录和UserID=100 的用户记录,都是HASH(UserID) % N = X,都会发送给负责连接的第 X 个线程。 关键点: 
第X个线程只需要对自己收到的Orders 数据和Users数据直接进行连接即可!
因为它收到的Orders和Users数据的UserID的哈希值对N取模都是X。这极大减少了跨线程传输(无需跨线程发送大量连接数据)和连接计算量。

真实例子

select /*+ PARALLEL(16) */* from "表A" a
inner join "表B" b
on a.BATCH_NO=B.BATCH_NO;
1   #NSET2: [8933, 63578794, 7326] 
2     #ERECV: [8933, 63578794, 7326]; stask_no(-1), l_stask_no(1), n_key(0), in_turn(0), trig(0)
3       #ESEND: [8933, 63578794, 7326]; stask_no(1), type(DIRECT), sites(6:16,7:16,15:16), sql_invoke(0), pwj_opt(0), table(-); INFO_BITS(0xc)
4         #PRJT2: [8933, 63578794, 7326]; exp_num(181), is_atom(FALSE) 
5           #HASH2 INNER JOIN: [8933, 63578794, 7326];  KEY_NUM(1); KEY(A.BATCH_NO=B.BATCH_NO) KEY_NULL_EQU(0)
6             #ERECV: [2522, 3796081, 2090]; stask_no(1), l_stask_no(0), n_key(0), in_turn(0), trig(0)
7               #ESEND: [2522, 3796081, 2090]; stask_no(0), type(N_DEST), sites(2:2,3:2,4:2,5:2,12:2,10:2,13:2,11:2,9:2,1:2), sql_invoke(0), pwj_opt(0), table(-) ,keys(A.BATCH_NO) ; INFO_BITS(0xc)
8                 #GI: [2522, 3796081, 2090]; policy(PART_UNIT), gi_unit[0..0], scan_type[0](FULL)
9                   #CSCN2: [2522, 3796081, 2090]; INDEX44188661(表A as A); btr_scan(1)
10            #ERECV: [2790, 1676530, 5236]; stask_no(1), l_stask_no(2), n_key(0), in_turn(0), trig(0)
11              #ESEND: [2790, 1676530, 5236]; stask_no(2), type(N_DEST), sites(2:2,3:2,4:2,5:2,12:2,10:2,13:2,11:2,9:2,1:2), sql_invoke(0), pwj_opt(0), table(-) ,keys(B.BATCH_NO) ; INFO_BITS(0x8)
12                #GI: [2790, 1676530, 5236]; policy(PART_UNIT), gi_unit[0..0], scan_type[0](FULL)
13                  #CSCN2: [2790, 1676530, 5236]; INDEX44188997(表B as B); btr_scan(1)N_DEST的N,由并发数PARALLEL(16)决定

与 BY HASH 区别:
BY HASH 的 N 是物理分区数,常用于数据初始分布或一般聚合。
BY N_DEST 的 N 是逻辑目标数,并发数

目的
相同数据在同一个线程做操作,减少数据传输(无需跨线程发送大量连接数据)和连接计算量。

2.1.5 DIRECT

直接发送给某个接收线程。如果接收线程数为 1,那么所有的数据全部发给了同一个线程;如果接收线程个数大于 1,则 ESEND 逐一发送给每个接收线程。

例如

单线程:所有订单数据都发给唯一的一个 ERECV 线程处理。多线程 (4):订单A ➔ 线程1, 订单B ➔ 线程2, 订单C ➔ 线程3, 订单D ➔ 线程4, 订单E ➔ 线程1, ...

目的
简单轮询分发。当数据分发规则不重要,或者只需要简单地把数据平均分配到各个线程进行处理时使用。它不关心数据内容,只是简单的轮流发送。

2.1.6 BROADCAST

每一批数据都向所有接收线程发送。

例如

有一个很小的表A。ESEND对它使用BROADCAST到 4 个线程。那么A表的全部数据会被复制 4 份,分别发送给 ERECV1, ERECV2, ERECV3, ERECV4。

成产示例

SELECT /*+ HASH_PLL_OPT_FLAG(74)*/* FROM 测试表
where gxysfjlid in ( SELECT gxysfjlidFROM ( SELECT *FROM 测试表where gxyglkid = '123'ORDER BY sjscsj DESC )WHERE ROWNUM = 1 )1   #NSET2: [58331, 3, 1192] 
2     #ERECV: [58331, 3, 1192]; stask_no(-1), l_stask_no(2), n_key(0), in_turn(0), trig(0)
3       #ESEND: [58331, 3, 1192]; stask_no(2), type(DIRECT), sites(2:2,3:2,4:2,5:2,12:2,10:2,13:2,11:2,9:2,1:2), sql_invoke(0), pwj_opt(0), table(-); INFO_BITS(0x8)
4         #PRJT2: [58331, 3, 1192]; exp_num(30), is_atom(FALSE) 
5           #HASH RIGHT SEMI JOIN2: [58331, 3, 1192]; n_keys(1) KEY(DMTEMPVIEW_894574253.colname=测试表.GXYSFJLID) KEY_NULL_EQU(0); INFO_BITS(0x1)
6             #ERECV: [6868, 1, 1180]; stask_no(2), l_stask_no(1), n_key(0), in_turn(0), trig(0)
7               #ESEND: [6868, 1, 1180]; stask_no(1), type(BROADCAST), sites(6:1), sql_invoke(0), pwj_opt(0), table(-); INFO_BITS(0x11)
8                 #PRJT2: [6868, 1, 1180]; exp_num(1), is_atom(FALSE) 
9                   #PRJT2: [6868, 1, 1180]; exp_num(1), is_atom(FALSE) 
10                    #ERECV: [6868, 1, 1180]; stask_no(1), l_stask_no(0), n_key(1), topN, in_turn(0), trig(0)
11                      #ESEND: [6868, 1, 1180]; stask_no(0), type(DIRECT), sites(2:2,3:2,4:2,5:2,12:2,10:2,13:2,11:2,9:2,1:2), sql_invoke(0), pwj_opt(0), table(-); INFO_BITS(0x34)
12                        #SORT3: [6868, 1, 1180]; key_num(1), partition_key_num(0), is_distinct(FALSE), top_flag(1), is_adaptive(0)
13                          #GI: [6050, 2178674, 1180]; policy(PART_UNIT), gi_unit[0..0], scan_type[0](FULL)
14                            #BLKUP2: [6050, 2178674, 1180]; YXY0731_02(测试表)
15                              #SSEK2: [6050, 2178674, 1180]; scan_type(ASC), YXY0731_02(测试表), scan_range[('123',min),('123',max)), is_global(0)
16            #GI: [33022, 87146967, 1192]; policy(PART_UNIT), gi_unit[0..0], scan_type[0](FULL)
17              #CSCN2: [33022, 87146967, 1192]; INDEX41730785(测试表); btr_scan(1)11行esend先将全部节点数据直接发送到6站点
7行esend再将处理后的数据广播到大表上进行右半连接

目的:
主要用于处理“大表 JOIN小表”的场景。
把小表广播到所有大表的线程上,这样每个线程都可以在本地完成与大表数据的连接操作,避免大表数据在节点间移动。代价是网络带宽消耗(广播数据量* 线程数)

2.2 接收操作符ERECV

ERECV
ERECV 操作符用于接收某个 ESEND 操作符的输出结果,并将接收的结果向上层操作符传递。

位置: 在查询执行计划树中,ERECV 通常是并行子计划树的根节点(接收子计划的结果),或者是需要接收远程/其他线程数据的操作符的父节点。

配对: 一个 ERECV 接收来自一个或多个 ESEND 的数据。

3 数据迭代操作符 GI

GI(Granule Iterator)操作符用于控制工作线程如何获取表中的数据,访问的粒度可以是单个子表,也可以是子表中的部分页。当数据来源是分区表且存在分区列条件过滤时,GI 会剔除不满足条件的分区,即进行分区裁剪优化。

核心作用:
控制并行工作线程访问表数据的粒度(一次拿多少)和 范围(拿哪部分)。

关键优化:分区裁剪 (Partition Pruning)
如果表是按日期分区的分区表,并且查询条件包含了分区列,GI 会非常智能地跳过那些完全不可能包含满足条件数据的分区(子表)。大大减少了 I/O 和计算量。

GI 的数据访问粒度分为 RANDOM、PART_UNIT(LV2_PART_UNIT)和 USE_SQC_NO(LV2_SQC_NO)。

3.1 RANDOM

RANDOM 表示粒度可以任意划分,既能以子表为单位也可以是子表中部分页为单位;

3.2 PART_UNIT

以一级分区子表为单位扫描,访问粒度固定为 一级分区子表。每个工作线程一次处理 一个完整的一级分区子表。

例如

select * from "测试表" a where a.时间字段(一级分区列)= '2025-06';
1   #NSET2: [125096, 3, 1641] 
2     #ERECV: [125096, 3, 1641]; stask_no(-1), l_stask_no(0), n_key(0), in_turn(0), trig(0)
3       #ESEND: [125096, 3, 1641]; stask_no(0), type(DIRECT), sites(2:2,3:2,4:2,5:2,12:2,10:2,13:2,11:2,9:2,1:2), sql_invoke(0), pwj_opt(0), table(-); INFO_BITS(0xc)
4         #GI: [125096, 3, 1641]; policy(PART_UNIT), gi_unit[0..0], scan_type[0](EQU,FULL), dynamic_pll[1]
5           #PRJT2: [125096, 3, 1641]; exp_num(39), is_atom(FALSE) 
6             #BLKUP2: [125096, 3, 1641]; INDEX44331476(A)
7               #SLCT2: [125096, 3, 1641]; A.时间字段(一级分区列) = var1
8                 #SSCN: [125096, 3, 1641]; INDEX44331476(测试表 as A); btr_scan(1); is_global(0)GI访问策略为PART_UNIT,按一级分区表为单位进行扫描

3.3 LV2_PART_UNIT

新版本扫描二级分区策略修改为了LVL2_UNIT
专门用于二级分区表。访问粒度固定为所有一级分区下相同序号的二级叶子子表。一个工作线程会扫描 所有一级分区中位置相同的那个二级子表。

例如

select 二级分区列 from "测试表"  group by  二级分区列;
1   #NSET2: [44234, 115324002, 48] 
2     #ERECV: [44234, 115324002, 48]; stask_no(-1), l_stask_no(0), n_key(0), in_turn(0), trig(0)
3       #ESEND: [44234, 115324002, 48]; stask_no(0), type(DIRECT), sites(2:2,3:2,4:2,5:2,12:2,10:2,13:2,11:2,9:2,1:2), sql_invoke(0), pwj_opt(0), table(-); INFO_BITS(0xc)
4         #PRJT2: [44234, 115324002, 48]; exp_num(1), is_atom(FALSE) 
5           #HAGR2: [44234, 115324002, 48]; grp_num(1), sfun_num(0); slave_empty(0) keys(测试表.二级分区列) 
6             #GI: [27514, 239804690, 48]; policy(LVL2_UNIT), gi_unit[0..0], scan_type[0](FULL)
7               #SSCN: [27514, 239804690, 48]; INDEX44331476(测试表); btr_scan(1); is_global(0)select * from "测试表1" a inner join "测试表2" b on a.测试表1二级分区列=B.测试表2二级分区列;
1   #NSET2: [564060, 1171990765, 2730] 
2     #ERECV: [564060, 1171990765, 2730]; stask_no(-1), l_stask_no(0), n_key(0), in_turn(0), trig(0)
3       #ESEND: [564060, 1171990765, 2730]; stask_no(0), type(DIRECT), sites(2:2,3:2,4:2,5:2,12:2,10:2,13:2,11:2,9:2,1:2), sql_invoke(0), pwj_opt(0), table(-); INFO_BITS(0xc)
4         #GI: [564060, 1171990765, 2730]; policy(LVL2_UNIT), gi_unit[0..1], scan_type[0](FULL), dynamic_pll[1]
5           #PRJT2: [564060, 1171990765, 2730]; exp_num(65), is_atom(FALSE) 
6             #HASH2 INNER JOIN: [564060, 1171990765, 2730];  KEY_NUM(1); KEY(A.测试表1二级分区列=B.测试表2二级分区列) KEY_NULL_EQU(0)
7               #GI: [126204, 243710817, 1629]; policy(LVL2_UNIT), gi_unit[0..0], scan_type[0](FULL)
8                 #CSCN2: [126204, 243710817, 1629]; INDEX44331475(测试表1 as A); btr_scan(1)
9               #GI: [196064, 560188120, 1101]; policy(LVL2_UNIT), gi_unit[1..1], scan_type[0](FULL)
10                #CSCN2: [196064, 560188120, 1101]; INDEX44342145(测试表2 as B); btr_scan(1)GI访问策略都为LVL2_UNIT,按二级分区表为单位进行扫描

3.4 USE_SQC_NO(LV2_SQC_NO)

这两种策略的访问粒度 与 PART_UNIT / LV2_PART_UNIT 完全相同 (即分别按一级子表或二级叶子子表组)

select * from "测试表1"   a inner join "测试表2" b on a.测试表1一级分区列=B.测试表2普通列;
1   #NSET2: [38270, 5555077291, 2586] 
2     #ERECV: [38270, 5555077291, 2586]; stask_no(-1), l_stask_no(0), n_key(0), in_turn(0), trig(0)
3       #ESEND: [38270, 5555077291, 2586]; stask_no(0), type(DIRECT), sites(2:2,3:2,4:2,5:2,12:2,10:2,13:2,11:2,9:2,1:2), sql_invoke(0), pwj_opt(0), table(-); INFO_BITS(0xc)
4         #PRJT2: [38270, 5555077291, 2586]; exp_num(62), is_atom(FALSE) 
5           #HASH2 INNER JOIN: [38270, 5555077291, 2586];  KEY_NUM(1); KEY(A.测试表1一级分区列=B.测试表2普通列) KEY_NULL_EQU(0)
6             #GI: [5176, 10621396, 1533]; policy(USE_SQC_NO), gi_unit[0..0], scan_type[0](FULL)
7               #CSCN2: [5176, 10621396, 1533]; INDEX44189228(测试表1 as A); btr_scan(1)
8             #ERECV: [17524, 52353174, 1053]; stask_no(0), l_stask_no(1), n_key(0), in_turn(0), trig(0)
9               #ESEND: [17524, 52353174, 1053]; stask_no(1), type(HASH), sites(2:2,3:2,4:2,5:2,12:2,10:2,13:2,11:2,9:2,1:2), sql_invoke(0), pwj_opt(1), table(测试表1) empty_type(PRUNE) ,keys(B.测试表2普通列) ; INFO_BITS(0xc)
10                #GI: [17524, 52353174, 1053]; policy(PART_UNIT), gi_unit[0..0], scan_type[0](FULL)
11                  #CSCN2: [17524, 52353174, 1053]; INDEX44189207(测试表2 as B); btr_scan(1)6行“ #GI: [5176, 10621396, 1533]; policy(USE_SQC_NO)”
测试表1表为固定线程扫描单个一级子表
流程为先进行2表数据收集,按测试表1的数据分布方式,将2表计算后的对应数据发送到1的每个节点固定线程上select * from "测试表1"   a inner join "测试表2" b on a.测试表1二级分区列=B.测试表2普通列;
1   #NSET2: [564060, 1171990765, 2730] 
2     #ERECV: [564060, 1171990765, 2730]; stask_no(-1), l_stask_no(0), n_key(0), in_turn(0), trig(0)
3       #ESEND: [564060, 1171990765, 2730]; stask_no(0), type(DIRECT), sites(2:2,3:2,4:2,5:2,12:2,10:2,13:2,11:2,9:2,1:2), sql_invoke(0), pwj_opt(0), table(-); INFO_BITS(0xc)
4         #PRJT2: [564060, 1171990765, 2730]; exp_num(65), is_atom(FALSE) 
5           #HASH2 INNER JOIN: [564060, 1171990765, 2730];  KEY_NUM(1); KEY(A.测试表1二级分区列=B.测试表2普通列) KEY_NULL_EQU(0)
6             #GI: [126204, 243710817, 1629]; policy(LVL2_SQC_NO), gi_unit[0..0], scan_type[0](FULL)
7               #CSCN2: [126204, 243710817, 1629]; INDEX44331475(测试表1 as A); btr_scan(1)
8             #ERECV: [196064, 560188120, 1101]; stask_no(0), l_stask_no(1), n_key(0), in_turn(0), trig(0)
9               #ESEND: [196064, 560188120, 1101]; stask_no(1), type(HASH), sites(2:2,3:2,4:2,5:2,12:2,10:2,13:2,11:2,9:2,1:2), sql_invoke(0), pwj_opt(1), table(测试表1_P201001) empty_type(PRUNE) ,keys(B.测试表2普通列) ; INFO_BITS(0xc)
10                #GI: [196064, 560188120, 1101]; policy(PART_UNIT), gi_unit[0..0], scan_type[0](FULL,FULL)
11                  #CSCN2: [196064, 560188120, 1101]; INDEX44342145(测试表2 as B); btr_scan(1)6行“#GI: [126204, 243710817, 1629]; policy(LVL2_SQC_NO)”
测试表1表为固定线程扫描所有一级分区下同序号二级子表
流程为进行2表数据收集,按测试表1的数据分布方式,将2表计算后的对应数据发送到1的每个节点固定线程上

总结

1.策略分类

GI粒度策略
RANDOM
一级分区策略
二级分区策略
PART_UNIT
USE_SQC_NO
LV2_PART_UNIT
LV2_SQC_NO

2.策略对比

策略适用场景访问粒度线程分配典型用途
RANDOM任意表最灵活(子表/数据页)动态分配通用扫描
PART_UNIT一级分区表单个一级子表动态分配普通分区查询
USE_SQC_NO一级分区表单个一级子表固定绑定PWJ优化
LV2_PART_UNIT二级分区表所有一级分区下同序号二级子表动态分配二级分区列操作
LV2_SQC_NO二级分区表所有一级分区下同序号二级子表固定绑定PWJ优化

更多其他数据库相关专栏:

1.数据库优化
数据库优化基本思路、索引详解、执行计划、统计信息、CBO原理、单表优化、多表优化、分布式优化、子查询、优化案例等
数据库优化(sql优化)专栏连接

2.达梦分布式数据库:
部署详细步骤(DEM)、备份还原实战、核心特性理解、使用心得、表分区方式详细介绍、表分区最佳实践、DPC架构详解等
达梦分布式DPC专栏连接

3.应用开发类
jdbc、hibernate、ibatis、mybatis、MyBatis-Plus、Spring、中间件mycat、Sharding-JDBC等
达梦数据库应用开发专栏连接

http://www.xdnf.cn/news/14234.html

相关文章:

  • 区间合并:牛奶
  • 【慢摹】TRL训练器细节详解(SFT/PPO/DPO/GRPO)
  • 自用方案总结
  • 如何在 Elementary OS 上安装 Snap Store
  • Thymeleaf功能标签th:block
  • java面试总结-20250605
  • 5G核心网Non-IP数据报文转发机制:Unstructured会话与协议栈解析
  • 抖音 pc + 翻页
  • C#最佳实践:推荐使用泛型而非装箱、拆箱
  • 60、数据访问-数据库场景的自动配置分析与整合测试
  • c++26新功能—契约编程
  • 单测时如何让 mock 的接口在长链路调用时一直生效
  • 从STM32到NXP:GPIO就像装修房子,多了个“智能开关”
  • 基于 SpringBoot+Servlet+JSP 的医院医保管理系统的设计与实现,论文7000字,可根据实际情况调整
  • ES+索引库文档操作
  • [CVPR 2025] DiCo:动态协作网络助力半监督3D血管分割新突破
  • AI Agent实战 - LangChain+Playwright构建火车票查询Agent
  • 人工智能学习28-BP过拟合
  • [k8s]--exec探针详细解析
  • java常见第三方依赖以及相关安全问题
  • http1.x VS http2.x 协议
  • Spring Cloud Alibaba 中间件
  • 硬编码(修改RIP相关指令)
  • HTML+CSS 半透明登录框
  • (LeetCode每日一题) 2566. 替换一个数字后的最大差值 ( 贪心 )
  • 安防市场的中小企业突围——从竞品分析到破局路径的思考
  • Spring Boot中Controller层规划与最佳实践详解
  • 【北京迅为】iTOP-4412精英版使用手册-第二十一章 延时函数专题
  • Python爬虫-批量爬取快手视频并将视频下载保存到本地
  • BeckHoff PLC --> 料筐(KLT Box)自动对中与抓取程序分析