1. Hbase基本概念
一. 為什么要用hbase
- 相比于HDFS
- HDFS適合批處理場景, 但不支持數(shù)據(jù)的隨機查找
- HDFS不支持數(shù)據(jù)的更新
- 相比于Hive
- Hive適合批處理的數(shù)據(jù)分析場景
- Hive不適合實時的數(shù)據(jù)訪問
- Hbase的特點
順序?qū)? 隨機度, 可擦寫, 可刪除
二. Hbase概念模型特點
- Hbase vs Mysql
- 列
- Hbase的列可以無限擴充
- Hbase的列沒有類型
- Hbase的多個列構(gòu)成一個列簇(column family)
- 主鍵 vs 索引
- Hbase僅有一個主鍵
- Hbase存儲的每條記錄根據(jù)主鍵進行排序
- 值
- Hbase的值是多版本的, 每個版本的值稱為Cell
- Cell的數(shù)目(每個值的版本數(shù))也沒有限制
- 列
- Hbase模型特點
- 可擴展性強
- 支持數(shù)十億行, 數(shù)百萬列
- 支持數(shù)十萬個版本
- 可以存儲很稀疏的數(shù)據(jù)
- 即使有90%的列是空值, Hbase也不會因此浪費存儲空間
- 支持隨機讀
- 根據(jù)主鍵會哦去一行數(shù)據(jù)
- 支持scan
- 快速獲取某些行區(qū)間內(nèi)的數(shù)據(jù)
- 快速獲取某幾列的數(shù)據(jù)
- 可擴展性強
- 行健舉例
歷史賬單查詢: userid+time+id
網(wǎng)絡(luò)爬蟲: URL+爬去實踐+優(yōu)先級
三. Hbase物理模型
- 行式存儲 vs 列式存儲
- 行式數(shù)據(jù)庫
- 沒建索引的行式數(shù)據(jù)庫, 查詢會產(chǎn)生大量IO
- 建立索引和物理視圖會話費大量時間和資源
- 若要提升查詢性能, 數(shù)據(jù)庫必須被大量膨脹
- 列式數(shù)據(jù)庫
- 數(shù)據(jù)按列單獨存放
- 數(shù)據(jù)即是索引
- 若只查詢指定的幾列, 可以大量降低IO
- 每一列可由一個thread單獨處理-查詢的并發(fā)
- 因為相同列的數(shù)據(jù)在一起存儲, 數(shù)據(jù)類型一致, 特征相似, 方便高效壓縮存儲
- 行式數(shù)據(jù)庫
- Hbase的列簇式存儲
- Hbase沒有選擇純粹的列式存儲, 因為Hbase支持上百萬個列, 每個列單獨形成文件的話會造成每個表的metadata信息多大, 而且會導致產(chǎn)生數(shù)百萬個小文件, 降低查詢效率
- 采用列簇式存儲: 同一個列簇的所有列的數(shù)據(jù)存放在一起; 不同列簇的數(shù)據(jù)在物理上分開
- 列簇是排好序的Cell構(gòu)成的
- Cell的構(gòu)成
- row key
- column family
- qualifier(列)
- timestamp
- value
(其中,前4個構(gòu)成key)
- Cell排序規(guī)則
- row key: 升序
- column family: 升序
- qualifier: 升序
- timestamp: 降序
- Cell支持二分查找
- Cell的構(gòu)成
四. Hbase劃分子表
- Region模型
- Hbase把一張大表拆分成多個子表, 每個子表成為一個Region
- 不同Region放在不同機器上
- 子表按照行健范圍劃分.
不存在一個row key的數(shù)據(jù)被劃分到2個不同region的情況 - 每個Region都包含所有列表
- Region的存儲思路
- Region內(nèi)部包含多個列簇, 每個列簇被一個Store存儲
- 每個Store把數(shù)據(jù)分為兩部分: MemStore和StoreFile
- MemStore只存儲新增的和修改過的數(shù)據(jù), 并在內(nèi)存滿的時候?qū)?shù)據(jù)刷新到StoreFile
五. Hbase的特點
- Hbase應(yīng)用場景
- 用戶畫像
- 用戶畫像中用戶的特征有幾十萬個, 但大部分特征都是空的, 是一個稀疏的矩陣
- mysql存儲上億行, 幾十萬列的情況會造成巨大浪費
- 網(wǎng)頁搜索倒排索引
- 淘寶交易數(shù)據(jù)
- 用戶畫像
- hbase是強一致性的
- 如果需要更新數(shù)據(jù), 或進行隨機讀寫, Hbase比hdfs更合適
- Hbase的字段部分數(shù)據(jù)類型, 全都按照字節(jié)存儲, 需要上次應(yīng)用系統(tǒng)按照類型翻譯字節(jié)
- Hbase只有聚簇索引(主鍵索引),沒有二級索引:
- 聚簇索引: 把這個字段抽取出來, 在這個字段上遍歷查找, 而不用去全表掃描, 減小磁盤IO
- 二級索引: 把索引字段排序后進行索引
- null記錄, 在Hbase中不占空間
- Hbase不是列式存儲,而是列族式存儲
- 把經(jīng)常需要一起讀寫的業(yè)務(wù)字段設(shè)置成一個列族
2. 架構(gòu)
一. 過濾器
- Filter簡介
- hbase為scan和get操作提供更加高級的filter
- Filter可以根據(jù)qualifier,列簇,版本等條件對行進行過濾.減少返回的結(jié)果數(shù)量, 降低網(wǎng)絡(luò)傳輸壓力
- 過濾操作在RegionServer上執(zhí)行
- SingleColumnValueFilter: 實現(xiàn)列簇/列上的where語義
- RowFilter, 實現(xiàn)row key的正則表達式過濾
- FamilyFilter,
- QualifierFilter,
- FilterList, 實現(xiàn)查詢條件and語法
各種過濾器
布隆過濾器
Hbase架構(gòu)
二. Compact原理
- Hbase基于
Log-Structed Merge Tree
架構(gòu)的 - 傳統(tǒng)的
樹能夠執(zhí)行快速隨機讀取, 但隨機寫入因為要調(diào)整樹形結(jié)構(gòu), 因此在大數(shù)據(jù)量下會變的很慢
- 為什么
樹的隨機讀取很快?
樹是多路查找樹, 所以要隨機查找一個數(shù)值, 將節(jié)點調(diào)入內(nèi)存的次數(shù)只和樹的高度有關(guān). 又因為
樹的每個節(jié)點包含很多值, 所以節(jié)點調(diào)入內(nèi)存時產(chǎn)生的磁盤IO是順序IO, 又加快了節(jié)點調(diào)入內(nèi)存的速度
- 為什么
- LSM tree如何快速執(zhí)行隨機寫?
- LSM tree首先在內(nèi)存中維護一個有序的樹形結(jié)構(gòu),類似
樹,稱作MemStore. 隨機寫入的數(shù)據(jù)會在MemStore中調(diào)整樹型后插入這個有序樹中.
- 為了保證數(shù)據(jù)的可靠性, 在寫入Memstore前會先以WAL(Write-Ahead-Log)的方式備份寫日志
- 當MemStore的數(shù)據(jù)量到達一定條件后, 會被刷新到磁盤上形成一個新文件"HFile". 因為這種寫磁盤是順序?qū)? 沒有舊文件被讀取修改, 因此執(zhí)行很快
- "HFile"在LSM tree架構(gòu)里又稱sstable, 意為相似的有序文件.當執(zhí)行隨機讀取時, 首先在MemStore中查找是否存在這個key, 是的話直接返回, 不是的話查找每個HFile中的記錄. 因為HFile中的記錄和Memstore一樣也是有序的樹形結(jié)構(gòu), 所以查找一個HFile的時間復雜度
, 所有的HFile查找時間復雜度
. 因此, 如果HFIle的數(shù)量很多, 會使得查找速度變慢, 需要Compact操作
- LSM tree主要是對寫優(yōu)化
綜上, 因為隨機寫入是分批量的順序?qū)懭氪疟P, 所以速度很快
- LSM tree首先在內(nèi)存中維護一個有序的樹形結(jié)構(gòu),類似
- Bloom過濾器, 減少HFile的篩選
為了減少隨機讀取時需要查找的HFile數(shù)量, 可使用Bloom過濾器先行判斷帶查找的key是否在這個HFile里. 如果Bloom過濾器返回False, 則key一定不在HFile里, 如果返回True, 仍有一定概率不在這個HFile里 - Compact
- Minor compact
將一些小的,相鄰的HFile合并成一個大的HFile. 此過程不會處理過期或已刪除的kv對 - Major compact
把一個Region下的所有HFile合并成一個大的HFile. 此過程會清理掉3類無意義的數(shù)據(jù): 被刪除的數(shù)據(jù), TTL過期數(shù)據(jù), 版本號超過限制的數(shù)據(jù)
Major compact耗時很長, 通常關(guān)閉自動major compact, 改為業(yè)務(wù)低峰期時手動處理Major Compact
- Minor compact
三. Bloom過濾器
- 上面的LSM tree架構(gòu)中提到, 隨機讀時使用Bloom過濾器減少查找的HFile數(shù)量, 在Hbase就是使用Bloom過濾器判斷該rowkey是否可能在該HFile中
- 算法描述
- Bloom過濾器用于判斷一個元素是否在一個集合內(nèi),是一種概率數(shù)據(jù)結(jié)構(gòu)
它返回"可能在集合內(nèi)"和"絕不可能在集合內(nèi)"2個結(jié)果. 集合中的元素越多, Bloom誤判成可能在集合中的概率就越大 - Bloom過濾器定義k個hash函數(shù), 和一個長度為m的bit數(shù)組(bitmap,java中用BitSet表示).
數(shù)組被初始化為全0數(shù)組, 每個哈希函數(shù)會把集合中的元素用射到數(shù)組的一個位置, 并把該位置的數(shù)組值置1.
測試一個元素是否在集合中, 用預先定義的k個hash函數(shù)依次哈希該值, 得到k個位置, 查看數(shù)組中這k個位置的值是否全部是1, 有一個為0則返回"一定不在集合中" - k個hash函數(shù)應(yīng)該選擇互相之間少有重疊的, 比如"2次方hash,3次方hash"
- Bloom過濾器用于判斷一個元素是否在一個集合內(nèi),是一種概率數(shù)據(jù)結(jié)構(gòu)
- 概率解釋
- 誤判成"可能存在"的概率
假設(shè)hash函數(shù)會把一個值等概率的映射到數(shù)組的m個位置, 則依次hash后, 該位置的數(shù)組沒有被置為1的概率為
k個不相關(guān)的hash函數(shù)均未把該位置置1的概率為
插入n個元素后, 該位置仍為0的概率
即該位置為1的概率 - k的選擇
k只由誤判的概率決定, 有
- 誤判成"可能存在"的概率
四. zookeeper中的meta表
-
meta表的作用
- 我們知道HBase的表是會分割為多個Region的,不同Region分布到不同RegionServer上。
- Region 是 HBase中分布式存儲和負載均衡的最小單元。
所以當我們從客戶端讀取,寫入數(shù)據(jù)的時候,我們就需要知道數(shù)據(jù)的Rowkey是在哪個Region以及我們需要的Region是在哪個RegionServer上。而這正是HBase Meta表所記錄的信息。
-
meta表也是一張Hbase的表
- meta表也是一張Hbase表, 有rowkey, 列族, 列
- hbase的所有查詢操作, 都要先訪問meta表, 找到自己所查記錄的表和rowkey在哪個Region上, 再到那個Region上執(zhí)行memstore或hfile的查找
-
meta表的rowkey
- meta表的rowkey組成結(jié)構(gòu): table名 + region的start key + 時間戳
- 上述3者的MD5值也是HBase在HDFS上的region名
-
meta表的列族和列
- 最主要的Family:info
- info里面包含三個列:regioninfo, server, serverstartcode。
其中regioninfo就是Region的詳細信息,包括StartKey, EndKey 以及每個Family的信息等。server存儲的就是管理這個Region的RegionServer的地址。所以當Region被拆分、合并或者重新分配的時候,都需要來修改這張表的內(nèi)容。
-
Hbase數(shù)據(jù)查詢過程:
- 客戶端連接zookeeper, 查找meta表在哪個機器上
- 查詢meta表, 根據(jù)每個region的
startKey
和endKey
找到region所在的server - 到指定Server上查找該region
- meta標的信息會被緩存起來, 便于下次查找
五. HBase的master-slave架構(gòu)
- HMaster
- 每個HregionServer都與HMaster保持心跳通信.
- HMaster負責給HRegionServer分配HRegion
- 當一臺HRegionServer宕機, HMaster會把這個RegionServer的Region標記為未分配, 然后再把他們分配到其他HRegionServer中
- HMaster的功能: HMaster主要負責
Table
和Region
的管理工作- 管理用戶對Table級別的增刪改查
- 負責HRegionServer的負載均衡, 調(diào)整Region在每臺機器的分布
- 在Region分片后, 負責新的Region分配
- 在HRegion Server停機后, 負責失效的HRegionServer上的Regions遷移
- HRegionServer
- 用戶通過訪問HRegionServer獲取這些數(shù)據(jù)
- 一臺機器上面一般只運行一個HRegionServer
- 一個HRegionServer上面有多個HRegion,一個HRegion 也只會被
- 一個HRegionServer維護
3. Rowkey設(shè)計
1. Rowkey
唯一標識一行記錄的主鍵, Hbase按照RowKey的字典順序全局排序
2. 稀疏矩陣
Hbase中表的數(shù)據(jù)按照稀疏方式存儲. 因為不想傳統(tǒng)數(shù)據(jù)庫組織數(shù)據(jù)形式, Hbase的列組成很靈活, 行與行之間不遵循相同的列定義
3. Region
Hbase不同于spark的hash code分區(qū), 使用"key range"劃分子表"Region". Region是Hbase負載均衡的基本單元, Region增大到一定大小后會自動分裂
4. column family
Region是對Hbase表的橫向切割, 則Column Family是對表的縱向切割. 每個column都要歸屬于一個Column Family, 這個歸屬關(guān)系是在寫數(shù)據(jù)時指定的而不是建表時預先定義的
5. kv形式存儲
Hbase中每行的每一列數(shù)據(jù)都以key-value形式存儲. key相同的任意數(shù)量的獨立key-value形成邏輯上的一行數(shù)據(jù)
Hbase行鍵設(shè)計
往往組合多個字段在一起作為rowkey, RowKey中的第一個字段稱之為"先導字段"
1. 反轉(zhuǎn)
如果先到字段本身會帶來熱點問題, 但該字段的尾部卻具有良好的隨機性; 此時可將先導字段做反轉(zhuǎn)處理
2. 加鹽
在rowkey前面加上長度固定的隨機Bytes
3. 哈希
將rowkey哈希后生成固定長度的byte串. 由于hash有很好的離散度, 所以可保障數(shù)據(jù)被均勻分散到各個region. hash rowkey既適合于隨機寫入, 也適合于知道rowkey后的隨機查詢, 帶在查詢一個范圍內(nèi)的rowkey時會有性能問題, 因為原本連續(xù)的rowkeyhash后被離散到多個region
列的設(shè)計
hbase中的每一列都是key-value形式的. 定義列就是要定義出列簇和列標識符. 列簇和列標識符應(yīng)該盡量短
一條數(shù)據(jù)的HBase之旅,簡明HBase入門教程-開篇
一. 前言
- Region是一個連續(xù)范圍內(nèi)的rowkey空間, 連續(xù)指Region中的rowkey在start key到end key范圍內(nèi)排序
- Region之間不會產(chǎn)生交叉, 一個rowkey只能屬于一個Region, 被一個RegionServer管理
- meta表中, 使用3層B樹找到某個表的某個rowkey所在的Region
- 當初次創(chuàng)建一張表時, Hbase只會分配一個Region給這張表. 這意味著初始時, 所有的request都會通往一個RegionServer上
二. PRE-SPLITTING
為什么使用預先劃分?
上面講到, 一張表初始創(chuàng)建時, 因為只有一個Region, 所以所有的請求都會發(fā)往一個RegionServer, 造成壓力巨大. 如果創(chuàng)建表時就制定Region的數(shù)量, 并按照一定的規(guī)則將rowkey發(fā)往不同的Region, 就會在初始就使用集群的分布式功能-
預劃分的start key和end key生成策略
預劃分需要計算表的分割點"split points", 有兩種默認的預劃分算法:-
HexStringSplit
:
如果rowkey是ASCII碼表示的, 可以使通過MD5生成或者其他產(chǎn)生均勻分布的16進制表示法. rowkey范圍為"00000000" => "FFFFFFFF"的范圍, 左側(cè)補0來使rowkey長度相同. 它把rowkey均勻分配到Region上, 這中做法會造成更大的空間使用量, 且不夠直觀 -
UniformSplit
:
如果rowkey是字節(jié)數(shù)組,可以用UniformSplit的方式進行,按照原始byte值(從0x00~0xFF)右邊以00填充。以這種方式分區(qū)的表在插入的時候需要對rowkey進行一個技巧性的改造, 比如原來的rowkey為rawStr,則需要對其取hashCode,然后進行按照比特位反轉(zhuǎn)后放在最初rowkey串的前面
Integer.reverse(Integer.valueOf(Integer.valueOf(i).hashCode())))
-
通常使用預劃分建表指定region數(shù)量以及region劃分算法, 后面使用Region自動劃分
三. AUTO SPLITTING
- 什么時候執(zhí)行自動劃分
無論是否開啟了pre-spliting, 一旦Region到一定限制, 就會自動化分為2個Region. 有3種預定義的自動劃分策略- ConstantSizeRegionSplitPolicy
- IncreasingToUpperBoundRegionSplitPolicy
- KeyPrefixRegionSplitPolicy.