openGauss源码解析(126)
openGauss源码解析:执行器解析(19)
7.3.4 连接算子
连接算子用于处理表关联,openGauss支持12种连接类型(inner join、left join、right join、full join、semi join、anti join等),提供了3种连接算子:hash join、merge join、nested loop join算子;下面分别介绍这3种算子。
1. hash join算子
hash join算子用于hash连接处理,对应的代码源文件是“nodeHashJoin.cpp”。hash连接是做大数据集连接时的常用方式,优化器使用两个表中较小的表(或数据源)利用连接键在内存中建立哈希表,然后扫描较大的表并探测哈希表,找出与哈希表匹配的行。这种方式适用于较小的表完全可以放于内存中的情况,这样总成本就是访问两个表的成本之和。但是在表很大的情况下并不能完全放入内存,执行器会将它分割成若干不同的分区,不能放入内存的部分就把该分区写入磁盘的临时段,此时要有较大的临时段从而尽量提高I/O的性能。算子对应的主要函数如表7-29所示。
表7-29 hash join算子主要函数
主要函数 | 说明 |
ExecInitHashJoin | 初始化hash join状态节点 |
ExecHashJoin | 利用哈希表迭代获取元组 |
ExecEndHashJoin | 清理hash join状态节点 |
ExecResScanHashJoin | 重置hash join状态节点 |
hash join算子对应的状态节点代码如下:
typedef struct HashJoinState {
JoinState js; /* Join节点 */
List* hashclauses; /* hash计算表达式 */
List* hj_OuterHashKeys; /* hash外表键表达式 */
List* hj_InnerHashKeys; /* hash内表键表达式 */
List* hj_HashOperators; /* hash计算运算符 */
HashJoinTable hj_HashTable; /* 哈希表 */
uint32 hj_CurHashValue; /* 当前哈希值 */
int hj_CurBucketNo; /* 当前桶编号 */
int hj_CurSkewBucketNo; /* 倾斜桶编号 */
HashJoinTuple hj_CurTuple; /* 当前处理元组 */
HashJoinTuple hj_PreTuple; /* 前一个处理元组 */
TupleTableSlot* hj_OuterTupleSlot; /* 外元组槽 */
TupleTableSlot* hj_HashTupleSlot; /* 内元组槽 */
TupleTableSlot* hj_NullOuterTupleSlot; /* NULL值外元组槽 */
TupleTableSlot* hj_NullInnerTupleSlot; /* NULL值内元组槽 */
TupleTableSlot* hj_FirstOuterTupleSlot; /* 第一个外元组槽 */
int hj_JoinState; /* 连接状态 */
bool hj_MatchedOuter; /* 匹配外元组 */
bool hj_OuterNotEmpty; /* 外表非空 */
bool hj_streamBothSides; /* 内外表是否都包含stream */
bool hj_rebuildHashtable; /* 重建哈希表标识 */
} HashJoinState;
ExecInitHashJoin函数用于初始化hash join执行节点,把hash join计划节点转换成计划执行节点,整个转换过程采用递归处理的方式。主要执行流程如下。
(1) 构建hash join状态节点(HashJoinState)。
(2) 初始化表达式(目标表达式、join连接表达式、条件过滤表达式等)。
(3) 初始化左右子节点(初始化外表和内表)。
(4) 初始化HashKey及hash函数链表。
ExecHashJoin函数实现元组迭代输出。主要执行流程是:
(1) 获取节点信息,然后做投影处理,接着重置上下文,最后执行hash join连接状态机(首先对内表做扫描,根据连接键计算哈希值,放入哈希表。
(2) 扫描外表元组,根据连接键计算哈希值,直接查找哈希表进行连接操作,如果匹配成功将结果输出(在内存中匹配),否则做落盘处理。
(3) 对外表和内表落盘的元组做连接。
ExecHashJoin函数操作流程图如图7-10所示。
图7-10 ExecHashJoin函数操作流程
ExecEndHashJoin函数用于资源清理。主要执行流程是:首先释放哈希表,然后清空表达资源,接着释放三个元组表,最后再释放子节点。操作流程如图7-11所示。
图7-11 ExecEndHashJoin函数操作流程
ExecReSetHashJoin函数用于重置hash join状态节点。主要执行流程是:首先调用ExecHashTableDestroy释放哈希表,然后调用ExecReSetRecursivePlanTree递归重置左右子树。
ExecReScanHashJoin函数用于重新执行扫描计划。主要执行流程是:首先判断重置状态信息,如果已经递归重置,只需执行重新扫描左右子树计划即可,否则则需要重建哈希表。