自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

淺析Mongodb源碼之游標Cursor

運維 數(shù)據(jù)庫運維 其他數(shù)據(jù)庫 MongoDB
在Mongodb中,其提供了類似關系型數(shù)據(jù)中cursor對象來遍歷數(shù)據(jù)集合,同時mongodb并要根據(jù)不同的場景生成不同的游標對象(cursor),比如順序遍歷游標(basicCursor),反向游標(reverseCursor), B樹索引游標(btreeCursor)等。

在Mongodb中,其提供了類似關系型數(shù)據(jù)中cursor對象來遍歷數(shù)據(jù)集合,同時mongodb并要根據(jù)不同的場景生成不同的游標對象(cursor),比如順序遍歷游標(basicCursor)反向游標(reverseCursor), B樹索引游標(btreeCursor)等。

下面是其游標體系架構類圖(位于cursor.cpp, cursor.h, clientcursor.cpp, clientcursor.h):

從該圖中,可以看到除了(ClientCursor)之外,其余游標均繼承自Cursor這個類(基類),下面我們看一下其具體實現(xiàn):

  1. class Cursor : boost::noncopyable//使類和派生類不可復制  
  2. {  
  3.    virtual bool ok() = 0;//游標當前指向的對象是否有效  
  4.    bool eof() { return !ok(); }//是否已到尾部  
  5.    virtual Record* _current() = 0;//游標當前指向的記錄(記錄是組成數(shù)據(jù)文件的最基本單位)  
  6.    virtual BSONObj current() = 0;//游標當前指向的BSONObj對象  
  7.    virtual DiskLoc currLoc() = 0;//游標當前指向的DiskLoc  
  8.    virtual bool advance() = 0; /*true=ok,將游標指向到下一條記錄所在位置*/ 
  9.    virtual BSONObj currKey() const { return BSONObj(); }  
  10.      
  11.   /* 標識游標是否為Tailable類型,該類型支持獲取最后一條記錄后,不馬上關閉游標,以便持續(xù)獲取后面新添加的記錄*/ 
  12.    virtual bool tailable()   
  13.    {  
  14.       return false;  
  15.    }  
  16.    //設置游標為Tailable類型  
  17.    virtual void setTailable() {}  
  18.    .....  
  19. }   

在mongodb中,提供了兩種遍歷數(shù)據(jù)集合的方向,分別是“向前”和“倒轉”方式,其聲明如下:

  1. class AdvanceStrategy   
  2. {  
  3.    public:  
  4.       virtual ~AdvanceStrategy() { }  
  5.       virtual DiskLoc next( const DiskLoc &prev ) const = 0;  
  6. };  
  7.  
  8. const AdvanceStrategy *forward(); //向前  
  9. const AdvanceStrategy *reverse(); //倒轉 

下面是其實現(xiàn)方式如下:

  1. class Forward : public AdvanceStrategy {  
  2.    virtual DiskLoc next( const DiskLoc &prev ) const {  
  3.    return prev.rec()->getNext( prev );  
  4.    }  
  5. } _forward;  
  6.  
  7. class Reverse : public AdvanceStrategy {  
  8.    virtual DiskLoc next( const DiskLoc &prev ) const {  
  9.    return prev.rec()->getPrev( prev );  
  10.    }  
  11. } _reverse;  
  12.  
  13. const AdvanceStrategy *forward() {  
  14.    return &_forward;  
  15. }  
  16. const AdvanceStrategy *reverse() {  
  17.    return &_reverse;  

 

看到這里,我們有必須簡要說明一下mongofile文件的結構,見下面說明:

  1. /*  a datafile - i.e. the "dbname.<#>" files :  
  2.  
  3. ----------------------  
  4. DataFileHeader  :數(shù)據(jù)頭文件信息,包括版本,文件長度,使用情況等  
  5. ----------------------  
  6. Extent (for a particular namespace) 特定namespace下的extent,可理解為數(shù)據(jù)集合  
  7.   Record : 單條數(shù)據(jù)記錄  
  8.   ...  
  9.   Record (some chained for unused space)  
  10. ----------------------  
  11. more Extents... 其它extent  
  12. ----------------------  
  13. */ 

在一個數(shù)據(jù)庫文件中,同一個namespace的extent可以有多個,每一個extent都有一些記錄(record)組成,如果訪問record,可以使用diskloc加上文件偏移(getOfs:位于diskloc中)獲取。
同時每個extent中包括還包括兩個重要屬性:

  1. DiskLoc xnext, xprev; /* next/prev extent for this namespace */ 

它們分別記錄了同一namespace下,在extent鏈表中,當前extent的前或后一個extent的位置信息,上面AdvanceStrategy中的next方法即實現(xiàn)了在兩種遍歷方向(上面已提到)上,在extent鏈接中跳轉的方式,比如在forward方向:

  1. inline DiskLoc Record::getNext(const DiskLoc& myLoc) {  
  2.    //如果當前 Record的nextOfs偏移不為空,表示在當前extent中還有后續(xù)記錄可訪問  
  3.    if ( nextOfs != DiskLoc::NullOfs ) {  
  4.       /* defensive */ 
  5.       if ( nextOfs >= 0 && nextOfs < 10 ) {//是否為已刪除的記錄  
  6.          sayDbContext("Assertion failure - Record::getNext() referencing a deleted record?");  
  7.          return DiskLoc();  
  8.       }  
  9.    return DiskLoc(myLoc.a(), nextOfs);//獲取下一條記錄  
  10.    }  
  11.    Extent *e = myExtent(myLoc);//獲取當前記錄所屬的Extent  
  12.    while ( 1 ) {  
  13.       if ( e->xnext.isNull() )  
  14.          return DiskLoc(); //已到表尾.  
  15.       e = e->xnext.ext();//跳轉到下一個extent(以便進行next遍歷)  
  16.       if ( !e->firstRecord.isNull() )  
  17.       break;  
  18.       // entire extent could be empty, keep looking  
  19.    }  
  20.    return e->firstRecord;//獲取下一個extent中的第一條記錄  
  21. }  
  22.  

在每個extent對象中,其還包括另外兩個屬性 firstRecord,lastRecord,兩者皆為DiskLoc類型,顧名思義,它們分別指向當前extent的第一條和最后一條記錄所在位置,這種定義它們是為了后者在extent中進行跳轉時使用,當前如果在更加復雜的capped collection情況下,其值在會刪除記錄等操作時不斷更新,比如下面代碼:

  1. //namespace.cpp 文件912行,該方法在刪除記錄時調用  
  2. void DataFileMgr::_deleteRecord(NamespaceDetails *d, const char *ns, Record *todelete, const DiskLoc& dl)   
  3. {  
  4.    ......  
  5.    //extents是一個數(shù)據(jù)文件區(qū)域,該區(qū)域有所有記錄(records)均屬于同一個名空間namespace  
  6.    /* remove ourself from extent pointers */ 
  7.    {  
  8.       Extent *e = getDur().writing( todelete->myExtent(dl) );  
  9.       if ( e->firstRecord == dl )   
  10.       {//如果要刪除記錄為該extents區(qū)域第一條記錄時  
  11.          if ( todelete->nextOfs == DiskLoc::NullOfs )//且為唯一記錄時  
  12.             e->firstRecord.Null();//則該空間第一元素為空  
  13.          else //將當前空間第一條(有效)記錄后移一位  
  14.             e->firstRecord.set(dl.a(), todelete->nextOfs);  
  15.       }  
  16.       if ( e->lastRecord == dl )   
  17.       {//如果要刪除記錄為該extents區(qū)域最后一條記錄時  
  18.          if ( todelete->prevOfs == DiskLoc::NullOfs )//如果要刪除記錄的前一條信息位置為空時  
  19.             e->lastRecord.Null();//該空間最后一條記錄清空  
  20.          else //設置該空間最后一條(有效)記錄位置前移一位  
  21.             e->lastRecord.set(dl.a(), todelete->prevOfs);  
  22.       }  
  23.    }  
  24.    ......  
  25. }  
  26.  

介紹了cursor基類的定義和遍歷方向這兩個基本概念后,下面介紹一下在mongodb中,廣泛使用的是basicCursor,其定義如下:

  1. class BasicCursor : public Cursor   
  2. {  
  3.    public:  
  4.       BasicCursor(DiskLoc dl, const AdvanceStrategy *_s = forward()) : curr(dl), s( _s ), _nscanned()   
  5.       {  
  6.          incNscanned();  
  7.          init();  
  8.       }  
  9.       BasicCursor(const AdvanceStrategy *_s = forward()) : s( _s ), _nscanned()   
  10.       {  
  11.          init();  
  12.       }  
  13.       bool ok() { return !curr.isNull(); }  
  14.       Record* _current()   
  15.       {  
  16.          assert( ok() );  
  17.          return curr.rec();  
  18.       }  
  19.       BSONObj current()   
  20.       {  
  21.          Record *r = _current();  
  22.          BSONObj j(r);  
  23.          return j;  
  24.       }  
  25.       virtual DiskLoc currLoc() { return curr; }  
  26.       virtual DiskLoc refLoc()  { return curr.isNull() ? last : curr; }  
  27.       bool advance();  
  28.       virtual string toString() { return "BasicCursor"; }  
  29.       virtual void setTailable()   
  30.       {  
  31.          if ( !curr.isNull() || !last.isNull() )  
  32.          tailable_ = true;  
  33.       }  
  34.       virtual bool tailable() { return tailable_; }  
  35.   ......  
  36. };  
  37.  

可認看到在其構造函數(shù)時,使用了forward方向的遍歷方式, 即然定義了Forward方向的游標,mongodb接下來定義了Reverse方向的游標:  

  1. /* 用于排序 { $natural: -1 } */ 
  2. class ReverseCursor : public BasicCursor   
  3. {  
  4.    public:  
  5.       ReverseCursor(DiskLoc dl) : BasicCursor( dl, reverse() ) { }  
  6.       ReverseCursor() : BasicCursor( reverse() ) { }  
  7.       virtual string toString() { return "ReverseCursor"; }  
  8. };  

 另外為了支持capped collection集合類型(有關capped collection,參見這篇鏈接),mongodb分別定義了ForwardCappedCursor和ReverseCappedCursor:  

  1. class ForwardCappedCursor : public BasicCursor, public AdvanceStrategy   
  2. {  
  3.    public:  
  4.  
  5.       ForwardCappedCursor( NamespaceDetails *nsd = 0, const DiskLoc &startLoc = DiskLoc() );  
  6.       virtual string toString() {  
  7.          return "ForwardCappedCursor";  
  8.       }  
  9.       virtual DiskLoc next( const DiskLoc &prev ) const;  
  10.       virtual bool capped() const { return true; }  
  11.    private:  
  12.       NamespaceDetails *nsd;  
  13. };  
  14.  
  1. class ReverseCappedCursor : public BasicCursor, public AdvanceStrategy   
  2. {  
  3.    public:  
  4.       ReverseCappedCursor( NamespaceDetails *nsd = 0, const DiskLoc &startLoc = DiskLoc() );  
  5.       virtual string toString() {  
  6.          return "ReverseCappedCursor";  
  7.       }  
  8.       virtual DiskLoc next( const DiskLoc &prev ) const;  
  9.       virtual bool capped() const { return true; }  
  10.    private:  
  11.       NamespaceDetails *nsd;  
  12. };  
  13.  

 只不過在ForwardCappedCursor和ReverseCappedCursor中,實現(xiàn)next方法會更復雜一下,因為其要考慮刪除的記錄不在遍歷結果中的情況。相當內容詳見cursor.cpp的實現(xiàn)代碼:)

介紹游標和mongofile結構之后,我們大體知道了mongodb如果遍歷數(shù)據(jù)文件,另外mongodb使用了b樹索引來加快查詢效率,因此mongodb也提供了相應的btreeCursor,其主要用于遍歷內存中的b樹索引。

除此以外,為了方便client端使用cursor訪問數(shù)據(jù)庫,mongodb提供了ClientCursor,其對Cursor進一步封裝(詳見clientcursor.h)。

下面我們看一下mongodb如果要據(jù)查詢方式來確定使用那種類型游標的:  

  1.  //pdfile.cpp 文件639行,查詢從指定記錄位置startLoc開始的記錄,這里要據(jù)不同的條件使用不同的注季  
  2. shared_ptr<Cursor> DataFileMgr::findAll(const char *ns, const DiskLoc &startLoc)   
  3. {  
  4.    NamespaceDetails * d = nsdetails( ns );  
  5.    if ( ! d )  
  6.       return shared_ptr<Cursor>(new BasicCursor(DiskLoc()));  
  7.  
  8.    DiskLoc loc = d->firstExtent;  
  9.    Extent *e = getExtent(loc);  
  10.  
  11.    ......  
  12.  
  13.    if ( d->capped )  
  14.       return shared_ptr<Cursor>( new ForwardCappedCursor( d , startLoc ) );  
  15.  
  16.    if ( !startLoc.isNull() )  
  17.       return shared_ptr<Cursor>(new BasicCursor( startLoc ));  
  18.  
  19.    ......  
  20.    return shared_ptr<Cursor>(new BasicCursor( e->firstRecord ));  
  21. }  
  22.  

到這里,可以看了,mongodb在cursor的設計和使用方式上是基于“策略模式”(strategy pattern)的,如下圖:

 

其中cursor就是各種遍歷數(shù)據(jù)集合的策略,而pdfile.cpp就是持有相應cursor的上下文(context)  ,該模式也是使用比較廣泛的一種設置模式,好處這里就不多說了。
  
好了,今天的內容到這里就告一段落了,在接下來的文章中,將會介紹mongodb中mmap的使用場景。

原文鏈接:http://www.cnblogs.com/daizhj/archive/2011/04/15/mongodb_cursor_source_code.html

【編輯推薦】

  1. 走進MongoDB的世界 展開MongoDB的學習之旅
  2. 野心勃勃的NoSQL新貴 MongoDB應用實戰(zhàn)
  3. MongoDB與CouchDB全方位對比
  4. MongoDB1.8發(fā)布,分布式文檔數(shù)據(jù)庫
責任編輯:艾婧 來源: 博客園
相關推薦

2011-12-02 13:04:06

Java

2011-05-26 10:05:48

MongoDB

2011-05-26 16:18:51

Mongodb

2021-10-26 10:22:27

ArrayList阿里云

2017-02-27 09:03:37

Mesos架構源碼

2011-04-06 09:30:29

游標腳本性能問題

2009-07-08 14:06:22

ClassLoaderJDK源碼

2022-01-12 18:35:54

MongoDB數(shù)據(jù)查詢

2011-09-14 15:30:00

MongoDB

2023-12-17 14:43:17

2025-01-03 08:50:23

2010-07-16 09:30:42

C#MongoDB

2021-05-26 05:22:09

Virtual DOMSnabbdom虛擬DOM

2009-07-08 12:53:29

JDK源碼Java.lang.B

2018-11-13 09:49:11

存儲云存儲云備份

2012-07-26 08:32:38

MongoDB

2015-03-31 18:26:43

陌陌社交

2009-10-27 16:26:58

2025-04-22 07:52:59

2011-04-29 13:40:37

MongoDBCommand
點贊
收藏

51CTO技術棧公眾號