mongodb源码分析(七)查询3之mongod的cursor的产生(续)

        上一篇文章我们说道了mongod对于QueryPlan的选取,由于篇幅过长,所以另起一篇文章接上一篇文章

继续谈plan的实际查询流程.

        上一篇文章说完了QueryPlanSet::make我们回到:MultiPlanScanner::init.

        // if _or == false, don't use or clauses for index selection
        if ( !_or ) {
            ++_i;//若query为{$or:[{a:1},{:1}]}这种以or开头的语句,那么frsp是无用的,具体见FieldRangeSet::handleMatchField
            auto_ptr<FieldRangeSetPair> frsp( new FieldRangeSetPair( _ns.c_str(), _query, true ) );
            updateCurrentQps( QueryPlanSet::make( _ns.c_str(), frsp, auto_ptr<FieldRangeSetPair>(),
                                                 _query, order, _parsedQuery, _hint,
                                                 _recordedPlanPolicy,
                                                 min, max, true ) );
        }
        else {//继续来看看这里的handleBeginningOfClause函数
            BSONElement e = _query.getField( "$or" );
            massert( 13268, "invalid $or spec",
                    e.type() == Array && e.embeddedObject().nFields() > 0 );
            handleBeginningOfClause();
        }

    void MultiPlanScanner::handleBeginningOfClause() {
        assertHasMoreClauses();
        ++_i;//这里使用$or的第一个语句执行QueryPlanSet::make得到其中的plan,过程和一般查询一样
        auto_ptr<FieldRangeSetPair> frsp( _org->topFrsp() );//这里的第一个语句如:{$or:[{x:1},{y:1}]}这里的{x:1},第一个QueryPlanSet执行完后会继续产生一个新的QueryPlanSet继续执行{y:1}的查询动作,后面会说到.
        auto_ptr<FieldRangeSetPair> originalFrsp( _org->topFrspOriginal() );
        updateCurrentQps( QueryPlanSet::make( _ns.c_str(), frsp, originalFrsp, _query,
                                             BSONObj(), _parsedQuery, _hint, _recordedPlanPolicy,
                                             BSONObj(), BSONObj(),
                                             // 'Special' plans are not supported within $or.
                                             false ) );
    }
我们继续向上回到CursorGenerator::generate函数:

    shared_ptr<Cursor> CursorGenerator::generate() {
        setArgumentsHint();
        shared_ptr<Cursor> cursor = shortcutCursor();
        if ( cursor ) {
            return cursor;
        }
        setMultiPlanScanner();
        cursor = singlePlanCursor();//这里产生实际的cursor,只有单个plan时才会产生
        if ( cursor ) {
            return cursor;
        }
        return newQueryOptimizerCursor( _mps, _planPolicy, isOrderRequired(), explain() );
    }
CursorGenerator::generate->CursorGenerator::singlePlanCursor:

  

    shared_ptr<Cursor> CursorGenerator::singlePlanCursor() {
        const QueryPlan *singlePlan = _mps->singlePlan();//确实是单plan并且$or语句不起作用,且没有额外的plan这个额外的plan参加QueryPlanSet::hasPossiblyExcludedPlans函数,那么就会返回plan
        if ( !singlePlan || ( isOrderRequired() && singlePlan->scanAndOrderRequired() ) ) {
            return shared_ptr<Cursor>();
        }//之前传入的_planPolicy为any支持任意的plan
        if ( !_planPolicy.permitPlan( *singlePlan ) ) {
            return shared_ptr<Cursor>();
        }
        if ( _singlePlanSummary ) {
            *_singlePlanSummary = singlePlan->summary();
        }//根据QueryPlan产生相应的Cursor
        shared_ptr<Cursor> single = singlePlan->newCursor();
        if ( !_query.isEmpty() && !single->matcher() ) {//建立相应的查询
            single->setMatcher( singlePlan->matcher() );
        }
        if ( singlePlan->keyFieldsOnly() ) {
            single->setKeyFieldsOnly( singlePlan->keyFieldsOnly() );
        }
        if ( _simpleEqualityMatch ) {
            if ( singlePlan->exactKeyMatch() && !single->matcher()->needRecord() ) {
                *_simpleEqualityMatch = true;
            }
        }
        return single;
    }
继续前进CursorGenerator::generate->CursorGenerator::singlePlanCursor->QueryPlan::newCursor

    shared_ptr<Cursor> QueryPlan::newCursor( const DiskLoc &startLoc ) const {
        if ( _type ) {//index plugin
            // hopefully safe to use original query in these contexts - don't think we can mix type with $or clause separation yet
            int numWanted = 0;//类似的空间地理索引
            if ( _parsedQuery ) {
                // SERVER-5390
                numWanted = _parsedQuery->getSkip() + _parsedQuery->getNumToReturn();
            }
            return _type->newCursor( _originalQuery , _order , numWanted );
        }
        if ( _utility == Impossible ) {//之前分析到如果产生了Impossible的plan那么将产生一个不带任何数据的游标,其就是在这里产生的
            // Dummy table scan cursor returning no results.  Allowed in --notablescan mode.
            return shared_ptr<Cursor>( new BasicCursor( DiskLoc() ) );
        }
        if ( willScanTable() ) {//要扫描全表的情况,根据_order情况建立BasicCursor或者ReverseCursor.
            checkTableScanAllowed();
            return findTableScan( _frs.ns(), _order, startLoc );
        }       
        massert( 10363 ,  "newCursor() with start location not implemented for indexed plans", startLoc.isNull() );
        if ( _startOrEndSpec ) {//建立Btree索引树并传入相应的范围条件
            // we are sure to spec _endKeyInclusive
            return shared_ptr<Cursor>( BtreeCursor::make( _d, _idxNo, *_index, _startKey, _endKey, _endKeyInclusive, _direction >= 0 ? 1 : -1 ) );
        }
        else if ( _index->getSpec().getType() ) {
            return shared_ptr<Cursor>( BtreeCursor::make( _d, _idxNo, *_index, _frv->startKey(), _frv->endKey(), true, _direction >= 0 ? 1 : -1 ) );
        }
        else {
            return shared_ptr<Cursor>( BtreeCursor::make( _d, _idxNo, *_index, _frv,
                                                         independentRangesSingleIntervalLimit(),
                                                         _direction >= 0 ? 1 : -1 ) );
        }
    }
        这里完成后那么cursor就建立了.回过来到CursorGenerator::generate函数,当singlePlanCursor无法返回

cursor时就将去到newQueryOptimizerCursor函数,在我们继续来分析这个函数之前先介绍下这个函数的流程.

        这个函数是处理多plan状况或者说$or操作符有效的情况.

一,当$or为false时,对于每一个plan那么建立一个QueryOptimizerCursorOp,这个结构内部保存了这个plan,在

初始化QueryOptimizerCursorOp时会根据其内部保存的plan产生cursor.QueryOptimizerCursorImpl的操作

对象就是这里的QueryOptimizerCursorOp,其内部每一次循环时都选择一个当前扫描对象最少的

QueryOptimizerCursorOp,选取它作为使用的QueryOptimizerCursorOp,当操作QueryOptimizerCursorOp

时,其内部做的动作就是操作cursor,然后做匹配,因为每次每一个QueryOptimizerCursorOp扫描的对象其实是

一样多的,这就相当于多plan的轮流执行,当一个QueryOptimizerCursorOp操作完成了也就是cursor不能再返

回document了那么这次查询也就结束了,第一个结束的plan表明这个plan是最优的,因为其查询的对象最少.

二.当$or语句有效时,举例db.coll.find({$or:[{a:1,c:1},{b:1,d:1}]}),这里之前也分析过首先执行查询的条件是

{a:1,c:1},根据这里的条件产生plan,然后照第一步的过程执行,当结束时继续对这里的{b:1,d:1}执行第一步描述

的流程.

下面来看代码吧.

    shared_ptr<Cursor> newQueryOptimizerCursor( auto_ptr<MultiPlanScanner> mps,
                                               const QueryPlanSelectionPolicy &planPolicy,
                                               bool requireOrder, bool explain ) {
            shared_ptr<QueryOptimizerCursorImpl> ret
                    ( QueryOptimizerCursorImpl::make( mps, planPolicy, requireOrder, explain ) );
            return ret;
    }
又是make函数.

        QueryOptimizerCursorImpl( auto_ptr<MultiPlanScanner> &mps,
                                 const QueryPlanSelectionPolicy &planPolicy,
                                 bool requireOrder ) :
        _requireOrder( requireOrder ),
        _mps( mps ),
        _initialCandidatePlans( _mps->possibleInOrderPlan(), _mps->possibleOutOfOrderPlan() ),
        _originalOp( new QueryOptimizerCursorOp( _nscanned, planPolicy, _requireOrder,
                                                !_initialCandidatePlans.hybridPlanSet() ) ),
        _currOp(),
        _completePlanOfHybridSetScanAndOrderRequired(),
        _nscanned() {
        }
        
        void init( bool explain ) {
            _mps->initialOp( _originalOp );
            if ( explain ) {
                _explainQueryInfo = _mps->generateExplainInfo();
            }
            shared_ptr<QueryOp> op = _mps->nextOp();
            rethrowOnError( op );
            if ( !op->complete() ) {
                _currOp = dynamic_cast<QueryOptimizerCursorOp*>( op.get() );
            }
        }
继续去到:MultiPlanScanner::nextOp

    shared_ptr<QueryOp> MultiPlanScanner::nextOp() {
        verify( !doneOps() );//查询时存在$or这种语言并且其是有意义的,具体什么时候有意义请看上一篇文章
        shared_ptr<QueryOp> ret = _or ? nextOpOr() : nextOpSimple();
        if ( ret->error() || ret->complete() ) {
            _doneOps = true;
        }
        return ret;
    }
    shared_ptr<QueryOp> MultiPlanScanner::nextOpSimple() {
        return iterateRunner( *_baseOp );
    }
    shared_ptr<QueryOp> MultiPlanScanner::nextOpOr() {
        shared_ptr<QueryOp> op;
        do {
            op = nextOpSimple();
            if ( !op->completeWithoutStop() ) {
                return op;
            }//记得$or语句中比如说{$or:[{a:1},{b:1}]},之前_or语句只传入了一部分{a:1},这里就是{a:1}这个查询条件结束后继续开始{b:1}的查询条件,同样的过程,创建QueryPlanSet继续执行.
            handleEndOfClause( op->queryPlan() );
            _baseOp = op;
        } while( mayHandleBeginningOfClause() );
        return op;
    }
去到nextOpSimple->iterateRunner:

    shared_ptr<QueryOp> MultiPlanScanner::iterateRunner( QueryOp &originalOp, bool retried ) {
        if ( _runner ) {//第一次还未初始化为null
            return _runner->next();
        }
        _runner.reset( new QueryPlanSet::Runner( *_currentQps, originalOp ) );//简单创建一个对象
        shared_ptr<QueryOp> op = _runner->next();
        if ( op->error() &&
            _currentQps->prepareToRetryQuery() ) {
            // Avoid an infinite loop here - this should never occur.
            verify( !retried );
            _runner.reset();
            return iterateRunner( originalOp, true );
        }
        return op;
    }
nextOpSimple->iterateRunner->QueryPlanSet::Runner::next

    shared_ptr<QueryOp> QueryPlanSet::Runner::next() {
        if ( _ops.empty() ) {//首先还没有开始初始化.
            shared_ptr<QueryOp> initialRet = init();
            if ( initialRet ) {//初始化遇到错误或者plan中已经有执行完毕的plan
                _done = true;
                return initialRet;
            }
        }
        shared_ptr<QueryOp> ret;
        do {
            ret = _next();//选取一个QueryOp这里一个QueryOp其实对应于一个plan.
        } while( ret->error() && !_queue.empty() );

        if ( _queue.empty() ) {
            _done = true;
        }
        return ret;
    }

nextOpSimple->iterateRunner->QueryPlanSet::Runner::next->QueryPlanSet::Runner::init

    shared_ptr<QueryOp> QueryPlanSet::Runner::init() {
        if ( _plans._plans.size() > 1 )//之前建立的多plan查询
            log(1) << "  running multiple plans" << endl;
        for( PlanSet::iterator i = _plans._plans.begin(); i != _plans._plans.end(); ++i ) {
            shared_ptr<QueryOp> op( _op.createChild() );
            op->setQueryPlan( i->get() );//一个plan建立一个QueryOptimizerCursorOp
            _ops.push_back( op );
        }
        // Initialize ops.
        for( vector<shared_ptr<QueryOp> >::iterator i = _ops.begin(); i != _ops.end(); ++i ) {
            initOp( **i );//这里不再输入,说明下这是调用QueryOptimizerCursorOp的init函数,根据上面的plan创建cursor,建立matcher这里的matcher是下一篇文章分析的内容
            if ( _explainClauseInfo ) {
                _explainClauseInfo->addPlanInfo( (*i)->generateExplainInfo() );
            }
        }
        // See if an op has completed.
        for( vector<shared_ptr<QueryOp> >::iterator i = _ops.begin(); i != _ops.end(); ++i ) {
            if ( (*i)->complete() ) {//如果一个plan完成则说明任务完成
                return *i;
            }
        }
        // Put runnable ops in the priority queue.
        for( vector<shared_ptr<QueryOp> >::iterator i = _ops.begin(); i != _ops.end(); ++i ) {
            if ( !(*i)->error() ) {//建立一个Queryop的队列,需要注意的是这里的_queue的定义,our_priority_queue<OpHolder> _queue,其是一个优先级队列,每次选取执行扫描数最少的QueryOp
                _queue.push( *i );
            }
        }
        if ( _queue.empty() ) {
            return _ops.front();
        }
        return shared_ptr<QueryOp>();
    }
nextOpSimple->iterateRunner->QueryPlanSet::Runner::next->QueryPlanSet::Runner::_next

    shared_ptr<QueryOp> QueryPlanSet::Runner::_next() {
        OpHolder holder = _queue.pop();//这里优先级队列选取已扫描最少对象的plan,一样则选取
        QueryOp &op = *holder._op;//第一个plan
        nextOp( op );//QueryOptimizerCursorOp::nextOp的调用,目的是记录当前扫描的对象数,可能的游标前进动作,检查游标是否已经执行完毕
        if ( op.complete() ) {//几个plan中最早结束的plan,将其cache,以后查找时直接找到这个plan,节约查找时间
            if ( _plans._mayRecordPlan && op.mayRecordPlan() ) {
                op.queryPlan().registerSelf( op.nscanned(), _plans.characterizeCandidatePlans() );
            }
            _done = true;
            return holder._op;
        }
        if ( _plans.hasPossiblyExcludedPlans() &&
            op.nscanned() > _plans._oldNScanned * 10 ) {
            verify( _plans.nPlans() == 1 && _plans.firstPlan()->special().empty() );
            holder._offset = -op.nscanned();
            _plans.addFallbackPlans();
            PlanSet::iterator i = _plans._plans.begin();
            ++i;
            for( ; i != _plans._plans.end(); ++i ) {
                shared_ptr<QueryOp> op( _op.createChild() );
                op->setQueryPlan( i->get() );
                _ops.push_back( op );
                initOp( *op );
                if ( op->complete() )
                    return op;
                _queue.push( op );
            }
            _plans._usingCachedPlan = false;
        }//将这个QueryOp插入到优先级队列末尾,最后构成了多plan的轮流执行.
        _queue.push( holder );
        return holder._op;
    }
回到nextOpSimple->iterateRunner,到此时QueryOp就已经选定了.退回到QueryOptimizerCursorImpl::init

        void init( bool explain ) {
            shared_ptr<QueryOp> op = _mps->nextOp();//这里就是之前返回的op
            if ( !op->complete() ) {//没有执行完毕则将其设置为当前的QueryOptimizerCursorOp
                _currOp = dynamic_cast<QueryOptimizerCursorOp*>( op.get() );
            }
        }
来看看QueryOptimizerCursorImpl::OK和QueryOptimizerCursorImpl::advance这两个函数.

        virtual bool ok() { return _takeover ? _takeover->ok() : !currLoc().isNull(); }//这里_takeover还没设置为null
        virtual DiskLoc currLoc() { return _takeover ? _takeover->currLoc() : _currLoc(); }
		        DiskLoc _currLoc() const {//这里就是调用之前得到的当前操作的QueryOptimizerCursorOp
            return _currOp ? _currOp->currLoc() : DiskLoc();
        }//这里_c是实际的cursor,调用其currLoc查看当前是否还有数据
        DiskLoc QueryOptimizerCursorOp::currLoc() const { return _c ? _c->currLoc() : DiskLoc(); }
        virtual bool advance() {return _advance( false );}
		bool _advance( bool force ) {
            if ( _takeover ) {//目前还为空
                return _takeover->advance();
            }
            if ( !force && !ok() ) {
                return false;
            }
            _currOp = 0;//上面已经分析了这个函数,这里是循环选取下一个QueryOp
            shared_ptr<QueryOp> op = _mps->nextOp();
            // Avoiding dynamic_cast here for performance.  Soon we won't need to
            // do a cast at all.
            QueryOptimizerCursorOp *qocop = (QueryOptimizerCursorOp*)( op.get() );
            if ( !op->complete() ) {//这里查询数据还未结束
                // The 'qocop' will be valid until we call _mps->nextOp() again.  We return 'current' values from this op.
                _currOp = qocop;//改变了当前的QueryOp
            }//一次查询最多会达到101条满足查询要求的docment,超过这个数目QueryOp就会设置为complete,但是并不是说数据查询完了
            else if ( op->stopRequested() ) {//cursor仍然有效,但是已查出的document已经达到了上限101
                if ( qocop->cursor() ) {//一次查询时得到的document数目有限,并不会一次返回所有数据.
                    _takeover.reset( new MultiCursor( _mps,
                                                     qocop->cursor(),
                                                     op->queryPlan().matcher(),
                                                     qocop->explainInfo(),
                                                     *op,
                                                     _nscanned - qocop->cursor()->nscanned() ) );
                }
            }
            else {
                if ( _initialCandidatePlans.hybridPlanSet() ) {
                    _completePlanOfHybridSetScanAndOrderRequired =
                            op->queryPlan().scanAndOrderRequired();
                }
            }
            return ok();
        }
        到这里所有cursor产生的执行流程以及分析完了,其流程是相当的复杂的,本人感觉查询部分是mongodb

中执行流程最复杂的部分了,因为空间有限,部分函数并未继续深入探讨,感兴趣的自己研究吧,也可以提出来大家

一起来讨论.下一篇文章我们将主要介绍docment的匹配.


原文链接: http://blog.youkuaiyun.com/yhjj0108/article/details/8260518

作者:yhjj0108,杨浩













评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值