达梦分布式集群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.策略分类
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等
达梦数据库应用开发专栏连接