HBase是什么
HBase構建在 HDFS 之上的分布式列式鍵值存儲系統。
HBase內部管理的文件全部存儲在HDFS中。
HBase VS HDFS
HDFS適合批處理場景
- 不支持數據隨機查找
- 不適合增量數據處理
- 不支持數據更新
HBase VS RDBMS
范式化和反范式化
事務(單行:多行ACID)
索引(RowKey: 健全索引)
RDBMS的優點
- SQL
- 索引
- 事務
- 輕量
- 久經考驗
RDBMS的缺陷
- 高並發讀寫的瓶頸
- 可擴展性的限制
- 事務一致性的負面影響
列式存儲
列式存儲的基礎:對於特定的查詢,不是所有值都是必需的。
- 以列為單位聚合數據,然后將列值順序地存入磁盤
- 數據類型一致,數據特征相似,更利於壓縮
- 大量降低系統I/O
Hbase 特性
- 容量巨大
單表可以有百億行,百萬列 - 面向列
- 稀疏性
空值不占用存儲空間 - 擴展性
由HDFS決定。
熱擴展 - 高可靠性
- WAL和Replication機制
- HDFS
- ZooKeeper
- 高性能
- LSM數據結構
- Rowkey有序排列
- 無模式
- 數據多版本
- 數據類型單一
- TTL
Hbase架構
Client
- 包含訪問 HBase 的接口,並維護 cache 來加快對 HBase 的訪問
- 通過RPC機制和Master,Region Server通信
Zookeeper
- 保證任何時候,集群中只有一個 master
- 存貯所有 Region 的尋址入口
- 實時監控 Region server 的上線和下線信息。並實時通知給 Master
- 存儲 HBase 元數據信息
- HBase中可以啟動多個HMaster,通過Zookeeper的Master Election機制保證總有一個Master運行
Master
- 為 Region server 分配 region
- 負責 Region server 的負載均衡
- 發現掛掉的 Region server 並重新分配其上的 region
- 負責表的建立,刪除等操作
(由於master只維護表和region的元數據,而不參與表數據IO的過程,master下線僅導致所有元數據的修改被凍結(無法創建刪除表,無法修改表的schema,無法進行region的負載均衡,無法處理region上下線,無法進行region的合並,唯一例外的是region的split可以正常進行,因為只有region server參與),表的數據讀寫還可以正常進行。
因此master下線短時間內對整個hbase集群沒有影響。)
Region Server
- Region server 維護 region ,處理對這些 region 的 IO 請求
- Region server 負責切分在運行過程中變得過大的 region
- Region Server 提供了行鎖
HRegionServer:HRegion:HStore = Column Family
HStore:
- MemStore:用戶數據首先寫入MemStore。 (flush操作)
- StoreFile:Hfile (compact操作 split操作)
Hbase 只有增加數據,所有更新和刪除都是在 compact 過程中進行的。
用戶寫操作只要寫入內存就可以立即返回,保證I/O高性能。
這台rs上的所有region共享相同的HLog files。
知道到目前為止,哪些數據已經被持久化了。
每個 update(或者說edit)都會
被寫到 log ,當通知客戶端成功后, rs 把數據再加載到內存中。
HBase數據模型
Row Key
行鍵,Table的主鍵,Table中的記錄按照Row Key排序。類型為Byte array
- 不宜過長
- 分布均勻
Column Family
列簇,Table在水平方向有一個或者多個Column Family組成,一個Column Family中可以由任意多個Column組成
Column
列 格式為:familyName:columnName。
列名稱是編碼在cell中的。
不同的cell可以擁有不同的列。
Version Number
版本號。默認值是系統時間戳。類型為long
Value (Cell)
具體的值。類型為Byte array
Hbase物理模型
KeyValue格式
{HBASE_HMOE}/bin/hbase hfile -p -f /hbase/data/default/kks1/68639b7c80a31bf91448d26bb7af17b7/cf/74e43a51ebd14007870ac58658330aeb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<br />K: 1/cf:age/1445860350861/Put/vlen=2/mvcc=0 V: 20
K: 1/cf:city/1445860381005/Put/vlen=4/mvcc=0 V: xian
K: 1/cf:name/1445860341592/Put/vlen=10/mvcc=0 V: kangkaisen
K: 2/cf:age/1445860401501/Put/vlen=3/mvcc=0 V: 200
K: 2/cf:city/1445860411217/Put/vlen=7/mvcc=0 V: beijing
K: 2/cf:name/1445860390752/Put/vlen=4/mvcc=0 V: kang
K: 3/cf:age/1445860419363/Put/vlen=4/mvcc=0 V: 2000
K: 3/cf:name/1445860428949/Put/vlen=6/mvcc=0 V: llllll
K: 4/cf:age/1446016552833/Put/vlen=2/mvcc=0 V: 23
K: 4/cf:city/1446016565367/Put/vlen=4/mvcc=0 V: xian
K: 5/cf:city/1446016578471/Put/vlen=3/mvcc=0 V: xan
K: 6/cf:age/1446016593194/Put/vlen=2/mvcc=0 V: 24
K: 7/cf:name/1446016604500/Put/vlen=8/mvcc=0 V: xiaoming
|
HFile格式
{HBASE_HOME}/bin/hbase hfile -m -f /hbase/data/default/kks1/68639b7c80a31bf91448d26bb7af17b7/cf/74e43a51ebd14007870ac58658330aeb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
Block index size as per heapsize: 392
reader=/hbase/data/default/kks1/68639b7c80a31bf91448d26bb7af17b7/cf/74e43a51ebd14007870ac58658330aeb,
compression=none,
cacheConf=CacheConfig:disabled,
firstKey=1/cf:age/1445860350861/Put,
lastKey=3/cf:name/1445860428949/Put,
avgKeyLen=18,
avgValueLen=5,
entries=8,
length=1211
Trailer:
fileinfoOffset=448,
loadOnOpenDataOffset=343,
dataIndexCount=1,
metaIndexCount=0,
totalUncomressedBytes=1123,
entryCount=8,
compressionCodec=NONE,
uncompressedDataIndexSize=31,
numDataIndexLevels=1,
firstDataBlockOffset=0,
lastDataBlockOffset=0,
comparatorClassName=org.apache.hadoop.hbase.KeyValue$KeyComparator,
majorVersion=2,
minorVersion=3
Fileinfo:
BLOOM_FILTER_TYPE = ROW
DELETE_FAMILY_COUNT = \x00\x00\x00\x00\x00\x00\x00\x00
EARLIEST_PUT_TS = \x00\x00\x01P\xA3\xFD\xF7X
KEY_VALUE_VERSION = \x00\x00\x00\x01
LAST_BLOOM_KEY = 3
MAJOR_COMPACTION_KEY = \x00
MAX_MEMSTORE_TS_KEY = \x00\x00\x00\x00\x00\x00\x00\x00
MAX_SEQ_ID_KEY = 10
TIMERANGE = 1445860341592....1445860428949
hfile.AVG_KEY_LEN = 18
hfile.AVG_VALUE_LEN = 5
hfile.LASTKEY = \x00\x013\x02cfname\x00\x00\x01P\xA3\xFFL\x95\x04
Mid-key: \x00\x011\x02cfage\x00\x00\x01P\xA3\xFE\x1B\x8D\x04
Bloom filter:
BloomSize: 8
No of Keys in bloom: 3
Max Keys for bloom: 6
Percentage filled: 50%
Number of chunks: 1
Comparator: RawBytesComparator
Delete Family Bloom filter:
Not present
|
物理上,表是按列族分開存儲的,每個 Column Family 存儲在 HDFS 上的一個單獨文件中(因此最好將具有共同I/O特性的列放在一個 Column Family中)
HBase 為每個值維護了多級索引,即: RowKey, column family, column name, timestamp
Table 中的所有行都按照 RowKey 的字典序排列
Table 在行的方向上分割為多個Region
Region 按大小分割的,每個表開始只有一個 region ,隨着數據增多, region 不斷增大,當增大到一個閥值的時候,region就會等分會兩個新的region,之后會有越來越多的region
Region 實際上是rowkey 排序后的按規則分割的連續的存儲空間
Region 是 HBase 中分布式存儲和負載均衡的最小單元。不同Region分布到不同RegionServer上
Region 雖然是分布式存儲的最小單元,但並不是存儲的最小單元。
- Region 由一個或者多個 Store 組成,每個 Store 保存一個columns family
- 每個 Strore 又由一個 MemStore 和 0 至多個 StoreFile 組成
- MemStore 存儲在內存中, StoreFile 存儲在 HDFS 上。
HBase 一致性模型
HBase 是強一致性的。
- WAL
- 行操作的存取操作是原子的。
HBase 容錯性
Master容錯
- Zookeeper重新選擇一個新的Master
- 無Master過程中,數據讀取仍照常進行
- 無master過程中,region切分、負載均衡等無法進行
RegionServer容錯
- 定時向Zookeeper匯報心跳
- Master將該RegionServer上的Region重新分配到其他RegionServer上
- WAL由Master進行分割並派送給新的RegionServer
Zookeeper容錯
- Zookeeper是一個可靠地服務
- 一般配置3或5個Zookeeper實例
HBase支持的操作
- 所有操作均基於Rowkey
- 支持CRUD 和SCAN
- 沒有內置join操作,可以使用MapReduce解決。
Write-Ahead-Log (WAL)
- 用戶每次寫入 MemStore 的同時,也會寫一份數據到HLOG文件中。只有當寫入成功后才通知客戶端該操作成功。
- 每個RegionServer只有一個HLOG文件
- HLOG文件定期會滾動更新,並刪除舊的文件(已持久化到StoreFile中的數據)
Hbase應用
何時使用Hbase
- 存儲大量數據(PB級數據)且能保證良好的訪問性能
- 高並發寫入,瞬間寫入量很大
- 業務場景簡單(無交叉列,交叉表,事務, 連接等)
- 可以優雅的數據擴展
Hbase 不適用場景
• 事務
• join、group by等關系查詢不計算
• 不按rowkey查詢數據
• 高並發隨機讀
• 低延遲隨機讀
Hbase應用場景
- 淘寶指數
- 淘寶交易歷史記錄查詢系統
- FB消息系統(聊天系統,郵件系統)
一個較小的臨時數據集,是經常變化的
一個不斷增加的數據集,是很少被訪問的 - 搜索引擎應用
- 增量數據存儲
OpenTSDB
FB Like按鈕 - 內容推薦引擎系統:搜狐
- 用戶模型服務:電商行業
HBase編程
- Native Java API
- HBase Shell
- Thrift Gateway (多語言編程)
- REST Gateway
- MapReduce
HBase Schema 設計
重點是RowKey設計
高級特性
過濾器 Filter
Scan.setFilter
所有的過濾器都在服務端生效
數據仍然需要從硬盤讀進RegionServer,過濾器在RegionServer里發揮作用
過濾器也可以自定義
計數器
如果沒有計數器,用戶需要針對一行加鎖,讀取一行的值,然后再加上特定的值,然后再寫回並釋放鎖,尤其是當客戶端進程崩潰之后,尚未釋放的鎖需要等待超時恢復,這樣在一個高負載的系統中會引起災難性后果。
計數器就是讀取並修改(Read and modify),保證一次客戶端操作的原子性。
將列作為計數器
通過Check-And-Save保證寫操作原子性
便於給某些在線應用提供實時統計功能
協處理器
允許用戶在region服務器上運行自己的代碼,也就是允許用戶執行region級操作
- observer
類似觸發器或者回調函數
在特定事件發生后執行 - endpoint
類似存儲過程
通過RPC,調用regionserver端的計算過程
Hbase 核心概念
LSM 樹
RDBMS 通常是尋道型的
LSM樹 屬於傳輸型的
LSM樹 會使用日志文件
為長期具有很高記錄更新頻率的文件提供低成本的索引機制
LSM 樹 最適合 索引插入 比 查詢操作 更常見的操作
主題思想是划分不同等級的樹
LSM樹和B+樹相比,LSM樹犧牲了部分讀性能,用來大幅提高寫性能。
LSM樹的設計思想非常朴素:將對數據的修改增量保持在內存中,達到指定的大小限制后將這些修改操作順序批量寫入磁盤,不過讀取的時候稍微麻煩,需要合並磁盤中歷史數據和內存中最近修改操作,所以寫入性能大大提升,讀取時可能需要先看是否命中內存,否則需要訪問較多的磁盤文件。(極端的說,基於LSM樹實現的HBase的寫性能比Mysql高了一個數量級,讀性能低了一個數量級)
LSM樹原理把一棵大樹拆分成N棵小樹,它首先寫入內存中,隨着小樹越來越大,內存中的小樹會flush到磁盤中,磁盤中的樹定期可以做merge操作,合並成一棵大樹,以優化讀性能。
split
當store的store file集合中總文件長度太大時(超過配置的閾值),這個region會一分為二.
父 region 下線,新分裂的倆個子 region 會被Master 分配到相應的 RegionServer.
compaction
minor compaction 將部分小文件合並成大文件
majar compaction
合並所有文件為一個
操作的是此列族的全量數據,所以可以做物理刪除。但是也由於是全量數據,執行起來耗費時間也會比價長
flush
用空的新memstore 獲取更新數據,將滿的舊memstore寫入磁盤。
Region 定位
第一次讀取:
步驟1:讀取ZooKeeper中META表的位置。
步驟2:讀取META表中用戶表的位置。
步驟3:讀取數據。
如果已經讀取過一次,則root表和.META都會緩存到本地,直接去用戶表的位置讀取數據。
META 表
當我們從客戶端讀取,寫入數據的時候,我們需要知道數據的 Rowkey是在哪個Region以及我們需要的Region是在哪個RegionServer上。
而這正是HBase Meta表所記錄的信息。
HBase 讀流程
- 定位Region
- blockcache
- block索引
- 布隆過濾器
- 更新時間
- scan
最小訪問單元 是 HFile 中的 一個 Block.
Hbase 寫流程
客戶端
1
2
3
4
5
6
7
8
9
10
|
Configuration conf = HBaseConfiguration.create();
HTable htable = new HTable(conf, "tablename");
Put put = new Put(Bytes.toBytes("rowkey"));
put.add(Bytes.toBytes("cf"), Bytes.toBytes("c1"), Bytes.toBytes("val1"));
put.add(Bytes.toBytes("cf"), Bytes.toBytes("c2"), Bytes.toBytes("val2"));
put.add(Bytes.toBytes("cf"), Bytes.toBytes("c3"), Bytes.toBytes("val3"));
htable.put(put);
htable.close();
}
|
- 緩存區
- 定位
比較 當前 Put 的行健和每個 Region 的Start Rowkey 和 Stop Rowkey。 - 線程池並發提交
- 等待請求處理結果
- 失敗重試
服務端
- 獲取Region
- 請求鎖
- 更新時間戳
- 更新WAL
- 寫入MemStore
- flush
二級索引
離線計算MR生成二級索引
客戶端處理
flush到磁盤時建立索引
協處理器方式
Bloom Filter
概念
當一個元素被加入集合時,通過K個散列函數將這個元素映射成一個位數組中的K個點,把它們置為1。檢索時,我們只要看看這些點是不是都是1就(大約)知道集合中有沒有它了:如果這些點有任何一個0,則被檢元素一定不在;如果都是1,則被檢元素很可能在。
優缺點
它的優點是空間效率和查詢時間都遠遠超過一般的算法,缺點是有一定的誤識別率和刪除困難
Bloom Filter不適合那些“零錯誤”的應用場合。而在能容忍低錯誤率的應用場合下,Bloom Filter通過極少的錯誤換取了存儲空間的極大節省。
應用
- 垃圾郵件過濾中黑白名單
- 網頁URL的去重
- 集合重復元素的判別
- 查詢加速
Hbase 優化
- 內存
- CPU
- 操作系統
JVM :SUN hotSpot 64位
使用notime選項掛載磁盤
關閉系統交換分區 - 網絡
- JVM優化
查詢優化
- 設置Scan 緩存
- 顯示指定列
- 關閉 ResultScanner,釋放資源
- 全表掃描時,禁用塊緩存。
- 優化行健查詢
- 使用過濾器,降低網絡I/O和客戶端壓力
- HtableTool
- 使用批量讀
- 使用協處理器統計行數
- 緩存查詢結果
寫入優化
- 關閉寫WAL日志 有風險
- 設置 autoflush 為false,客戶端會緩存。 有風險
- 預創建region
- 延遲日志flush
- htabletool訪問
- 使用批量寫
優化 split 和 compact
表設計優化
- 開啟布隆過濾器
- 調整列族塊大小
- 調整最大版本數
- 設置ttl屬性
- 關閉MR預測執行
布隆過濾器
None 默認
ROW 行級
ROWCOL 列標識符級
數據壓縮
LZO SNAPPY GZIP