SequoiaDB 筆記

SequoiaDB 筆記

這幾天翻了翻SequoiaDB的代碼,記了點筆記。不保證下面內容的正確性(確定有錯的地方)php

我的觀感

優勢

  1. 代碼還不錯,設計也算簡潔。
  2. EDU和CB的使用讓整個系統變得簡單不少,讓代碼更關注邏輯。
  3. 從設計上應該就是一個分佈式系統,麻雀雖小五臟俱全。
  4. 沒用什麼亂七八糟的東西改,基本是本身的代碼(雖然支持SQL可是基本能夠認爲是經過Postgresql支持的)。

八卦&吐槽

  1. 八卦一下Sequoia這個名字,本意是紅杉的意思,同時豐田有款車叫這個名字,還有那個著名的紅杉資本。不知道有什麼關係沒有。
  2. 掃了一眼他們的招聘列表,竟然沒有招數據庫開發的,不知道是數據庫的人已經夠了仍是不打算搞了。
  3. 吐槽一下縮寫,我實在沒法從代碼的縮寫裏分辨出含義來,好比bar這個文件夾,裏面包含了barBackup和barRestore,難道bar = backup + restore? 問題是barrier情何以堪?ixm = index mananger?dps = Data Protection Service?PD=Problem Determination ? PMD = Process MoDel ? 又好比_extentInsertRecord的參數叫deletedRecordPtr, 而_extentRemoveRecord的參數叫作recordPtr
  4. 註釋極少,基本上仍是沒有養分的重複一下類名什麼的,也沒有找到TODO之類,更關鍵的是註釋基本都是用縮寫表示,不排除開源版本特地把註釋都刪掉了。代碼顯得有點太乾淨了,乾淨的有點過了。

綜述

關於總體介紹,要看官方文件(這份文件看起來有些原始,應該是不一樣時期不一樣人的文件堆積起來的,創業公司不能要求過高),若是隻關注一個摘要的話,請參考這個ppthtml

SequoiaDB 數據庫是一款新型企業級分佈式非關係型數據庫,幫助企業用戶下降 IT 成本,並對大數據的存儲與分析提供了一個堅實,可靠,高效與靈活的底層平臺。linux

優點
• 經過非結構化存儲與分佈式處理,提供了近線性的水平擴張能力,讓底層的存儲再也不成爲瓶頸
• 提供了精確到分區級別的高可用性,預防服務器,機房故障以及人爲錯誤,讓數據24x7永遠在線
• 提供了完善的企業級功能,讓用戶輕鬆管理高併發性任務,以及海量數據分析
• 加強的非關係型數據模型,幫助企業快速開發和部署應用程序,作到應用程序的隨需應變 • 提供了最終一致性的保障,從根本上杜絕數據缺失
• 提供了在線應用與大數據分析的後臺數據庫的結合,經過讀寫分離機制作到同系統中數據分析與在線業 務互不干擾git

系統架構

SequoiaDB 使用分佈式架構,下圖提供了對 SequoiaDB 體系結構的通常概述。
Alt text
在客戶機端(或應用程序端),本地或/和遠程應用程序都與 SequoiaDB 客戶機庫連接。本地與遠程客戶機 使用 TCP/IP 協議與協調節點進行通信。
協調節點不保存任何用戶數據,僅做爲請求分發節點將用戶請求分發至相應的數據節點。 編目節點保存系統的元數據信息,協調節點經過與編目節點通信從而瞭解數據在數據節點中的實際分佈。一
個或多個編目節點可組成複製組集羣。
Alt text
數據節點保存用戶的數據信息。一個或多個數據節點能夠構成一個複製組(又稱分區組)。複製組中每一個數 據節點都存儲該複製組的一份完整數據,又稱爲複製組實例(或分區組實例);複製組中的數據節點之間採 用最終一致性同步數據,不一樣的複製組中保存的數據無重複。
每一個複製組中能夠包含一個或多個數據節點。當存在多個數據節點時,節點間數據進行異步複製。複製組中 能夠存在最多一個主節點與若干從節點。其中主節點能夠進行讀寫操做,從節點進行只讀操做
Alt text
從節點離線不影響主節點的正常工做。主節點離線後會在從節點中自動選舉出新的主節點處理寫請求
Alt text
節點恢復後,或新的節點加入複製組後會進行自動同步,保障數據在同步完成時與主節點一致。程序員

在單個數據節點中的體系結構以下:
Alt text
在數據節點,活動由引擎可調度單元(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 的數量與該集合所在每一個副本集中包含節點數量相等時,系統能夠被認爲是強一致,不然爲最終一致。

對比

VS MongoDB

總體感受,SequoiaDB就是一個refine版的mongoDB。修正或者說增強了mongoDB的不少問題,好比那個臭名昭著的鎖啊,頁管理了,有限的支持事務啥的。這個應該就是所謂的後發優點了,前面有人給你趟過一遍坑了,問題簡化了不少。
至於說雙方誰的質量高,誰的分佈式架構好,誰皮實耐用那就是一個仁者見仁智者見智的問題。
一切交給時間去考察吧。
我的理解的有點在於

  1. 實現了行級鎖,相對mongoDB極大的改善了插入性能,一些測試報告 也證實了這一點。
  2. 雖然依然是mmap可是彷佛作了一些精細化管理。
  3. 有了最基礎的事務支持。
  4. 沒作複雜的mempool,緩存池之類的事情;把有限的精力投入到了最須要的地方。(竊覺得即便依賴系統實現總比本身山寨一個亂七八糟的東西強,寫內核的人是頂尖程序員,即便他們的程序不是爲數據庫優化的)。

VS FounderXML

這裏的比較忽略XML和Json的巨大不一樣之處。
和FounderXML相比,SequoiaDB放棄了或者說不支持不少東西:

  1. 事務。換到的是相對簡單的設計(C:SequoiaDB支持到read uncommit,比徹底不支持好那麼一點點),不用引入複雜的Postgresql或其的事務數據庫/引擎。固然能夠本身開發一個,嗯,這麼天才的主意仍是算了吧。
  2. 大文件支持,有沒有,這是一個有點複雜的選擇,支持吧,確實給系統帶來更多的複雜性,性能也受到拖累;不支持吧,確實有實際須要。就快速出一個產品的角度說,仍是不支持的好。
    相對於FounderXML,SequoiaDB主要的進步點在於:
  3. 用簡化的設計(代價是犧牲某些功能)快速完成一個產品。
  4. 依賴相對較少,其核心代碼基本上都是本身開發的。只依賴boost,php,parser,crypto等少數東西。忽然想起一個哥們吐槽某數據庫是學化學的人寫的,少依賴點離化學就遠了一點點。

其它和代碼相關的

CB

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)

對於不一樣的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執行方式:

  1. rtnQuery 完成查詢計劃的build(查詢優化彷佛作了一些事情,還沒來得及細看),定位到第一個結果處。
  2. rtnGetMore 從第一個結果處繼續掃描結果。
    從而避免了可能的查詢結果過大的問題。

操做系統封裝

在OSS中封裝了常見的系統調用,好比read,create,open,malloc之類的。btw,有的文檔中只列出了linux支持的版本,從代碼上看windows應該也是支持的。

相關文章
相關標籤/搜索