這幾天翻了翻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應該也是支持的。