上一篇文章我们说道了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,杨浩