大數據篇:Hbase
- Hbase是什么
Hbase是一個分布式、可擴展、支持海量數據存儲的NoSQL數據庫,物理結構存儲結構(K-V)。
- 如果沒有Hbase
如何在大數據場景中,做到上億數據秒級返回。(有條件:單條數據,范圍數據)
1 Hbase結構及數據類型
- 邏輯結構
- 物理結構
整張表會按照水平方向按照Row Key切割(Region)。再按垂直方向按ColumnFamily切割(Store),
-
Name Space:命名空間
- 類似於關系型數據庫中的database概念,每個命名空間下可以放多個表,默認存在2個命名空間:hbase和default,hbase存放Hbase內置的表,default表是用戶默認使用的命名空間。(例如給order表賦予命名空間test,可以寫為test:order)
-
Row:行
- Hbase中每行數據都由一個RowKey和多個列組成。
-
Column:列
- Hbase中的每個列都由ColumnFamily(列族)和ColumnQualifier(列限定符)進行限定,(例如:personal_info:name,personal_info:city)
-
Cell:單元
- 由{RowKey,ColumnFamily,ColumnQualifier,TimeStamp}唯一確定的單元,Cell中的數據是沒有類型的,全部為字節碼形式儲存。
-
Row Key:行鍵
- Row Key在表中必須是唯一的而且必須存在的。
- Row Key是 按照字典序一位一位比較有序排列的(有值比沒有值大)。例如row_key11 排列在row_key1和row_ley2之間。
- 所有對表的訪問都要通過Row Key 。(單個RowKey訪問,或RowKey范圍訪問,或全表掃描)
-
ColumnFamily:列族
- 創建Hbase表時,只需要給定CF即可,在插入數據時,列(字段)可以動態、按需增加。
- 每個CF可以有一個或多個列成員(ColumnQualifier)。
- 不同列族放在hdfs不同文件夾中存儲。
-
TimeStamp:時間戳
- 用於標識數據的不同版本,如果不指定時間戳,Hbase在寫入數據時會自動加上當前系統時間戳為該字段值。
2 Hbase架構
下面從小到大解釋上圖中的各組件中的功能。
-
StoreFile
- StoreFile為HBase真正存儲的文件,最終通過HDFS客戶端存入DataNode。(也就是linux磁盤中)
-
Store
- 可以理解為一個切片Region中的一組列族。(如上圖一個Region中有多個Store)
- Store中包含Mem Store(內存存儲),StoreFile(由內存刷入的數據,數量多了會合並,數據大了會切分)
-
Region
- Region可以理解為一張表的切片,Region按照數據量大小閥值和Row key進行切分。
- HBase自動把表水平(按行)划分成多個區域(region),每個region會保存一個表里面某段連續的數據。
- 每個表一開始只有一個region,隨着數據不斷插入,region不斷增大,當增大到一個閥值的時候,region就會根據Row key等分為兩個新的region,以此類推。
- Table中的行不斷增多,就會有越來越多的region,一張表數據就被保存在多個Region 上。
-
HLog
- Hbase的預寫日志,防止特殊情況下的數據丟失。
-
RegionServer
- 數據的操作(DML):get,put,delete
- 管理Region:SplitRegion(切分),CompactRegion(合並)
-
Master
- 表級別操作(DDL):create,delete,alter
- 管理RegionServer:監控RegionServer狀態,分配Regions到RegionServer,(如有機器rs1,rs2,rs3,數據寫入rs1,rs2上的Region,r3空閑--->這時rs1被大量寫入數據達到Region上限,rs1將Region等分后,就會通知Master將其中一份發往rs3管理。)
3 命令行操作
3.1 鏈接hbase
- 鏈接hbase
hbase shell
- 查看幫助命令或命令詳細使用
help
help '命令'
3.2 命名空間操作
3.2.1 查詢命名空間
list_namespace
3.2.2 查詢命名空間下的表
list_namespace_tables '命名空間名'
3.2.3 創建命名空間
create_namespace '命名空間名'
3.2.4 刪除命名空間(需要namespace是空的)
drop_namespace '命名空間名'
3.3 DDL操作
3.3.1 查詢所有用戶表
list
3.3.2 創建表
create '命名空間:表', '列族1', '列族2', '列族3','列族4'...
如圖發現有一串亂亂序文件夾,這串亂序就代表了Region號
3.3.3 查看表詳情
describe '命名空間:表'
可以看出VERSIONS為1,代表這個表只能存放一個版本的數據。
3.3.4 變更表信息
主要用於修改表的版本保存信息,也可以創建表的時候指定,但是shell命令復雜,故一般使用變更命令。
alter '命名空間:表',{NAME=>'列族名',VERSIONS=>3}
3.3.5 修改表狀態(刪除前必須失效表)
- 失效表
disable '表'
- 啟用表
enable '表'
3.3.6 刪除表
delete '表'
3.4 DML操作
3.4.1 插入數據
put '命名空間:表','RowKey','列族:列','值'
put '命名空間:表','RowKey','列族:列','值',時間戳(版本控制)
如圖發現並沒有數據文件生成,因為數據在內存中,需要flush '表',而后就可以看見數據落地了。(flush一次就是生成一個StoreFile)
3.4.2 掃描表
#全表掃描
scan '命名空間:表'
#范圍掃描(左閉右開)
scan '命名空間:表',{STARTROW => 'RowKey',STOPROW=>'RowKey'}
#掃描N個版本的數據
scan '命名空間:表',{RAW=>true,VERSIONS=>10}
3.4.3 Flush刷寫
flush '命名空間:表'
- 數據版本保留機制
從上面知道flush一次就是生成一個StoreFile,那么數據就會根據建表保留版本個數來存儲最近個數的數據。
比如:保留版本個數為2,那么如果插入v1,v2,v3三條數據,flush后,就只剩下v2,v3兩條數據,這時再插入v4,v5,v6三條數據,flush后,剩下的為v2,v3,v5,v6四個版本的數據(此時是2個StoreFile文件),如果發生Region合並或者分裂,那么StoreFile文件會被合並后在放入對應的Region中,這時數據就又會根據保留版本個數刪除,v2,v3,v5,v6,就變成了v5,v6。(如果沒有手動flush,或者到設置的自動flush時間,那么數據不會根據版本個數刪除)(默認超過3個StoreFile文件則會進行大合並)
- 一個列族對應一個MemStore
- 每個MemStore在刷寫到HDFS時,生成的StoreFile是獨立的
- RegionServer全局MemStore刷寫時機:hbase.regionserver.global.memstore.size
- 單個Memstore刷寫時機:hbase.hregion.memstore.flush.size
3.4.3 查詢數據
get '命名空間:表','RowKey'
get '命名空間:表','RowKey','列族'
get '命名空間:表','RowKey','列族:列'
#獲取N個版本的數據
get '命名空間:表','RowKey',{COLUMN=>'列族:列',VERSIONS=>10}
3.4.4 清空表
truncate '命名空間:表'
3.4.5 刪除數據
#delete '命名空間:表','RowKey','列族'(此命令行刪除有問題,但是API可以)
delete '命名空間:表','RowKey','列族:列'
deleteall '命名空間:表','RowKey'
4 讀寫流程
4.1 寫流程
- 客戶端通過ZK查詢元數據存儲表的所在RegionServer所在位置並返回
- 查詢元數據,返回需要表的RegionServer
-
客戶端緩存信息,方便下次使用
-
發送PUT請求到RegionServer,寫操作日志(WAL),再寫入內存,然后同步wal到HDFS,則結束。(此步驟由事務回滾保證日志、內存都寫入成功)
4.2 讀流程
在讀取數據時候,MemStore和StoreFile一起讀取,將StoreFile中的數據放入BlockCache,然后在將內存數據和BlockCache比較時間戳做Merge,取最新的數據返回。
5 合並切分
- 合並Compaction
由於Memstore每次刷寫都會生成一個新的HFile,且同一個字段的不同版本和不同類型有可能會分布在不同的HFile中,因此查詢時需要遍歷所有的HFile。為了減少HFile的個數,以及清理掉過期和刪除的數據,會進行StoreFile合並。
Compaction分為Minor Compaction和Major Compaction。
Minor Compaction會將臨近的若干個較小的HFile合並成一個較大的HFile,但不會清理過期和刪除的數據。
Major Compaction會將一個Store下的所有HFile合並成一個大的HFile,並且會清理掉過期和刪除的數據。
參數設置:
hbase.hregion.majorcompaction=0
hbase.hregion.majorcompaction.jitter=0
hbase.hstore.compactionThreshold=3
- 切分
默認情況下,每個Table起初只有一個Region,隨着數據的不斷寫入,Region會自動進行拆分,剛拆分時,兩個子Region都位於當前Region Server,但處於負載均衡的考慮,HMaster有可能會將某個Region轉移給其他的Region Server。
參數設置:
hbase.hregion.max.filesize=5G (如下公式中為Max1)(可以減小該值,提高並發)
hbase.hregion.memstore.flush.size=258M (如下公式中為Max2)
每次切分將會比較Max1和Max2的值,取小的。[min(Max1,Max2 * Region個數 * 2)],其中Region個數為當前Region Server中數據該Table的Region個數。
由於自動切分無法避免熱點問題,所以在生產中我們常常使用預分區和設計RowKey避免出現熱點問題
6 優化
6.1 盡量不要使用多個列族
為了避免flush時產生多個小文件。
6.2 內存優化
主要作用來緩存Table數據,但是flush時會GC,不要太大,根據集群資源,一般分配整個Hbase集群內存的70%,16->48G就可以了
6.3 允許在HDFS中追加內容
dfs.support.append=true (hdfs-site.xml、hbase-site.xml)
6.4 優化DataNode允許最大文件打開數
dfs.datanode.max.transfer.threads=4096 (HDFS配置)
在Region Server級別的合並操作中,Region Server不可用,可以根據集群資源調整該值,增加並發。
6.5 調高RPC監聽數量
hbase.regionserver.handler.count=30
根據集群情況,可以適當增加該值,主要決定是客戶端的請求數。
6.6 優化客戶端緩存
hbase.client.write.buffer=100M (寫緩存)
調高該值,可以減少RPC調用次數,單數會消耗更多內存,根據集群資源情況設定。
6.7 合並切分優化
參考5合並切分
6.8 預分區
- 創建表時候加入參數SPLITS
create '命名空間:表', '列族1', '列族2', '列族3','列族4'...,SPLITS=>['分區號','分區號','分區號','分區號']
根據數據量預估半年到一年的數據量,和Region最大值來選擇預分區數。
6.9 RowKey
- 散列性:均勻分部到不同的Region里
- 唯一性:不會重復
- 長度:70-100位
方案一:隨機數,hash值,但是這種不能范圍查詢,沒有數據的集中性。
方案二:字符串反轉,比如時間戳反轉后就達到了散列性,但是在查看的時候集中性只是優於第一種。
- 生產方案推薦:
#設計預分區鍵(如比如200個區) | ASCLL碼為124只有 } 和 ~ 比它大,那么不管以后的RowKey使用什么字符,都是小於這個字符的,所以可以有效的得到RowKey規律
000|
001|
......
199|
# 1 設計RowKey鍵_ASCLL碼為95
000_
001_
......
199_
# 2 根據業務唯一標識(如用戶ID,手機號,身份證)和時間維度(比如按月:202004)計算后根據分區數取余(13408657784^202004)%199=分區號
# 想以什么時間進行查詢就把什么往前提,如下數據需要查1月數據范圍就是 000_13408657784_2020-04 -> 000_13408657784_2020-04|
000_13408657784_2020-04-01 12:12:12
......
199_13408657784_2020-04-01 24:12:12