文档章节

SequoiaDB源码分析

AlvaYi
 AlvaYi
发布于 2015/04/10 19:03
字数 2363
阅读 137
收藏 0

1. 前言

在年前大概翻看了一下SequoiaDB的源代码,是为了寻找其宣称的比MongoDB领先的特性,尽管泛泛而过,但是其设计给我留下了比较深刻的映像。从业8年来,虽偶有冲动,从未敢于深入到数据库服务器这种由c++实现的动辄几十万行代码的大型软件的源代码中去,抱着学习的态度,第一次专门花了几周时间,对整个SequoiaDB的代码进行了研究,收获颇多,其高质量的代码,设计和思路,很值得学习。同时也希望在这里分享给大家,减少乐意学习的同学阅读代码的困难。

2.  总体架构

先上大图,对整个数据库引擎的模块有一个了解,能够很好的把握代码的脉络。

我根据我的理解,对数据库引擎的层次结构画了一个图如下。

从代码中可以看出,其层次结构非常的清晰。数据库引擎对外提供TCPREST两种形式的接口,不管哪种接口,最终会把协议上的payload转换为内部标准的Message,然后在内核中完成相应的操作。它使用直接内存映射的方式,使用底层的文件用于数据存储,这些都由一层称为DMS的数据管理服务层实现。

3. 控制块(ControlBlock)管理

在SequoiaDB中,功能模块通过ControlBlock组织在一起,其各种功能特性,通过各种各样的CB实现。其中有一个核心的CB称为KRCB(Kernel Control Block),由它负责对其他的CB进行管理,对于每个数据库进程,只有一个KRCB实例,其他所有的CB都可以调用KRCB上的对应方法获取。在不同的数据库角色下,会初始化不同的CB集注册到KRCB中。每种角色下的CB如下所示:

所有的CB都有init,active,deactive,fini四个方法,分别表示控制块的四个生命周期,这样的设计,实现了SequoiaDB模块化的设计思路,也便于其统一管理和控制,更方便了可扩展性。目前具备的CB分别实现了存储管理,事务管理,数据保护,集群管理,权限认证,SQL,聚合等功能,根据CB名称的简写,顾名思义,很容易了解其代表的含义,如果要增加功能,增加对应功能的CB即可。

4. 可执行单元及其管理

为了高效的处理各种操作,SequoiaDB实现了一个工作线程池的东西。线程池的设计在很多软件中都可以用到,其核心思想是为了减少线程的创建的开销,实现线程池中工作线程的复用。在SequoiaDB中其工作线程成为EDU(Engine Dispatchable Unit),它们在EDUMGR中进行管理,也就是说EDUMGR负责线程的创建,初始化,状态设置和销毁。需要工作线程进行处理的数据,都通过封装为事件消息发布到线程池的消息队列中让EDU进行处理。

其工作线程的状态按照如下的状态表转换:


Creating Running Waiting Idle Destroy
Creating createNewEDU



Running activeEDU - activeEDU activeEDU -
Waiting - waitEDU - - -
Idle deactiveEDU - deactiveEDU - -
Destroy destroyEDU - destroyEDU destroyEDU -

对于可执行单元,按照类型分为了28种,每种可执行单元有一个自己的类型编号,每种可执行单元的实现,实际上通过实现一个入口函数,在运行时根据可执行单元所代表的类型动态调用。在pmdEDUEntryPoint中,定义了一个getEntryFuncByType的函数,负责动态定位入口函数。

在数据库引擎启动时,由pmdController启动和注册三个system EDU,分别为TCPListener,RESTListener和SYNCClock,这三个系统EDU分别负责处理TCP连接,Rest连接和同步节点间的时钟。

所有的功能模块,最终靠各种不同类型的EDU来执行,他们之间的关系如下:

5.存储服务实现

在SequoiaDB中,数据存储使用BSON格式,它直接使用操作系统提供的文件内存映射方式,获得高效的文件访问方式。借助google的snappy库,SequoiaDB还实现了数据压缩的功能。在SequoiaDB中,CollectionSpace相当于传统数据库中的库的概念,Collection相当于表的概念;对于一个CollectionSpace有一个dmsStorageUnit负责管理。它有几个特点:第一,对于数据,索引和二进制文件的存储都单独处理,分别由dmsStorageData,dmsStorageIndex,dmsStorageLob进行处理。第二,数据文件分块存放,有多个实际的数据文件组成完整的存储空间,这跟传统的数据库中通常看到的方式不一样,我想这应该是更方便了在集群系统中的数据文件的处理。

6 典型源码分析

下面列举几个典型的过程,对于不熟悉SequoiaDB源码的同学,通过跟这几个过程,结合前面的内容,应该可以很容易的熟悉代码的主要脉络,进而快速的学习到整个数据库引擎的实现过程。

6.1 数据库引擎启动过程相关

pmdMain.cpp为主入口,在pmdMainThreadMain中,实现了读取命令行参数,初始化KRCB,然后调用pmdController中的registerCB方法,根据数据库的角色,注册ControlBlock,最后启动一个while循环每100毫秒检测一下状态,完成数据库的启动。

pmdController本身也是一个ControlBlock,在前面pmdMain中调用其registerCB的过程中,pmdController实例也注册到了KRCB中,随后其状态被统一管理。所以,虽然在pmdMain中仅仅调用了registerCB方法,但是实际上很多初始化工作由pmdController才刚刚开始。

在init方法中,pmdController主要实现了对监听端口的绑定。

在active方法中,启动和注册了三个系统EDU,分别负责时钟同步,TCP连接处理和REST连接处理。

以TCPListener为例,其启动之后将不断的检查事件队列中是否有传递进来的连接,如果有,则启动一个pmdAgent EDU,由其工作线程负责处理后续的工作。

在pmdTcpListener.cpp中,可以看到其其实只是定义了一个入口函数名为pmdTcpListenerEntryPoint,由它来处理当EDU type为EDU_TYPE_TCPLISTENER时的具体工作。其他类型的EDU和其类型的映射,通过查看pmdEDUEntryPoint.cpp中的getEntryFuncByType函数可以得到。

6.2 数据查询的实现流程

pmdTcpListener.cpp中实现了tcpListener这个EDU的入口函数,在这个EDU执行时,会不断的检查客户端到来的tcp连接,如果建立了连接,就启动一个Agent类型的EDU来处理这个连接。在pmdAgent.cpp中实现了Agent这个类型的EDU的入口函数。其主要代码如下:

      SOCKET s = *(( SOCKET *) &arg ) ;
      pmdLocalSession localSession( s ) ;
      localSession.attach( cb ) ;

      if ( pmdGetDBRole() == SDB_ROLE_COORD )
      {
         pmdCoordProcessor coordProcessor ;
         localSession.attachProcessor( &coordProcessor ) ;
         rc = localSession.run() ;
         localSession.detachProcessor() ;
      }
      else
      {
         pmdDataProcessor dataProcessor ;
         localSession.attachProcessor( &dataProcessor ) ;
         rc = localSession.run() ;
         localSession.detachProcessor() ;
      }
      localSession.detach() ;

根据当前启动的数据库引擎的角色,是协调节点还是数据节点,分别交由pmdCoordProcessor和pmdDataProcessor来处理。在pmdSession中实现了对socket上来的信息进行处理和转换,提取消息头,并且封装成后续模块能够处理的消息,如果不对通信协议细节感兴趣,其消息的处理过程和定义可以略过,看pmdProcessor即可。在processMsg中可以看到,在processMsg函数中,根据消息类型不同,转而让各种对应的函数处理,我们现在感兴趣的是_onQueryReqMsg函数,接着往下跟。

对于一个查询请求,需要提取的是集合名,返回数量,查询条件,返回字段,排序等信息,通过msgExtractQuery函数对请求中的相关信息进行提取:

rc = msgExtractQuery ( (CHAR *)msg, &flags, &pCollectionName,
                             &numToSkip, &numToReturn, &pQueryBuff,
                             &pFieldSelector, &pOrderByBuffer, &pHintBuffer ) ;

随后封装为对应的BSON对象,交给rtnQuery执行:

            BSONObj matcher ( pQueryBuff ) ;
            BSONObj selector ( pFieldSelector ) ;
            BSONObj orderBy ( pOrderByBuffer ) ;
            BSONObj hint ( pHintBuffer ) ;

            // 省略部分代码

            rc = rtnQuery( pCollectionName, selector, matcher, orderBy,
                           hint, flags, eduCB(), numToSkip, numToReturn,
                           _pDMSCB, _pRTNCB, contextID, &pContext, TRUE ) ;

在rtnQuery中,根据需要查找的结果集的名字查找其存储的块的位置,最后建立对应的上下文环境:

      rc = rtnResolveCollectionNameAndLock ( pCollectionName, dmsCB, &su,
                                             &pCollectionShortName, suID ) ;//找到结果集对应的存储块

      rc = su->data()->getMBContext( &mbContext, pCollectionShortName, -1 ) ;

      rc = rtnCB->contextNew ( ( flags & FLG_QUERY_PARALLED ) ?
                               RTN_CONTEXT_PARADATA : RTN_CONTEXT_DATA,
                               (rtnContext**)&dataContext, contextID, cb ) ;

然后根据是否需要排序,调用dataContext的open方法,见rtnContext.cpp中的_rtnContextData::open的代码。根据查询的条件,如果有索引则使用索引扫描,如果没有则使用表扫描。然后落实在_openTBScan和_openIXScan两个函数的实现之上。以表扫描为例,会把表所涉及的分区块的id号dmsExtendID找到并存储在这个dataContext中;最后调用此dataContext->getMore方法读取所需的数据。

根据前面整理出来的模块关系,搞清楚了EDU和CB这两个核心概念,基本上循着所需要探索的功能特性一步步把每个模块的代码搞清楚,SequoiaDB的内部世界也就毫无保留的展现出来了。从源码中学习大牛的思想,进而提高自己的能力,也许这就是开源的魅力吧!


© 著作权归作者所有

共有 人打赏支持
AlvaYi
粉丝 4
博文 3
码字总数 7331
作品 0
深圳
部门经理
私信 提问
SequoiaDB 巨杉数据库 2.6 版本正式发布

2016年,SequoiaDB已经正式进入 “2.0时代”,并且正式发布了SequoiaDB 2.0 企业版。2.0版本以来,各个版本在众多的企业用户中得到了广泛的应用,通过实战的检验,也得到了众多企业客户的认可...

巨杉数据库
2016/11/10
2.6K
9
Spark官方Blog:SequoiaDB与Spark深度整合

这是一篇来自我们的技术合作伙伴,SequoiaDB巨杉数据库的博客。作者是SequoiaDB的联合创始人和CTO王涛先生,SequoiaDB是一款JSON文档型的事务型数据库。王涛带着技术上非凡的远见,带领Sequo...

ark43420
2015/08/04
1
0
文档型 NoSQL 数据库 SequoiaDB 正式宣布开源

文档型 NoSQL 数据库 SequoiaDB (巨杉数据库)正式宣布开源, 项目源码已经托管到 Git@OSC,地址是: http://git.oschina.net/wangzhonnew/SequoiaDB 2014年12月19日,文档型 NoSQL 数据库 ...

oschina
2014/12/19
8.4K
82
【技术教程】SequoiaDB对接Kafka

1、 背景 当前互联网、金融、政府等行业,活动流数据几乎无处不在。对这种数据通常的处理方式是先把各种活动以日志的形式写入某种文件,然后周期性地对这些文件进行统计分析。活动流数据的这...

巨杉数据库
2017/11/02
0
0
bankmark 的 NoSQL 性能对比测试:Cassandra、MongoDB 和 SequoiaDB

近期,知名独立基准测评机构bankmark,针对SequoiaDB、MongoDB以及Cassandra三款NoSQL数据库产品做了性能对比测试。在所有的测试中,SequoiaDB的性能在大多数情境下都大大优于其他的产品,但...

红薯
2014/12/09
3.1K
9

没有更多内容

加载失败,请刷新页面

加载更多

Linux Wireshark普通用户启动使用方案

当系统安装好Wireshark后请正常启动是否可以进行正常使用,如果不行请参考下列指导 向系统添加一个用户组 sudo groupadd wireshark //如提示此组存在可跳过 将指定用户添加到这个组中 sudo...

CHONGCHEN
今天
2
0
CSS 选择器参考手册

CSS 选择器参考手册 选择器 描述 [attribute] 用于选取带有指定属性的元素。 [attribute=value] 用于选取带有指定属性和值的元素。 [attribute~=value] 用于选取属性值中包含指定词汇的元素。...

Jack088
今天
2
0
数据库篇一

数据库篇 第1章 数据库介绍 1.1 数据库概述  什么是数据库(DB:DataBase) 数据库就是存储数据的仓库,其本质是一个文件系统,数据按照特定的格式将数据存储起来,用户可以对数据库中的数据...

stars永恒
今天
4
0
Intellij IDEA中设置了jsp页面,但是在访问页面时却提示404

在Intellij IDEA中设置了spring boot的jsp页面,但是在访问时,却出现404,Not Found,经过查找资料后解决,步骤如下: 在Run/Debug Configurations面板中设置该程序的Working Directory选项...

uknow8692
昨天
4
0
day24:文档第五行增内容|每月1号压缩/etc/目录|过滤文本重复次数多的10个单词|人员分组|

1、在文本文档1.txt里第五行下面增加如下内容;两个方法; # This is a test file.# Test insert line into this file. 分析:给文档后增加内容,可以用sed 来搞定;也可以用while do done...

芬野de博客
昨天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部