為什麽不建議在 HBase 中使用過多的列族
我們知道,一張 HBase 表包含一個或多個列族。
HBase 的官方文檔中關於 HBase 表的列族的個數有兩處描述:
A typical schema has between 1 and 3 column families per table. HBase tables should not be designed to mimic RDBMS tables. 以及 HBase currently does not do well with anything above two or three column families so keep the number of column families in your schema low.
上面兩句話其實都是說一件事,HBase 中每張表的列族個數建議設在1~3之間。
其實,HBase 支持的列族個數並沒有限制,但為什麽文檔建議在1~3之間呢?
我將從幾個方面來闡述這麽做的原因。
列族數對 Flush 的影響
在 HBase 中,調用 API 往對應的表插入數據是會寫到 MemStore 的,而 MemStore 是一種內存結構,每個列族對應一個 MemStore(和零個或多個 HFile)。如果我們的表有兩個列族,那麽相應的 Region 中存在兩個 MemStore,如下圖:
從上圖可以看出,越多的列族,將會導致內存中存在越多的 MemStore;
而儲存在 MemStore 中的數據在滿足一定條件的時候將會進行 Flush 操作;
每次 Flush 的時候,每個 MemStore 將在磁盤生產一個 HFile 文件,如下:
這樣會導致越多的列族最終持久化到磁盤的 HFile 越多。更要命的是,當前 Flush 操作是 Region 級別的(當然,從HBase 1.1,HBase 2.0 開始 Flush 已經可以設置成列族級別的了),也就是說, Region 中某個 MemStore 被 Flush,同一個 Region 的其他 MemStore 也會進行 Flush 操作。當表有很多列族,而且列族之間數據不均勻,比如一個列族有100W行,一個列族只有10行,這樣會導致持久化到磁盤的文件數很多,同時有很多小文件,而且每次 Flush 操作也涉及到一定的 IO 操作。
為了解決每次 Flush 都對整個 Region 中 MemStore 進行的,HBASE-10201/HBASE-3149引入了對 Flush 策略進行選擇的功能(hbase.regionserver.flush.policy
hbase.hregion.percolumnfamilyflush.size.lower.bound.min
)的 MemStore 進行 Flush 操作。但是如果沒有 MemStore 大於這個閾值,還是會對所有的 MemStore 進行 Flush 操作。
此外,如果我們的列族數過多,這可能會導致觸發 RegionServer 級別的 Flush 操作;這將會導致落在該 RegionServer上的更新操作被阻塞,而且阻塞時間可能會達到分鐘級別。
列族數對 Split 的影響
我們知道,當 HBase 表中某個 Region 過大(比如大於 hbase.hregion.max.filesize
配置的大小。當然,Region 分裂並不是說整個 Region 大小加起來大於 hbase.hregion.max.filesize
就拆分,而是說 Region 中某個最大的 Store/HFile/storeFile 大於 hbase.hregion.max.filesize
才會觸發 Region 拆分的),會被拆分成兩個。如果我們有很多個列族,而這些列族之間的數據量相差懸殊,比如有些列族有 100W 行,而有些列族只有10行,這樣在 Region Split 的時候會導致原本數據量很小的 HFile 文件進一步被拆分,從而產生更多的小文件。註意,Region Split 是針對所有的列族進行的,這樣做的目的是同一行的數據即使在 Split 後也是存在同一個 Region 的。
列族數對 Compaction 的影響
與 Flush 操作一樣,目前 HBase 的 Compaction 操作也是 Region 級別的,過多的列族也會產生不必要的 IO。
列族數對 HDFS 的影響
我們知道,HDFS 其實對一個目錄下的文件數有限制的(dfs.namenode.fs-limits.max-directory-items
)。如果我們有 N 個列族,M 個 Region,那麽我們持久化到 HDFS 至少會產生 N*M 個文件;而每個列族對應底層的 HFile 文件往往不止一個,我們假設為 K 個,那麽最終表在 HDFS 目錄下的文件數將是 N*M*K,這可能會操作 HDFS 的限制。
列族數對 RegionServer 內存的影響
前面說了,一個列族在 RegionServer 中對應於一個 MemStore。而 HBase 從 0.90.1 版本開始引入了 MSLAB(Memstore-Local Allocation Buffers,參考HBASE-3455),這個功能默認是開啟的(通過hbase.hregion.memstore.mslab.enabled
),這使得每個 MemStore 在內存占用了 2MB (通過hbase.hregion.memstore.mslab.chunksize
配置)的 buffer。如果我們有很多的列族,而且一般一個 RegionServer 上會存在很多個 Region,這麽算起來光 MemStore 的緩存就會占用很多的內存。要註意的是,如果沒有往 MemStore 裏面寫數據,那麽 MemStore 的 MSLAB 是不占用空間的。
關於列族數設置的建議
在設置列族之前,我們最好想想,有沒有必要將不同的列放到不同的列族裏面。如果沒有必要最好放一個列族。如果真要設置多個列族,但是其中一些列族相對於其他列族數據量相差非常懸殊,比如1000W相比100行,是不是考慮用另外一張表來存儲相對小的列族。
為什麽不建議在 HBase 中使用過多的列族