- Hbase 的物理模型是Master和RegionServer,RegionServer存儲的是Region,Region裏面有不少的Store,一個Store對應一個列簇,一個Store中有一個memstore和多個StoreFile,Store的底層是hfile,hfile是hadoop 的二級制文件,其中HFile和Hlog是Hbase兩大文件存儲格式,Hfile用於存儲數據,HLog保證能夠寫到Hfile中。
- Kudu的物理模型是Master和tserver,其中table根據hash和range分區,分爲多個Tablet存儲到tserver中,Tablet分爲Leader和Follower,Leader負責寫請求,Follower負責讀請求,總結來講,一個ts能夠服務多個Tablet,一個Tablet能夠被多個ts服務(基於Tablet的分區,最低爲2個分區);
- 設計理念和想法是一致的;Kudu的思想是基於Hbase的,以前Cloudera公司向對Hbase改造,支持大數據量更新,但是因爲改動源碼太大,因此todd直接開發了Kudu;Hbase基於rowkey查詢和kudu基於主鍵查詢是很快的。
總體架構java


Kudu結構看上去跟HBase差異並不大,主要的區別包括:數據庫
- Kudu將HBase中zookeeper的功能放進了TMaster內,Kudu中TMaster的功能比HBase中的Master任務要多一些,Kudu全部集羣的配置信息均存儲在本地磁盤中,hbase的集羣配置信息是存儲在zookeeper中;
- Hbase將數據持久化這部分的功能交給了Hadoop中的HDFS,最終組織的數據存儲在HDFS上。Kudu本身將存儲模塊集成在本身的結構中,內部的數據存儲模塊經過Raft協議來保證leader Tablet和replica Tablet內數據的強一致性,和數據的高可靠性。爲何不像HBase同樣利用HDFS來實現數據存儲,因此Kudu本身從新完成了底層的數據存儲模塊,並將其集成在TServer中,可是kudu對磁盤的IO要求很高,它是以寫的性能換取讀的性能;
數據存儲方式緩存

- HBase是面向列族式的存儲,每一個列族都是分別存放的,HBase表設計時,不多使用設計多個列族,大多狀況下是一個列族。這個時候的HBase的存儲結構已經與行式存儲無太大差異了。而Kudu,實現的是一個真正的面向列的存儲方式,表中的每一列都是單獨存放的;因此HBase與Kudu的差別主要在於相似於行式存儲的列族式存儲方式與典型的面向列式的存儲方式的差別;
- HBase是一款NoSQL類型的數據庫,對錶的設計主要在於rowkey與列族的設計,列的類型能夠不指定,由於HBase在實際存儲中都會將全部的value字段轉換成二進制的字節流。由於不須要指定類型,因此在插入數據的時候能夠任意指定列名(列限定名),這樣至關於能夠在建表以後動態改變表的結構。Kudu由於選擇了列式存儲,爲了更好的提升列式存儲的效果,Kudu要求在建表時指定每一列的類型,這樣的作法是爲了根據每一列的類型設置合適的編碼方式,實現更高的數據壓縮比,進而下降數據讀入時的IO壓力;
- HBase對每個cell數據中加入了timestamp字段,這樣可以實現記錄同一rowkey和列名的多版本數據,另外HBase將數據更新操做、刪除操做也是做爲一條數據寫入,經過timestamp來標記更新時間,type來區分數據是插入、更新仍是刪除。HBase寫入或者更新數據時能夠指定timestamp,這樣的設置能夠完成某些特定的操做;
- Kudu也在數據存儲中加入了timestamp這個字段,不像HBase能夠直接在插入或者更新數據時設置特殊的timestamp值,Kudu的作法是由Kudu內部來控制timestamp的寫入。不過Kudu容許在scan的時候設置timestamp參數,使得客戶端能夠scan到歷史數據;
- 相對於HBase容許多版本的數據存在,Kudu爲了提升批量讀取數據時的效率,要求設計表時提供一列或者多列組成一個主鍵,主鍵惟一,不容許多個相同主鍵的數據存在。這樣的設置下,Kudu不能像HBase同樣將更新操做直接轉換成插入一條新版本的數據,Kudu的選擇是將寫入的數據,更新操做分開存儲;固然還有一些其餘的行式存儲與列式存儲之間在不一樣應用場景下的性能差別。
- Hbase中,同一個主鍵數據是能夠存在多個storefile裏的,爲了讓mutation和磁盤的存在的key組合在一塊兒,hbase須要基於rowkey執行merge。Rowkey能夠是任意長度的字符串,所以對比rowkey是很是耗性能的。另外,在一個查詢中,即便key列沒有被使用(例如聚合計算),它們也要被讀取出來,這致使了額外的IO。複合主鍵在hbase應用中很常見,主鍵的大小可能比你關注的列大一個數量級,特別是查詢的列被壓縮的狀況下;
- Kudu中,讀取一條數據或者執行非排序查詢,不須要merge操做。例如,聚合必定範圍內的key能夠獨立的查詢每一個RowSet(甚至能夠並行的),而後執行求和,由於key的順序是不重要的,顯然查詢的效率更高,kudu中,mutation是與rowid綁定的。因此merge會更加高效,經過維護計數器的方式,給定下一個須要保存的mutation,咱們能夠簡單的相減,就能夠獲得從base data到當前版本有多少個mutation。或者,直接尋址能夠用來高效的獲取最新版本的數據。獲取block也很是的高效,由於mutation直接指向了block的索引地址;
- Hbase的系統中,每一個cell的timstamp都是暴露給用戶的,本質上組成了這個cell的一個符合主鍵。意味着,這種方式能夠高效的直接訪問指定版本的cell,且它存儲了一個cell的整個時間序列的全部版本;
- 而Kudu卻不高效(須要執行多個mutation),它的timestamp是從MVCC實現而來的,它不是主鍵的另一個描述;
- Hbase採用的LSM(LogStructured Merge,很難對數據進行特殊編碼,因此處理效率不高),hbase會將多條更新記錄前後Flush到不一樣的Storefile中,因此讀取時須要掃描多個文件,比較rowkey,比較版本等,而後進行更新操做,特別是major compaction操做的時候,會佔用大量的性能;
- Kudu對同一行的數據更新記錄的合併工做,不是在查詢的時候發生的,而是在更新的時候進行,在Kudu中一行數據只會存在於一個DiskRowSet中,避免讀操做時的比較合併工做。對於列式存儲的數據文件,要原地變動一行數據是很困難的,因此在Kudu中,對於Flush到磁盤上的DiskRowSet(DRS)數據,其實是分兩種形式存在的,一種是Base的數據,按列式存儲格式存在,一旦生成,就再也不修改,另外一種是Delta文件,存儲Base數據中有變動的數據,一個Base文件能夠對應多個Delta文件(Kudu用MVCC(多版本併發控制)來實現數據的刪改功能。更新、刪除操做須要記錄到特殊的數據結構裏,保存在內存中的DeltaMemStore或磁盤上的DeltaFIle裏面。DeltaMemStore是B-Tree實現的,所以速度快,並且可修改。磁盤上的DeltaFIle是二進制的列式的塊,和base數據同樣都是不可修改的。所以當數據頻繁刪改的時候,磁盤上會有大量的DeltaFiles文件,Kudu借鑑了Hbase的方式,會按期對這些文件進行合併),這種方式意味着,插入數據時相比HBase,須要額外走一次檢索流程來斷定對應主鍵的數據是否已經存在。所以,Kudu是犧牲了寫性能來換取讀取性能的提高。另外,若是在查詢中沒有指定key,那執行計劃就不會查閱key,除了須要肯定key邊界狀況;
- Hbase中insert和mutation是相同的操做,直接存儲到storefile中。
- Kudu中insert和mutation是不一樣的操做:insert寫入數據至MemRowSet,而mutation(delete、update)寫入存在這條數據的RowSet的DeltaMemStore裏,寫入時必須肯定這是一條新數據。這會產生一個bloom filter查詢全部RowSet。若是布隆過濾器獲得一個可能的match(即計算出可能在一個RowSet裏),接着爲了肯定是不是insert仍是update,一個尋址就必須被執行。 假設,只要RowSet足夠小,bloom filter的結果就會足夠精確,那麼大部分插入將不須要物理磁盤尋址。另外,若是插入的key是有序的,例如timeseries+「_」+xxx,因爲頻繁使用,key所在的block可能會被保存在數據塊緩存中。Update時,須要肯定key在哪一個RowSet。與上雷同,須要執行bloom filter。 這有點相似於關係型數據庫RDBMS,當插入一條主鍵存在的數據時會報錯,且不會更新這條數據。相似的,更新一條數據時,若是這條數據不存在也會報錯。hbase的語法卻不是這樣,它不存在主鍵的概念;
寫入和讀取過程
寫過程數據結構
- HBase寫的時候,不論是新插入一條數據仍是更新數據,都看成插入一條新數據來進行;而Kudu將插入新數據與更新操做分別看待;
- Kudu表結構中必須設置一個惟一鍵,插入數據的時候必須判斷一些該數據的主鍵是否惟一,因此插入的時候其實有一個讀的過程;而HBase沒有太多限制,待插入數據將直接寫進memstore;
- HBase實現數據可靠性是經過將落盤的數據寫入HDFS來實現,而Kudu是經過將數據寫入和更新操做同步在其餘副本上實現數據可靠性;
- 結合以上幾點,能夠看出Kudu在寫的性能上相對HBase有必定的劣勢;
讀過程架構
- 在HBase中,讀取的數據可能有多個版本,因此須要結合多個storefile進行查詢;Kudu數據只可能存在於一個DiskRowset或者MemRowset中,可是由於可能存在還未合併進原數據的更新,因此Kudu也須要結合多個DeltaFile進行查詢;
- HBase寫入或者更新時能夠指定timestamp,致使storefile之間timestamp範圍的規律性下降,增長了實際查詢storefile的數量;Kudu不容許人爲指定寫入或者更新時的timestamp值,DeltaFile之間timestamp連續,能夠更快的找到須要的DeltaFile;
- HBase經過timestamp值能夠直接取出數據;而Kudu實現多版本是經過保留UNDO records(已經合併過的操做)和REDO records(未合併過的操做)完成的,在一些狀況下Kudu須要將base data結合UNDO records進行回滾或者結合REDO records進行合併而後才能獲得真正所須要的數據;
- 結合以上三點能夠得出,不論是HBase仍是Kudu,在讀取一條數據時都須要從多個文件中搜尋相關信息。相對於HBase,Kudu選擇將插入數據和更新操做分開,一條數據只可能存在於一個DiskRowset或者memRowset中,只須要搜尋到一個rowset中存在指定數據就不用繼續往下找了,用戶不能設置更新和插入時的timestamp值,減小了在rowset中DeltaFile的讀取數量。這樣在scan的狀況下能夠結合列式存儲的優勢實現較高的讀性能,特別是在更新數量較少的狀況下可以有效提升scan性能;
- 另外,本文在描述HBase讀寫過程當中沒有考慮讀寫中使用的優化技術如Bloomfilter、timestamp range等。其實Kudu中也有使用相似的優化技術來提升讀寫性能,本文只是簡單的分析,所以就再也不詳細討論讀寫過程;
其餘差別併發
- HBase:使用的java,內存的釋放經過GC來完成,在內存比較緊張時可能引起full GC進而致使服務不穩定;
- Kudu:核心模塊用的C++來實現,沒有full gc的風險;
總結oop
- Kudu經過要求完整的表結構設置,主鍵的設定,以列式存儲做爲數據在磁盤上的組織方式,更新和數據分開等技巧,使得Kudu可以實現像HBase同樣實現數據的隨機讀寫以外,在HBase不太擅長的批量數據掃描(scan)具備較好的性能。而批量讀數據正是olap型應用所關注的重點,正如Kudu官網主頁上描述的,Kudu實現的是既能夠實現數據的快速插入與實時更新,也能夠實現數據的快速分析。Kudu的定位不是取代HBase,而是以下降寫的性能爲代價,提升了批量讀的性能,使其可以實現快速在線分析。
參考 https://blog.csdn.net/weixin_39478115/article/details/78470294性能