mongodb源码分析(七)查询3之mongod的cursor的产生(续)
来源:互联网 发布:淘宝top排行榜 编辑:程序博客网 时间:2024/05/19 23:54
上一篇文章我们说道了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.csdn.net/yhjj0108/article/details/8260518
作者:yhjj0108,杨浩
- mongodb源码分析(七)查询3之mongod的cursor的产生(续)
- mongodb源码分析(六)查询3之mongod的cursor的产生
- mongodb源码分析(五)查询2之mongod的数据库加载
- mongodb源码分析(八)查询4之mongod文档的匹配
- mongodb源码分析(二)mongod的启动
- MongoDB---mongod的网页后台
- Mongodb源码分析--游标Cursor
- Mongodb源码分析--游标Cursor
- Mongodb源码分析--游标Cursor
- Mongodb源码分析--游标Cursor
- mongodb源码分析(四)查询1之mongo的查询请求
- MongoDB的使用学习之(七)MongoDB的聚合查询(spring-data-mongodb两种方式)附项目源码
- 手动关闭mongodb的mongod进程
- 手动关闭mongodb的mongod进程
- mongodb源码分析--查询
- mongodb源码分析--查询
- mongodb源码分析--查询
- mongodb源码分析--查询
- JS阻止用户默认事件的发生
- 2012-06-14 19:16 ios消息推送机制原理与实现
- POJ1094 字母排序(拓扑排序)
- ubuntu下乱码问题解决方案
- 实时数据库系统及其特征
- mongodb源码分析(七)查询3之mongod的cursor的产生(续)
- postgresql 安装配置
- MyEclipse安装CDT插件
- 只顾当前就业率还是长远考虑国家的发展和社会的进步?
- 【构建Android缓存模块】(三)Controller & 异步图片加载
- 最小点覆盖poj 2226
- VxWorks启动过程
- 致Android初学者:如何学习Android开发
- 结构体来实现格式化输出字符串