HBase 數據存儲結構

HBase中, 從邏輯上來說數據大概就長這樣:json

image-20210227123337628

單從圖中的邏輯模型來看, HBase 和 MySQL 的區別就是:數據結構

  1. 將不一樣的列歸屬與同一個列族下
  2. 支持多版本數據

這看着感受也沒有那麼太大的區別呀, 它解決了 MySQL 的那些問題呢? 每個新事物的出現, 都是爲了解決本來存在的問題.併發

  1. 對寫入友好, 支持異步大批量併發寫入
  2. 可動態添加列
  3. 按列存儲數據, 不存在的列不會落盤, 節省空間. 而 MySQL 中不存在的內容也要用 null 填充
  4. 支持海量數據分佈式存儲(BigTable 最開始就是 Google 爲了解決數據存儲問題而提出來的)
  5. 等等

那麼他是如何解決這些問題的呢? 他的數據是如何進行存儲的呢?app

HBase 數據物理結構

在介紹其物理結構以前, 要先簡單提一下 LSM 樹異步

LSM樹

和 MySQL 所使用的B+樹同樣, 也是一種磁盤數據的索引結構. B+樹是一種對讀取友好的存儲結構, 可是當大量寫入的時候, 好比日誌信息, 由於涉及到隨機寫入, 就顯得捉襟見肘了.分佈式

LSM樹就是針對這種大量寫入的場景而提出的. 他的中文名字叫: 日誌結構合併樹. 文件存儲的是對數據的修改操做, 數據會 append 但不會去修改原有的數據. 是順序寫入操做.性能

可是, 若是無論不顧的將全部的操做都順序寫入了, 那讀取數據的時候沒有任何根據, 須要掃描全部操做才能讀到. LSM 樹的作法是, 先在內存中維護一份小的有序的數據(內存不存在隨機讀寫的問題), 當這份數據超過必定大小的時候, 將其整個放入磁盤中.優化

這樣, 磁盤中就存在不少個有序的文件了, 可是會有大量的小文件, 讀取數據時要依次查找, 致使讀取性能下降. 這時就須要對多個小文件進行多路歸併合成一個文件來優化讀取的性能.日誌

至此, 基本就是LSM 樹的所有思想了.code

  1. 在內存中維護一個有序的數據
  2. 將內存中的數據push 到磁盤中
  3. 將磁盤中的多個有序文件進行歸併, 合成一個較大的有序文件

HBase存儲

HBase中, 數據的存儲就使用了 LSM 樹進行存儲. 其中每一條數據都是一條操做記錄. 那麼在HBase實現中的部份內容以下.

內存有序結構的實現

經過跳錶來維護內存中的有序結構, 當一個跳錶裝滿以後, 將禁止新的寫入操做並將其 push 到磁盤中, 同時開一個新的數據結構來接收新到的操做請求.

每條數據的存儲內容

存儲了一個KV 鍵值對, 其中的 V 就是咱們寫入的值, 而這個 key 由如下部分組成:

  • row key
  • 列族
  • 列名
  • 時間戳
  • 操做類型: Put、Delete、DeleteColumn、DeleteFamily 等等

整個列表是 key 的順序列表. 其排序規則以下:

  1. row key小的排在前面
  2. 同 row key 比較列族
  3. 同列族比較列名
  4. 同列名比較時間戳, 時間戳大的在前面.

按照這個順序進行讀取指定 row key 的某一列數據時, 最早拿到的數據就是最新的版本, 如果 delete 操做, 說明最後執行了刪除操做, 即便後面有數據, 最新數據也是空.

磁盤文件的結構

由三部分組成:

image-20210227221614633

  1. 頭信息: 存儲文件大小, 文件塊數量, 索引位置, 索引大小等信息
  2. 索引數據: 用戶對文件中全部數據塊進行索引, 其中每個數據塊都包含一條索引數據, 索引內容包括
    • 數據塊的最後一條數據. 用於對索引進行二分查找, 快速定位到指定的數據塊
    • 數據塊在文件中的位置
    • 數據塊的大小
    • 布隆過濾器. 用戶在掃描時快速過濾不存在的數據塊
  3. 數據塊. 其中存儲了每一條 KV 數據.

按照這個結構, 用戶在進行指定row_key 讀取的時候, 每一個文件的操做以下:

  1. 根據頭信息內容, 加載索引數據
  2. 經過二分查找, 找到 row_key 在哪一數據塊下
  3. 根據布隆過濾器過濾掉不存在的數據塊, 加速讀取
  4. 根據數據塊的位置和大小, 找到指定數據塊並二分查找指定數據

HBase 數據列族式存儲

先簡單回顧一下行式存儲和列式存儲.

行式存儲

行式存儲, 將一行數據存儲在一塊兒, 一行數據寫完了纔會寫下一行. 例如典型的 MySQL.

行式存儲在讀取一行數據的時候是比較快的, 但若是讀取的是某一列數據, 也須要將整行讀取到內存中進行過濾.

列式存儲

與行式存儲相對應的就是列式存儲, 既將一列數據存儲在一塊兒, 不一樣列的數據分別存儲.

列式存儲對於只讀取某一列比較友好, 但相對的, 若是要讀取多列數據, 須要讀取屢次並進行合併.

列族式存儲

而 HBase 中選用了一種折中的方案, 列族式存儲, 將列族放到一塊兒存儲, 不一樣列族分別存儲.

那麼也就是說, 若是一個表有多個列族, 每一個列族下只有一列, 那麼就等同於列式存儲

若是一個表只有一個列族, 該列族下有多個列, 那麼就等同與行式存儲.

HBase 會將一張表同一列族的數據, 分配到同一個 region 上, 這個region 分配在集羣中的某一個 regionServer. 全部的 region 存儲在表: hbase:meta 表中, 表結構以下:

image-20210227234232984

表不一樣列含義以下:

  • row_key 由如下字段拼接(逗號)而成
    • 表名
    • 起始 row_key
    • 建立時間戳
    • 上面三個字段的md5
  • info:regioninfo 主要存儲如下數據(json)
    • STARTKEY: 起始 row_key
    • ENDKEY: 結束 row_key
    • NAME: region 名
    • ENCODED: 不清楚是什麼
  • info:seqnumDuringOpen 表示regionServer 在線時長
  • info:server 落在哪一個 regionServer 上
  • info:serverstartcode regionServer 的啓動時間
  • 等等

總結

簡單瞭解了HBase的數據落盤格式, 也大概解釋 HBase 的不少疑惑, 好比:

  1. 爲何只支持 row key 索引查詢
    • 由於整個文件是按照 row key 排序的
  2. 爲何讀取效率比 MySQL 低
    • 由於要依次讀取文件進行查找
  3. 爲何支持高效率的寫入操做
    • 由於所有都是順序讀寫操做
  4. 應該如何設置 HBase 的列族
    • 將同一場景讀取的放到同一列族下, 不一樣場景讀取的放到不一樣列族下
  5. 等等
相關文章
相關標籤/搜索