Apache Kudu(二) 原理

Kudu 介紹

Hadoop 的存儲層可實現對數據的快速分析。

在 KUDU 之前,大數據主要以兩種方式存儲;

(1)靜態數據: 以 HDFS 引擎作爲存儲引擎,適用於高吞吐量的離線大數據分析場景。 這類存儲的侷限性是數據無法進行隨機的讀寫。

(2)動態數據: 以 HBase、Cassandra 作爲存儲引擎,適用於大數據隨機讀寫場景。 侷限性是批量讀取吞吐量遠不如 HDFS,不適用於批量數據分析的場景。

從上面分析可知,這兩種數據在存儲方式上完全不同,進而導致使用場景完 全不同,但在真實的場景中,邊界可能沒有那麼清晰,面對既需要隨機讀寫,又 需要批量分析的大數據場景,該如何選擇呢?

image-20200504120954482

如上圖所示,數據實時寫入 HBase,實時的數據更新也在 HBase 完成,爲 了應對 OLAP 需求,我們定時將 HBase 數據寫成靜態的文件(如:Parquet)導 入到 OLAP 引擎(如:Impala、hive)。這一架構能滿足既需要隨機讀寫,又可 以支持 OLAP 分析的場景,但他有如下缺點:

(1)架構複雜。從架構上看,數據在 HBase、消息隊列、HDFS 間流轉,涉及 環節太多,運維成本很高。並且每個環節需要保證高可用,都需要維護多個副本, 存儲空間也有一定的浪費。最後數據在多個系統上,對數據安全策略、監控等都 提出了挑戰。

(2)時效性低。數據從 HBase 導出成靜態文件是週期性的,一般這個週期是 一天(或一小時),在時效性上不是很高。

(3)難以應對後續的更新。真實場景中,總會有數據是延遲到達的。如果這 些數據之前已經從 HBase 導出到 HDFS,新到的變更數據就難以處理了,一個方 案是把原有數據應用上新的變更後重寫一遍,但這代價又很高。 爲了解決上述架構的這些問題,

KUDU 應運而生。KUDU 的定位是 Fast Analytics on Fast Data,是一個既支持隨機讀寫、又支持 OLAP 分析大數據存儲引擎

image-20200504121134450

kudu 是什麼

Apache Kudu 是由 Cloudera 開源的存儲引擎,可以同時提供低延遲的隨機 讀寫和高效的數據分析能力。它是一個融合 HDFS 和 HBase 的功能的新組件,具 備介於兩者之間的新存儲組件。 Kudu 支持水平擴展,並且與 Cloudera Impala 和 Apache Spark 等當前流 行的大數據查詢和分析工具結合緊密。理解爲一個數據庫

kudu 應用場景

  • 適用於那些既有隨機訪問,也有批量數據掃描的複合場景

  • 高計算量的場景

  • 使用了高性能的存儲設備,包括使用更多的內存

  • 支持數據更新,避免數據反覆遷移

  • 支持跨地域實時數據備份和查詢

  • 國內使用的 kudu 一些案例可以查看《構建近實時分析系統.pdf》文檔。

概念

OLAP & OLTP

  • OLTP: 快速插入和更新,並且可以精確查詢。

  • OLAP: 大規模的批量的數據的分析

列式存儲 & 行式存儲

  • 列式存儲: 適合於掃描性能,只需要加載需要的列。每一列都解耦掉了。
  • 行式存儲:適合對一行所有數據都查詢的場景。

存儲模型

結構:
  • Kudu的存儲模型是有Schema的
  • Mysql、Oracle也是由Schema
  • Hbase 是 Key—Map的數據模型,RowKey是主鍵,其它每一列有一個值,只是用ColumnFamily
主鍵
  • Kudu 採用了 Raft 協議,有唯一主鍵

  • 關係數據庫有唯一鍵

  • Hbase的RowKey不是唯一主鍵

事務支持
  • Kudu 的事務不能跨行,也是因爲Raft的協議才實現的(一致性)。
  • 關係型數據庫在單機上都是支持ACID事務的
性能
  • Kudu 的 隨機讀寫目標和Hbase相似,但是這個目標是建立在使用SSD上面(?)
  • KUdu的批量查詢 目標是比HDFS的Parquet 慢兩倍以內
硬件需求
  • Kudu 的時代 SSD 已經比較常見了,能夠做更多的磁盤操作和內存操作
  • Hadoop 對此要求不高,但是Kudu爲了大內存和SSD而設計的,所以Kudu對硬件的需求更大一點。

對比

Hadoop

  • HDFS 支持很多壓縮的列式存儲的格式
  • HDFS 本身支持並行

Hbase

  • Hbase 能夠以極高的吞吐量進行數據存儲,無論是批量加載還是大量put(單列)
  • Hbase 能夠對主鍵進行高效的掃描,因爲它底層時根據主鍵進行排序和維護的,但是對於主鍵以外的列就性能極差,可以用 phoneix 進行二級索引維護。

Kudu 設計目標

  • 儘可能快速的掃描,能夠達到HDFS的Parquet的二分之一速度

  • 儘可能的支持隨機讀寫,達到1ms的時間

  • 列式存儲

  • 支持 Nosql 樣式的API,例如 put 、 delete、scan、get,不支持SQL

  • Kudu 設計是面向結構化存儲的,因此,Kudu 的表需要用戶在建表時定義它的 Schema 信息,這些 Schema 信息包含:列定義(含類型),Primary Key 定義(用戶指定的若干個列的有序組合)。

  • 數據的唯一性,依賴於用戶所提供的Primary Key 中的 Column 組合的值的唯一性。Kudu 提供了 Alter 命令來增刪列,但位於 Primary Key 中的列是不允許刪除的。

  • 從用戶角度來看,Kudu 是一種存儲結構化數據表的存儲系統。在一個 Kudu集羣中可以定義任意數量的 table,每個 table 都需要預先定義好 schema。每個table 的列數是確定的,每一列都需要有名字和類型,每個表中可以把其中一列或多列定義爲主鍵。這麼看來,Kudu 更像關係型數據庫,而不是像 HBase、Cassandra 和 MongoDB 這些 NoSQL 數據庫。不過 Kudu 目前還不能像關係型數據一樣支持二級索引

  • Kudu 使用確定的列類型,而不是類似於 NoSQL 的“everything is byte”。帶來好處:確定的列類型使 Kudu 可以進行類型特有的編碼,可以提供元數據給其他上層查詢工具

架構

主從結構

image-20200504121524042

  • 與 HDFS 和 HBase 相似,Kudu 使用單個的 Master 節點,用來管理集羣的元 數據
  • 使用任意數量的 Tablet Server(類似 HBase 中的 RegionServer 角 色)節點用來存儲實際數據。
  • 可以部署多個 Master 節點來提高容錯性。
  • 以上的圖形是一個表,分爲多個Tablet,然後每個Tablet有多個副本,每個副本分佈在不同的機器上

組件

1. Table 表(Table)

是數據庫中用來存儲數據的對象,是有結構的數據集合。kudu 中的表具有 schema(綱要)和全局有序的 primary key(主鍵)。kudu 中一個 table 會被水平分成多個被稱之爲 tablet 的片段。 不支持SQL。 如果需要sql,需要和Impala一起使用。

2. Tablet

一個 tablet 是一張 table 連續的片段,tablet 是 kudu 表的水平分區, 類似於 HBase 的 region。每個 tablet 存儲着一定連續 range 的數據(key), 且 tablet 兩兩間的 range 不會重疊。一張表的所有 tablet 包含了這張表的所 有 key 空間。 tablet 會冗餘存儲,放置到多個 tablet server 上,並且在任何給定的 時間點,其中一個副本被認爲是 leader tablet,其餘的被認之爲 follower tablet(類似於kafka的partition)。每個 tablet 都可以進行數據的讀請求,但只有 Leader tablet 負責寫 數據請求。

  • 存放表的數據

3. Tablet Server

tablet server 集羣中的小弟,負責數據存儲,並提供數據讀寫服務 一個 tablet server 存儲了 table 表的 tablet,向 kudu client 提供讀 取數據服務。對於給定的 tablet,一個 tablet server 充當 leader,其他 tablet server 充當該 tablet 的 follower 副本。 只有 leader 服務寫請求,然而 leader 或 followers 爲每個服務提供讀 請求 。一個 tablet server 可以服務多個 tablets ,並且一個 tablet 可以 被多個 tablet servers 服務着。

4. Master Server

集羣中的老大,負責集羣管理、**元數據管理(表)**等功能。

kudu 底層數據模型

Kudu 的底層數據文件的存儲,未採用 HDFS 這樣的較高抽象層次的分佈式文件系統,而是自行開發了一套可基於 Table/Tablet/Replica 視圖級別的底層存儲系統。

這套實現基於如下的幾個設計目標:

  • 可提供快速的列式查詢
  • 可支持快速的隨機更新
  • 可提供更爲穩定的查詢性能保障

image-20200504220546543

一張 table 會分成若干個 tablet,每個 tablet 包括 MetaData 元信息及若干個 RowSet。

RowSet 包含一個 MemRowSet 及若干個 DiskRowSetDiskRowSet 中包含一個
BloomFileAd_hoc IndexBaseDataDeltaMem及若干個RedoFileUndoFile

  • **MemRowSet:**用於新數據 insert 及已在 MemRowSet 中的數據的更新,一個MemRowSet 寫滿後會將數據刷到磁盤形成若干個 DiskRowSet。默認是 1G 或者或者 120S。
  • **DiskRowSet:**用於老數據的變更,後臺定期對 DiskRowSet 做 compaction,以刪除沒用的數據及合併歷史數據,減少查詢過程中的 IO 開銷。
  • **BloomFile:**根據一個 DiskRowSet 中的 key 生成一個 bloom filter,用於快速模糊定位某個 key 是否在 DiskRowSet 中。
  • **Ad_hocIndex:**是主鍵的索引,用於定位 key 在 DiskRowSet 中的具體哪個偏移位置。
  • BaseData 是 MemRowSet flush 下來的數據,按列存儲,按主鍵有序。
  • UndoFile 是基於 BaseData 之前時間的歷史數據,通過在 BaseData 上 apply UndoFile 中的記錄,可以獲得歷史數據。
  • RedoFile 是基於 BaseData 之後時間的變更記錄,通過在 BaseData 上 apply RedoFile 中的記錄,可獲得較新的數據。
  • DeltaMem 用於 DiskRowSet 中數據的變更,先寫到內存中,寫滿後 flush 到磁盤形成 RedoFile。

REDO 與 UNDO 與關係型數據庫中的 REDO 與 UNDO 日誌類似(在關係型數據庫中,REDO 日誌記錄了更新後的數據,可以用來恢復尚未寫入 Data File 的已成功事務更新的數據。而 UNDO 日誌用來記錄事務更新之前的數據,可以用來在事務失敗時進行回滾

image-20200504221144604

MemRowSets 可以對比理解成 HBase 中的 MemStore, 而 DiskRowSets 可理解成 HBase 中的 HFile。

MemRowSets 中的數據被 Flush 到磁盤之後,形成 DiskRowSets。 DisRowSets中的數據,按照32MB 大小爲單位,按序劃分爲一個個的 DiskRowSetDiskRowSet中的數據按照 Column 進行組織,與 Parquet 類似。

這是 Kudu 可支持一些分析性查詢的基礎。每一個 Column 的數據被存儲在一個相鄰的數據區域,而這個數據區域進一步被細分成一個個的小的 Page 單元,與 HBase File 中的 Block 類似,對每一個 Column Page 可採用一些 Encoding 算法,以及一些通用的 Compression 算法。

既然可對 Column Page 可採用 Encoding以及 Compression 算法,那麼,對單條記錄的更改就會比較困難了。


前面提到了 Kudu 可支持單條記錄級別的更新/刪除,是如何做到的? 與 HBase 類似,也是通過增加一條新的記錄來描述這次更新/刪除操作的

DiskRowSet 是不可修改了,那麼 KUDU 要如何應對數據的更新呢?在 KUDU 中,把 DiskRowSet 分爲了兩部分:base datadelta stores

base data 負責存儲基礎數據delta stores 負責存儲 base data 中的變更數據.

image-20200504220628537

如上圖所示,數據從 MemRowSet 刷到磁盤後就形成了一份 DiskRowSet(只包含 base data ), 每 份 DiskRowSet 在 內 存 中 都 會 有 一 個 對 應 的 DeltaMemStore,負責記錄此 DiskRowSet 後續的數據變更(更新、刪除)。

DeltaMemStore 內部維護一個 B-樹索引,映射到每個 row_offset 對應的數據變更。DeltaMemStore 數據增長到一定程度後轉化成二進制文件存儲到磁盤,形成一個 DeltaFile,隨着 base data 對應數據的不斷變更,DeltaFile 逐漸增長。

操作實現原理

tablet 發現過程

創建 Kudu 客戶端時,其會從主服務器上獲取 tablet 位置信息,然後直接與服務於該 tablet 的服務器進行交談。

爲了優化讀取和寫入路徑,客戶端將保留該信息的本地緩存,以防止他們在每個請求時需要查詢主機的 tablet 位置信息。

隨着時間的推移,客戶端的緩存可能會變得過時,並且當寫入被髮送到不再是 tablet 領導者的 tablet 服務器
時,則將被拒絕

然後客戶端將通過查詢主服務器發現新領導者的位置來更新其緩存。 (所以客戶端不能太多,可能對導致更新時master壓力過大)

image-20200504220356059

kudu 寫流程

當 Client 請求寫數據時,

  1. 先根據主鍵從 Master Server 中獲取要訪問的目標 Tablets,
  2. 然後到依次對應的 Tablet 獲取數據。

因爲 KUDU 表存在主鍵約束,所以需要進行主鍵是否已經存在的判斷,這裏就涉及到之前說的索引結構對讀寫的優化了。一個 Tablet中存在很多個 RowSets,爲了提升性能,我們要儘可能地減少要掃描的 RowSets 數量。

首先,我們先通過每個 RowSet 中記錄的主鍵的(最大最小)範圍,過濾掉一批不存在目標主鍵的 RowSets,然後在根據 RowSet 中的布隆過濾器,過濾掉確定不存在目標主鍵的 RowSets,最後再通過 RowSets 中的 B-樹索引,精確定位目標主鍵是否存在。 如果主鍵已經存在,則報錯(主鍵重複),否則就進行寫數據(寫 MemRowSet)。

image-20200504220338247

數據讀取過程

  1. 先根據要掃描數據的主鍵範圍,定位到目標的Tablets,然後讀取 Tablets 中的 RowSets。
  2. 在讀取每個 RowSet 時,先根據主鍵過濾要 scan 範圍,然後加載範圍內的
    base data,
  3. 再找到對應的 delta stores,應用所有變更,
  4. 最後 union 上 MemRowSet中的內容,返回數據給 Client。

image-20200504220213144

kudu 更新流程

數據更新的核心是定位到待更新數據的位置,這塊與寫入的時候類似,就不展開了,等定位到具體位置後,然後將變更寫到對應的 delta store 中。

image-20200504220121192

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章