mongodb源码分析(三)

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

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

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

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

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

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

[cpp]  view plain copy
  1. shared_ptr<Cursor> CursorGenerator::generate() {  
  2.     setArgumentsHint();  
  3.     shared_ptr<Cursor> cursor = shortcutCursor();  
  4.     if ( cursor ) {  
  5.         return cursor;  
  6.     }  
  7.     setMultiPlanScanner();  
  8.     cursor = singlePlanCursor();//这里产生实际的cursor,只有单个plan时才会产生  
  9.     if ( cursor ) {  
  10.         return cursor;  
  11.     }  
  12.     return newQueryOptimizerCursor( _mps, _planPolicy, isOrderRequired(), explain() );  
  13. }  
CursorGenerator::generate->CursorGenerator::singlePlanCursor:

  

[cpp]  view plain copy
  1. shared_ptr<Cursor> CursorGenerator::singlePlanCursor() {  
  2.     const QueryPlan *singlePlan = _mps->singlePlan();//确实是单plan并且$or语句不起作用,且没有额外的plan这个额外的plan参加QueryPlanSet::hasPossiblyExcludedPlans函数,那么就会返回plan  
  3.     if ( !singlePlan || ( isOrderRequired() && singlePlan->scanAndOrderRequired() ) ) {  
  4.         return shared_ptr<Cursor>();  
  5.     }//之前传入的_planPolicy为any支持任意的plan  
  6.     if ( !_planPolicy.permitPlan( *singlePlan ) ) {  
  7.         return shared_ptr<Cursor>();  
  8.     }  
  9.     if ( _singlePlanSummary ) {  
  10.         *_singlePlanSummary = singlePlan->summary();  
  11.     }//根据QueryPlan产生相应的Cursor  
  12.     shared_ptr<Cursor> single = singlePlan->newCursor();  
  13.     if ( !_query.isEmpty() && !single->matcher() ) {//建立相应的查询  
  14.         single->setMatcher( singlePlan->matcher() );  
  15.     }  
  16.     if ( singlePlan->keyFieldsOnly() ) {  
  17.         single->setKeyFieldsOnly( singlePlan->keyFieldsOnly() );  
  18.     }  
  19.     if ( _simpleEqualityMatch ) {  
  20.         if ( singlePlan->exactKeyMatch() && !single->matcher()->needRecord() ) {  
  21.             *_simpleEqualityMatch = true;  
  22.         }  
  23.     }  
  24.     return single;  
  25. }  
继续前进CursorGenerator::generate->CursorGenerator::singlePlanCursor->QueryPlan::newCursor

[cpp]  view plain copy
  1. shared_ptr<Cursor> QueryPlan::newCursor( const DiskLoc &startLoc ) const {  
  2.     if ( _type ) {//index plugin  
  3.         // hopefully safe to use original query in these contexts - don't think we can mix type with $or clause separation yet  
  4.         int numWanted = 0;//类似的空间地理索引  
  5.         if ( _parsedQuery ) {  
  6.             // SERVER-5390  
  7.             numWanted = _parsedQuery->getSkip() + _parsedQuery->getNumToReturn();  
  8.         }  
  9.         return _type->newCursor( _originalQuery , _order , numWanted );  
  10.     }  
  11.     if ( _utility == Impossible ) {//之前分析到如果产生了Impossible的plan那么将产生一个不带任何数据的游标,其就是在这里产生的  
  12.         // Dummy table scan cursor returning no results.  Allowed in --notablescan mode.  
  13.         return shared_ptr<Cursor>( new BasicCursor( DiskLoc() ) );  
  14.     }  
  15.     if ( willScanTable() ) {//要扫描全表的情况,根据_order情况建立BasicCursor或者ReverseCursor.  
  16.         checkTableScanAllowed();  
  17.         return findTableScan( _frs.ns(), _order, startLoc );  
  18.     }         
  19.     massert( 10363 ,  "newCursor() with start location not implemented for indexed plans", startLoc.isNull() );  
  20.     if ( _startOrEndSpec ) {//建立Btree索引树并传入相应的范围条件  
  21.         // we are sure to spec _endKeyInclusive  
  22.         return shared_ptr<Cursor>( BtreeCursor::make( _d, _idxNo, *_index, _startKey, _endKey, _endKeyInclusive, _direction >= 0 ? 1 : -1 ) );  
  23.     }  
  24.     else if ( _index->getSpec().getType() ) {  
  25.         return shared_ptr<Cursor>( BtreeCursor::make( _d, _idxNo, *_index, _frv->startKey(), _frv->endKey(), true, _direction >= 0 ? 1 : -1 ) );  
  26.     }  
  27.     else {  
  28.         return shared_ptr<Cursor>( BtreeCursor::make( _d, _idxNo, *_index, _frv,  
  29.                                                      independentRangesSingleIntervalLimit(),  
  30.                                                      _direction >= 0 ? 1 : -1 ) );  
  31.     }  
  32. }  
        这里完成后那么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}执行第一步描述

的流程.

下面来看代码吧.

[cpp]  view plain copy
  1. shared_ptr<Cursor> newQueryOptimizerCursor( auto_ptr<MultiPlanScanner> mps,  
  2.                                            const QueryPlanSelectionPolicy &planPolicy,  
  3.                                            bool requireOrder, bool explain ) {  
  4.         shared_ptr<QueryOptimizerCursorImpl> ret  
  5.                 ( QueryOptimizerCursorImpl::make( mps, planPolicy, requireOrder, explain ) );  
  6.         return ret;  
  7. }  
又是make函数.

[cpp]  view plain copy
  1. QueryOptimizerCursorImpl( auto_ptr<MultiPlanScanner> &mps,  
  2.                          const QueryPlanSelectionPolicy &planPolicy,  
  3.                          bool requireOrder ) :  
  4. _requireOrder( requireOrder ),  
  5. _mps( mps ),  
  6. _initialCandidatePlans( _mps->possibleInOrderPlan(), _mps->possibleOutOfOrderPlan() ),  
  7. _originalOp( new QueryOptimizerCursorOp( _nscanned, planPolicy, _requireOrder,  
  8.                                         !_initialCandidatePlans.hybridPlanSet() ) ),  
  9. _currOp(),  
  10. _completePlanOfHybridSetScanAndOrderRequired(),  
  11. _nscanned() {  
  12. }  
  13.   
  14. void init( bool explain ) {  
  15.     _mps->initialOp( _originalOp );  
  16.     if ( explain ) {  
  17.         _explainQueryInfo = _mps->generateExplainInfo();  
  18.     }  
  19.     shared_ptr<QueryOp> op = _mps->nextOp();  
  20.     rethrowOnError( op );  
  21.     if ( !op->complete() ) {  
  22.         _currOp = dynamic_cast<QueryOptimizerCursorOp*>( op.get() );  
  23.     }  
  24. }  
继续去到:MultiPlanScanner::nextOp

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

[cpp]  view plain copy
  1. shared_ptr<QueryOp> MultiPlanScanner::iterateRunner( QueryOp &originalOp, bool retried ) {  
  2.     if ( _runner ) {//第一次还未初始化为null  
  3.         return _runner->next();  
  4.     }  
  5.     _runner.reset( new QueryPlanSet::Runner( *_currentQps, originalOp ) );//简单创建一个对象  
  6.     shared_ptr<QueryOp> op = _runner->next();  
  7.     if ( op->error() &&  
  8.         _currentQps->prepareToRetryQuery() ) {  
  9.         // Avoid an infinite loop here - this should never occur.  
  10.         verify( !retried );  
  11.         _runner.reset();  
  12.         return iterateRunner( originalOp, true );  
  13.     }  
  14.     return op;  
  15. }  
nextOpSimple->iterateRunner->QueryPlanSet::Runner::next

[cpp]  view plain copy
  1. shared_ptr<QueryOp> QueryPlanSet::Runner::next() {  
  2.     if ( _ops.empty() ) {//首先还没有开始初始化.  
  3.         shared_ptr<QueryOp> initialRet = init();  
  4.         if ( initialRet ) {//初始化遇到错误或者plan中已经有执行完毕的plan  
  5.             _done = true;  
  6.             return initialRet;  
  7.         }  
  8.     }  
  9.     shared_ptr<QueryOp> ret;  
  10.     do {  
  11.         ret = _next();//选取一个QueryOp这里一个QueryOp其实对应于一个plan.  
  12.     } while( ret->error() && !_queue.empty() );  
  13.   
  14.     if ( _queue.empty() ) {  
  15.         _done = true;  
  16.     }  
  17.     return ret;  
  18. }  

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

[cpp]  view plain copy
  1. shared_ptr<QueryOp> QueryPlanSet::Runner::init() {  
  2.     if ( _plans._plans.size() > 1 )//之前建立的多plan查询  
  3.         log(1) << "  running multiple plans" << endl;  
  4.     for( PlanSet::iterator i = _plans._plans.begin(); i != _plans._plans.end(); ++i ) {  
  5.         shared_ptr<QueryOp> op( _op.createChild() );  
  6.         op->setQueryPlan( i->get() );//一个plan建立一个QueryOptimizerCursorOp  
  7.         _ops.push_back( op );  
  8.     }  
  9.     // Initialize ops.  
  10.     for( vector<shared_ptr<QueryOp> >::iterator i = _ops.begin(); i != _ops.end(); ++i ) {  
  11.         initOp( **i );//这里不再输入,说明下这是调用QueryOptimizerCursorOp的init函数,根据上面的plan创建cursor,建立matcher这里的matcher是下一篇文章分析的内容  
  12.         if ( _explainClauseInfo ) {  
  13.             _explainClauseInfo->addPlanInfo( (*i)->generateExplainInfo() );  
  14.         }  
  15.     }  
  16.     // See if an op has completed.  
  17.     for( vector<shared_ptr<QueryOp> >::iterator i = _ops.begin(); i != _ops.end(); ++i ) {  
  18.         if ( (*i)->complete() ) {//如果一个plan完成则说明任务完成  
  19.             return *i;  
  20.         }  
  21.     }  
  22.     // Put runnable ops in the priority queue.  
  23.     for( vector<shared_ptr<QueryOp> >::iterator i = _ops.begin(); i != _ops.end(); ++i ) {  
  24.         if ( !(*i)->error() ) {//建立一个Queryop的队列,需要注意的是这里的_queue的定义,our_priority_queue<OpHolder> _queue,其是一个优先级队列,每次选取执行扫描数最少的QueryOp  
  25.             _queue.push( *i );  
  26.         }  
  27.     }  
  28.     if ( _queue.empty() ) {  
  29.         return _ops.front();  
  30.     }  
  31.     return shared_ptr<QueryOp>();  
  32. }  
nextOpSimple->iterateRunner->QueryPlanSet::Runner::next->QueryPlanSet::Runner::_next

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

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

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

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

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


原文链接: http://blog.csdn.net/yhjj0108/article/details/8260518

作者:yhjj0108,杨浩



 

mongodb源码分析(八)查询4之mongod文档的匹配


  前面用两篇文章讲解了游标的产生流程,下面我们将继续讲解文档的匹配过程.查询的流程很简单

就是取出document然后与条件比对,比对成功,在开启shard时还要继续查看当前document是否在在当

前的chunkManager中,最后将查看当前文档是否已经记录了,若已经记录则跳过,需要排序则这里将满足

要求的文档排序.

          当我使用mongodb时我对于查询有几个疑问1. mongodb对于{x:{y:{z:1}}这种深层次的嵌套是怎么

做的匹配工作.2 mongodb的elemMatch具体是怎么匹配的.本文将作出解答.先来看看mongodb官方文

档的三种形式的匹配处理吧.


        对于这种{author:{name:jane}}的匹配,mongodb将author当着匹配域,{name:jane}当着匹配的内容,匹配方式

为相等,意思是使用类似memcmp的方式,内容必须一致且排列方式也得一样,否则则视为不匹配.

        对于{author.name:"jane"}这种方式则将整个的author.name作为匹配域的名称,匹配的时候会递归查询直到

找到name对象为止,以name的值来匹配jane.

        


        这里只有$elemMatch会匹配想要的结果,因为elemMatch会建立子查询,在子查询中完成elem内部的匹配,最后

达到目的,下文将会讨论到.

下面来看代码吧.

document的匹配是通过CoveredIndexMatcher这个类来实现的,首先看看这个类对象的创建吧.

[cpp]  view plain copy
  1. shared_ptr<Cursor> CursorGenerator::singlePlanCursor() {  
  2.     shared_ptr<Cursor> single = singlePlan->newCursor();  
  3.     if ( !_query.isEmpty() && !single->matcher() ) {  
  4.         single->setMatcher( singlePlan->matcher() );//创建具体的类对象并设置.  
  5.     }  
  6. }  
回到queryWithQueryOptimizer函数来看看查询流程,去掉了其它和流程无关的代码.

[cpp]  view plain copy
  1. string queryWithQueryOptimizer() {          
  2.     for( ; cursor->ok(); cursor->advance() ) {  
  3.         if ( pq.getMaxScan() && cursor->nscanned() > pq.getMaxScan() ) {//超出了请求的document数目则停止查询  
  4.             break;  
  5.         }//实际的比对过程  
  6.         if ( !queryResponseBuilder->addMatch() ) {  
  7.             continue;  
  8.         }  
  9.     }  
  10. }  
[cpp]  view plain copy
  1. bool QueryResponseBuilder::addMatch() {  
  2.     MatchDetails details;  
  3.     if ( _parsedQuery.getFields() && _parsedQuery.getFields()->getArrayOpType() == Projection::ARRAY_OP_POSITIONAL ) {  
  4.         // field projection specified, and contains an array operator  
  5.         details.requestElemMatchKey();  
  6.     }//我们关心的主角,匹配过程  
  7.     if ( !currentMatches( details ) ) {//for query match  
  8.         return false;  
  9.     }  
  10.     if ( !chunkMatches() ) {//chunkMatch,用于在开启了shard的服务器查询  
  11.         return false;  
  12.     }  
  13.     bool orderedMatch = false;  
  14.     bool match = _builder->handleMatch( orderedMatch, details );//for field projection  
  15.     return match;  
  16. }  
QueryResponseBuilder::addMatch->QueryResponseBuilder::currentMatches

[cpp]  view plain copy
  1. bool QueryResponseBuilder::currentMatches( MatchDetails& details ) {  
  2.     if ( _cursor->currentMatches( &details ) ) {//最后调用了cursor的匹配函数,这里上一篇文章分析到了cursor的类别其类别很多,这里挑选一个最复杂的QueryOptimizerCursorImpl作讲解,其它cursor的匹配大同小异,感兴趣的自己去看看吧  
  3.         return true;  
  4.     }  
  5. }  
[cpp]  view plain copy
  1. virtual bool QueryOptimizerCursorImpl::currentMatches( MatchDetails *details = 0 ) {  
  2.           if ( _takeover ) {  
  3.               return _takeover->currentMatches( details );  
  4.           }//调用了QueryOp的currentMatches函数做匹配,也就是转发了匹配要求而已.  
  5.           return _currOp->currentMatches( details );  
  6.       }  
[cpp]  view plain copy
  1. bool QueryOptimizerCursorOp::currentMatches( MatchDetails *details ) {  
  2.     if ( !_c || !_c->ok() ) {//当前游标没有数据了,肯定不会再匹配什么,返回false  
  3.         _matchCounter.setMatch( false );  
  4.         return false;  
  5.     }//匹配过程再次被转发到了queryPlan的matcher中,这里的matcher就是singlePlanCursor那么讲解的生成的CoveredIndexMatcher对象  
  6.     bool match = queryPlan().matcher()->matchesCurrent( _c.get(), details );  
  7.     // Cache the match, so we can count it in mayAdvance().  
  8.     bool newMatch = _matchCounter.setMatch( match );//记录这次匹配结果.  
  9.     return match;  
  10. }  
在继续之前我们有必要分析一下CoveredIndexMatcher对象的构造.

[cpp]  view plain copy
  1. CoveredIndexMatcher::CoveredIndexMatcher( const BSONObj &jsobj,  
  2.                                          const BSONObj &indexKeyPattern ) :  
  3.     _docMatcher( new Matcher( jsobj ) ),//建立整个查询的匹配的docMatcher对象  
  4.     _keyMatcher( *_docMatcher, indexKeyPattern ) {//根据docMatcher和索引pattern只建立包含索引域的keyMatcher匹配对象  
  5.     init();//这里设置匹配过程中是否需要加载实际的数据做匹配,当查询中带有不是索引的域时就需要加载实际数据  
  6. }  
Matcher对象的构造:

[cpp]  view plain copy
  1. Matcher::Matcher(const BSONObj &jsobj, bool nested) :  
  2.     _where(0), _jsobj(jsobj), _haveSize(), _all(), _hasArray(0), _haveNeg(), _atomic(false) {  
  3.     //再一次的遍历查询  
  4.     BSONObjIterator i(_jsobj);  
  5.     while ( i.more() ) {  
  6.         parseMatchExpressionElement( i.next(), nested );  
  7.     }  
  8. }  
[cpp]  view plain copy
  1. void Matcher::parseMatchExpressionElement( const BSONElement &e, bool nested ) {  
  2.     if ( parseClause( e ) ) {//这个地方将对以$and,$or,$not开始的查询语句分别建立结构为list< shared_ptr< Matcher > >的对象_andMatcher,_orMatcher,_notMatcher  
  3.         return;     
  4.     }  
  5.     const char *fn = e.fieldName();//mongodb支持的javascript语句查询  
  6.     if ( str::equals(fn, "$where") ) {//like {$where: this.a>3}  
  7.         parseWhere(e);  
  8.         return;  
  9.     }  
  10.     if ( e.type() == RegEx ) {//正则表达式匹配  
  11.         addRegex( fn, e.regex(), e.regexFlags() );  
  12.         return;  
  13.     }  
  14.     // greater than / less than...  
  15.     // e.g., e == { a : { $gt : 3 } },{x:{$elemMatch:{a:1,b:{$gt:1}}}}  
  16.     //       or  
  17.     //            { a : { $in : [1,2,3] } }  
  18.     if ( e.type() == Object ) {  
  19.         // support {$regex:"a|b", $options:"imx"}  
  20.         const char* regex = NULL;  
  21.         const char* flags = "";  
  22.         // e.g., fe == { $gt : 3 }  
  23.         BSONObjIterator j(e.embeddedObject());  
  24.         bool isOperator = false;  
  25.         while ( j.more() ) {//下面遍历对象建立一个ElementMatcher对象,其包括了三个参数,匹配的对象,匹配的操作符,是否是非匹配  
  26.             BSONElement fe = j.next();//举例来说就是这里的addBasic(elem,op,isnot)这里的elem比如说为{x:"foo"}  
  27.             const char *fn = fe.fieldName();  
  28.             if ( fn[0] == '$' && fn[1] ) {  
  29.                 isOperator = true;  
  30.                 if ( fn[1] == 'n' && fn[2] == 'o' && fn[3] == 't' && fn[4] == 0 ) {  
  31.                     _haveNeg = true;  
  32.                     switch( fe.type() ) {  
  33.                         case Object: {  
  34.                             BSONObjIterator k( fe.embeddedObject() );  
  35.                             uassert( 13030, "$not cannot be empty", k.more() );  
  36.                             while( k.more() ) {  
  37.                                 addOp( e, k.next(), true, regex, flags );  
  38.                             }  
  39.                             break;  
  40.                         }  
  41.                         case RegEx:  
  42.                             addRegex( e.fieldName(), fe.regex(), fe.regexFlags(), true );  
  43.                             break;  
  44.                         default:  
  45.                             uassert( 13031, "invalid use of $not"false );  
  46.                     }  
  47.                 }  
  48.                 else {//对于类似这种{a:{$all:[10,20,30]}}这种操作的匹配动作,方式同样是建立ElementMatcher,这里就不再贴出其代码分析了,感兴趣的自己阅读吧  
  49.                     if ( !addOp( e, fe, false, regex, flags ) ) {  
  50.                         isOperator = false;  
  51.                         break;  
  52.                     }  
  53.                 }  
  54.             }  
  55.             else {  
  56.                 isOperator = false;  
  57.                 break;  
  58.             }  
  59.         }  
  60.         if (regex) {  
  61.             addRegex(e.fieldName(), regex, flags);  
  62.         }  
  63.         if ( isOperator )  
  64.             return;  
  65.     }  
  66.     if ( e.type() == Array ) {  
  67.         _hasArray = true;  
  68.     }  
  69.     else if( *fn == '$' ) {  
  70.         if( str::equals(fn, "$atomic") || str::equals(fn, "$isolated") ) {  
  71.             uassert( 14844, "$atomic specifier must be a top level field", !nested );  
  72.             _atomic = e.trueValue();  
  73.             return;  
  74.         }  
  75.     }  
  76.     // normal, simple case e.g. { a : "foo" }  
  77.     addBasic(e, BSONObj::Equality, false);  
  78. }  
这里我们来看看ElemMatch相关的ElementMatcher的构造:

[cpp]  view plain copy
  1. ElementMatcher::ElementMatcher( BSONElement e , int op, bool isNot )  
  2.     : _toMatch( e ) , _compareOp( op ), _isNot( isNot ), _subMatcherOnPrimitives(false) {  
  3.     else if ( op == BSONObj::opELEM_MATCH ) {//{$elemMatch:{x:1,b:{$gt:1}}}  
  4.         BSONElement m = e;  
  5.         BSONObj x = m.embeddedObject();//ElementMatcher内部再建立一个子的Matcher对象来完成elemMatch的匹配工作  
  6.         if ( x.firstElement().getGtLtOp() == BSONObj::Equality &&  
  7.             !str::equals( x.firstElement().fieldName(), "$not" ) ) {  
  8.             _subMatcher.reset( new Matcher( x ) );  
  9.             _subMatcherOnPrimitives = false;  
  10.         }  
  11.         else {  
  12.             // meant to act on primitives  
  13.             _subMatcher.reset( new Matcher( BSON( "" << x ) ) );  
  14.             _subMatcherOnPrimitives = true;  
  15.         }  
  16.     }  
  17. }  
到这里CoveredIndexMatcher对象初始化完成,继续来看匹配的过程.

QueryResponseBuilder::addMatch->QueryResponseBuilder::currentMatches->CoveredIndexMatcher::matchesCurrent

[cpp]  view plain copy
  1. bool CoveredIndexMatcher::matchesCurrent( Cursor * cursor , MatchDetails * details ) const {  
  2.     // bool keyUsable = ! cursor->isMultiKey() && check for $orish like conditions in matcher SERVER-1264  
  3.     return matches( cursor->currKey() , cursor->currLoc() , details ,  
  4.                    !cursor->indexKeyPattern().isEmpty() // unindexed cursor  
  5.                    && !cursor->isMultiKey() // multikey cursor  
  6.                    );  
  7. }  
[cpp]  view plain copy
  1. bool CoveredIndexMatcher::matches( const BSONObj& key, const DiskLoc& recLoc,  
  2.                                    MatchDetails* details, bool keyUsable ) const {  
  3.     if ( keyUsable ) {//index usable  
  4.         if ( !_keyMatcher.matches(key, details ) ) {  
  5.             return false;  
  6.         }//这里_needRecord在CoveredIndexMatched::init函数调用时初始化,含义是查询条件中所有的查询域都是索引,表明不需要加载整个document,匹配完成后直接就行了  
  7.         bool needRecordForDetails = details && details->needRecord();  
  8.         if ( !_needRecord && !needRecordForDetails ) {  
  9.             return true;  
  10.         }  
  11.     }  
  12.     BSONObj obj = recLoc.obj();//从内存映射对象中得到具体的每一条record中的bson对象  
  13.     bool res =  
  14.         _docMatcher->matches( obj, details ) &&//具体的匹配动作  
  15.         !isOrClauseDup( obj );//这里的MultiCursor相关的部分,不做分析  
  16.     return res;  
  17. }  
[cpp]  view plain copy
  1. bool Matcher::matches(const BSONObj& jsobj , MatchDetails * details ) const {  
  2.     for ( unsigned i = 0; i < _basics.size(); i++ ) {//单个ElementMatcher的匹配动作  
  3.         const ElementMatcher& bm = _basics[i];  
  4.         const BSONElement& m = bm._toMatch;  
  5.         // -1=mismatch. 0=missing element. 1=match  
  6.         int cmp = matchesDotted(m.fieldName(), m, jsobj, bm._compareOp, bm , false , details );  
  7.         if ( cmp == 0 && bm._compareOp == BSONObj::opEXISTS ) {//查询域不存在,就看看是不是查询条件为{x{$exist:true}},这种条件将是匹配的  
  8.             // If missing, match cmp is opposite of $exists spec.  
  9.             cmp = -retExistsFound(bm);  
  10.         }  
  11.         if ( bm._isNot )  
  12.             cmp = -cmp;  
  13.         if ( cmp < 0 )  
  14.             return false;  
  15.         if ( cmp == 0 ) {  
  16.             /* missing is ok iff we were looking for null */  
  17.             if ( m.type() == jstNULL || m.type() == Undefined ||  
  18.                 ( ( bm._compareOp == BSONObj::opIN || bm._compareOp == BSONObj::NIN ) && bm._myset->count( staticNull.firstElement() ) > 0 ) ) {  
  19.                 if ( bm.negativeCompareOp() ^ bm._isNot ) {  
  20.                     return false;  
  21.                 }  
  22.             }  
  23.             else {  
  24.                 if ( !bm._isNot ) {  
  25.                     return false;  
  26.                 }  
  27.             }  
  28.         }  
  29.     }  
  30.     for (vector<RegexMatcher>::const_iterator it = _regexs.begin();  
  31.          it != _regexs.end();  
  32.          ++it) {  
  33.         BSONElementSet s;  
  34.         if ( !_constrainIndexKey.isEmpty() ) {  
  35.             BSONElement e = jsobj.getFieldUsingIndexNames(it->_fieldName, _constrainIndexKey);  
  36.   
  37.             // Should only have keys nested one deep here, for geo-indices  
  38.             // TODO: future indices may nest deeper?  
  39.             if( e.type() == Array ){  
  40.                     BSONObjIterator i( e.Obj() );  
  41.                     while( i.more() ){  
  42.                             s.insert( i.next() );  
  43.                     }  
  44.             }  
  45.             else if ( !e.eoo() )  
  46.                 s.insert( e );  
  47.         }  
  48.         else {  
  49.             jsobj.getFieldsDotted( it->_fieldName, s );  
  50.         }  
  51.         bool match = false;//正则表达式的匹配.  
  52.         for( BSONElementSet::const_iterator i = s.begin(); i != s.end(); ++i )  
  53.             if ( regexMatches(*it, *i) )  
  54.                 match = true;  
  55.         if ( !match ^ it->_isNot )  
  56.             return false;  
  57.     }//所有的and or not其实最内部都是基本的ElementMatcher的匹配,所以后面会重点分析matchesDotted函数.  
  58.     if ( _andMatchers.size() > 0 ) {//这里and条件匹配一个条件没匹配成功则返回false  
  59.         for( list< shared_ptr< Matcher > >::const_iterator i = _andMatchers.begin();  
  60.             i != _andMatchers.end(); ++i ) {  
  61.             // SERVER-3192 Track field matched using details the same as for  
  62.             // top level fields, at least for now.  
  63.             if ( !(*i)->matches( jsobj, details ) ) {  
  64.                 return false;  
  65.             }  
  66.         }  
  67.     }  
  68.     if ( _orMatchers.size() > 0 ) {  
  69.         bool match = false;//or的匹配,只要一个匹配成功了就表示匹配成功了,  
  70.         for( list< shared_ptr< Matcher > >::const_iterator i = _orMatchers.begin();  
  71.                 i != _orMatchers.end(); ++i ) {  
  72.             // SERVER-205 don't submit details - we don't want to track field  
  73.             // matched within $or  
  74.             if ( (*i)->matches( jsobj ) ) {  
  75.                 match = true;  
  76.                 break;  
  77.             }  
  78.         }  
  79.         if ( !match ) {  
  80.             return false;  
  81.         }  
  82.     }  
  83.     if ( _norMatchers.size() > 0 ) {  
  84.         for( list< shared_ptr< Matcher > >::const_iterator i = _norMatchers.begin();  
  85.                 i != _norMatchers.end(); ++i ) {  
  86.             // SERVER-205 don't submit details - we don't want to track field  
  87.             // matched within $nor  
  88.             if ( (*i)->matches( jsobj ) ) {  
  89.                 return false;  
  90.             }  
  91.         }  
  92.     }//这里执行javascript代码的匹配,笔者未分析过这段代码,感兴趣的自己分析吧  
  93.     if ( _where ) {  
  94.         return _where->exec( jsobj );  
  95.     }  
  96.     return true;  
  97. }  
重点来看看matchesDotted,其中删除了$all的匹配动作.

[cpp]  view plain copy
  1. int Matcher::matchesDotted(const char *fieldName, const BSONElement& toMatch, const BSONObj& obj, int compareOp, const ElementMatcher& em , bool isArr, MatchDetails * details ) const {  
  2.     BSONElement e;  
  3.     bool indexed = !_constrainIndexKey.isEmpty();  
  4.     if ( indexed ) {//keyMatcher的匹配流程,走这里  
  5.         e = obj.getFieldUsingIndexNames(fieldName, _constrainIndexKey);  
  6.         if( e.eoo() ) {  
  7.             cout << "obj: " << obj << endl;  
  8.             cout << "fieldName: " << fieldName << endl;  
  9.             cout << "_constrainIndexKey: " << _constrainIndexKey << endl;  
  10.             verify( !e.eoo() );  
  11.         }  
  12.     }  
  13.     else {//正常的doc匹配来到这里  
  14.         const char *p = strchr(fieldName, '.');  
  15.         if ( p ) {//这里对于db.coll.find({x.y:1})这种查询条件首先从文档中找出y域的值,然后再进行匹配  
  16.             string left(fieldName, p-fieldName);  
  17.   
  18.             BSONElement se = obj.getField(left.c_str());  
  19.             if ( se.eoo() )  
  20.                 ;  
  21.             else if ( se.type() != Object && se.type() != Array )  
  22.                 ;  
  23.             else {//这里y是对象或者数组  
  24.                 BSONObj eo = se.embeddedObject();  
  25.                 return matchesDotted(p+1, toMatch, eo, compareOp, em, se.type() == Array , details );  
  26.             }  
  27.         }  
  28.         // An array was encountered while scanning for components of the field name.  
  29.         if ( isArr ) {//这里array中只要有一个满足要求就算满足要求  
  30.             BSONObjIterator ai(obj);  
  31.             bool found = false;//这里对于这种状况docment:{x:[1,2,3,4,5]},查询条件{x:4}循环找出只要匹配一个值那么这个数组就算匹配  
  32.             while ( ai.moreWithEOO() ) {  
  33.                 BSONElement z = ai.next();  
  34.   
  35.                 if( strcmp(z.fieldName(),fieldName) == 0 ) {  
  36.                     if ( compareOp == BSONObj::opEXISTS ) {  
  37.                             return retExistsFound( em );  
  38.                     }  
  39.                     if ( compareOp != BSONObj::opELEM_MATCH &&  
  40.                          valuesMatch(z, toMatch, compareOp, em) ) {//这里的valuesMatch其实就是类似于memcmp的BSONElement match.  
  41.                             // "field.<n>" array notation was used//可自行阅读代码分析  
  42.                         if ( details )  
  43.                             details->setElemMatchKey( z.fieldName() );  
  44.                         return 1;  
  45.                     }  
  46.                 }  
  47.                 if ( z.type() == Object ) {//对象则递归继续分析  
  48.                     BSONObj eo = z.embeddedObject();  
  49.                     int cmp = matchesDotted(fieldName, toMatch, eo, compareOp, em, false, details );  
  50.                     if ( cmp > 0 ) {  
  51.                         if ( details )  
  52.                             details->setElemMatchKey( z.fieldName() );  
  53.                         return 1;  
  54.                     }  
  55.                     else if ( cmp < 0 ) {  
  56.                         found = true;  
  57.                     }  
  58.                 }  
  59.             }  
  60.             return found ? -1 : 0;  
  61.         }  
  62.         if( p ) {  
  63.             // Left portion of field name was not found or wrong type.  
  64.             return 0;  
  65.         }  
  66.         else {  
  67.             e = obj.getField(fieldName);  
  68.         }  
  69.     }  
  70.     if ( compareOp == BSONObj::opEXISTS ) {  
  71.         if( e.eoo() ) {  
  72.             return 0;  
  73.         } else {  
  74.             return retExistsFound( em );     
  75.         }  
  76.     }  
  77.     else if ( ( e.type() != Array || indexed || compareOp == BSONObj::opSIZE ) &&  
  78.               compareOp != BSONObj::opELEM_MATCH &&  
  79.               valuesMatch(e, toMatch, compareOp, em ) ) {  
  80.         return 1;  
  81.     }//对于array,只要有一个满足条件则判断为满足要求  
  82.     else if ( e.type() == Array && compareOp != BSONObj::opSIZE ) {  
  83.         BSONObjIterator ai(e.embeddedObject());  
  84.         while ( ai.more() ) {  
  85.             BSONElement z = ai.next();  
  86.             bool match = false;//遇到elemMatch了,对于每一个数组中的对象,调用其match过程,因为是对于数组中的每一个对象的子match过程,就说明array中的每一个对象中一定要有一个满足elemMatch的要求才会匹配,这就是所谓elemMatch的实现  
  87.   
  88.             if ( compareOp == BSONObj::opELEM_MATCH ) {  
  89.                 if ( em._subMatcherOnPrimitives ) {  
  90.                     match = em._subMatcher->matches( z.wrap( "" ) );  
  91.                 }  
  92.                 else {  
  93.                     match = ( z.isABSONObj() && em._subMatcher->matches( z.embeddedObject() ) );  
  94.                 }  
  95.             }  
  96.             else {  
  97.                 match = valuesMatch( z, toMatch, compareOp, em );  
  98.             }  
  99.             if ( match ) {  
  100.                 if ( details ) {  
  101.                     details->setElemMatchKey( z.fieldName() );  
  102.                 }  
  103.                 return 1;  
  104.             }  
  105.         }  
  106.         // match an entire array to itself  
  107.         if ( compareOp == BSONObj::Equality && e.woCompare( toMatch , false ) == 0 ) {  
  108.             return 1;  
  109.         }  
  110.         if ( compareOp == BSONObj::opIN && valuesMatch( e, toMatch, compareOp, em ) ) {  
  111.             return 1;  
  112.         }  
  113.     }  
  114.     else if ( e.eoo() ) {  
  115.         return 0;  
  116.     }  
  117.     return -1;  
  118. }  
到这里查询匹配流程结束,回到QueryResponseBuilder::addMatch:

[cpp]  view plain copy
  1. bool QueryResponseBuilder::addMatch() {  
  2.     bool match = _builder->handleMatch( orderedMatch, details );//for field projection  
  3.     return match;  
  4. }  
继续看这里的handleMatch,这里的_builder对应三种情况.

1 查询的结果不需要排序,对应OrderedBuildStrategy

2查询的结果需要排序,对应ReorderBuildStrategy

3多查询plan中有些plan需要排序有些plan不需要排序对应HybridBuildStrategy

这里我们选择ReOrderBuildStrategy做分析.

[cpp]  view plain copy
  1. bool ReorderBuildStrategy::handleMatch( bool &orderedMatch, MatchDetails& details ) {  
  2.     orderedMatch = false;//查询结果的地址已经被记录了,这种情况是multikey索引或者多plan扫描的结果,这里过滤掉  
  3.     if ( _cursor->getsetdup( _cursor->currLoc() ) ) {  
  4.         return false;  
  5.     }  
  6.     _handleMatchNoDedup();  
  7.     return true;  
  8. }  
[cpp]  view plain copy
  1. void ReorderBuildStrategy::_handleMatchNoDedup() {  
  2.     DiskLoc loc = _cursor->currLoc();//scanAndOrder将结果添加到一个需要Map中排序,最后得到的结果就是需要的顺序,排序只能排列最大一共32M的总文档大小,文档总大小超过了这个值将报错误.  
  3.     _scanAndOrder->add( current( false ), _parsedQuery.showDiskLoc() ? &loc : 0 );  
  4. }  

        这里排序完了后查询取出数据时会完成域的映射,就是说db.coll.find({x:1},{_id:0})这里不显示_id的值

 的映射就在这里处理,最后来看看吧.

[cpp]  view plain copy
  1.    string queryWithQueryOptimizer() {  
  2.     int nReturned = queryResponseBuilder->handoff( result );  
  3. }  

QueryResponseBuilder::handoff->ReorderBuildStrategy::rewriteMatches->ScanAndOrder::fill:

[cpp]  view plain copy
  1. void ScanAndOrder::fill( BufBuilder& b, const ParsedQuery *parsedQuery, int& nout ) const {  
  2.     for ( BestMap::const_iterator i = _best.begin(); i != _best.end(); i++ ) {  
  3.         n++;  
  4.         if ( n <= _startFrom )  
  5.             continue;  
  6.         const BSONObj& o = i->second;//实际的映射部分,只取出查询部分指定的映射,具体自己分析代码.这里不再分析  
  7.         fillQueryResultFromObj( b, projection, o, details.get() );  
  8.         nFilled++;  
  9.         if ( nFilled >= _limit )  
  10.             break;  
  11.     }  
  12.     nout = nFilled;  
  13. }  
       到这里mongodb的查询部分讲解完毕了,我感觉查询部分是整个mongodb源码部分最难的了,读懂

这里的源码需要耐心 细心,不断的读,不断的调试,最终还是能够搞定的.


原文链接:mongodb源码分析(八)查询4之mongod文档的匹配

作者:yhjj0108,杨浩



 

mongodb源码分析(九)mongodb的存储管理


1.  mongodb中能够保存的最大collection数目.

mongodb官网(Using a Large Number of Collections,)的信息如下:

By default MongoDB has a limit of approximately 24,000 namespaces per database. Each namespace is 628 bytes, the .ns file is 16MB by default.

Each collection counts as a namespace, as does each index.  Thus if every collection had one index, we can create up to 12,000 collections.  The --nssize parameter allows you to increase this limit (see below).

        但是我实际测试发现每一个namespace大小为496,而且并不是一个collection有一个index,collection
数目就得减半为12000,创建的前10个索引是保存到namespaces中的也就是程序代码中的结构namespaceDtails
中,如果一个collection上索引数目超过10个那么将分配一个namespaceDtails::extra结构保存索引.extra结构同样
为496字节,一个extra上最多能够保存30个索引.因为限制一个collection最多能有64个索引,所以一个collection最多
能够拥有2个extra,extra同样是保存在ns文件上的,那么可以算出默认下一个database能够保存的collection数目为
16M/496*3~16M/496大概为:[11275,33825].当然可以通过--nssize选项调整ns文件的大小,然后
db.repairDatabase()让一个数据库能够保存更多的集合.

2. mongodb的文件信息

        mongodb的存储文件分两部分.1是保存集合信息的xxx.ns,另一部分是保存具体数据的xxx.0,xxx.1.

默认xxx.ns文件为16M.其中保存了collection的头信息,具体见

mongodb源码分析(五)查询2之mongod的数据库加载,

xxx.0,xxx.1.......xxx.n这些数据文件的空间默认是从64M增长的,每一个文件都是前一个文件的2倍大小,直到2G,对于

32位系统单个文件最大为512M,如果指定了--smallfiles,则上面的空间大小依次除以4也就是16M,32M.......512M.

3. mongodb内部存储空间的分配.

        mongodb内部空间的分配是按照extent来分配的,当存储数据时,collection首先查找该collection是否还有空间,

有则分配空间,没有就申请一个extent.一个collection中extent是按照双向链表来组织的,其中namespace保存了头

和尾.


申请到的extent除头部信息外其余的空间将会被加入到一个deleteList的头部中,其结构如下:


这里每一个bucket中的可用数据大小在上面标注的范围到下一个bucket大小减一的范围内,如32其bucket内部保存

的可用空间长度就在[32,63].

       这里每次collection的空间分配就是根据要分配的空间大小来找到相应的bucket,然后遍历bucket,找到相应的

可用的空间,将分配空间后将剩余的空间作为一个新的结构加入到相应的bucket中.如果剩余的空间不足24字节或

者分配空间的1/8,那么这段空间将不再加入到对应的bucket中.这部分空间将被划入相应的document中,其在更新

数据时可能会用到.

       mongodb的最小数据存储单位是document,每当插入一条document时其内部分配空间,分配的空间除了

document本身占用的内存外还有一个document头如下:

[cpp]  view plain copy
  1. int _lengthWithHeaders;//该条文档和头部总大小  
  2. int _extentOfs;//该条文档所在的extent位置  
  3. int _nextOfs;//下一条文档所在位置  
  4. int _prevOfs;//上一条文档所在位置  
分配空间时首先向collection申请空间,其就遍历上面那一张表,如果没找到合适的空间那么collection将向
database申请一个extent,上面提到过.申请的extent空间是根据当前要存储的document大小以及前一个extent大
小计算出来的.document加上头部信息为:a.要申请的extent空间为len,前一个extent大小为prelen.则有:
[cpp]  view plain copy
  1. len = a * 16;  
  2. if(len < 1000)  
  3.     len *= 64;  
  4. if(len > 1000000000)  
  5.     len = 1000000000;  
  6. len = len & 0xffffff00;//256字节对齐  
  7. int y = prelen < 4000000 ? prelen * 4.0 : prelen * 1.35;//一般按照前一个extent大小分配,前一个大小小于4M则按照4倍的大小扩展,否则按照1.35倍大小扩展  
  8. len = y > len ? y : len;  
  9. if(len > 0x7ff00000)//最大是一个文件的大小,当指定了--smallfiles则空间除以4  
  10.     len = 0x7ff00000;  
        这里就得到了要分配的extent大小,然后再想database申请空间,database先从一个名叫$freelist的collection
(这个collection保存了释放的extent)查看是否有extent满足要求,没有则遍历xxx.0,xxx.1...xxx.n文件询问是否有足够
的空间,有则分配,没有就再分配一个文件xxx.n+1,然后从这个文件中分配该extent.
4. 索引的保存
        每一个索引对应于一个collection,如db.coll collection的索引{x:1,y:1},其索引保存到了collection: 
db.coll.$x_1_y_1.可以通过db.system.namespaces.find()查看当前有数据库中存在哪些collection.

作者: yhjj0108,杨浩

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值