1. 程式人生 > >mongodb原始碼分析(二)

mongodb原始碼分析(二)

   在之前的2篇文章中分別分析了mongod和mongo的啟動流程,下面開始將分析mongodb的查詢,由於查詢部分流程比較長,將分成mongo端的請求,mongod端的資料庫的載入,mongod query的選取,mongod文件的匹配與資料的響應幾部分來分析。

         首先進入mongo的查詢請求部分.mongo的查詢請求部分歸納起來很簡單就是將請求分裝成一個Message結構,然後將其傳送到服務端,等待服務端的相應資料,取得資料最後顯示結果.下面來看具體流程分析吧.

   當我們點選db.coll.find({x:1})時按照上一篇文章的講解,我們首先來到了mongo/shell/dbshell.cpp

  1. if ( ! wascmd ) {  
  2.     try {  
  3.         if ( scope->exec( code.c_str() , "(shell)" , false , true , false ) )//執行相應的javascript程式碼
  4.             scope->exec( "shellPrintHelper( __lastres__ );" , "(shell2)" , true , true , false );  
  5.     }  
  6.     catch ( std::exception& e ) {  
  7.         cout << "error:"
     << e.what() << endl;  
  8.     }  
  9. }  
下面進入javascript程式碼,其在mongo/shell/collection.js. [javascript] view plaincopy
  1. //這裡因為我們只設置了query,所以其它選項都是空的,this.getQueryOptions()目前只有一個SlaveOK的option,在replset模式下是不能查詢secondary伺服器的,需要呼叫rs.SlaveOK()之後才能對secondary進行查詢,其執行SlaveOK後每次查詢時都會新增一個QueryOption.
  2. DBCollection.prototype.find = function (query, fields, limit, skip, batchSize, options) {  
  3.     returnnew DBQuery( this._mongo , this._db , this ,  
  4.                         this._fullName , this._massageObject( query ) , fields , limit , skip , batchSize , options || this.getQueryOptions() );  
  5. }  
繼續前進看看DBQuery,上一篇文章提到這裡的new DBQuery物件的建立發生在:
  1. JSBool dbquery_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {  
  2.     try {  
  3.         smuassert( cx ,  "DDQuery needs at least 4 args" , argc >= 4 );  
  4. n style="white-space:pre">  </span>    //整個程式碼都是建立一個DBQuery物件,並未進行任何的查詢請求動作
  5.         Convertor c(cx);  
  6.         c.setProperty( obj , "_mongo" , argv[0] );  
  7.         c.setProperty( obj , "_db" , argv[1] );  
  8.         c.setProperty( obj , "_collection" , argv[2] );  
  9.         c.setProperty( obj , "_ns" , argv[3] );  
  10.         if ( argc > 4 && JSVAL_IS_OBJECT( argv[4] ) )  
  11.             c.setProperty( obj , "_query" , argv[4] );  
  12.         else {  
  13.             JSObject * temp = JS_NewObject( cx , 0 , 0 , 0 );  
  14.             CHECKNEWOBJECT( temp, cx, "dbquery_constructor" );  
  15.             c.setProperty( obj , "_query" , OBJECT_TO_JSVAL( temp ) );  
  16.         }  
  17.         if ( argc > 5 && JSVAL_IS_OBJECT( argv[5] ) )  
  18.             c.setProperty( obj , "_fields" , argv[5] );  
  19.         else
  20.             c.setProperty( obj , "_fields" , JSVAL_NULL );  
  21.         if ( argc > 6 && JSVAL_IS_NUMBER( argv[6] ) )  
  22.             c.setProperty( obj , "_limit" , argv[6] );  
  23.         else
  24.             c.setProperty( obj , "_limit" , JSVAL_ZERO );  
  25.         if ( argc > 7 && JSVAL_IS_NUMBER( argv[7] ) )  
  26.             c.setProperty( obj , "_skip" , argv[7] );  
  27.         else
  28.             c.setProperty( obj , "_skip" , JSVAL_ZERO );  
  29.         if ( argc > 8 && JSVAL_IS_NUMBER( argv[8] ) )  
  30.             c.setProperty( obj , "_batchSize" , argv[8] );  
  31.         else
  32.             c.setProperty( obj , "_batchSize" , JSVAL_ZERO );  
  33.         if ( argc > 9 && JSVAL_IS_NUMBER( argv[9] ) )  
  34.             c.setProperty( obj , "_options" , argv[9] );  
  35.         else
  36.             c.setProperty( obj , "_options" , JSVAL_ZERO );  
  37.         c.setProperty( obj , "_cursor" , JSVAL_NULL );  
  38.         c.setProperty( obj , "_numReturned" , JSVAL_ZERO );  
  39.         c.setProperty( obj , "_special" , JSVAL_FALSE );  
  40.     }  
  41.     catch ( const AssertionException& e ) {  
  42.         if ( ! JS_IsExceptionPending( cx ) ) {  
  43.             JS_ReportError( cx, e.what() );  
  44.         }  
  45.         return JS_FALSE;  
  46.     }  
  47.     catch ( const std::exception& e ) {  
  48.         log() << "unhandled exception: " << e.what() << ", throwing Fatal Assertion" << endl;  
  49.         fassertFailed( 16323 );  
  50.     }  
  51.     return JS_TRUE;  
  52. }  
可以看到上面只有DBQuery物件的構建動作,並沒有真正的查詢請求,那麼查詢請求去哪裡了呢?回到
  1. try {  
  2.     if ( scope->exec( code.c_str() , "(shell)" , false , true , false ) )//執行相應的javascript程式碼
  3.         scope->exec( "shellPrintHelper( __lastres__ );" , "(shell2)" , true , true , false );  
  4. }  
繼續分析shellPrintHelper函式,這裡__lastres__是什麼,搜尋整個source insight工程發現mongo/scripting/engine_spidermonkey.cpp中:
  1. bool exec( const StringData& code,const string& name = "(anon)",bool printResult = false,bool reportError = truebool assertOnError = true,int timeoutMs = 0 ) {  
  2.     JSBool worked = JS_EvaluateScript( _context,  
  3.                                        _global,  
  4.                                        code.data(),  
  5.                                        code.size(),  
  6.                                        name.c_str(),  
  7.                                        1,  
  8.                                        &ret );  
  9.     if ( worked )  
  10.         _convertor->setProperty( _global , "__lastres__" , ret );  
  11. }  
原來__lastres__就是上一條執行語句的結果也就是這裡的DBQuery物件.繼續分析shellPrintHelper函式(mongo/util/util.js) [javascript] view plaincopy
  1. shellPrintHelper = function (x) {  
  2.     if (typeof (x) == "undefined") {  
  3.         // Make sure that we have a db var before we use it
  4.         // TODO: This implicit calling of GLE can cause subtle, hard to track issues - remove?
  5.         if (__callLastError && typeof( db ) != "undefined" && db.getMongo ) {  
  6.             __callLastError = false;