源代码链接地址
今天来分析的是位于 src/dms/ 下面的 Dms.cpp 源文件,其中的方法在头文件 src/include/Dms.hpp
中实现,所以首先来查看 Dms.hpp 这一头文件。
//Dms.hpp
-
/*******************************************************************************
-
Copyright (C) 2013 SequoiaDB Software Inc.
-
-
This program is free software: you can redistribute it and/or modify
-
it under the terms of the GNU Affero General Public License, version 3,
-
as published by the Free Software Foundation.
-
-
This program is distributed in the hope that it will be useful,
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-
GNU Affero General Public License for more details.
-
-
You should have received a copy of the GNU Affero General Public License
-
along with this program. If not, see <http://www.gnu.org/license/>.
-
*******************************************************************************/
-
#ifndef DMS_HPP__
-
#define DMS_HPP__
-
-
#include "ossLatch.hpp"
-
#include "ossMmapFile.hpp"
-
#include "bson.h"
-
#include "dmsRecord.hpp"
-
#include "ixmBucket.hpp"
-
#include <vector>
-
-
#define DMS_EXTEND_SIZE 65536
-
// DMS_EXTEND_SIZE 变量用于在 DMS 文件空间不够的时候用于扩展空间所用,每次扩展空间的大小是 65536 个字节
-
// 65536 = 1024*8*8 Byte = 8K*8 Byte = 64KB
-
-
#define DMS_PAGESIZE 4194304
-
// DMS_PAGESIZE 定义的是页面的大小, 在 emeralddb 数据库中,分配的空间是以页面为最小单位的,
-
// 通过变量 DMS_PAGESIZE 宏来定义数据库中一次分配的页面大小是 4194304 = 1024 *1024 *4B = 4MB
-
-
#define DMS_MAX_RECORD (DMS_PAGESIZE-sizeof(dmsHeader)-sizeof(dmsRecord)-sizeof(SLOTOFF))
-
// 通过宏 DMS_MAX_RECORD 来定义 DMS记录的最大值,规定上限,一旦超过上限系统会做出相关的处理措施,
-
// 以防止会因为空间消耗过多而造成错误
-
-
#define DMS_MAX_PAGES 262144
-
// DMS_MAX_PAGES 用于定义的是系统中最多可以分配的页面数目,如果系统中分配的页面数目超过这个数值的话,将会引发错误
-
// 262144 , 262144 * 每个页面的大小(4MB)= 1024*256*4MB = 1024GB
-
-
typedef unsigned int SLOTOFF ;
-
// 在这里通过 typedef 来定义一个 SLOTOFF 的新的类型
-
-
#define DMS_INVALID_SLOTID 0xFFFFFFFF
-
// 定义宏 DMS_INVALID_SLOTID 用来标定无效 SLOTOID 数值,常用于刚刚创建 SLOTID 变量时的初始化赋值
-
-
#define DMS_INVALID_PAGEID 0xFFFFFFFF
-
// 定义宏 DMS_INVALID_PAGEID 用来标定无效 PAGEID 数值,常用于初始化赋值
-
-
#define DMS_KEY_FIELDNAME "_id"
-
//定义文件的键值,通常用于为文件命名的时候,用于文件名的开头,标定文件的属性和类型
-
-
extern const char *gKeyFieldName ;
-
// 定义一个全局变量 getKeyFieldName 用来存放根据 键 key 锁定的 value 这个值,推断 应是用于散列索引
-
-
// each record has the following header, include 4 bytes size and 4 bytes flag
-
// 每一个记录中都包括一个下面定义的头文件,该头文件中有一个占位 4 个字节的int 变量, 用来指定记录的长度
-
// 头文件中还有一个占位 4个字节的 flag 标识符号 ,
-
// 标识符号可以被赋值为下面 DMS_RECORD_FLAG_NORMAL (数值0) 或是 DMS_RECORD_FLAG_DROPPED (数值 1)
-
// 如果是 0 表示该记录是正常的记录类型, 如果 flag 的数值为 1 表示这个记录已被删除
-
-
#define DMS_RECORD_FLAG_NORMAL 0 // DMS_RECORD_FLAG_NORMAL 用来表示记录类型为普通类型
-
#define DMS_RECORD_FLAG_DROPPED 1 // DMS_RECORD_FLAG_DROPPED 用来表示记录已经被删除
-
struct dmsRecord
-
{
-
unsigned int _size ; // 用来表示该记录的大小
-
unsigned int _flag ; // 用来表示该记录是否被删除
-
char _data[0] ; // 用来存放该记录中的数据信息, 这种表示方法在 unix/linux 通信中极为常见
-
// 是一种十分灵活并且节省空间的方法,如果该记录中没有消息的话, 则将该字段置为 NULL
-
// 如果该记录中需要携带一定信息的话,把 _data[0] 当做指针来使用,&(_data[0]) = (...)malloc(...) ;
-
// 来为其分配空间即可
-
} ;
-
-
// dms header , 下面定义的便是 dms 记录的头文件的信息字段
-
// 如果熟悉 TPC/IP 协议的话,那么我们应该知道,在消息头中多半定义的是,消息的长度,类型,以及消息序列号(超时重传)等等
-
-
#define DMS_HEADER_EYECATCHER "DMSH"
-
#define DMS_HEADER_EYECATCHER_LEN 4
-
#define DMS_HEADER_FLAG_NORMAL 0
-
#define DMS_HEADER_FLAG_DROPPED 1
-
-
#define DMS_HEADER_VERSION_0 0
-
// DMS_HEADER_VERSION 定义的是消息头的版本号,用于消息收发的双方保证传输的消息的正确性
-
-
#define DMS_HEADER_VERSION_CURRENT DMS_HEADER_VERSION_0
-
// DMS_HEADER_VERSION_CURRENT 定义的是消息头当前的版本号
-
-
struct dmsHeader
-
{
-
char _eyeCatcher[DMS_HEADER_EYECATCHER_LEN] ;
-
unsigned int _size ; // 记录的是消息的大小
-
unsigned int _flag ; // 记录消息是否被丢弃 0 表示没有被丢弃, 1 表示消息被丢弃
-
unsigned int _version ; // 消息记录的版本号码,保证收发双方通信的正确性与及时性
-
} ;
-
-
// page structure 下面是页面结构划分,一个页面为数据库系统分配空间的最小计量单位
-
/*********************************************************
-
PAGE STRUCTURE
-
-------------------------
-
| PAGE HEADER | 首先是页面头
-
-------------------------
-
| Slot List | 然后是 “插槽” 的链表
-
-------------------------
-
| Free Space | 接下来便是空闲空间
-
-------------------------
-
| Data | 最后便是存放数据的区域
-
-------------------------
-
在这里补习一下数据库只中的基本知识。
-
-
slotted-page structure
-
-
**********************************************************/
-
#define DMS_PAGE_EYECATCHER "PAGH"
-
#define DMS_PAGE_EYECATCHER_LEN 4
-
#define DMS_PAGE_FLAG_NORMAL 0
-
#define DMS_PAGE_FLAG_UNALLOC 1
-
#define DMS_SLOT_EMPTY 0xFFFFFFFF
-
// 下面的这个结构体是用来创建 数据记录中的记录头
-
// 在该源代码中,一共有两个头记录,一个是 dmsPageHeader 用做标定页面中的记录头
-
// 一个是 dmsHeader 用作数据文件中的记录头
-
struct dmsPageHeader
-
{
-
char _eyeCatcher[DMS_PAGE_EYECATCHER_LEN] ;
-
unsigned int _size ; // 页面的大小
-
unsigned int _flag ; // 页面是否被 dropped
-
unsigned int _numSlots ; // 槽的个数
-
unsigned int _slotOffset ; // 页面在槽中的偏移量
-
unsigned int _freeSpace ; // 页面中空闲区域
-
unsigned int _freeOffset ; // 页面中空闲区域的偏移量
-
char _data[0] ; // 页面中的指向存放数据空间的指针 通过 &(_data[0]) 来访问该空间
-
} ;
-
-
#define DMS_FILE_SEGMENT_SIZE 134217728 // 数据文件是以段为单位,一个段(槽 slot)的大小 (字节)
-
#define DMS_FILE_HEADER_SIZE 65536 // 数据文件是由 文件头 + (0到多个)段(槽 slot)组成,文件头大小(字节)
-
#define DMS_PAGES_PER_SEGMENT (DMS_FILE_SEGMENT_SIZE/DMS_PAGESIZE)
-
// 每个文件段(槽)中包含的页面个数 = 文件段(槽,slot) 的大小 / 每个页面的大小
-
#define DMS_MAX_SEGMENTS (DMS_MAX_PAGES/DMS_PAGES_PER_SEGMENT)
-
// 数据文件中能够包含段的最大个数
-
-
/*
-
这个类我理解为 数据库中用来存放数据的数据文件,data meta file
-
它的父类是抽象了操作系统文件对象的类: ossMmapFile
-
*/
-
class dmsFile : public ossMmapFile
-
{
-
private :
-
// points to memory where header is located
-
// 指向文件头的指针,该文件头中记录了文件的总长度,和用以表示文件是否被删除的标识符
-
// 以及文件的版本号码,用来防止同名文件和更新的时间不同造成的冲突
-
dmsHeader *_header ;
-
-
std::vector<char *> _body ;
-
// 文件是由头文件记录+(0到多个)文件段(槽)组成,而_body 变量用来记录的便是每个文件段
-
// 在系统中的起始地址值,而 body.size() 便是该数据文件中包含了多少个文件段(槽)
-
-
// free space to page id map
-
std::multimap<unsigned int, PAGEID> _freeSpaceMap ;
-
// 该变量用来记录的是空闲空间中的键值对结构,它是由一个键值对结构组成的
-
// 其中 unsigned int 便是 key 用来表示的是该页面在文件中的地址值
-
// 而作为 value 的 PAGEID 是文件中的页面的ID号码 (PAGEID 是 unsigned int 类型)
-
-
ossSLatch _mutex ; // 共享锁的互斥量
-
ossXLatch _extendMutex ; // 互斥锁的互斥量
-
-
char *_pFileName ; // 数据库中的数据文件名称
-
ixmBucketManager *_ixmBucketMgr ; // 指向数据库中索引管理器对象的指针
-
public :
-
dmsFile ( ixmBucketManager *ixmBucketMgr ) ;
-
~dmsFile () ;
-
-
// dmsFile 对象的构建函数和析构函数
-
-
int initialize ( const char *pFileName ) ;
-
-
//initialize: initialize the dms file
-
// 通过传入的文件名称来初始化数据文件
-
-
int insert ( bson::BSONObj &record, bson::BSONObj &outRecord, dmsRecordID &rid ) ;
-
-
//insert: insert into file
-
// 将数据对象 record , outRecord 插入到数据文件中,并将插入的数据所在文件中的 (段ID, 页面ID)
-
// 存放到 dmsRecordID 对象中作为(值-地址 参数类型的)返回值
-
-
int remove ( dmsRecordID &rid ) ;
-
-
//remove: 根据传入的 dmsRecordID(其中包含有数据记录所在的 段ID ,页面ID ) 在文件中查找数据记录并将其删除
-
// 同时释放数据记录所在的空间
-
-
int find ( dmsRecordID &rid, bson::BSONObj &result ) ;
-
-
//find : 根据传入的 dmsRecordID 结构体信息(段ID,页面ID) 在文件中找到数据记录并将该数据记录存放到
-
// BSONObj& 类型的 result 对象中,该对象之所以以引用类型作为参数而非对象,是因为该参数要作为返回值
-
-
private :
-
-
int _extendSegment () ;
-
-
//_extendSegment: create a new segment for the current file
-
// 为当前文件扩充一个新的段, 当插入记录的时候,如果文件中的段中没有足够的空间
-
// 通过 _extendSegment 来为其追加空间,每次追加空间的大小是 DMS_FILE_SEGMENT_SIZE
-
-
int _initNew () ;
-
-
//_initNew: init from empty file, creating header only
-
// 创建一个空文件,在文件中仅仅包含一个头文件
-
-
int _extendFile ( int size ) ;
-
-
//_extendFile: extend the file for given bytes
-
// 同样是为文件扩充空间,扩充的空间大小为传入参数 size 个数的字节
-
-
int _loadData () ;
-
-
//_loadData: load data from beginning
-
// 将数据从数据文件导入到数据库中
-
-
int _searchSlot ( char *page, dmsRecordID &recordID, SLOTOFF &slot ) ;
-
-
//_searchSlot : search slot
-
// 在给定页面全局地址之后,在当前文件中查找该页面所在的段(槽)ID和页面ID数值,将其置于
-
// 传入的变量 recordID 和 SLOTOFF(它的类型是 unsigned int) 中作为返回值
-
-
void _recoverSpace ( char *page ) ;
-
-
//_recoverSpace 将 *page 页面指针所指向的页面空间进行覆盖填充
-
-
void _updateFreeSpace ( dmsPageHeader *header, int changeSize, PAGEID pageID ) ;
-
-
//_updateFreeSpace : update free space
-
// 将空闲区域的空间根据需要修改的大小和页面ID 等信息来更新数据记录的头记录中的信息
-
-
PAGEID _findPage ( size_t requiredSize ) ;
-
//_findPage: find a page id to insert, return invalid_pageid if there's no page canb e found for required size bytes
-
// 在插入数据记录的时候,根据记录的大小在数据文件的文件段(槽)中查找大小合适的空间,如果找到合适的页面空间
-
// 则将该页面ID作为返回值返回,如果没有找到合适大小的空间那么就返回无效地址
-
-
public :
-
inline unsigned int getNumSegments ()
-
{
-
return _body.size() ;
-
}
-
//getNumSegments: 该方法用来返回该数据文件中文件段(槽) 的个数是多少
-
-
inline unsigned int getNumPages ()
-
{
-
return getNumSegments() * DMS_PAGES_PER_SEGMENT ;
-
}
-
//getNumPages : 该方法用来返回该数据文件中含有的页面的个数
-
-
inline char *pageToOffset ( PAGEID pageID )
-
{
-
if ( pageID >= getNumPages () )
-
{
-
return NULL ;
-
}
-
return _body [ pageID / DMS_PAGES_PER_SEGMENT ] + DMS_PAGESIZE * ( pageID % DMS_PAGES_PER_SEGMENT ) ;
-
}
-
//pageToOffset :该方法根据传入的页面ID数值,来返回页面的地址值,
-
// 它的流程是这样的:PAGEID 的数值从 (0 -- getNumPages()) 如果传入的页面ID大于文件中总共包含的页面个数,说明越界了
-
// 返回错误值
-
// 如果该 pageID 的数值合法,那么则这样来计算该页面在当前系统中的地址信息
-
// 页面地址 = 页面所在的数据文件中的文件段起始地址 + 该页面在所在文件段中的偏移个数*页面大小
-
-
// 其中页面所在的文件段起始地址 = _body[页面所在的文件段ID] 页面所在的文件段ID = 页面ID / 每个文件段中可以容纳的页面个数
-
// 页面在所在文件段的偏移个数(也就是页面在该文件段中是第几个页面) = 页面ID % 每个文件段中可以容纳的页面个数
-
-
inline bool validSize ( size_t size )
-
{
-
if ( size < DMS_FILE_HEADER_SIZE )
-
{
-
return false ;
-
}
-
size = size - DMS_FILE_HEADER_SIZE ;
-
if ( size % DMS_FILE_SEGMENT_SIZE != 0 )
-
{
-
return false ;
-
}
-
return true ;
-
}
-
//validSize : 这个方法是用来判断传入的文件长度是否是合法的数值
-
// 这种方法的流程是这样的,因为文件= 文件头(DMS_FILE_HEADER_SIZE ) + (零到多个)文件段(DMS_FILE_SEGMENT_SIZE )
-
// 而从前面的文件初始化方法中也可以得知,创建的空文件至少具有一个文件头,所以该文件的长度一定是 >= DMS_FILE_HEADER_SIZE
-
// 如果出现 size < DMS_FILE_HEADER_SIZE 那么返回错误信息
-
// 如果满足题意,那么 使用size - DMS_FILE_HEADER_SIZE 之后,让 size 的值与 DMS_FILE_SEGMENT_SIZE 取余
-
// 如果得到的是非零,说明文件段并非是整数个,同样报错
-
} ;
-
-
#endif
阅读(1167) | 评论(0) | 转发(0) |