HBase 数据存储结构
在HBase中, 從邏輯上來講數(shù)據(jù)大概就長這樣:
單從圖中的邏輯模型來看, HBase 和 MySQL 的區(qū)別就是:
這看著感覺也沒有那么太大的區(qū)別呀, 它解決了 MySQL 的那些問題呢? 每一個新事物的出現(xiàn), 都是為了解決原本存在的問題.
那么他是如何解決這些問題的呢? 他的數(shù)據(jù)是如何進(jìn)行存儲的呢?
HBase 數(shù)據(jù)物理結(jié)構(gòu)
在介紹其物理結(jié)構(gòu)之前, 要先簡單提一下 LSM 樹
LSM樹
和 MySQL 所使用的B+樹一樣, 也是一種磁盤數(shù)據(jù)的索引結(jié)構(gòu). B+樹是一種對讀取友好的存儲結(jié)構(gòu), 但是當(dāng)大量寫入的時候, 比如日志信息, 因?yàn)樯婕暗诫S機(jī)寫入, 就顯得捉襟見肘了.
而LSM樹就是針對這種大量寫入的場景而提出的. 他的中文名字叫: 日志結(jié)構(gòu)合并樹. 文件存儲的是對數(shù)據(jù)的修改操作, 數(shù)據(jù)會 append 但不會去修改原有的數(shù)據(jù). 是順序?qū)懭氩僮?
但是, 如果不管不顧的將所有的操作都順序?qū)懭肓? 那讀取數(shù)據(jù)的時候沒有任何根據(jù), 需要掃描所有操作才能讀到. LSM 樹的做法是, 先在內(nèi)存中維護(hù)一份小的有序的數(shù)據(jù)(內(nèi)存不存在隨機(jī)讀寫的問題), 當(dāng)這份數(shù)據(jù)超過一定大小的時候, 將其整個放入磁盤中.
這樣, 磁盤中就存在很多個有序的文件了, 但是會有大量的小文件, 讀取數(shù)據(jù)時要依次查找, 導(dǎo)致讀取性能降低. 這時就需要對多個小文件進(jìn)行多路歸并合成一個文件來優(yōu)化讀取的性能.
至此, 基本就是LSM 樹的全部思想了.
HBase存儲
在HBase中, 數(shù)據(jù)的存儲就使用了 LSM 樹進(jìn)行存儲. 其中每一條數(shù)據(jù)都是一條操作記錄. 那么在HBase實(shí)現(xiàn)中的部分內(nèi)容如下.
內(nèi)存有序結(jié)構(gòu)的實(shí)現(xiàn)
通過跳表來維護(hù)內(nèi)存中的有序結(jié)構(gòu), 當(dāng)一個跳表裝滿之后, 將禁止新的寫入操作并將其 push 到磁盤中, 同時開一個新的數(shù)據(jù)結(jié)構(gòu)來接收新到的操作請求.
每條數(shù)據(jù)的存儲內(nèi)容
存儲了一個KV 鍵值對, 其中的 V 就是我們寫入的值, 而這個 key 由以下部分組成:
- row key
- 列族
- 列名
- 時間戳
- 操作類型: Put、Delete、DeleteColumn、DeleteFamily 等等
整個列表是 key 的順序列表. 其排序規(guī)則如下:
按照這個順序進(jìn)行讀取指定 row key 的某一列數(shù)據(jù)時, 最先拿到的數(shù)據(jù)就是最新的版本, 若是 delete 操作, 說明最后執(zhí)行了刪除操作, 即使后面有數(shù)據(jù), 最新數(shù)據(jù)也是空.
磁盤文件的結(jié)構(gòu)
由三部分組成:
- 數(shù)據(jù)塊的最后一條數(shù)據(jù). 用于對索引進(jìn)行二分查找, 快速定位到指定的數(shù)據(jù)塊
- 數(shù)據(jù)塊在文件中的位置
- 數(shù)據(jù)塊的大小
- 布隆過濾器. 用戶在掃描時快速過濾不存在的數(shù)據(jù)塊
按照這個結(jié)構(gòu), 用戶在進(jìn)行指定row_key 讀取的時候, 每個文件的操作如下:
HBase 數(shù)據(jù)列族式存儲
先簡單回顧一下行式存儲和列式存儲.
行式存儲
行式存儲, 將一行數(shù)據(jù)存儲在一起, 一行數(shù)據(jù)寫完了才會寫下一行. 例如典型的 MySQL.
行式存儲在讀取一行數(shù)據(jù)的時候是比較快的, 但如果讀取的是某一列數(shù)據(jù), 也需要將整行讀取到內(nèi)存中進(jìn)行過濾.
列式存儲
與行式存儲相對應(yīng)的就是列式存儲, 既將一列數(shù)據(jù)存儲在一起, 不同列的數(shù)據(jù)分別存儲.
列式存儲對于只讀取某一列比較友好, 但相對的, 如果要讀取多列數(shù)據(jù), 需要讀取多次并進(jìn)行合并.
列族式存儲
而 HBase 中選用了一種折中的方案, 列族式存儲, 將列族放到一起存儲, 不同列族分別存儲.
那么也就是說, 如果一個表有多個列族, 每個列族下只有一列, 那么就等同于列式存儲
如果一個表只有一個列族, 該列族下有多個列, 那么就等同與行式存儲.
HBase 會將一張表同一列族的數(shù)據(jù), 分配到同一個 region 上, 這個region 分配在集群中的某一個 regionServer. 所有的 region 存儲在表: hbase:meta 表中, 表結(jié)構(gòu)如下:
表不同列含義如下:
- row_key 由以下字段拼接(逗號)而成
- 表名
- 起始 row_key
- 創(chuàng)建時間戳
- 上面三個字段的md5
- info:regioninfo 主要存儲以下數(shù)據(jù)(json)
- STARTKEY: 起始 row_key
- ENDKEY: 結(jié)束 row_key
- NAME: region 名
- ENCODED: 不清楚是什么
- info:seqnumDuringOpen 表示regionServer 在線時長
- info:server 落在哪個 regionServer 上
- info:serverstartcode regionServer 的啟動時間
- 等等
總結(jié)
簡單了解了HBase的數(shù)據(jù)落盤格式, 也大概解釋 HBase 的很多疑惑, 比如:
- 因?yàn)檎麄€文件是按照 row key 排序的
- 因?yàn)橐来巫x取文件進(jìn)行查找
- 因?yàn)槿慷际琼樞蜃x寫操作
- 將同一場景讀取的放到同一列族下, 不同場景讀取的放到不同列族下
總結(jié)
以上是生活随笔為你收集整理的HBase 数据存储结构的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 论文翻译——FingerSound:Re
- 下一篇: 代码整洁之道-对象和数据结构