Chinaunix首页 | 论坛 | 博客
  • 博客访问: 581041
  • 博文数量: 104
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1559
  • 用 户 组: 普通用户
  • 注册时间: 2014-08-21 00:58
个人简介

锻炼精神,首先要锻炼肉体

文章分类

全部博文(104)

文章存档

2018年(1)

2016年(1)

2015年(101)

2014年(1)

我的朋友

分类: 数据库开发技术

2015-02-22 12:29:40

源代码链接地址


今天来分析的是位于 src/dms/ 下面的 Dms.cpp 源文件,其中的方法在头文件 src/include/Dms.hpp
中实现,所以首先来查看 Dms.hpp 这一头文件。

//Dms.hpp


点击(此处)折叠或打开

  1. /*******************************************************************************
  2.    Copyright (C) 2013 SequoiaDB Software Inc.

  3.    This program is free software: you can redistribute it and/or modify
  4.    it under the terms of the GNU Affero General Public License, version 3,
  5.    as published by the Free Software Foundation.

  6.    This program is distributed in the hope that it will be useful,
  7.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  8.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9.    GNU Affero General Public License for more details.

  10.    You should have received a copy of the GNU Affero General Public License
  11.    along with this program. If not, see <http://www.gnu.org/license/>.
  12. *******************************************************************************/
  13. #ifndef DMS_HPP__
  14. #define DMS_HPP__

  15. #include "ossLatch.hpp"
  16. #include "ossMmapFile.hpp"
  17. #include "bson.h"
  18. #include "dmsRecord.hpp"
  19. #include "ixmBucket.hpp"
  20. #include <vector>

  21. #define DMS_EXTEND_SIZE 65536  
  22. // DMS_EXTEND_SIZE 变量用于在 DMS 文件空间不够的时候用于扩展空间所用,每次扩展空间的大小是 65536 个字节
  23. // 65536 = 1024*8*8 Byte = 8K*8 Byte = 64KB

  24. #define DMS_PAGESIZE 4194304
  25. // DMS_PAGESIZE 定义的是页面的大小, 在 emeralddb 数据库中,分配的空间是以页面为最小单位的,
  26. // 通过变量 DMS_PAGESIZE 宏来定义数据库中一次分配的页面大小是 4194304 = 1024 *1024 *4B = 4MB 

  27. #define DMS_MAX_RECORD (DMS_PAGESIZE-sizeof(dmsHeader)-sizeof(dmsRecord)-sizeof(SLOTOFF))
  28. // 通过宏 DMS_MAX_RECORD 来定义 DMS记录的最大值,规定上限,一旦超过上限系统会做出相关的处理措施,
  29. // 以防止会因为空间消耗过多而造成错误

  30. #define DMS_MAX_PAGES 262144 
  31. // DMS_MAX_PAGES 用于定义的是系统中最多可以分配的页面数目,如果系统中分配的页面数目超过这个数值的话,将会引发错误
  32. // 262144 , 262144 * 每个页面的大小(4MB)= 1024*256*4MB = 1024GB

  33. typedef unsigned int SLOTOFF ;   
  34. // 在这里通过 typedef 来定义一个 SLOTOFF 的新的类型

  35. #define DMS_INVALID_SLOTID 0xFFFFFFFF 
  36. // 定义宏 DMS_INVALID_SLOTID 用来标定无效 SLOTOID 数值,常用于刚刚创建 SLOTID 变量时的初始化赋值

  37. #define DMS_INVALID_PAGEID 0xFFFFFFFF 
  38. // 定义宏 DMS_INVALID_PAGEID 用来标定无效 PAGEID 数值,常用于初始化赋值

  39. #define DMS_KEY_FIELDNAME "_id"       
  40. //定义文件的键值,通常用于为文件命名的时候,用于文件名的开头,标定文件的属性和类型

  41. extern const char *gKeyFieldName ;   
  42. // 定义一个全局变量 getKeyFieldName 用来存放根据 键 key 锁定的 value 这个值,推断 应是用于散列索引

  43. // each record has the following header, include 4 bytes size and 4 bytes flag
  44. // 每一个记录中都包括一个下面定义的头文件,该头文件中有一个占位  4 个字节的int 变量, 用来指定记录的长度
  45. // 头文件中还有一个占位 4个字节的 flag 标识符号 ,
  46. // 标识符号可以被赋值为下面 DMS_RECORD_FLAG_NORMAL (数值0) 或是 DMS_RECORD_FLAG_DROPPED (数值 1) 
  47. // 如果是 0 表示该记录是正常的记录类型, 如果 flag 的数值为 1 表示这个记录已被删除

  48. #define DMS_RECORD_FLAG_NORMAL 0        // DMS_RECORD_FLAG_NORMAL 用来表示记录类型为普通类型
  49. #define DMS_RECORD_FLAG_DROPPED 1        // DMS_RECORD_FLAG_DROPPED 用来表示记录已经被删除
  50. struct dmsRecord
  51. {
  52.    unsigned int _size ; // 用来表示该记录的大小
  53.    unsigned int _flag ; // 用来表示该记录是否被删除
  54.    char _data[0] ;       // 用来存放该记录中的数据信息, 这种表示方法在 unix/linux 通信中极为常见
  55.    //  是一种十分灵活并且节省空间的方法,如果该记录中没有消息的话, 则将该字段置为 NULL
  56.    //  如果该记录中需要携带一定信息的话,把 _data[0] 当做指针来使用,&(_data[0]) = (...)malloc(...) ;
  57.    //  来为其分配空间即可
  58. } ;

  59. // dms header , 下面定义的便是 dms 记录的头文件的信息字段
  60. // 如果熟悉 TPC/IP 协议的话,那么我们应该知道,在消息头中多半定义的是,消息的长度,类型,以及消息序列号(超时重传)等等

  61. #define DMS_HEADER_EYECATCHER "DMSH"
  62. #define DMS_HEADER_EYECATCHER_LEN 4
  63. #define DMS_HEADER_FLAG_NORMAL 0
  64. #define DMS_HEADER_FLAG_DROPPED 1

  65. #define DMS_HEADER_VERSION_0 0      
  66. // DMS_HEADER_VERSION 定义的是消息头的版本号,用于消息收发的双方保证传输的消息的正确性

  67. #define DMS_HEADER_VERSION_CURRENT DMS_HEADER_VERSION_0
  68. // DMS_HEADER_VERSION_CURRENT 定义的是消息头当前的版本号

  69. struct dmsHeader
  70. {
  71.    char _eyeCatcher[DMS_HEADER_EYECATCHER_LEN] ;
  72.    unsigned int _size ;    // 记录的是消息的大小
  73.    unsigned int _flag ;    // 记录消息是否被丢弃 0 表示没有被丢弃, 1 表示消息被丢弃
  74.    unsigned int _version ; // 消息记录的版本号码,保证收发双方通信的正确性与及时性
  75. } ;

  76. // page structure 下面是页面结构划分,一个页面为数据库系统分配空间的最小计量单位
  77. /*********************************************************
  78. PAGE STRUCTURE     
  79. -------------------------
  80. | PAGE HEADER |  首先是页面头
  81. -------------------------
  82. | Slot List |    然后是 “插槽” 的链表
  83. -------------------------
  84. | Free Space |   接下来便是空闲空间
  85. -------------------------
  86. | Data |         最后便是存放数据的区域
  87. -------------------------
  88. 在这里补习一下数据库只中的基本知识。

  89. slotted-page structure

  90. **********************************************************/
  91. #define DMS_PAGE_EYECATCHER "PAGH"
  92. #define DMS_PAGE_EYECATCHER_LEN 4
  93. #define DMS_PAGE_FLAG_NORMAL 0
  94. #define DMS_PAGE_FLAG_UNALLOC 1
  95. #define DMS_SLOT_EMPTY 0xFFFFFFFF
  96. // 下面的这个结构体是用来创建 数据记录中的记录头 
  97. // 在该源代码中,一共有两个头记录,一个是 dmsPageHeader 用做标定页面中的记录头
  98. // 一个是 dmsHeader 用作数据文件中的记录头
  99. struct dmsPageHeader
  100. {
  101.    char _eyeCatcher[DMS_PAGE_EYECATCHER_LEN] ;
  102.    unsigned int _size ;  // 页面的大小
  103.    unsigned int _flag ;  // 页面是否被 dropped 
  104.    unsigned int _numSlots ;  // 槽的个数
  105.    unsigned int _slotOffset ; // 页面在槽中的偏移量
  106.    unsigned int _freeSpace ;  // 页面中空闲区域
  107.    unsigned int _freeOffset ; // 页面中空闲区域的偏移量
  108.    char _data[0] ;            // 页面中的指向存放数据空间的指针 通过 &(_data[0]) 来访问该空间
  109. } ;

  110. #define DMS_FILE_SEGMENT_SIZE 134217728 // 数据文件是以段为单位,一个段(槽 slot)的大小 (字节)
  111. #define DMS_FILE_HEADER_SIZE 65536       // 数据文件是由 文件头 + (0到多个)段(槽 slot)组成,文件头大小(字节)
  112. #define DMS_PAGES_PER_SEGMENT (DMS_FILE_SEGMENT_SIZE/DMS_PAGESIZE)
  113. // 每个文件段(槽)中包含的页面个数 = 文件段(槽,slot) 的大小 / 每个页面的大小
  114. #define DMS_MAX_SEGMENTS (DMS_MAX_PAGES/DMS_PAGES_PER_SEGMENT)
  115. // 数据文件中能够包含段的最大个数

  116. /*
  117. 这个类我理解为 数据库中用来存放数据的数据文件,data meta file
  118. 它的父类是抽象了操作系统文件对象的类: ossMmapFile 
  119. */
  120. class dmsFile : public ossMmapFile
  121. {
  122. private :
  123.    // points to memory where header is located
  124.    // 指向文件头的指针,该文件头中记录了文件的总长度,和用以表示文件是否被删除的标识符
  125.    // 以及文件的版本号码,用来防止同名文件和更新的时间不同造成的冲突
  126.    dmsHeader *_header ;

  127.    std::vector<char *> _body ;
  128.     //  文件是由头文件记录+(0到多个)文件段(槽)组成,而_body 变量用来记录的便是每个文件段
  129.     //  在系统中的起始地址值,而 body.size() 便是该数据文件中包含了多少个文件段(槽)

  130.     // free space to page id map
  131.    std::multimap<unsigned int, PAGEID> _freeSpaceMap ;
  132.    // 该变量用来记录的是空闲空间中的键值对结构,它是由一个键值对结构组成的
  133.    // 其中 unsigned int 便是 key 用来表示的是该页面在文件中的地址值
  134.    // 而作为 value 的 PAGEID 是文件中的页面的ID号码 (PAGEID 是 unsigned int 类型)
  135.  
  136.    ossSLatch _mutex ;         // 共享锁的互斥量
  137.    ossXLatch _extendMutex ;   // 互斥锁的互斥量

  138.    char *_pFileName ;         // 数据库中的数据文件名称
  139.    ixmBucketManager *_ixmBucketMgr ;        // 指向数据库中索引管理器对象的指针
  140. public :
  141.    dmsFile ( ixmBucketManager *ixmBucketMgr ) ;
  142.    ~dmsFile () ;
  143.    
  144.    // dmsFile 对象的构建函数和析构函数

  145.    int initialize ( const char *pFileName ) ;
  146.    
  147.     //initialize: initialize the dms file
  148.    // 通过传入的文件名称来初始化数据文件
  149.    
  150.    int insert ( bson::BSONObj &record, bson::BSONObj &outRecord, dmsRecordID &rid ) ;
  151.   
  152.    //insert: insert into file
  153.   // 将数据对象 record , outRecord 插入到数据文件中,并将插入的数据所在文件中的 (段ID, 页面ID)
  154.  // 存放到 dmsRecordID 对象中作为(值-地址 参数类型的)返回值

  155.    int remove ( dmsRecordID &rid ) ;
  156.   
  157.   //remove: 根据传入的 dmsRecordID(其中包含有数据记录所在的 段ID ,页面ID ) 在文件中查找数据记录并将其删除
  158.  // 同时释放数据记录所在的空间

  159.    int find ( dmsRecordID &rid, bson::BSONObj &result ) ;

  160.   //find : 根据传入的 dmsRecordID 结构体信息(段ID,页面ID) 在文件中找到数据记录并将该数据记录存放到
  161.  // BSONObj& 类型的 result 对象中,该对象之所以以引用类型作为参数而非对象,是因为该参数要作为返回值

  162. private :
  163.   
  164.    int _extendSegment () ;

  165.   //_extendSegment: create a new segment for the current file  
  166.   // 为当前文件扩充一个新的段, 当插入记录的时候,如果文件中的段中没有足够的空间
  167.   // 通过 _extendSegment 来为其追加空间,每次追加空间的大小是 DMS_FILE_SEGMENT_SIZE
  168.    int _initNew () ;

  169.  //_initNew: init from empty file, creating header only
  170.  // 创建一个空文件,在文件中仅仅包含一个头文件

  171.    int _extendFile ( int size ) ;

  172.  //_extendFile: extend the file for given bytes
  173.  // 同样是为文件扩充空间,扩充的空间大小为传入参数 size 个数的字节

  174.    int _loadData () ;

  175.  //_loadData: load data from beginning
  176.  // 将数据从数据文件导入到数据库中
  177.    int _searchSlot ( char *page, dmsRecordID &recordID, SLOTOFF &slot ) ;

  178.  //_searchSlot : search slot
  179. // 在给定页面全局地址之后,在当前文件中查找该页面所在的段(槽)ID和页面ID数值,将其置于
  180.  // 传入的变量 recordID 和 SLOTOFF(它的类型是 unsigned int) 中作为返回值

  181.    void _recoverSpace ( char *page ) ;

  182.  //_recoverSpace 将 *page 页面指针所指向的页面空间进行覆盖填充

  183.    void _updateFreeSpace ( dmsPageHeader *header, int changeSizePAGEID pageID ) ;

  184. //_updateFreeSpace : update free space
  185. // 将空闲区域的空间根据需要修改的大小和页面ID 等信息来更新数据记录的头记录中的信息

  186.    PAGEID _findPage ( size_t requiredSize ) ;
  187. //_findPage: find a page id to insert, return invalid_pageid if there's no page canb e found for required size bytes
  188. // 在插入数据记录的时候,根据记录的大小在数据文件的文件段(槽)中查找大小合适的空间,如果找到合适的页面空间
  189. // 则将该页面ID作为返回值返回,如果没有找到合适大小的空间那么就返回无效地址 

  190. public :
  191.    inline unsigned int getNumSegments ()
  192.    {
  193.       return _body.size() ;
  194.    }
  195. //getNumSegments: 该方法用来返回该数据文件中文件段(槽) 的个数是多少

  196.    inline unsigned int getNumPages ()
  197.    {
  198.       return getNumSegments() * DMS_PAGES_PER_SEGMENT ;
  199.    }
  200. //getNumPages : 该方法用来返回该数据文件中含有的页面的个数

  201.    inline char *pageToOffset ( PAGEID pageID )
  202.    {
  203.       if ( pageID >= getNumPages () )
  204.       {
  205.          return NULL ;
  206.       }
  207.       return _body [ pageID / DMS_PAGES_PER_SEGMENT ] + DMS_PAGESIZE * ( pageID % DMS_PAGES_PER_SEGMENT ) ;
  208.    }
  209. //pageToOffset :该方法根据传入的页面ID数值,来返回页面的地址值,
  210. // 它的流程是这样的:PAGEID 的数值从 (0 -- getNumPages()) 如果传入的页面ID大于文件中总共包含的页面个数,说明越界了
  211. // 返回错误值
  212. // 如果该 pageID 的数值合法,那么则这样来计算该页面在当前系统中的地址信息
  213. // 页面地址 = 页面所在的数据文件中的文件段起始地址 + 该页面在所在文件段中的偏移个数*页面大小

  214. // 其中页面所在的文件段起始地址 = _body[页面所在的文件段ID] 页面所在的文件段ID = 页面ID / 每个文件段中可以容纳的页面个数
  215. // 页面在所在文件段的偏移个数(也就是页面在该文件段中是第几个页面) = 页面ID % 每个文件段中可以容纳的页面个数

  216.    inline bool validSize ( size_t size )
  217.    {
  218.       if ( size < DMS_FILE_HEADER_SIZE )
  219.       {
  220.          return false ;
  221.       }
  222.       size = size - DMS_FILE_HEADER_SIZE ;
  223.       if ( size % DMS_FILE_SEGMENT_SIZE != 0 )
  224.       {
  225.          return false ;
  226.       }
  227.       return true ;
  228.    }
  229. //validSize : 这个方法是用来判断传入的文件长度是否是合法的数值
  230. // 这种方法的流程是这样的,因为文件= 文件头(DMS_FILE_HEADER_SIZE ) + (零到多个)文件段(DMS_FILE_SEGMENT_SIZE )
  231. // 而从前面的文件初始化方法中也可以得知,创建的空文件至少具有一个文件头,所以该文件的长度一定是 >= DMS_FILE_HEADER_SIZE 
  232. // 如果出现 size < DMS_FILE_HEADER_SIZE 那么返回错误信息
  233. // 如果满足题意,那么 使用size - DMS_FILE_HEADER_SIZE 之后,让 size 的值与 DMS_FILE_SEGMENT_SIZE 取余
  234. // 如果得到的是非零,说明文件段并非是整数个,同样报错
  235. } ;

  236. #endif
阅读(1167) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~