SequoiaDB的查詢執行過程

SequoiaDB的查詢執行過程

繼續讀了SDB的代碼,重點仍是內核的代碼。從客戶端–查詢優化—查詢執行的過程來描述一下查詢的過程。但願能夠搞清楚2個問題:php

  1. SDB能作什麼查詢?html

  2. 搞清楚SDB是怎麼作查詢的?node

第一個問題的答案是:mysql

  1. 理論上,SDB能作mongoDB能作的全部查詢,SDB還支持SQL(我指的是SDB內建的支持,不是經過PG支持的查詢)c++

  2. 實際上,我沒有一個一個去測,SDB看起來mongoDB類的查詢基本上完成了;SQL的支持還更像一個玩具。至於說我怎麼獲得這個結論的,請看下面的分析吧。git

至於第二個問題的答案請看下文:程序員

  • 第1部分是技術感言&&吐槽sql

  • 第2部分是客戶端接口。數據庫

  • 第3部分是Json查詢分析。編程

  • 第4部分是SQL查詢分析。

  • 最後閒扯一下SDB和mongoDB的對比。

注意:

  1. 本文涉及到查詢解析/優化/執行都是指的是單機版本或者分佈式條件下數據節點。基本不涉及CoodNode上的解析部分。

  2. 內容以我感興趣&&能看懂爲主,受限於個人水平/工做經歷/對某數據庫的怨念,不保證內容的正確性,也不保證是系統的精華部分,更不表明SDB官方(他們一毛錢都沒給我)。

  3. 文中提到的mongoDB是2.6.1版本。

  4. 雖然SDB支持事務,可是本文不涉及事務相關的內容。

1. 技術感言

就我看來:SDB就是一個json模型的關係數據庫。具體說就是分佈式關係數據庫(DB2)和以mongoDB爲表明的key-document數據庫的雜交產物。(DB2出身的人受了mongoDB的啓發搞出來的東西?)

目前看來,SDB只搞定了mongoDB相關的東西,SQL那面還沒徹底搞定(固然搞定SQL很難了)。鑑於SDB已經有一個簡單的SQL框架,我猜SDB可能打算同時支持mongoDB的接口和一個SQL子集,以彌補mongoDB接口的不足(好比mongoDB不支持join,而join在OLAP的場景下仍是頗有用的)。

數據庫,尤爲是沒明顯bug的的數據庫,是很難搞的;加上事務就更難搞;加上分佈式越發難搞;加上週邊支持,幾乎就搞不定了;最後把它賣出,幾乎是不可能的,歷史上成功的公司很少,好比Oracle。SDB想json/SQL通吃,只能說前途是光明的,道路是XXX的。

問題和可能的改進

  1. BSON的限制是16MB,問題是若是用戶的數據超過了16MB怎麼辦?(我指的是單個json文件超過16MB)。固然mongoDB也解決不了這個問題,目前只能在應用層去解決,應用層解決就須要N個json作join的高效方式。

  2. BSON這種自解釋的結構好處就很少吹了。但Bson存在着數據量偏大的問題。好比:{'name':'zhangsan', 'age':16}。這個包在網絡上傳輸和解析都沒有什麼問題。問題在於它存儲在collection裏的時候,相同的key會重複n屢次。固然能夠把’name‘簡化成’n’:問題是英文字母只有26個,並且太短的名字基本表達不了什麼含義。我方案是bson保存在數據庫裏的時候能夠考慮把key轉換成id,在返回給用戶以前在把它還原成key。這樣作能夠減小io(固然同時增長了cpu和系統複雜性)。

  3. 底層的內存分配基本上都是直接使用的malloc/free, 在代碼中也大量的使用了stl,好比vector之類的。而沒有使用memoryContext。好處是簡化了開發,壞處是glic的ptmalloc有的時候不靠譜的,好比這個。理論上講,這麼作一方面存在內存泄漏的危險,另外一方面故障率可能會提高。這種內存管理方式和mmap結合起來遲早是個坑,坑到啥程度很差說,但終歸是個隱患。老一代的數據庫系統,好比pg,mysql等等有本身buffer 管理和memoryContext管理不是沒有道理的。(PS. SDB號稱核心代碼都不用STL,應該是爲了不我說的問題吧)

吐槽

  1. ms 開源代碼裏doc文件夾被刪掉了,刪除時間在14年12月31日,這個新年禮物可不怎麼好。做爲一個開源項目,有doc是應該的。國內的作數據庫人才很少,作出一個完整產品的更少。說老實話大牛們不會抄,不牛的想抄的其實也抄不明白。

  2. 再次吐槽簡寫,代碼裏的簡寫實在看不懂。做爲這個世界上最不會起名字的碼農,我實在猜不出來不少縮寫是啥,雖然我能猜出它是幹嗎的。

  3. 除了對外接口,在內部的不少函數中也使用了BsonObj,我的認爲這是個雙刃劍了:一方面在寫代碼的時候確實方便;另一方面時間長了,這個參數就變成一個大籮筐,裏面什麼都有。設想一個場景:若是一個函數裏面10幾20幾個參數的話,會引起無數吐槽;可是若是用一個BsonObj當參數,可

縮寫列表

淚流滿面的是,我找到了部分縮寫的對應關係:

auth : Authentication 
bps : BufferPool Services 
cat : Catalog Services 
cls : Cluster Services 
dps : Data Protection Services 
mig : Migration Services 
msg : Messaging Services 
net : Network Services 
oss : Operating System Services 
pd : Problem Determination 
rtn : RunTime 
sql : SQL Parser 
tools : Tools 
bar : Backup And Recovery 
client : Client 
coord : Coord Services 
dms : Data Management Services 
ixm : Index Management Services 
mon : Monitoring Services 
mth : Methods Services 
opt : Optimizer 
pmd : Process Model 
rest : RESTful Services 
spt : Scripting 
util : Utilities

做爲一個不會起名字的人,我表示很欣慰。


2. 客戶端

網絡傳輸

第三方庫

使用的是boost的庫,在boost的基礎上實現了同步/異步的傳輸。

協議

網絡傳輸協議的頭是MsgHeader。

struct _MsgHeader
    {
    SINT32 messageLength ; // total message size, including this SINT32 opCode ; // operation code UINT32 TID ; // client thead id MsgRouteID routeID ; // route id 8 bytes UINT64 requestID ; // identifier for this message } ; typedef struct _MsgHeader MsgHeader ;

在這個頭的基礎上,會有更多的結構出來,好比MsgOpQuery,MsgOpGetMore,MsgOpDelete等等。 
這個header其他部分都好理解,主要說明兩個東西

  1. opCode 在msg.h裏的enum MSG_TYPE中有詳細定義,這裏不拷貝了,有意思的是request和reply的id是基本上對應的: #define MAKE_REPLY_TYPE(type) ((UINT32)type | 0x80000000), 好比 MSG_BS_INSERT_REQ = 2002, MSG_BS_INSERT_RES = MAKE_REPLY_TYPE(MSG_BS_INSERT_REQ),.

  2. routeID 路由的id

union _MsgRouteID
    { struct {
          UINT32 groupID ;
          UINT16 nodeID ;
          UINT16 serviceID ;
       } columns;
       UINT64 value ;
    } ;

其中,servieId是:

typedef enum _MSG_ROUTE_SERVICE_TYPE {  MSG_ROUTE_LOCAL_SERVICE = 0,  MSG_ROUTE_REPL_SERVICE,
       MSG_ROUTE_SHARD_SERVCIE,
       MSG_ROUTE_CAT_SERVICE,
       MSG_ROUTE_REST_SERVICE,
       MSG_ROUTE_OM_SERVICE,

       MSG_ROUTE_SERVICE_TYPE_MAX
    }MSG_ROUTE_SERVICE_TYPE;

至於說_MsgRoutId之因此是一個union,緣由在於MsgRouteID畢竟是一個id,把他看成id來用的時候,難免要涉及到compare,使用value就比較方便了,而且性能也更好。

Json API

json api其實沒有多少好說的,託js的福,實際上在客戶端就已經完成了傳統數據庫parse的過程,不用yacc/lex一番,也不用定義一堆相似於xxxValue,xxxTable,xxxRef,xxxFun,xxxItem之類的東西,通通都是BsonObj就能夠。讓咱們看一下query的接口

virtual INT32 query  ( sdbCursor &cursor, const bson::BSONObj &condition = _sdbStaticObject, const bson::BSONObj &selected  = _sdbStaticObject, const bson::BSONObj &orderBy   = _sdbStaticObject, const bson::BSONObj &hint      = _sdbStaticObject,
                             INT64 numToSkip    = 0,
                             INT64 numToReturn  = -1,
                             INT32 flag         = 0 ) = 0 ;

SQL API

sql的接口和關係數據庫差很少。

virtual INT32 exec( const CHAR *sql,
                          sdbCursor &result ) = 0 ;

postgresql API

安裝文檔

  • 本質上,postgresql的插件就是一個client程序,有點相似於mysql Federated引擎,就是一個代理。

  • 至於性能能達到什麼程度很差說。可是做爲一個噱頭或者說添頭足夠了。

  • 用一個很低的成本完成了對SQL的支持。SDB自身也是支持SQL,雖然還不成熟

沒有去細看storm/hive/hadoop什麼的,猜想實現方式相似。


3. Json接口查詢過程

json接口這個稱呼可能不夠準確,就是mongoDB那樣的接口。

總體流程

下圖是我本身畫的, 中間過程名字是我本身起的,不大嚴謹。

解析

json沒有什麼好解析的,語法樹已經傳過來了。 
對於json接口來講入口在rtnQuery,最終會獲得一個物理查詢計劃。(這個說法其實不夠嚴謹,相似group by的操做是在rtnAggregate裏面完成的,這裏忽略吧)

生成查詢計劃

其輸出 optAccessPlan是比較複雜的class,對應着具體的查詢計劃。

INT32 _rtnAccessPlanManager::getPlan ( const BSONObj &query,
                                          const BSONObj &orderBy,
                                          const BSONObj &hint,
                                          const CHAR *collectionName,
                                          optAccessPlan **out ) class _optAccessPlan : public SDBObject
   { private:
      dmsExtentID _indexCBExtent ;
      dmsExtentID _indexLID ;

      OID _indexOID ; // the oid for the index, for validation mthMatcher _matcher ; // matcher that should be used by the plan rtnPredicateList *_predList ; // predicate list that generated from _dmsStorageUnit *_su ; // pointer for the storage unit _rtnAccessPlanManager *_apm ; // parent access plan manager CHAR _collectionName[ DMS_COLLECTION_NAME_SZ+1 ] ; CHAR _idxName[IXM_KEY_MAX_SIZE + 1] ;

      BSONObj _orderBy ; // order by called by the user BSONObj _query ; // query condition called by the user BSONObj _hint ; // hint called by the user BOOLEAN _hintFailed ;

      INT32 _direction ; // direction called by the user optScanType _scanType ; BOOLEAN _isInitialized ; BOOLEAN _isValid ; BOOLEAN _isAutoPlan ; // auto plan, TRUE when the plan is not UINT32 _hashValue ;
      ossAtomicSigned32 _useCount ; BOOLEAN _sortRequired ; // whether we need to explicit sort the resultset ....

_rtnAccessPlanManager::getPlan 的邏輯很簡單:

  1. planCache裏有對應build參數的計劃,若是有則返回;

  2. 不然build一個新的計劃。 
    在build的過程當中夾雜了一個很是重要的函數:

其中cost的估算方法在

INT32 _optAccessPlan::_estimateIndex ( dmsExtentID indexCBExtent,
                                          INT64 &costEstimation,
                                          INT32 &dir,
                                          _estimateDetail &detail )
    ....
         orderFactor = 1.0f - ((nFields == 0) ? (0) :
                                (((FLOAT32)matchedFields)/((FLOAT32)nFields)));
         orderFactor = OSS_MIN(1.0f, orderFactor) ;
         orderFactor = OSS_MAX(0.0f, orderFactor) ;
    ..... if ( nFields == 0 || nQueryFields == 0 )
            queryFactor = 1.0f ; else queryFactor = 1.0f - ((FLOAT32)matchedFields)/
                  (OSS_MIN(((FLOAT32)nFields),((FLOAT32)nQueryFields))) ;
         queryFactor = OSS_MIN(1.0f, queryFactor) ;
         queryFactor = OSS_MAX(0.0f, queryFactor) ;

我的理解就是index的選擇儘可能和條件/order保持一致。

openCursorBaseOnPlan

實際上調用的是下面的函數,基本根據scan的類型,打開響應的table/index。注意scan過程根本沒有真正獲取任何數據,只是拉開架勢準備scan而已。

INT32 _rtnContextData::open( dmsStorageUnit *su, dmsMBContext *mbContext,
                                optAccessPlan *plan, pmdEDUCB *cb, const BSONObj &selector, INT64 numToReturn,
                                INT64 numToSkip, const BSONObj *blockObj,
                                INT32 direction )
      .... if ( TBSCAN == plan->getScanType() )
      {
         rc = _openTBScan( su, mbContext, plan, cb, blockObj ) ;
         PD_RC_CHECK( rc, PDERROR, "Failed to open tbscan, rc: %d", rc ) ;
      } else if ( IXSCAN == plan->getScanType() ) {
         rc = _openIXScan( su, mbContext, plan, cb, blockObj, direction ) ;
         PD_RC_CHECK( rc, PDERROR, "Failed to open ixscan, rc: %d", rc ) ;
      }
      ...

查詢執行

對於Json接口而言,在rtnQuery的時候,除了build好了plan其實什麼都沒有作。真正開始執行的時候:會展轉調用到下面的函數,基本上這個函數的主要用途就是,基本上就是調整遊標到指定的位置,而後把結果放到bufferObj裏面去。

INT32 _rtnContextBase::getMore( INT32 maxNumToReturn,
                                   rtnContextBuf &buffObj,
                                   pmdEDUCB *cb )

具體的工做其實是由

class _dmsScanner : public SDBObject
   { public:
         _dmsScanner ( _dmsStorageData *su, _dmsMBContext *context,
                       _mthMatcher *match,
                       DMS_ACCESS_TYPE accessType = DMS_ACCESS_TYPE_FETCH ) ; virtual ~_dmsScanner () ; public: virtual INT32 advance ( dmsRecordID &recordID,
                                 ossValuePtr &recordDataPtr,
                                 _pmdEDUCB *cb, vector<INT64> *dollarList = NULL ) = 0 ;
  }

完成的,對於table,index都有本身的實現。注意還同時處理了事務,skip,match之類的,因此代碼看起來有點混亂。因爲SDB底層其實是由mmap實現的,內存結構和存儲結構是一致的。

4. SQL接口的查詢過程

SQL解析

詳細的語法定義請見:engine/include/SQlgrammer.hpp, 具體支持的方式不是傳統的yacc而是boost的spirit庫。這裏有一篇中文的簡介。解析器生成程序遵循 Extended Backus Naur Form (EBNF) 規範並使用 C++ 編寫。簡單說這是一個簡化版的yacc。

我猜想SDB應該不會花大力氣去支持什麼SQL 2008之類的標準,應該只是支持簡單的SQL語法,最終完成SQL和Json的某種和解。最大多是像hive同樣,搞本身的SQL玩兒:叫SSQL?


查詢優化

下面的文字來源於我自身感悟,和SDB徹底無關,和這篇文章關係都不大,也不保證正確性。

所謂查詢優化,既能夠理解成通常意義上優化,也能夠狹義理解成針對查詢計劃生成的優化。好比採用group commit能夠有效減小io,讓查詢變得更快,這屬於前者;在一堆index中選擇一個selectivity最小的索引生成相應的查詢計劃,則屬於後者。本文所指的查詢優化是後者。 
查詢優化有基於規則的方法,有基於代價的優化方法,或者二者的結合。真正作的極致的系統其實不多不多。感興趣的同窗請參考 http://blog.163.com/li_hx/。基本上PG和Mysql有些事情作的也很土。 
一個好的優化器,不只僅應該完成優化任務,更重要的是要清晰,正交,可維護,可配置,易調試。 
忽然想起見過的一個的MPP SQL優化器,輸入是一個SQL語句,輸出是一系列的SQL語句,能夠直接拿到目標數據節點上執行。徹底就是用一個又一個的規則的結合,在Mysql那一團麻的優化比起來,嗯,能夠說是彙編語言和Java的區別。印象中也是DB2的人寫的。 
閒扯一句,對於查詢優化這種事兒來講,用C++開發彷佛不是什麼好的主意。雖然主流數據庫都是這麼作的,可是如今的編程語言選擇比Oracle,DB2出道的那個時代豐富多了。更重要的是查詢優化也不怎麼涉及底層操做,c/c++剩下的惟一好處也許就是和其它的代碼能夠共用一套數據結構這個優點了;不過壞處卻很明顯:容易core,編程複雜,不少東東沒有內建的語言支持等等。嗯,找不到一個會其它語言的程序員,拜託,能搞定查詢優化的程序員會care學習新的語言?

扯遠了,書歸正傳。下面的內容包括:

  1. 根據語法樹構造邏輯執行計劃。(相對容易)

  2. 世面上的SQL數據庫應該有個過程是把邏輯執行計劃變成等價的邏輯查詢計劃(好比out join to inner join之類的,根本目的是把查詢變簡單)。彷佛SDB沒有這個過程(也許有,我沒注意)

  3. 構造物理查詢計劃(容易)若是有若干個可行的查詢計劃,選出其中最好的一個。(很難的問題)

  4. 不包括查詢執行(真正從磁盤上獲取數據),相關內容會在查詢執行章節介紹。

查詢優化的流程

入口在_pmdDataProcessor::_onSQLMsg–> _sqlCB::exec

步驟1 邏輯查詢計劃的生成(buildLogicPlan)

qgmBuilder builder( container->ptrTable(),
                          container->paramTable()) ;
      rc = builder.build( container->ast().trees, opti ) ;

具體能夠看_qgmbuilder的build函數

public: //  INT32 build( const SQL_CONTAINER &tree,
                   _qgmOptiTreeNode *&node ) ;

最終的結果會獲得一個邏輯語法樹,實現的很經典。關於語法樹的定義以下:

class _qgmOptTree : public SDBObject
    { private:
             qgmOptiTreeNode            *_pRoot ;
             qgmPtrTable                *_prtTable ;
             qgmParamTable              *_paramTable ;
         ...
    } class _qgmOptiTreeNode : public SDBObject
   { friend class _qgmOptTree ; public:
      .... public:
      qgmOptiTreeNodePtrVec      _children ;
      qgmField                   _alias ;
      _qgmOptiTreeNode           *_father ;
      QGM_OPTI_TYPE              _type ;
      BOOLEAN                    _releaseChildren ;
      _qgmPtrTable               *_table ;
      _qgmParamTable             *_param ;
      QGM_HINS                   _hints ; protected:
      qgmOprUnitPtrVec           _oprUnits ; private:
      UINT32                     _nodeID ;

   } ;

其中幾個基礎元素是:

class _qgmField : public SDBObject
   { private: const CHAR *_begin ;
      UINT32 _size ;
   ...
class _qgmDbAttr : public SDBObject
   { public:
      _qgmDbAttr( const qgmField &relegation, const qgmField &attr )
      :_relegation(relegation),
       _attr(attr)
      {

      }
   ... typedef vector< qgmDbAttr* > qgmDbAttrPtrVec ; typedef vector< qgmDbAttr > qgmDbAttrVec ; struct _qgmOpField : public SDBObject
   {
      qgmDbAttr value ;
      qgmField alias ;
      INT32 type ;
   ... typedef std::vector< qgmOpField > qgmOPFieldVec ; typedef std::vector< qgmOpField* > qgmOPFieldPtrVec ;

至於說怎麼把一個ast結構變成一個_qgmTreeNode的過程,具體過程參考class _qgmBuilder::build。是一個比較複雜&&機械的過程,這裏就不贅述了。

步驟2 extend

rc = opti->extend( extend ) ;

extend是幹什麼的呢? 
opti的類型是_qgmOptiTreeNode,其實是上一步獲得的邏輯語法樹根節點,extend實際上就是依次調用children nodes的extend函數,和本身的_extend函數。實際上extend完成了語法樹的後根遍歷過程。 
_extend是個虛函數,全部opiTreeNode都有本身的實現,可是實際的邏輯實際上是在各類各樣的qgmExtendPlan裏面實現的。

class _qgmExtendPlan : public SDBObject
   { public:
      _qgmExtendPlan() ; virtual ~_qgmExtendPlan() ; public: INT32 extend( qgmOptiTreeNode *&extended ) ; INT32 insertPlan( UINT32 id, qgmOptiTreeNode *ex = NULL ) ; protected:
      QGM_EXTEND_TABLE _table ;
      qgmOptiTreeNode  *_local ;
      UINT32 _localID ;
      std::queue<qgmField> _aliases ;

   } ; typedef class _qgmExtendPlan qgmExtendPlan ;

那麼說了這麼多廢話,extend到底是幹嗎的?我理解:extend就是把簡單的邏輯概念變得具體一點。好比(這個例子不大嚴謹)qgmExtendSelectPlan就會把select a from t group by a;擴展成:

吐槽一句,難道這個extend不該該塞到build裏嗎?我覺得這個是啥高級方法來着。

步驟3 optimize

_optQgmOptimizer optimizer ; rc = optimizer.adjust( tree ) ;

注意一點這個函數稱之爲adjust而不是optimize,實際上adjust真的是adjust。基本上adjust乾的事情就是把field和condition adjust到它們應該在的位置上面去。舉例來講select t1.a,t2.a from t1 join t2 on t1.b=t2.b where t1.c > 10; ,adjust的目的就是t1.a 和 t1.c下推成select t1.a, t1.b from t1 where t1.c > 10。 固然事實比這個例子複雜的多。 
ajust以後的類型包括:

enum QGM_OPTI_TYPE {
      QGM_OPTI_TYPE_SELECT  = 0 ,
      QGM_OPTI_TYPE_SORT,
      QGM_OPTI_TYPE_FILTER,
      QGM_OPTI_TYPE_AGGR,
      QGM_OPTI_TYPE_SCAN,
      QGM_OPTI_TYPE_JOIN,
      QGM_OPTI_TYPE_JOIN_CONDITION,
      QGM_OPTI_TYPE_INSERT,
      QGM_OPTI_TYPE_DELETE,
      QGM_OPTI_TYPE_UPDATE,
      QGM_OPTI_TYPE_COMMAND, 
      QGM_OPTI_TYPE_MTHMCHSEL, // with matcher的select
      QGM_OPTI_TYPE_MTHMCHSCAN,
      QGM_OPTI_TYPE_MTHMCHFILTER,
      QGM_OPTI_TYPE_SPLIT,

      QGM_OPTI_NODE_MAX
   } ;

adjust邏輯分散在各個optQgmStrategyBase和各個_qgmOptiTreeNode中。在_optQgmStrategyTable::init中描述了當前的全部optQgmStrategyBase。

class _optQgmStrategyBase : public SDBObject
   { public:
         _optQgmStrategyBase () {} virtual ~_optQgmStrategyBase () {} public: virtual INT32 calcResult( qgmOprUnit *oprUnit,
                                    qgmOptiTreeNode *curNode,
                                    qgmOptiTreeNode *subNode,
                                    OPT_QGM_SS_RESULT &result ) = 0 ; virtual const CHAR* strategyName() const = 0 ;

   }; typedef _optQgmStrategyBase optQgmStrategyBase ;

實際上,這裏沒有optimize只有ajust。尚未函數好意思叫本身optimize?ajust只完成了最基本的SQL功能,真正的優化尚未開始。

步驟4 生成物理查詢計劃

INT32 build( _qgmOptiTreeNode *logicalTree,
                   _qgmPlan *&physicalTree) ;

具體的build過程就是遍歷上一步獲得的logicalTree,獲得physicalTree的過程。 
最終的到的_qpmPlan

class _qgmPlan : public SDBObject
   { public:
      _qgmPlan( QGM_PLAN_TYPE type, const qgmField &alias ) ; virtual ~_qgmPlan() ; private: virtual INT32 _execute( _pmdEDUCB *eduCB ) = 0 ; virtual INT32 _fetchNext( qgmFetchOut &next ) = 0 ;

物理計劃的類型包括

enum QGM_PLAN_TYPE {  QGM_PLAN_TYPE_RETURN = 0,  QGM_PLAN_TYPE_FILTER,
      QGM_PLAN_TYPE_SCAN,
      QGM_PLAN_TYPE_NLJOIN,
      QGM_PLAN_TYPE_INSERT,
      QGM_PLAN_TYPE_UPDATE,
      QGM_PLAN_TYPE_AGGR,
      QGM_PLAN_TYPE_SORT,
      QGM_PLAN_TYPE_DELETE,
      QGM_PLAN_TYPE_COMMAND,
      QGM_PLAN_TYPE_SPLIT,
      QGM_PLAN_TYPE_HASHJOIN,

      QGM_PLAN_TYPE_MAX,
   } ;

基本上和上面的邏輯查詢計劃一一對應的mapping就行。順便說一句,join的物理查詢計劃有兩種方法,nest loop和hash join(終於不糾結縮寫了),build哪個呢?答案竟然是這樣的:

if ( join->_hints.empty() )
      {
         phy = SDB_OSS_NEW _qgmPlNLJoin( join->joinType() ) ; if ( NULL == phy )
         {
            PD_LOG( PDERROR, "failed to allocate mem." ) ;
            rc = SDB_OOM ; goto error ;
         }
      } else {
         phy = SDB_OSS_NEW _qgmPlHashJoin( join->joinType() ) ; if ( NULL == phy )
         {
            PD_LOG( PDERROR, "failed to allocate mem." ) ;
            rc = SDB_OOM ; goto error ;
         }
      }

雖然有效,可是有點低級的說。


查詢執行

同json接口同樣,query只是build好了邏輯執行樹,getMore才觸發每一個物理操做的_fetchNext操做獲取。 
以qgmPlScan(scan操做)來講:

  1. 在data node上就是一個scan操做了,基本上執行select xxx from xxx where xxx order by xxx的工做,其實它乾的活都是_dmsScanner乾的(參考上面的json接口)。它只是一個wrap而已。

  2. 在cood node上,它乾的事兒實際上是拼一個請求,發送到具體的data node上。

rc = msgBuildQueryMsg ( &qMsg, &bufSize,
                              _collection.toString().c_str(),0, 0, _skip, _return,
                              &_condition, &selector,
                              &_orderby, &_hint ) ;

小結

總體來講,這個SQL框架仍是很不錯的,雖然如今還很簡單,可是仍是有很大發展空間的。至於最終這玩意兒是個玩具仍是個利器就要看SDB的開發者的水平&&市場需求了。

多扯一句,分佈式SQL解析優化這條路很差走,很難很難啊。在hint的道路上大步前進也不錯。

回到本文開頭的兩個問題上來,從理論上SDB是具有把Json和SQL有效結合起來的可能性的。最終可能出現這樣一個數據庫:

  1. 它支持SQL:你能夠用SQL語句直接CRUD。

  2. 它支持分佈式,支持HA等時髦概念。

  3. 它多少支持那麼一點事務。雖然不如傳統關係數據庫,甚至在極端狀況下會出錯,不過總比一點事務沒有強。

一家之言,隨便說說


VS mongoDB

網上有一篇訪談,【先鋒】SequoiaDB CTO王濤談打造超越MongoDB的事務、高性能NoSQL。這是SDB本身的說法。 
我我的的理解以下:

代碼層面

Item SDB MongoDB
代碼規模 太多了,不知道里面放了些什麼,核心的source其實沒那麼多,至少不該該上百兆,我試着在個人VPS上git clone,build,而後我可憐的VPS就滿了。 mongoDB的代碼顯得更加簡潔一些,至少源碼包小得多。
代碼註釋 我能說看代碼基本靠猜嘛? 相對來講mongoDB的代碼註釋就大方多了。固然也有一些很奇怪的地方,好比全部的error code都是直接寫的數字,不過瑕不掩瑜。
代碼的我的觀感 竊覺得mongoDB代碼的目的性很強,須要幹嗎就幹嗎。實用性氣息重些,不大像傳統數據庫的寫法。好處就是好讀一些。 相對來講SDB學院氣就重了點,代碼顯得也高大上一點,有傳統數據庫的影子在;反作用就是沒背景&文檔的話,難讀一點。

若是隻是想看看key-document數據庫怎麼寫,建議讀mongoDB

設計

Item SDB MongoDB 點評
Json接口 應該是山寨的mongoDB 基本上實現了select/order/groupby 同樣同樣的啊
SQL接口 算是支持吧 不支持,基本上沒有支持的可能了 能支持SQL固然好,問題是很難啊
文件系統 mmap mmap 不是說mmap很差,至少不那麼可控,兩家都用的緣由是比較簡單?
事務 支持,雖然隔離級別爲 UR 不支持 傳統企業沒有事務會死人的
應該在document上吧 數據庫級,至少2.6依然如此,無數的人吐槽,2.6裏的代碼裏面, Lock::DBWrite lk(ns.ns());出如今了不少不該該它出現的地方 著名的mongoDB坑,不知道爲啥這麼久還沒解決
數據分片方式 一致性hash Mongodb數據分片方式有hash和range兩種方式,當一個chunk的大小超過配置時,Mongodb會將過大的chunk一分爲二,而後將chunk在負載差異過大的分片羣組(Shard)之間進行自動的遷移。聽說數據多了,mongo hash的並很差 一致性hash顯然是更好的方案

性能

做爲一個懶人&&窮人。我認可我沒測,csdn上有這樣一份數據。簡單總結下,結論就是SDB寫比MongoDB快;mongoDB讀快,不過SDB也差不太遠;讀寫混合狀況下,SDB好一點;hbase讀寫都不怎麼樣。 
這種稿子都是有PR嫌疑的,具體數字當不得真,可是幾個定性的結論仍是能夠說一下的:

  1. 受mongo那個偉大的鎖的影響,凡是涉及到併發寫mongoDB都不怎麼樣。

  2. Hbase的下層是hdfs,怎麼折騰也搞不過mongoDB和SDB的mmap。

小結

  1. 不得不認可,SDB站在了mongoDB的肩膀上。

  2. mongoDB雖然有坑,可是這麼多年來,無數人已經踩過了,大不了有坑繞着走;SDB則存在着未知的,尚未被你們踩過的坑。

  3. 從設計上看,SDB比mongoDB更優;從實現上看SDB至少不比mongoDB。

  4. 從成熟度上,你們都不怎麼樣;竊覺得你們的版本號上都應該加個0.。

一句話總結

SDB就是一個json模型的關係數據庫。

不會這個就是他們創業的idea吧?

相關文章
相關標籤/搜索