PostgreSQL源码修改 ——查询优化(三)

本文详细介绍了在PostgreSQL中针对查询优化的解决方案,包括参数传递思路、执行流程修改、简单选择置换方案、Select(S,K)算法以及外存修改方案。通过这些优化,实现了在limit_count较小情况下时间复杂度降低,达到O(n)的效果,增强了系统的扩展性和健壮性。" 53426424,5736669,使用监听器统计网站在线人数,"['Java', 'Servlet', 'Web开发', '监听器']
摘要由CSDN通过智能技术生成

第三章    解决方案设计与实现

3.1   总体思路

3.1.1   参数的传递思路

从第二章的分析可以看出,在进入ExecLimit(LimitState *node) 函数之后,先调用recompute_limits(node);来计算limit_countlimit_offset,但这两个数据都是存入了LimitState *node中,而没有传入到其子结点(sort)的执行状态中去。因此,如果要让PostgreSQLlimit_countlimit_offset进行优化,首先要解决的问题就如何把这两个参数传入进去。

对于传递参数,我们有两种方案,其一是修改状态数据结构,在其成员域里增加我们需要传的变量。其二是修改被调用的函数的参数列表,但为了修改此函数的时候不影响PostgreSQL的其他部分的调用,最好的方式是增加一个新的函数,用这个新的函数来代替以前的函数,并在其形参里增加几个我们要传入的变量。

3.1.2           执行流程修改思路

现在假设在ExecSort(SortState *node) 中已经通过上述的思路获得了需要的数据,那又应该如何修改函数执行的流程以取得我们需要的优化呢?

从上一章的流程详细分析中,我们看到,ExecSort是调用tuplesort_performsort(Tuplesortstate *state)来完成排序,因此考虑对这个函数进行修改,在tuplesort_performsort内部,当为内排序时,直接调用快速排序来完成。从这个流程中我们可以很轻易的看出内排序的修改的地方:假设limit_countlimit_offset两个参数成功的传入到tuplesort_performsort函数,则可以写一个函数来代替快速排序来全排序,而只是把需要返回的元组放到正确的位置即可。

下面我们就对针对上述思路来设计和实现具体的解决方案。

3.2   简单选择置换方案

3.2.1   方案设计思路

阅读快速排序可以知道,在qsort_argvoid *a, size_t n, size_t es, qsort_arg_comparator cmp, void *arg)中,只是对以地址a开始的n个元组数据,对每项长为es元组排序后还是放在a处。

参数传入之后,如果limit_countoffset比较小的话,便只需用简单的比较选择算法(0limit_count+limit_offset)个元组到(0limit_count+limit_offset)位置上。

下面就如何传入参数和修改流程两个方面,设计和实现具体的解决方案来实现上述简单的比较选择算法。

3.2.2 参数传递

ExecLimit中,为了把limit_count limit_offset传入到ExecSort中,我们选择了第一种参数传递方案,即修改SortState结构体。在结构体增加三个域    bool hasLimit;int64 sort_count;int64 sort_offset;具体代码如下所示:

// execnodes.h

typedef struct SortState

{

    ScanState   ss;             /* its first field is NodeTag */

    bool        randomAccess;   /* need random access to sort output? */

    bool        sort_Done;      /* sort completed yet? */

    void       *tuplesortstate; /* private state of tuplesort.c */

 

    //ouyang 12-12在代码中搜索ouyang,即可得到所有修改的地方.

    bool hasLimit;       //是否为limit结点排序

    int64 sort_count; //只需要排序的项数.limit,limit_count

    int64 sort_offset;   //从第几项开始排序.limit,limit_offset

} SortState;

图表 15SortState修改关键代码及注释

然后,在ExecLimit(LimitState *node)的计算完count/offset之后,添加几行代码,以把这两个参数传入到SortState中。关键代码列表如下:

//nodeLimit.c

TupleTableSlot * ExecLimit(LimitState *node)    /* return: a tuple or NULL */

{

    TupleTableSlot *slot;

    PlanState  *outerPlan;

    //获取子计划结点

    outerPlan = outerPlanState(node);

    /*

    *  ExecLimit状态变化及运动的逻辑主体

    */

    switch (node->lstate)

    {

    case LIMIT_INITIAL:   //处理offset限制

        //计算limit_countoffset等数据

        recompute_limits(node); 

        //判断参数是否合法

        if (node->count <= 0 && !node->noCount)

        {

            node->lstate = LIMIT_EMPTY;

            return NULL;

        }

 

       /*ouyang 12-11*/

       if(outerPlan->type == T_SortState)

       {

           ((SortState *)outerPlan)->hasLimit = true;

           ((SortState *)outerPlan)->sort_count = node->count;

           ((SortState *)outerPlan)->sort_offset = node ->offset;

       }

       else

       {             

           ((SortState *)outerPlan)->hasLimit = false;

       }

 

        //处理至offset

        for (;;)

        {

            /*这里开始了第一次递归调用,在此递归调用中,会引有子计划结点的执行

            根据我们的示例select * from teacher order by name limits 2 offset 1

            和图,其子计划结点为T_SortState在即将运行的ExecProcNode,将会运行result = ExecSort((SortState *) node);

            */

            slot = ExecProcNode(outerPlan);

            if (TupIsNull(slot))

            {

                //如果子计划返回的元组为空,即元组不够

                node->lstate = LIMIT_EMPTY; 

                return NULL;

            }

            //……

        }

        /*

        * 我们已经通过执行子结点,获取了正确的元组,将状态修改为LIMIT_INWINDOW

        */

        node->lstate = LIMIT_INWINDOW;  //接下来返回的原组是满足要求的。

        break;

    }

 

        case LIMIT_INWINDOW:

    ……

}

return slot;

}

图表 16ExecLimit修改关键代码及注释

最后,在ExecSort(SortState *node)函数中调用自己新添加的函数my_tuplesort_performsort(Tuplesortstate *state,int64 limit_count,int64 limit_offset),以传入两个加入的参数int64 limit_count,int64 limit_offset

//nodeSort.c

TupleTableSlot *ExecSort(SortState *node)

{

    EState     *estate;

    Tuplesortstate *tuplesortstate;

    TupleTableSlot *slot;

    //获取执行状态

    estate = node->ss.ps.state;

    tuplesortstate = (Tuplesortstate *) node->tuplesortstate;

    //如果还没有排序,则排序

    if (!node->sort_Done) 

    {

        tuplesortstate = tuplesort_begin_heap(tupDesc, plannode->numCols, plannode->sortOperators, 

            plannode->sortColIdx, work_mem, node->randomAccess);  //分配空间

 

        //ouyang 12-12在第二种优化的选择方案中,将添加下面几行,在此方案中,还无须添加

        /*tuplesortstate -> hasLimit = node ->hasLimit;

        tuplesortstate -> sort_count = node ->sort_count;

        tuplesortstate -> sort_offset = node ->sort_offset;*/

        node->tuplesortstate = (void *) tuplesortstate;

        //下面的循环是调用其子计划结点来获取待排序的元组,在示例语句中,此子计划为T_SeqScan类型,由ExecSeqScan来执行

        for (;;)

        {

            slot = ExecProcNode(outerNode); 

            //在这里outerNode是一种方法取出数据库中数据,比方说ExecSeqScan()  方式

            //运行:每一次从outerNode返回一个元组

            if (TupIsNull(slot))

                break;

            tuplesort_puttupleslot(tuplesortstate, slot);  //放入刚取出的元组

        }

       /*ouyang 12-10――当父计划结点hasLimit时,调用my_tuplesort_performsort 否则还调用以前的函数*/

       if(node->hasLimit)

       {

           my_tuplesort_performsort(tuplesortstate,node ->sort_count,node->sort_offset);

       }

       else  

       {

           tuplesort_performsort(tuplesortstate);

       }

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值