这几天翻了翻SequoiaDB的代码,记了点笔记。不保证下面内容的正确性(确定有错的地方)php
关于总体介绍,要看官方文件(这份文件看起来有些原始,应该是不一样时期不一样人的文件堆积起来的,创业公司不能要求过高),若是只关注一个摘要的话,请参考这个ppt。html
SequoiaDB 数据库是一款新型企业级分布式非关系型数据库,帮助企业用户下降 IT 成本,并对大数据的存储与分析提供了一个坚实,可靠,高效与灵活的底层平台。linux
优点
• 经过非结构化存储与分布式处理,提供了近线性的水平扩张能力,让底层的存储再也不成为瓶颈
• 提供了精确到分区级别的高可用性,预防服务器,机房故障以及人为错误,让数据24x7永远在线
• 提供了完善的企业级功能,让用户轻松管理高并发性任务,以及海量数据分析
• 加强的非关系型数据模型,帮助企业快速开发和部署应用程序,作到应用程序的随需应变 • 提供了最终一致性的保障,从根本上杜绝数据缺失
• 提供了在线应用与大数据分析的后台数据库的结合,经过读写分离机制作到同系统中数据分析与在线业 务互不干扰git
SequoiaDB 使用分布式架构,下图提供了对 SequoiaDB 体系结构的通常概述。
在客户机端(或应用程序端),本地或/和远程应用程序都与 SequoiaDB 客户机库连接。本地与远程客户机 使用 TCP/IP 协议与协调节点进行通信。
协调节点不保存任何用户数据,仅做为请求分发节点将用户请求分发至相应的数据节点。 编目节点保存系统的元数据信息,协调节点经过与编目节点通信从而了解数据在数据节点中的实际分布。一
个或多个编目节点可组成复制组集群。
数据节点保存用户的数据信息。一个或多个数据节点能够构成一个复制组(又称分区组)。复制组中每一个数 据节点都存储该复制组的一份完整数据,又称为复制组实例(或分区组实例);复制组中的数据节点之间采 用最终一致性同步数据,不一样的复制组中保存的数据无重复。
每一个复制组中能够包含一个或多个数据节点。当存在多个数据节点时,节点间数据进行异步复制。复制组中 能够存在最多一个主节点与若干从节点。其中主节点能够进行读写操做,从节点进行只读操做
从节点离线不影响主节点的正常工做。主节点离线后会在从节点中自动选举出新的主节点处理写请求
节点恢复后,或新的节点加入复制组后会进行自动同步,保障数据在同步完成时与主节点一致。程序员
在单个数据节点中的体系结构以下:
在数据节点,活动由引擎可调度单元(EDU)控制。每个节点为操做系统中的一个进程。每一个 EDU 在节点 中为一个线程。对于外部用户请求其处理线程为代理线程,对于集群内部请求则由同步代理线程处理分区内 同步事件;或分区代理线程处理分区间同步事件。
全部对数据的写操做均会记录入日志缓冲区,经过日志记录器将其异步写入磁盘。 用户数据会由代理线程直接写入文件系统缓冲池,而后由操做系统将其异步写入底层磁盘。sql
基本上和MongoDB区别不大,用js搞得,接口粗看起来也差很少,努努力估计双方能兼容。SQL的支持经过postgresql实现,同时还实现了rest接口。mongodb
SequoiaDB 数据库使用 JSON 数据模型,而非传统的关系型数据模型。
JSON 数据结构的全称为 JavaScript Object Notation,是一种轻量级的数据交换格式,很是易于人阅读和编 写,同时也易于机器生成和解析。其底层存储是BSON。The same as MongoDB
单个文件的限制依然是16MB。数据库
其具体实现也是经典的文件--数据段---数据页结构
文件能够跨页(废话,页的大小最大64k),可是跨不了块。
不知道是copy mongoDB仍是处于系统简化的考虑,底层实际上仍是mmap,也就是说读和换页什么的仍是靠系统本身去搞得,相对于mongoDB的改进在于有后台任务刷脏页到磁盘。windows
SequoiaDB 是 ACID 兼容文档级别,支持提交回滚等事务操做。默认状况下,SequoiaDB为了保证性能关闭事务的支持,若是用户须要则能够在启动数据库时指定参数打开事务。
在关闭事务支持时,一个单一操做可以写一个或多个字段,包括多个子文档的更新和数组元素更新。SequoiaDB 的 ACID 保证了文档更新的完整隔离性,任何错误都会引起回滚操做,以及客户能获得文档的一致性视图。
而当事务打开时,任何在事务启动到提交(回滚)之间的操做都会在数据节点写入事务日志并跟踪事务 ID。更改的记录在事务提交(回滚)前持有互斥锁,不可被其余会话更改。当前 SequoiaDB 事务的隔离级别为 UR。数组
SequoiaDB 采用集合级别的可配置一致性策略,容许用户在对记录修改时,根据该记录所在集合判断是否须要等待备节点的确认。
譬如,对于一个对性能要求较高、对数据可靠性要求通常的集合来讲,能够在建立集合时将 write concern 参数设置为 1,表明只要写入主节点就能够返回成功信息。而该操做会在后台异步地发送给从节点执行。
而对于性能要求较低、对数据可靠性要求很高的集合来讲,能够在建立集合时指定 write concern 参数为 3,表明该操做最少被三个节点确认执行成功后才可以返回。
当 write concern 的数量与该集合所在每一个副本集中包含节点数量相等时,系统能够被认为是强一致,不然为最终一致。
总体感受,SequoiaDB就是一个refine版的mongoDB。修正或者说增强了mongoDB的不少问题,好比那个臭名昭著的锁啊,页管理了,有限的支持事务啥的。这个应该就是所谓的后发优点了,前面有人给你趟过一遍坑了,问题简化了不少。
至于说双方谁的质量高,谁的分布式架构好,谁皮实耐用那就是一个仁者见仁智者见智的问题。
一切交给时间去考察吧。
我的理解的有点在于
这里的比较忽略XML和Json的巨大不一样之处。
和FounderXML相比,SequoiaDB放弃了或者说不支持不少东西:
CB的含义是control block。在SequoiaDB中这是很重要的接口:控制相关的逻辑就靠它了
/* _IControlBlock define */ class _IControlBlock : public SDBObject, public _ISDBRoot { public: _IControlBlock () {} virtual ~_IControlBlock () {} virtual SDB_CB_TYPE cbType() const = 0 ; virtual const CHAR* cbName() const = 0 ; virtual INT32 init () = 0 ; virtual INT32 active () = 0 ; virtual INT32 deactive () = 0 ; virtual INT32 fini () = 0 ; virtual void onConfigChange() {} } ; typedef _IControlBlock IControlBlock ;
整个数据库的入口在pmdMain.cpp中,基本上是读配置,初始化一堆mananger,恢复上次失败的数据的。关键代码是根据启动类型的不一样启动不一样的code block(CB):
void _pmdController::registerCB( SDB_ROLE dbrole ) { if ( SDB_ROLE_DATA == dbrole ) { PMD_REGISTER_CB( sdbGetDPSCB() ) ; // DPS PMD_REGISTER_CB( sdbGetTransCB() ) ; // TRANS PMD_REGISTER_CB( sdbGetClsCB() ) ; // CLS PMD_REGISTER_CB( sdbGetBPSCB() ) ; // BPS } else if ( SDB_ROLE_COORD == dbrole ) { PMD_REGISTER_CB( sdbGetTransCB() ) ; // TRANS PMD_REGISTER_CB( sdbGetCoordCB() ) ; // COORD PMD_REGISTER_CB( sdbGetFMPCB () ) ; // FMP } else if ( SDB_ROLE_CATALOG == dbrole ) { PMD_REGISTER_CB( sdbGetDPSCB() ) ; // DPS PMD_REGISTER_CB( sdbGetTransCB() ) ; // TRANS PMD_REGISTER_CB( sdbGetClsCB() ) ; // CLS PMD_REGISTER_CB( sdbGetCatalogueCB() ) ; // CATALOGUE PMD_REGISTER_CB( sdbGetBPSCB() ) ; // BPS PMD_REGISTER_CB( sdbGetAuthCB() ) ; // AUTH } else if ( SDB_ROLE_STANDALONE == dbrole ) { PMD_REGISTER_CB( sdbGetDPSCB() ) ; // DPS PMD_REGISTER_CB( sdbGetTransCB() ) ; // TRANS PMD_REGISTER_CB( sdbGetBPSCB() ) ; // BPS } else if ( SDB_ROLE_OM == dbrole ) { PMD_REGISTER_CB( sdbGetDPSCB() ) ; // DPS PMD_REGISTER_CB( sdbGetTransCB() ) ; // TRANS PMD_REGISTER_CB( sdbGetBPSCB() ) ; // BPS PMD_REGISTER_CB( sdbGetAuthCB() ) ; // AUTH PMD_REGISTER_CB( sdbGetOMManager() ) ; // OMSVC } //Data Management Service Control Block //This file contains code logic for data management control block, which is the metat// data information for DMS component. // 包括collection space等 PMD_REGISTER_CB( sdbGetDMSCB() ) ; // DMS // 和context相关,create和delete context PMD_REGISTER_CB( sdbGetRTNCB() ) ; // RTN // SQL PMD_REGISTER_CB( sdbGetSQLCB() ) ; // SQL // 集合 PMD_REGISTER_CB( sdbGetAggrCB() ) ; // AGGR //启动服务器/rest服务器/管理sessionInfo PMD_REGISTER_CB( sdbGetPMDController() ) ; // CONTROLLER }
对于不一样的EDU有不一样的入口函数,好比监听一个端口的作法就是
rc = pEDUMgr->startEDU( EDU_TYPE_TCPLISTENER, (void*)_pTcpListener, &eduID ) ;
其定义为:
// 根据type,启动一个线程(实际比这个复杂),把参数传递进去,并把eduid返回 INT32 _pmdEDUMgr::startEDU ( EDU_TYPES type, void* arg, EDUID *eduid )
系统会在启动一个线程,并调用pmdTcpListenerEntryPoint函数。
static const _eduEntryInfo entry[] = { ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_SHARDAGENT, FALSE, pmdAsyncSessionAgentEntryPoint, "ShardAgent" ), ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_COORDAGENT, FALSE, pmdAgentEntryPoint, "CoordAgent" ), // 最终调用_pmdDataProcessor::processMsg处理每条,顺便提一句这里有全部命令的列表 ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_AGENT, FALSE, pmdLocalAgentEntryPoint, "Agent" ), ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_REPLAGENT, FALSE, pmdAsyncSessionAgentEntryPoint, "ReplAgent" ), ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_HTTPAGENT, FALSE, pmdHTTPAgentEntryPoint, "HTTPAgent" ), ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_RESTAGENT, FALSE, pmdRestAgentEntryPoint, "RestAgent" ), // 端口监听 ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_TCPLISTENER, TRUE, pmdTcpListenerEntryPoint, "TCPListener" ), // rest监听 ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_RESTLISTENER, TRUE, pmdRestSvcEntryPoint, "RestListener" ), ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_CLUSTER, TRUE, pmdCBMgrEntryPoint, "Cluster" ), ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_CLUSTERSHARD, TRUE, pmdCBMgrEntryPoint, "ClusterShard" ), ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_CLSLOGNTY, TRUE, pmdClsNtyEntryPoint, "ClusterLogNotify" ), ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_REPR, TRUE, pmdAsyncNetEntryPoint, "ReplReader" ), ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_LOGGW, TRUE, pmdLoggWEntryPoint, "LogWriter" ), ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_SHARDR, TRUE, pmdAsyncNetEntryPoint, "ShardReader" ), ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_PIPESLISTENER, TRUE, pmdPipeListenerEntryPoint, "PipeListener" ), ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_BACKGROUND_JOB, FALSE, pmdBackgroundJobEntryPoint, "Task" ), ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_CATMAINCONTROLLER, TRUE, pmdCBMgrEntryPoint, "CatalogMC" ), ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_CATNODEMANAGER, TRUE, pmdCBMgrEntryPoint, "CatalogNM" ), ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_CATCATALOGUEMANAGER, TRUE, pmdCBMgrEntryPoint, "CatalogManager" ), ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_CATNETWORK, TRUE, pmdAsyncNetEntryPoint, "CatalogNetwork" ), ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_COORDNETWORK, TRUE, pmdCoordNetWorkEntryPoint, "CoordNetwork" ), ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_DPSROLLBACK, TRUE, pmdDpsTransRollbackEntryPoint, "DpsRollback"), ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_LOADWORKER, FALSE, pmdLoadWorkerEntryPoint, "MigLoadWork" ), ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_PREFETCHER, FALSE, pmdPreLoaderEntryPoint, "PreLoader" ), ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_OMMGR, TRUE, pmdCBMgrEntryPoint, "OMManager" ), ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_OMNET, TRUE, pmdAsyncNetEntryPoint, "OMNet" ), ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_SYNCCLOCK, TRUE, pmdSyncClockEntryPoint, "SyncClockWorker" ), ON_EDUTYPE_TO_ENTRY1 ( EDU_TYPE_MAXIMUM, FALSE, NULL, "Unknow" ) };
虽然定义了SDBObject,不少类都是从这个类继承而来,这个类重写了new/delete,不过到底层仍是使用了malloc解决问题,不排除之后会升级成mempool。目前仍是只加了一些checking而已。
看起来查询时用的内存都是临时分配的,而系统的内存应该仍是消耗在了mmap上。
一个基本的query执行方式:
在OSS中封装了常见的系统调用,好比read,create,open,malloc之类的。btw,有的文档中只列出了linux支持的版本,从代码上看windows应该也是支持的。