歡迎訪問網易雲社區,瞭解更多網易技術產品運營經驗。
html
背景java
Cloudera在2016年發佈了新型的分佈式存儲系統——kudu,kudu目前也是apache下面的開源項目。Hadoop生態圈中的技術繁多,HDFS做爲底層數據存儲的地位一直很牢固。而HBase做爲Google BigTable的開源產品,一直也是Hadoop生態圈中的核心組件,其數據存儲的底層採用了HDFS,主要解決的是在超大數據集場景下的隨機讀寫和更新的問題。Kudu的設計有參考HBase的結構,也可以實現HBase擅長的快速的隨機讀寫、更新功能。那麼同爲分佈式存儲系統,HBase和Kudu兩者有何差別?二者的定位是否相同?咱們經過分析HBase與Kudu總體結構和存儲結構等方面對二者的差別進行比較。
總體結構Hbase的總體結構算法
HBase的主要組件包括Master,zookeeper服務,RegionServer,HDFS。
(1)Master:用來管理與監控全部的HRegionServer,也是管理HBase元數據的模塊。
(2)zookeeper:做爲分佈式協調服務,用於保存meta表的位置,master的位置,存儲RS當前的工做狀態。
(3)RegionServer:負責維護Master分配的region,region對應着表中一段區間內的內容,直接接受客戶端傳來的讀寫請求。
(4)HDFS:負責最終將寫入的數據持久化,並經過多副本複製實現數據的高可靠性。
Kudu的總體結構sql
Kudu的主要組件包括TServer和TMaster。數據庫
(1) TServer:負責管理Tablet,tablet是負責一張表中某塊內容的讀寫,接收其餘TServer中leader tablet傳來的同步信息。
(2) TMaster:集羣中的管理節點,用於管理tablet的基本信息,表的信息,並監聽TServer的狀態。多個TMaster之間經過Raft 協議實現數據同步和高可用。
主要區別
Kudu結構看上去跟HBase差異並不大,主要的區別包括:
(1)Kudu將HBase中zookeeper的功能放進了TMaster內,Kudu中TMaster的功能比HBase中的Master任務要多一些。
(2)Hbase將數據持久化這部分的功能交給了Hadoop中的HDFS,最終組織的數據存儲在HDFS上。Kudu本身將存儲模塊集成在本身的結構中,內部的數據存儲模塊經過Raft協議來保證leader Tablet和replica Tablet內數據的強一致性,和數據的高可靠性。爲何不像HBase同樣,利用HDFS來實現數據存儲,筆者猜想多是由於HDFS讀小文件時的時延太大,因此Kudu本身從新完成了底層的數據存儲模塊,並將其集成在TServer中。
數據存儲方式
apache
HBase
HBase是一款Nosql數據庫,典型的KV系統,沒有固定的schema模式,建表時只需指定一個或多個列族名便可,一個列族下面能夠增長任意個列限定名。一個列限定名錶明瞭實際中的一列,HBase將同一個列族下面的全部列存儲在一塊兒,因此HBase是一種面向列族式的數據庫。
緩存
HBase將每一個列族中的數據分別存儲,一個列族中的每行數據中,將rowkey\列族名、列名、timestamp組成最終存取的key值, 另 外 爲 了 支 持 修 改 , 刪 除 ,增 加 了 一 個 表 徵 該 行 數 據 是 否 刪 除 的 標 記 。 在 同 一 個 列 族 中 的 所 有 數 據 , 按照rowkey:columnfamily:columnQulifier:timestamp組成的key值大小進行升序排列,其中 rowkey 、 columnfamily 、columnQulifier 採用的是字典順序,其值越大,Key越大,而timestamp是值越大,Key越小。HBase經過按照列族分開存儲,相對於行式存儲可以實現更高的壓縮比,這也是其比較重要的一個特性。
HBase對一行數據進行更新時,HBase也是至關於插入一行新數據,在讀數據時HBase按照timestamp的大小獲得通過更新過的最新數據。分佈式
Kudu
Kudu是一種徹底的列式存儲引擎,表中的每一列數據都是存放在一塊兒,列與列之間都是分開的。
oop
爲了可以保存一部分歷史數據,並實現MVCC,Kudu將數據分爲三個部分。一個部分叫作base data,是當前的數據;第二個部分叫作UNDO records,存儲的是從插入數據時到造成base data所進行的全部修改操做,修改操做以必定形式進行組織,實現快速查看歷史數據;第三個部分是REDO records,存儲的是還未merge到當前數據中的更新操做。下圖中表示的是在Kudu中插入一條數據、更新數據兩個操做的作法,固然作法不惟一,不惟一的緣由是Kudu能夠選擇先不將更新操做合併到base data中。性能
差別分析
(1)HBase是面向列族式的存儲,每一個列族都是分別存放的,HBase表設計時,不多使用設計多個列族,大多狀況下是一個列族。這個時候的HBase的存儲結構已經與行式存儲無太大差異了。而Kudu,實現的是一個真正的面向列的存儲方式,表中的每一列都是單獨存放的;因此HBase與Kudu的差別主要在於相似於行式存儲的列族式存儲方式與典型的面向列式的存儲方式的差別;
(2) HBase是一款NoSQL類型的數據庫,對錶的設計主要在於rowkey與列族的設計,列的類型能夠不指定,由於HBase在實際存儲中都會將全部的value字段轉換成二進制的字節流。由於不須要指定類型,因此在插入數據的時候能夠任意指定列名(列限定名),這樣至關於能夠在建表以後動態改變表的結構。Kudu由於選擇了列式存儲,爲了更好的提升列式存儲的效果,Kudu要求在建表時指定每一列的類型,這樣的作法是爲了根據每一列的類型設置合適的編碼方式,實現更高的數據壓縮比,進而下降數據讀入時的IO壓力;
(3) HBase對每個cell數據中加入了timestamp字段,這樣可以實現記錄同一rowkey和列名的多版本數據,另外HBase將數據更新操做、刪除操做也是做爲一條數據寫入,經過timestamp來標記更新時間,type來區分數據是插入、更新仍是刪除。HBase寫入或者更新數據時能夠指定timestamp,這樣的設置能夠完成某些特定的操做;
(4) 相對於HBase容許多版本的數據存在,Kudu爲了提升批量讀取數據時的效率,要求設計表時提供一列或者多列組成一個主鍵,主鍵惟一,不容許多個相同主鍵的數據存在。這樣的設置下,Kudu不能像HBase同樣將更新操做直接轉換成插入一條新版本的數據,Kudu的選擇是將寫入的數據,更新操做分開存儲;
(5)固然還有一些其餘的行式存儲與列式存儲之間在不一樣應用場景下的性能差別。
HBase
HBase做爲一種很是典型的LSM結構的分佈式存儲系統,是Google bigtable的apache開源版本。通過近10年的發展,HBase 已經成爲了一個成熟的項目,在處理OLTP型的應用如消息日誌,歷史訂單等應用較適用。在HBase中真正接受客戶端讀寫請求的RegionServer的結構以下圖所示:
關於HBase的幾個關鍵點:
(1)在HBase中,充當寫入緩存的這個結構叫作Memstore,另外會將寫入操做順序寫入HLOG(WAL)中以保證數據不丟失;
(2)爲了提升讀的性能,HBase在內存中設置了blockcache,blockcache採用LRU策略將最近使用的數據塊放在內存中;
(3)做爲分佈式存儲系統,爲保證數據不由於集羣中機器出現故障而致使數據丟失,HBase將實際數據存放在HDFS上,包括storefile與HLOG。HBase與HDFS低耦合,HBase做爲HDFS的客戶端,向HDFS讀寫數據。
1. HBase寫過程
(1)客戶端經過客戶端上保存的RS信息緩存或者經過訪問zk獲得須要讀寫的region所在的RS信息;
(2)RS接受客戶端寫入請求,先將寫入的操做寫入WAL,而後寫入Memstore,這時HBase向客戶端確認寫入成功;
(3)HBase在必定狀況下將Memstore中的數據flush成storefile(多是Memstore大小達到必定閾值或者region佔用的內存超過必定閾值或者手動flush之類的),storefile以HFile的形式存放在HDFS上;
(4)HBase會按照必定的合併策略對HDFS上的storefile進行合併操做,減小storefile的數量。
2. Hbase讀過程
HBase讀數據的過程比較麻煩,緣由包括:
(1)HBase採用了LSM-tree的多組件算法做爲數據組織方式,這種算法會致使一個region中有多個storefile;
(2)HBase中採用了非原地更新的方式,將更新操做和刪除操做轉換成插入一條新數據的形式,雖然這樣可以較快的實現更新與刪除,可是將致使知足指定rowkey,列族、列名要求的數據有多個,而且可能分佈在不一樣的storefile中;
(3)HBase中容許設置插入和刪除數據行的timestamp屬性,這樣致使按順序落盤的storefile內數據的timestamp可能不是遞增的。
下面介紹從HBase中讀取一條指定(rowkey,column family,column)的記錄:
(1)讀過程與HBase客戶端寫過程第一步同樣,先嚐試獲取須要讀的region所在的RS相關信息;
( 2 ) RS 接收讀請求, 由於HBase中支持多版本數據( 容許存在rowkey、列族名、列名相同的數據, 不一樣版本的數據經過
timestamp進行區分),另外更新與刪除數據都是經過插入一條新數據實現的。因此要準確的讀到數據,須要找到全部可能存儲有該條數據的位置,包括在內存中未flush的memstore,已經flush到HDFS上的storefile,因此須要在1 memstore +N storefile中查找;
(3)在找到的全部數據中經過判斷timestamp值獲得最終的數據。
Kudu
(1)Kudu中的Tablet是負責表中一塊內容的讀寫工做,Tablet由一個或多個Rowset組成。其中有一個Rowset處於內存中,叫作Memrowset,Memrowset主要負責處理新的數據寫入請求。DiskRowSet是MemRowset達到必定程序刷入磁盤後生成的,實質上是由一個CFile(Base Data)、多個DeltaFile(UNDO records &REDO records)和位於內存的DeltaMemStore組成。Base data、UNDO records、和REDO records都是不可修改的,DeltaMemStore達到必定大小後會將數據刷入磁盤生成新的REDO records。Kudu後臺會有一個相似HBase的compaction線程按照必定的compaction 策略對tablet進行合併處理:
a. 將多個DeltaFile(REDO records)合併成一個大的DeltaFile;
b. 將多個REDO reccords文件與Base data進行合併,並生成新的 UNDO records;
c. 將多個DiskRowset之間進行合併,減小DiskRowset的數量。
(2)Kudu將最終的數據存儲在本地磁盤上,爲了保證數據可靠性,Kudu爲一個tablet設置了多個副本(通常爲3或5個)。因此一個tablet會由多個TServer負責維護,其中有個副本稱爲leader tablet,寫入的請求只能經過leader tablet來處理,副本之間經過Raft協議保證其餘副本與leader tablet的強一致性。
1. Kudu寫過程
Kudu與HBase不一樣,Kudu將寫入操做分爲兩種,一種是插入一條新數據,一種是對一條已插入數據的更新。Kudu插入一條新數據:
(1)客戶端鏈接TMaster獲取表的相關信息,包括分區信息,表中全部tablet的信息;
(2)客戶端找到負責處理讀寫請求的tablet所負責維護的TServer。Kudu接受客戶端的請求,檢查請求是否符合要求(表結構);
(3) Kudu在Tablet中的全部rowset(memrowset,diskrowset)中進行查找,看是否存在與待插入數據相同主鍵的數據,若是存在就返回錯誤,不然繼續;
(4) Kudu在MemRowset中寫入一行新數據,在MemRowset數據達到必定大小時,MemRowset將數據落盤,並生成一個diskrowset用於持久化數據,還生成一個memrowset繼續接收新數據的請求。
Kudu對原有數據的更新
(1)客戶端鏈接TMaster獲取表的相關信息,包括分區信息,表中全部tablet的信息;
(2)Kudu接受請求,檢查請求是否符合要求;
(3)由於待更新數據可能位於memrowset中,也可能已經flush到磁盤上,造成diskrowset。所以根據待更新數據所處位置不一樣,kudu有不一樣的作法:
a. 當待更新數據位於memrowset 時, 找到待更新數據所在行, 而後將更新操做記錄在所在行中一個mutation鏈表中;在memrowset將數據落盤時,Kudu會將更新合併到base data,並生成UNDO records用於查看歷史版本的數據和MVCC,UNDO records實際上也是以DeltaFile的形式存放;
b. 當待更新數據位於DiskRowset 時, 找到待更新數據所在的DiskRowset , 每一個DiskRowset 都會在內存中設置一個DeltaMemStore,將更新操做記錄在DeltaMemStore中,在DeltaMemStore達到必定大小時,flush在磁盤,造成Delta並存在方DeltaFile中。
實際上Kudu提交更新時會使用Raft協議將更新同步到其餘replica上去,固然若是在memrowset和diskrowset中都沒有找到這條數據,那麼返回錯誤給客戶端;另外當DiskRowset中的deltafile太多時,Kudu會採用必定的策略對一組deltafile進行合併。
(1)客戶端鏈接TMaster獲取表的相關信息,包括分區信息,表中全部tablet的信息;
(2) 客戶端找到須要讀取的數據的tablet所在的TServer,Kudu接受讀請求,並記錄timestamp信息,若是沒有顯式指定,那麼表示使用當前時間;
(3) Kudu找到待讀數據的全部相關信息, 當目標數據處於memrowset時, 根據讀取操做中包含的timestamp 信息將該 timestamp前提交的更新操做合併到base data中,這個更新操做記錄在該行數據對應的mutation鏈表中;
(4) 當讀取的目標數據位於diskrowset中,在全部DeltaFile中找到全部目標數據相關的UNDO record和REDO records,REDO records可能位於多個DeltaFile中,根據讀操做中包含的timestamp信息判斷是否須要將base data進行回滾或者利用REDO records將base data進行合併更新。
1.寫過程
(1)HBase寫的時候,不論是新插入一條數據仍是更新數據,都看成插入一條新數據來進行;而Kudu將插入新數據與更新操做分 別看待;
(2)Kudu表結構中必須設置一個惟一鍵,插入數據的時候必須判斷一些該數據的主鍵是否惟一,因此插入的時候其實有一個讀的 過程;而HBase沒有太多限制,待插入數據將直接寫進memstore;
(3)HBase實現數據可靠性是經過將落盤的數據寫入HDFS來實現,而Kudu是經過將數據寫入和更新操做同步在其餘副本上實現 數據可靠性。
結合以上幾點,能夠看出Kudu在寫的性能上相對HBase有必定的劣勢。
2. 讀過程
(1)在HBase中,讀取的數據可能有多個版本,因此須要結合多個storefile進行查詢;Kudu數據只可能存在於一個DiskRowset或 者MemRowset中,可是由於可能存在還未合併進原數據的更新,因此Kudu也須要結合多個DeltaFile進行查詢;
(2)HBase寫入或者更新時能夠指定timestamp,致使storefile之間timestamp範圍的規律性下降,增長了實際查詢storefile的數 量;Kudu不容許人爲指定寫入或者更新時的timestamp值,DeltaFile之間timestamp連續,能夠更快的找到須要的DeltaFile;
(3)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的詳細讀寫過程,
3. 其它差別
HBase:使用的java,內存的釋放經過GC來完成,在內存比較緊張時可能引起full GC進而致使服務不穩定;
Kudu:核心模塊用的C++來實現,沒有full gc的風險。
本文主要簡單介紹了一下Kudu,並在總體結構、數據存儲結構和讀寫過程等方面上對HBase和Kudu這兩款分佈式存儲系統進行大 體上的比較。Kudu經過要求完整的表結構設置,主鍵的設定,以列式存儲做爲數據在磁盤上的組織方式,更新和數據分開等技巧, 使得Kudu可以實現像HBase同樣實現數據的隨機讀寫以外,在HBase不太擅長的批量數據掃描(scan)具備較好的性能。而批量 讀數據正是olap型應用所關注的重點,正如Kudu官網主頁上描述的,Kudu實現的是既能夠實現數據的快速插入與實時更新,也能夠實現數據的快速分析。Kudu的定位不是取代HBase,而是以下降寫的性能爲代價,提升了批量讀的性能,使其可以實現快速在線分析。
想要了解網易大數據,請戳這裏網易大數據|專業的私有化大數據平臺
相關文章:
【推薦】 如何成爲一名獲客專家?
【推薦】 Persistent and Transient Data Structures in Clojure