一. 前言
本文主要走读openGauss来了解openGauss中排序算子是如何实现的。
二. 排序算子实现过程代码走读
排序算子的实现入口在ExecSort中,以简单的order by为例,其代码的实现过程主要如下所示:
ExecSort
if (!node->sort_Done) {
for (;;) {
slot = ExecProcNode(outer_node); // 获取元组
tuplesort_puttupleslot(tuple_sortstate, slot); // 往内存中暂存元组
puttuple_common(state, &stup);
switch (state->status) {
case TSS_INITIAL:
state->memtuples[state->memtupcount++] = *tuple; // 将元祖存放在内存中
if (...tate->memtupcount > state->bound * 2...) { // 如果内存元组个数超过2倍的需要返回个数,转成堆方式排序(top n)
make_bounded_heap(state);
state->status = TSS_BOUNDED; // 切换到堆排序
return
}
if ((state->memtupcount < state->memtupsize)) // 内存充足但是又不能按照堆方式进行排序场景
return
// 如下将是内存无法放下所有元素场景
inittapes(state, true); // 如果也不符合堆排序要求,就切换到磁盘落盘排序
state->status = TSS_BUILDRUNS; // 磁盘落盘排序
dumptuples(state, false); // 将元祖排序并且写到磁盘
dumpbatch(state, alltuples);
tuplesort_sort_memtuples(state); // 先对当前子串进行排序
for (i = 0; i < memtupwrite; i++) {
WRITETUP(....); // 将当前有序的子串写入到磁盘
state->memtupcount--;
}
case TSS_BOUNDED:
// 如果是基于落盘的方式存储,先判断当前元组和堆顶元组
if (COMPARETUP(state, tuple, &state->memtuples[0]) <= 0)
free_sort_tuple(state, tuple); // 如果当前元组小于堆顶元组,可以直接丢弃
else
free_sort_tuple(state, &state->memtuples[0]); // 丢弃堆顶元组
tuplesort_heap_replace_top(state, tuple); // 将当前的元组插入到当前堆中
case TSS_BUILDRUNS:
dumptuples(state, false); // 此处为处理为第二、三、四...子串落盘场景
dumpbatch(state, alltuples); // 调用栈同上边
}
}
}
tuplesort_performsort(tuple_sortstate); // 排序
switch (state->status) {
case TSS_INITIAL: // 内存排序
tuplesort_sort_memtuples(state);
qsort_tuple // 使用快速排序对元组进行排序
case TSS_BOUNDED: // 堆排序
sort_bounded_heap(state);
case TSS_BUILDRUNS: // 磁盘排序
dumptuples(state, true); // 将最后一个字串进行落盘
mergeruns(state); // 将已经落盘的各个有序子串进行合并
}
tuplesort_gettupleslot // 依次返回有序元祖
tuplesort_gettuple_common
switch (state->status) {
case TSS_SORTEDINMEM: // 直接内存中排序
*stup = state->memtuples[state->current++];
return
case TSS_SORTEDONTAPE: // 磁盘排序
READTUP(state, stup, state->result_tape, tuplen);
return
case TSS_FINALMERGE: // 返回前需要先合并
mergereadnext(state, srcTape, &newtup) // 合并
tuplesort_heap_delete_top(state);
return
}