KUDU - Cloudera開發的又一個Hadoop系存儲系統
來自: http://blog.csdn.net/colorant/article/details/50803226
作者:劉旭暉 Raymond 轉載請注明出處
Email:colorant at 163.com
BLOG:http://blog.csdn.net/colorant/
## == 是什么 ==
Kudu是Todd Lipcon@Cloudera帶頭開發的存儲系統,其整體應用模式和HBase比較接近,即支持行級別的隨機讀寫,并支持批量順序檢索功能。
那既然有了HBase,為什么還需要Kudu呢,簡單的說,就是嫌棄HBase在OLAP場合,SQL/MR類的批量檢索場景中,性能不夠好。通常這種海量數據OLAP場景,要不走預處理的路,比如像EBAY麒麟這樣走Cube管理的,或者像谷歌Mesa這樣按業務需求走預定義聚合操作。再有就是自己構建數據通道,串接實時和批量處理兩種系統,發揮各自的特長。
但是OLAP是個復雜的問題,場景眾多,必然不可能有完美的通用解決方案,Kudu定位于應對快速變化數據的快速分析型數據倉庫,希望靠系統自身能力,支撐起同時需要高吞吐率的順序和隨機讀寫的應用場景(可能的場景,比如時間序列數據分析,日志數據實時監控分析),提供一個介于HDFS和HBase的性能特點之間的一個系統,在隨機讀寫和批量掃描之間找到一個平衡點,并保障穩定可預測的響應延遲
那為什么不能想辦法改進HBase呢?Todd自己做為HBase的重要貢獻者之一,沒有選擇這條路,自然是因為任何系統設計時都有Tradeoff,基于HBase的設計思想很難實現Kudu所定位的目標
相關鏈接:
- http://getkudu.io/kudu.pdf
- http://getkudu.io/ </ul>
## == 核心思想 ==
### 數據模型:
數據模型定義上,Kudu管理的是類似關系型數據庫的結構化的表,表結構由類Sql的Schema進行定義,相比于HBase這樣的NoSql類型的數據庫,Kudu的行數據是由固定個數有明確類型定義的列組成,并且需要定義一個由一個或多個列組成的主鍵來對每行數據進行唯一索引,相比于傳統的關系型數據庫,kudu在索引上有更多的限制,比如暫時不支持二級索引,不支持主鍵的更新等等。
盡管表結構類似于關系型數據庫,但是Kudu自身并不提供SQL類型的語法接口,而是由上層其他系統實現,比如目前通過Impala提供SQL語法支持。
Kudu底層API,主要面對簡單的更新檢索操作,Insert/Update/Delete等必須指定一個主鍵進行,而Scan檢索類型的操作則支持條件過濾和投影等能力。
### 集群架構:
Kudu的集群架構基本和HBase類似,采用主從結構,Master節點管理元數據,Tablet節點負責分片管理數據,
和HBase不同的是,Kudu沒有借助于HDFS存儲實際數據,而是自己直接在本地磁盤上管理分片數據,包括數據的Replication機制,kudu的Tablet server直接管理Master分片和Slave分片,自己通過raft協議解決一致性問題等,多個Slave可以同時提供數據讀取服務,相對于HBase依托HDFS進行Region數據的管理方式,自主性會強一些,不過比如Tablet節點崩潰,數據的遷移拷貝工作等,也需要Kudu自己完成。
### 存儲結構:
因為數據是有嚴格Schema類型定義,所以Kudu底層可以使用列式存儲的方案來提高存儲和投影檢索效率(不過,設計kudu時,因果關系我估計是倒過來的,先決定要使用列式存儲,再決定需要schema)
和HBase一樣,Kudu也是通過Tablet的分區來支持水平擴展,與HBase不同的是,Kudu的分區策略除了支持按照Key Range來分區以外,還支持Hash based的策略,實際上,在主鍵上,Kudu可以混合使用這兩種不同的策略
Hash分區的策略在一些場合下可以更好的做到負載均衡,避免數據傾斜,但是它最大的問題就是分區數一旦確定就很難再調整,所以目前Kudu的分區數必須預先指定(對Range的分區策略也有這個要求,估計是先簡單化統一處理),不支持動態分區分裂,合并等,因此表的分區一開始就需要根據負載和容量預先進行合理規劃。
在處理隨機寫的效率問題方面,Kudu的基本流程和HBase的方案差不多,在內存中對每個Tablet分區維護一個MemRowSet來管理最新更新的數據,當尺寸超過一定大小后Flush到磁盤上形成DiskRowSet,多個DiskRowSet在適當的時候進行歸并處理
和HBase采用的LSM(LogStructured Merge)方案不同的是,Kudu對同一行的數據更新記錄的合并工作,不是在查詢的時候發生的(HBase會將多條更新記錄先后Flush到不同的Storefile中,所以讀取時需要掃描多個文件,比較rowkey,比較版本等),而是在更新的時候進行,在Kudu中一行數據只會存在于一個DiskRowSet中,避免讀操作時的比較合并工作。那Kudu是怎么做到的呢? 對于列式存儲的數據文件,要原地變更一行數據是很困難的,所以在Kudu中,對于Flush到磁盤上的DiskRowSet(DRS)數據,實際上是分兩種形式存在的,一種是Base的數據,按列式存儲格式存在,一旦生成,就不再修改,另一種是Delta文件,存儲Base數據中有變更的數據,一個Base文件可以對應多個Delta文件,這種方式意味著,插入數據時相比HBase,需要額外走一次檢索流程來判定對應主鍵的數據是否已經存在。因此,Kudu是犧牲了寫性能來換取讀取性能的提升。
既然存在Delta數據,也就意味著數據查詢時需要同時檢索Base文件和Delta文件,這看起來和HBase的方案似乎又走到一起去了,不同的地方在于,Kudu的Delta文件與Base文件不同,不是按Key排序的,而是按被更新的行在Base文件中的位移來檢索的,號稱這樣做,在定位Delta內容的時候,不需要進行字符串比較工作,因此能大大加快定位速度。但是無論如何,Delta文件的存在對檢索速度的影響巨大。因此Delta文件的數量會需要控制,需要及時的和Base數據進行合并。由于Base文件是列式存儲的,所以Delta文件合并時,可以有選擇性的進行,比如只把變化頻繁的列進行合并,變化很少的列保留在Delta文件中暫不合并,這樣做也能減少不必要的IO開銷。
除了Delta文件合并,DRS自身也會需要合并,為了保障檢索延遲的可預測性(這一點是HBase的痛點之一,比如分區發生Major Compaction時,讀寫性能會受到很大影響),Kudu的compaction策略和HBase相比,有很大不同,kudu的DRS數據文件的compaction,本質上不是為了減少文件數量,實際上Kudu DRS默認是以32MB為單位進行拆分的,DRS的compaction并不減少文件數量,而是對內容進行排序重組,減少不同DRS之間key的overlap,進而在檢索的時候減少需要參與檢索的DRS的數量。
以32MB這樣小的單位進行拆分,也是為了能夠以有限的資源快速的完成compaction的任務,及時根據系統負載調整Compaction行為,而不至于像HBase一樣,Major Compaction動作成為導致性能不穩定的一個重要因素。所以對于Kudu來說,IO操作可以是一個持續平緩的過程,這點對響應的可預測性至關重要。
### 其它
Kudu底層核心代碼使用C++開發,對外提供Java API接口,沒有使用Java開發核心代碼,也許有部分原因是希望通過自己管理內存,更好的適應和利用當前服務器上普遍越來越大的內存空間(256G+),另外也便于在關鍵邏輯中更好的優化代碼。
## == 小結 ==
總體來說,個人感覺,Kudu本質上是將性能的優化,寄托在以列式存儲為核心的基礎上,希望通過提高存儲效率,加快字段投影過濾效率,降低查詢時CPU開銷等來提升性能。而其他絕大多數設計,都是為了解決在列式存儲的基礎上支持隨機讀寫這樣一個目的而存在的。比如類Sql的元數據結構,是提高列式存儲效率的一個輔助手段,唯一主鍵的設定也是配合列式存儲引入的定制策略,至于其他如Delta存儲,compaction策略等都是在這個設定下為了支持隨機讀寫,降低latency不確定性等引入的一些Tradeoff方案
官方測試結果上,如果是存粹的隨機讀寫,或者單行的檢索請求這類場景,由于這些Tradeoff的存在,HBASE的性能吞吐率是要優于Kudu不少的(2倍到4倍),kudu的優勢還是在支持類SQL檢索這樣經常需要進行投影操作的批量順序檢索分析場合。
目前kudu還處在Incubator階段,并且還沒有成熟的線上應用(小米走在了前面,做了一些業務應用的嘗試),在數據安全,備份,系統健壯性等方面也還要打個問號,所以是否使用kudu,什么場合,什么時間點使用,是個需要好好考量的問題 ;)