openGauss源码解析(104)
openGauss源码解析:SQL引擎源解析(19)
然后对每条染色体计算适应度(worth),计算适应度的过程实际上就是根据染色体的基因编码顺序产生连接树,并对连接树求代价的过程。
在openGauss数据库中,每个染色体都默认使用的是左深树,因此每个染色体的基因编码确定后,它的连接树也就随之确定了,比如针对{2, 4, 3, 1}这样一条染色体,它对应的连接树就是(((t2,t4),t3), t1),如图6-14所示。

图6-14 染色体连接树
openGauss数据库通过geqo_eval函数来生成计算适应度,它首先根据染色体的基因编码生成一棵连接树,然后计算这棵连接树的代价。
遗传算法使用gimme_tree函数来生成连接树,函数内部递归调用了merge_clump函数。merge_clump函数将能够进行连接的表尽量连接并且生成连接子树,同时记录每个连接子树中节点的个数。再将连接子树按照节点个数从高到低的顺序记录到clumps链表。代码如下:
/* 循环遍历所有的表,尽量将能连接的表连接起来 */
For (rel_count = 0; rel_count < num_gene; rel_count++) {
int cur_rel_index;
RelOptInfo* cur_rel = NULL;
Clump* *cur_clump = NULL;
/* tour 代表一条染色体,这里是获取染色体里的一个基因,也就是一个基表 */
cur_rel_index = (int) tour[rel_count];
cur_rel = (RelOptInfo *) list_nth(private->initial_rels, cur_rel_index - 1);
/* 给这个基表生成一个Clump,size=1代表了当前Clump中只有一个基表*/
cur_clump = (Clump*)palloc(sizeof(Clump));
cur_clump->joinrel = cur_rel;
cur_clump->size = 1;
/* 开始尝试连接,递归操作,并负责记录Clump到clumps链表 */
clumps = merge_clump(root, clumps, cur_clump, false);
}
以之前生成的{2, 4, 3, 1}这样一条染色体为例,并假定。
(1) 2和4不能连接。
(2) 4和3能够连接。
(3) 2和1能够连接。
在这些条件下,连接树生成的过程见表6-19。
表6-19 连接树生成过程
轮数 relcount | 连接结果集 clumps | 说明 |
初始 | NULL | 创建基因为2的节点cur_clump,cur_clump.size = 1 |
0 | {2} | 因为clumps == NULL,cur_clump没有连接表,将cur_clump直接加入clumps |
1 | {2},{4} | 创建基因为4的节点cur_clump,cur_clump.size = 1,将基因为4的cur_clump和clumps链表的里的节点尝试连接,因为2和4不能连接,节点4也被加入clumps |
2 | {2} | 创建基因为3的节点cur_clump,cur_clump.size = 1,遍历clumps链表,分别尝试和2、4进行连接,发现和4能进行连接,创建基于3和4的连接的新old_clumps节点,ols_clumps.size = 2,在clumps链表中删除节点4 |
{3, 4} {2} | 用2和4连接生成的新的old_clumps作为参数递归调用merge_clump,用old_clumps和clumps链表里的节点再尝试连接,发现不能连接(即{3,4}和{2}不能连接),那么将old_clumps加入clumps,因为old_clumps.size目前最大,插入clumps最前面 | |
3 | {3, 4} | 创建基因为1的节点cur_clump,cur_clump.size = 1 遍历clumps链表,分别尝试和{3,4}、{2}进行连接,发现和2能进行连接,创建基于1和2的新old_clumps节点,ols_clumps.size = 2,在clumps链表中删除节点2 |
{3, 4} {1, 2} | 用1和2连接生成的新的old_clumps作为参数递归调用merge_clump,用old_clumps和clumps链表里的节点尝试连接,发现不能连接,将old_clumps加入clumps,因为old_clumps.size = 2,插入clumps最后 |
结合例子中的步骤,可以看出merge_clumps函数的流程就是不断的尝试生成更大的clump。
/* 如果能够生成连接,则通过递归尝试生成节点数更多的连接 */
if (joinrel != NULL) {
…………
/* 生成新的连接节点,连接的节点数增加 */
old_clump->size += new_clump->size;
pfree_ext(new_clump);
/* 把参与了连接的节点从clumps连表里删除 */
clumps = list_delete_cell(clumps, lc, prev);
/* 以clumps和新生成的连接节点(old_clump)为参数,继续尝试生成连接 */
return merge_clump(root, clumps, old_clump, force);
}
根据上方表中的示例,最终clumps链表中有两个节点,分别是两棵连接子树,然后将force设置成true后,再次尝试连接这两个节点。
/* clumps中有多个节点,证明连接树没有生成成功 */
if (list_length(clumps) > 1) {
……
foreach(lc, clumps) {
Clump* clump = (Clump*)lfirst(lc);
/* 设置force参数为true,尝试无条件连接 */
fclumps = merge_clump(root, fclumps, clump, true);
}
clumps = fclumps;
}

浙公网安备 33010602011771号