Mongodb源码分析--游标Cursor

mongodb源码分析(六)查询3之mongod的cursor的产生 上一篇文章分析了mongod的数据库加载部分,下面这一篇文章将继续分析mongod cursor的产生,这里cursor 的生成应该是mongodb系统中最复杂的部分.下面先介绍几个关于mongodb的游标概念. basicCursor: 直接扫描整个collection的游标,可设置初始的扫描位置,扫描为顺序扫描. ReverseCursor: 反向扫描游标,相对于顺序扫描,这里是 阅读详情

 在Mongodb中,其提供了类似关系型数据中cursor对象来遍历数据集合,同时mongodb并要根据不同的场景生成不同的游标对象 (cursor),比如顺序遍历游标(basicCursor),反向游标(reverseCursor), B树索引游标(btreeCursor)等。    下面是其游标体系架构类图(位于cursor.cpp, cursor.h, clientcursor.cpp, clientcursor.h):
    

    从该图中,可以看到除了(ClientCursor)之外,其余游标均继承自Cursor这个类(基类),下面我们看一下其具体实现:

  1. class  Cursor : boost::noncopyable // 使类和派生类不可复制  
  2.     {  
  3.          virtual   bool  ok()  =   0 ; // 游标当前指向的对象是否有效  
  4.          bool  eof() {  return   ! ok(); } // 是否已到尾部  
  5.          virtual  Record *  _current()  =   0 ; // 游标当前指向的记录(记录是组成数据文件的最基本单位)  
  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类型,该类型支持获取最后一条记录后,不马上关闭游标,以便持续获取后面新添加的记录 */  
  12.          virtual   bool  tailable() {  
  13.              return   false ;  
  14.         }  
  15.          // 设置游标为Tailable类型  
  16.          virtual   void  setTailable() {}  
  17.         .....  
  18.     }  


     在mongodb中,提供了两种遍历数据集合的方向,分别是“向前”和“倒转”方式,其声明如下:

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


     下面是其实现方式如下:

 

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

 

    看到这里,我们有必须简要说明一下mongofile文件的结构,见下面说明:

  1. /*  a datafile - i.e. the "dbname. < # > " files : 
  2.       ---------------------- 
  3.       DataFileHeader  :数据头文件信息,包括版本,文件长度,使用情况等 
  4.       ---------------------- 
  5.       Extent (for a particular namespace) 特定namespace下的extent,可理解为数据集合 
  6.         Record : 单条数据记录 
  7.         ... 
  8.         Record (some chained for unused space) 
  9.       ---------------------- 
  10.       more Extents... 其它extent 
  11.       ---------------------- 
  12. */  

    
     在一个数据库文件中,同一个namespace的extent可以有多个,每一个extent都有一些记录(record)组成,如果访问record,可以使用diskloc加上文件偏移(getOfs:位于diskloc中)获取。
     同时每个extent中包括还包括两个重要属性:

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

     它们分别记录了同一namespace下,在extent链表中,当前extent的前或后一个extent的位置信息,上面 AdvanceStrategy中的next方法即实现了在两种遍历方向(上面已提到)上,在extent链接中跳转的方式,比如在forward方 向:

  1. inline DiskLoc Record::getNext( const  DiskLoc &  myLoc) {  
  2.         // 如果当前 Record的nextOfs偏移不为空,表示在当前extent中还有后续记录可访问  
  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.     }  

    
     在每个extent对象中,其还包括另外两个属性 firstRecord ,lastRecord , 两者皆为DiskLoc类型,顾名思义,它们分别指向当前extent的第一条和最后一条记录所在位置,这种定义它们是为了后者在extent中进行跳转 时使用,当前如果在更加复杂的capped collection情况下,其值在会删除记录等操作时不断更新,比如下面代码:

  1. // namespace.cpp 文件912行,该方法在删除记录时调用  
  2. id  DataFileMgr::_deleteRecord(NamespaceDetails  * d,  const   char   * ns, Record  * todelete,  const  DiskLoc &  dl) {  
  3.       ......  
  4.        // extents是一个数据文件区域,该区域有所有记录(records)均属于同一个名空间namespace  
  5.        /*  remove ourself from extent pointers  */  
  6.       {  
  7.           Extent  * e  =  getDur().writing( todelete -> myExtent(dl) );  
  8.            if  ( e -> firstRecord  ==  dl ) { // 如果要删除记录为该extents区域第一条记录时  
  9.                if  ( todelete -> nextOfs  ==  DiskLoc::NullOfs ) // 且为唯一记录时  
  10.                   e -> firstRecord.Null(); // 则该空间第一元素为空  
  11.                else // 将当前空间第一条(有效)记录后移 一位  
  12.                   e -> firstRecord. set (dl.a(), todelete -> nextOfs);  
  13.           }  
  14.            if  ( e -> lastRecord  ==  dl ) { // 如果要删除记录为该extents区域最后一条记录时  
  15.                if  ( todelete -> prevOfs  ==  DiskLoc::NullOfs ) // 如果要删除记录的前一条信息位置为空时  
  16.                   e -> lastRecord.Null(); // 该空间最后一条记录清空  
  17.                els e // 设置该空间最后一条(有效)记录位置前移一位  
  18.                   e -> lastRecord. set (dl.a(), todelete -> prevOfs);  
  19.           }  
  20.       }  
  21.       ......  
  22.   }  


     介绍了cursor基类的定义和遍历方向这两个基本概念后,下面介绍一下在mongodb中,广泛使用的是basicCursor,其定义如下:

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


      可认看到在其构造函数时,使用了forward方向的遍历方式, 即然定义了Forward方向的游标,mongodb接下来定义了Reverse方向的游标:

 

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

 

      另外为了支持capped collection集合类型(有关capped collection,参见这篇链接 ),mongodb分别定义了ForwardCappedCursor 和ReverseCappedCursor:

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

    
      只不过在ForwardCappedCursor和ReverseCappedCursor中,实现next方法会更复杂一下,因为其要考虑删除的记录不在遍历结果中的情况。相当内容详见cursor.cpp的实现代码:)

    介绍游标和mongofile结构之后,我们大体知道了mongodb如果遍历数据文件,另外mongodb使用了b树索引来加快查询效率,因此mongodb也提供了相应的btreeCursor,其主要用于遍历内存中的b树索引。
     除此以外,为了方便client端使用cursor访问数据库,mongodb提供了ClientCursor,其对Cursor进一步封装(详见clientcursor.h)。

    下面我们看一下mongodb如果要据查询方式来确定使用那种类型游标的:

 
  1. // pdfile.cpp 文件639行,查询从指定记录位置startLoc开始的记录,这里要据不同的条件使用不同的注季  
  2.      shared_ptr < Cursor >  DataFileMgr::findAll( const   char   * ns,  const  DiskLoc  & startLoc) {  
  3.         NamespaceDetails  *  d  =  nsdetails( ns );  
  4.          if  (  !  d )  
  5.              return  shared_ptr < Cursor > ( new  BasicCursor(DiskLoc()));  
  6.         DiskLoc loc  =  d -> firstExtent;  
  7.         Extent  * e  =  getExtent(loc);  
  8.         ......  
  9.          if  ( d -> capped )  
  10.              return  shared_ptr < Cursor > (  new  ForwardCappedCursor( d , startLoc ) );  
  11.          if  (  ! startLoc.isNull() )  
  12.              return  shared_ptr < Cursor > ( new  BasicCursor( startLoc ));  
  13.         ......  
  14.          return  shared_ptr < Cursor > ( new  BasicCursor( e -> firstRecord ));  
  15.     }  


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

    
 

     其中cursor就是各种遍历数据集合的策略,而pdfile.cpp就是持有相应cursor的上下文(context)  ,该模式也是使用比较广泛的一种设置模式,好处这里就不多说了。
        
     好了,今天的内容到这里就告一段落了,在接下来的文章中,将会介绍mongodb中mmap的使用场景。

     原文链接:http://www.cnblogs.com/daizhj/archive/2011/04/15/mongodb_cursor_source_code.html
     作者: daizhj, 代震军   
     微博: http://t.sina.com.cn/daizhj
     Tags: mongodb,c++,cursor

mongo aggregate聚合出现没有配置游标问题:The 'cursor' option is required 有时一个月的时间没有写博客了,是最近比较忙,也是自己在学习的阶段,所以花在博客的时间就相对比较少了;最近一个月都是在学mongo的相关内容,包括mongo的map-reduce,聚合管道以及mongo的索引等技术点;今天写的文章内容就是我在学习聚合管道遇到的问题; mongo聚合的java实现,mongo API提供两种的实现方式:           1.基于mongotemplate提供的... 阅读详情

相关推荐

MongoDB中Table类文件如何实现获取MongoCursor的方法

总结一下,通过在MongoDB的Table类文件中实现获取MongoCursor的方法,我们可以方便地执行查询并迭代返回的结果。这样的实现提供了更灵活的方式来操作MongoDB数据库,并且可以根据实际需求进行定制化的查询操作。在方法内部,首先我们获取了MongoDB连接的读取偏好(read preference),读取关注点(read concern)和写入关注点(write concern)。方法,我们可以在MongoDB的Table类文件中获取MongoCursor对象,并使用它来遍历查询结果。

MdlForward的博客 165

mongodb基础篇--cursor对象

文章目录limit和skipcount()和size()sort()next()和forEach()toArray()map() Cursor 对象不是查询结果,而是查询返回的 接口。当我们调用 find()/findMany()/findOne() 时,Shell 并不是立即从数据库中取出数据,而是在我们使用时才会取出数据。Cursor 对象有很多方法,例如 close(),hasNext(),...

兴国-为梦想而战 8629

别再只用三次多项式了!用MATLAB手把手教你搞定七次多项式轨迹规划(附完整代码)

本文详细介绍了七次多项式在轨迹规划中的优势与应用,通过MATLAB实现从理论推导到实际代码的全过程。相比传统的三次多项式,七次多项式能精确控制位置、速度、加速度和加加速度,实现更平滑的运动控制,特别适用于工业机器人和数控机床等高精度场景。文章包含完整的MATLAB代码示例和优化技巧,帮助工程师快速掌握这一高级轨迹规划方法。

weixin_30872337的博客 298

MongoDB之游标的使用,简单易懂

通俗的说,游标不是查询结果,而是查询的返回资源,或者接口。通过这个接口,你可以逐条读取。就像 fopen 打开文件,得到一个资源一样,通过资源,可以一行一行的读文件。

nuhao的博客 1037

BMS菊花链芯片怎么选?一张表对比ADI、TI、NXP五大厂家的隔离通信方案

本文深入对比了ADI、TI、NXP等五大厂商的BMS菊花链芯片隔离通信方案,涵盖技术演进、核心参数和场景化选型策略。通过实测数据和开发经验分享,帮助工程师在新能源汽车和储能系统中选择最优方案,提升通信可靠性和成本效益。

weixin_33724046的博客 433

JavaScript 书签脚本实战:腾讯/优酷/爱奇艺等5大视频站m3u8链接一键提取

本文详细介绍了如何使用JavaScript书签脚本一键提取腾讯视频、优酷、爱奇艺等五大视频平台的m3u8流媒体链接。通过分析播放器内部对象结构,开发者可以高效获取视频源文件,避免录屏画质损失。文章包含各平台适配方案、实战增强功能开发及企业级解决方案设计,帮助用户快速掌握这一实用技术。

weixin_27945229的博客 613

MongoDB中的游标(Cursor)

定义。

2403_83147124的博客 826

【一文搞懂几种常用推荐算法】

协同过滤:适合用户-物品交互数据丰富的场景。矩阵分解:能处理稀疏数据,适合显式评分预测。基于内容:依赖物品特征,解决冷启动问题。NMF:适用于非负数据(如点击率、购买记录)。根据实际需求选择算法,或结合多种方法构建混合推荐系统。

专注技术的博主 2638

无涯教程-MongoDB - 游标方法

MongoDB游标方法修改了指定查询的执行方式。以下是带有说明,语法和示例的游标方法的列表。

无涯教程 1221

思岚A1 上手教程

前言 寒假在家闲着也是闲着,索性就向是实验室借了一个思岚 A1激光雷达,来进一步完成自己的实验室实习项目。经过一个月在Linux上和ROS上的摸爬滚打,我已经对机器人开发和SLAM有了初步了解,现在就写个上手教程吧(以防自己以后忘了)。 Basic Support 硬件 一台电脑 思岚 A1激光雷达 软件 安装Virtual Box虚拟机 在虚拟机安装Ubuntu 16操作系统 在Ubuntu 16搭建ROS kinetic 简要步骤 1.现在w..

ajxdhe的博客 6385

时频分析之短时傅里叶变换(STFT)

本文利用MATLAB自带的spectrogram函数对信号做短时傅里叶变换。首先介绍了短时傅里叶变换(STFT)的背景及意义;其次,对spectrogram函数的用法以及变量的含义做了详细的解释;最后,利用MATLAB编写程序实现了信号的STFT。

总结论文中的算法并给出论文的复现代码 7万+

Mongodb源码分析之cursor(四)

查询部分: 1. mongo客户端的请求 2. mongod配置服务器端对数据库的加载 3. mongod query中cursor操作 4. mongod对于document的匹配 mongod query中cursor操作 几个关于mongodb的游标概念: basicCursor: 直接扫描整个collection的游标,可设置初始的扫描位置,扫描为顺序扫描. ReverseCursor: 反向扫描游标,相对于顺序扫描,这里是反向扫描. GeoCursorBase: 空间地理索引游标的基类.

pourtheworld的博客 658

STM32 驱动直流无刷电机(BLDC)发声

STM32 驱动直流无刷电机(BLDC)发声

perseverance51博客 3403

java mongo aggregate options,mongoaggregate聚合出现没有配置游标问题:Thecursoroptionisrequired...

有时一个月的时间没有写博客了,是最近比较忙,也是自己在学习的阶段,所以花在博客的时间就相对比较少了;最近一个月都是在学mongo的相关内容,包括mongo的map-reduce,聚合管道以及mongo的索引等技术点;今天写的文章内容就是我在学习聚合管道遇到的问题;mongo聚合的java实现,mongo API提供两种的实现方式:1.基于mongotemplate提供的聚合接口:aggregate...

weixin_29117669的博客 641

基于物联网设计温室水培系统(NBIOT+华为云IOT)_338

本项目设计并实现了一套基于物联网的温室水培系统。系统以STM32F103RCT6单片机为核心控制器,结合SHT30温湿度传感器、SGP30二氧化碳传感器、BH1750光照传感器及液位检测传感器等硬件,实现对温室内环境参数的实时采集。系统支持自动和手动两种控制模式,能够根据传感器数据自动控制补光灯、通风风扇、遮光卷帘、降温水帘及营养液补充电机。通过NBIoT模块,系统可将采集的数据通过MQTT协议上传至华为云IOT平台,用户可通过Android手机APP或Windows上位机进行远程监控与控制。

414

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Mongo之pymongo 源码分析 find sort aggregate

Mongo之pymongo 源码分析 find  sort  aggregate一次查询的过程    简单来说,pymongo就是python关于Mongo db的一个包,主要是通过对一些命令的包装,通过socket发送给mongo服务,获取到一些结果,对结果进行包装,然后以包装为游标对象返回给使用者,其中游标对象实现了 next方法,同时对__next__进行更新,是的用户可以通过python迭...

这里是标题 1901

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

LinvoDB3源码解析:深入理解基于LevelUP的数据库架构设计

LinvoDB3是一个为Node.js、NW.js和Electron环境设计的持久化数据库,它提供了类似MongoDB和Mongoose的API接口,但底层基于LevelUP存储引擎。这个数据库架构设计巧妙,既保持了MongoDB的查询灵活性,又利用了LevelUP的高效存储能力。本文将深入解析LinvoDB3的核心架构设计,帮助开发者理解其内部工作原理。 ## 项目概述与核心特性 Linvo

gitblog_00865的博客 1131

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

mongodb遍历游标

go相关学习资料:https://edu.51cto.com/video/4196.htmlMongoDB遍历游标 MongoDB是一个流行的NoSQL数据库,它使用BSON格式存储数据。在MongoDB中,游标(Cursor)是一个非常重要的概念,它允许我们遍历查询结果。本文将介绍如何使用MongoDB的游标进行数据遍...

weixin_41188955的博客 279

MongoDB学习笔记--游标(Cursor)(四)

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34253126的博客 270

MongoTemplate使用Cursor处理大数量的数据,并查询指定字段

MongoTemplate使用Cursor处理大数量的数据,并查询指定字段

qq_20542501的博客 993

MongoDB中游标的使用

游标是一种对象,用于逐条遍历查询结果集,特别适用于处理大量数据时。通过游标,可以逐步提取数据而不是一次性加载到内存中,从而减少内存占用。游标是高效操作大数据量的核心工具,如果你的查询涉及大量文档,使用游标可以有效降低内存压力并优化性能。在 MongoDB Shell 中,查询会默认返回一个游标。接口实现,常用于分批获取大量数据。在 C# 中,游标通过。MongoDB 支持。

yuanpan的专栏 936
上一篇: Mongodb源码分析--更新记录
下一篇: Mongodb源码分析--内存文件映射(MMAP)
jackfirst86
博客等级 码龄18年 28粉丝 27原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值