參考自《HBASE總結(jié)與實(shí)踐》
xmind轉(zhuǎn)markdown存在圖片丟失各吨,源文件下載地址:github hbase xmind下載地址
系統(tǒng)特性
優(yōu)勢(shì)
-
容量巨大
HBase的單表可以支持千億行鱼辙、百萬(wàn)列的數(shù)據(jù)規(guī)模凌外,數(shù)據(jù)容量可以達(dá)到TB甚至PB級(jí)別苞也。傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù)汁针,如Oracle和MySQL等透葛,如果單表記錄條數(shù)超過億行续镇,讀寫性能都會(huì)急劇下降,在HBase中并不會(huì)出現(xiàn)這樣的問題陵究。
-
擴(kuò)展性強(qiáng)
HBase集群可以非常方便地實(shí)現(xiàn)集群容量擴(kuò)展眠饮,主要包括數(shù)據(jù)存儲(chǔ)節(jié)點(diǎn)擴(kuò)展以及讀寫服務(wù)節(jié)點(diǎn)擴(kuò)展。HBase底層數(shù)據(jù)存儲(chǔ)依賴于HDFS系統(tǒng)畔乙,HDFS可以通過簡(jiǎn)單地增加DataNode實(shí)現(xiàn)擴(kuò)展君仆,HBase讀寫服務(wù)節(jié)點(diǎn)也一樣翩概,可以通過簡(jiǎn)單的增加RegionServer節(jié)點(diǎn)實(shí)現(xiàn)計(jì)算層的擴(kuò)展牲距。
-
稀疏性支持好
HBase支持大量稀疏存儲(chǔ),即允許大量列值為空钥庇,并不占用任何存儲(chǔ)空間牍鞠。這與傳統(tǒng)數(shù)據(jù)庫(kù)不同,傳統(tǒng)數(shù)據(jù)庫(kù)對(duì)于空值的處理要占用一定的存儲(chǔ)空間评姨,這會(huì)造成一定程度的存儲(chǔ)空間浪費(fèi)难述。因此可以使用HBase存儲(chǔ)多至上百萬(wàn)列的數(shù)據(jù)萤晴,即使表中存在大量的空值,也不需要任何額外空間胁后。
-
高性能
HBase目前主要擅長(zhǎng)于OLTP場(chǎng)景店读,數(shù)據(jù)寫操作性能強(qiáng)勁,對(duì)于隨機(jī)單點(diǎn)讀以及小范圍的掃描讀攀芯,其性能也能夠得到保證屯断。對(duì)于大范圍的掃描讀可以使用MapReduce提供的API,以便實(shí)現(xiàn)更高效的并行掃描侣诺。
-
支持?jǐn)?shù)據(jù)版本
HBase支持多版本特性殖演,即一個(gè)KV可以同時(shí)保留多個(gè)版本,用戶可以根據(jù)需要選擇最新版本或者某個(gè)歷史版本年鸳。
-
支持?jǐn)?shù)據(jù)過期特性TTL
HBase支持TTL過期特性趴久,用戶只需要設(shè)置過期時(shí)間,超過TTL的數(shù)據(jù)就會(huì)被自動(dòng)清理搔确,不需要用戶寫程序手動(dòng)刪除彼棍。
-
hadoop原生支持
HBase是Hadoop生態(tài)中的核心成員之一,很多生態(tài)組件都可以與其直接對(duì)接膳算。HBase數(shù)據(jù)存儲(chǔ)依賴于HDFS滥酥,這樣的架構(gòu)可以帶來很多好處辜羊,比如用戶可以直接繞過HBase系統(tǒng)操作HDFS文件白群,高效地完成數(shù)據(jù)掃描或者數(shù)據(jù)導(dǎo)入工作;再比如可以利用HDFS提供的多級(jí)存儲(chǔ)特性(Archival Storage Feature)揪荣,根據(jù)業(yè)務(wù)的重要程度將HBase進(jìn)行分級(jí)存儲(chǔ)宇葱,重要的業(yè)務(wù)放到SSD瘦真,不重要的業(yè)務(wù)放到HDD∈蚯疲或者用戶可以設(shè)置歸檔時(shí)間诸尽,進(jìn)而將最近的數(shù)據(jù)放在SSD,將歸檔數(shù)據(jù)文件放在HDD印颤。另外您机,HBase對(duì)MapReduce的支持也已經(jīng)有了很多案例,后續(xù)還會(huì)針對(duì)Spark做更多的工作年局。
劣勢(shì)
-
不支持復(fù)雜聚合運(yùn)算
HBase本身不支持很復(fù)雜的聚合運(yùn)算(如Join际看、GroupBy等)。如果業(yè)務(wù)中需要使用聚合運(yùn)算矢否,可以在HBase之上架設(shè)Phoenix組件或者Spark組件仲闽,前者主要應(yīng)用于小規(guī)模聚合的OLTP場(chǎng)景,后者應(yīng)用于大規(guī)模聚合的OLAP場(chǎng)景僵朗。
-
不支持全局跨行事務(wù)
HBase原生不支持全局跨行事務(wù)赖欣,只支持單行事務(wù)模型屑彻。同樣,可以使用Phoenix提供的全局事務(wù)模型組件來彌補(bǔ)HBase的這個(gè)缺陷顶吮。
體系結(jié)構(gòu)
總架構(gòu)圖
HBase客戶端
HBase客戶端(Client)提供了Shell命令行接口社牲、原生Java API編程接口、Thrift/REST API編程接口以及MapReduce編程接口悴了。HBase客戶端支持所有常見的DML操作以及DDL操作膳沽,即數(shù)據(jù)的增刪改查和表的日常維護(hù)等。其中Thrift/REST API主要用于支持非Java的上層業(yè)務(wù)需求让禀,MapReduce接口主要用于批量數(shù)據(jù)導(dǎo)入以及批量數(shù)據(jù)讀取挑社。HBase客戶端訪問數(shù)據(jù)行之前,首先需要通過元數(shù)據(jù)表定位目標(biāo)數(shù)據(jù)所在RegionServer巡揍,之后才會(huì)發(fā)送請(qǐng)求到該RegionServer痛阻。同時(shí)這些元數(shù)據(jù)會(huì)被緩存在客戶端本地,以方便之后的請(qǐng)求訪問腮敌。如果集群RegionServer發(fā)生宕機(jī)或者執(zhí)行了負(fù)載均衡等阱当,從而導(dǎo)致數(shù)據(jù)分片發(fā)生遷移,客戶端需要重新請(qǐng)求最新的元數(shù)據(jù)并緩存在本地糜工。
ZooKeeper
ZooKeeper(ZK)也是Apache Hadoop的一個(gè)頂級(jí)項(xiàng)目弊添,基于Google的Chubby開源實(shí)現(xiàn),主要用于協(xié)調(diào)管理分布式應(yīng)用程序捌木。在HBase系統(tǒng)中油坝,ZooKeeper扮演著非常重要的角色。
-
實(shí)現(xiàn)Master高可用
通常情況下系統(tǒng)中只有一個(gè)Master工作刨裆,一旦Active Master由于異常宕機(jī)澈圈,ZooKeeper會(huì)檢測(cè)到該宕機(jī)事件,并通過一定機(jī)制選舉出新的Master帆啃,保證系統(tǒng)正常運(yùn)轉(zhuǎn)瞬女。
-
管理系統(tǒng)核心元數(shù)據(jù)
比如,管理當(dāng)前系統(tǒng)中正常工作的RegionServer集合努潘,保存系統(tǒng)元數(shù)據(jù)表hbase:meta所在的RegionServer地址等诽偷。
-
參與RegionServer宕機(jī)恢復(fù)
ZooKeeper通過心跳可以感知到RegionServer是否宕機(jī),并在宕機(jī)后通知Master進(jìn)行宕機(jī)處理疯坤。
-
實(shí)現(xiàn)分布式表鎖
HBase中對(duì)一張表進(jìn)行各種管理操作(比如alter操作)需要先加表鎖报慕,防止其他用戶對(duì)同一張表進(jìn)行管理操作,造成表狀態(tài)不一致贴膘。和其他RDBMS表不同卖子,HBase中的表通常都是分布式存儲(chǔ),ZooKeeper可以通過特定機(jī)制實(shí)現(xiàn)分布式表鎖刑峡。
Master
-
處理用戶的各種管理請(qǐng)求
包括建表洋闽、修改表、權(quán)限操作突梦、切分表诫舅、合并數(shù)據(jù)分片以及Compaction等
-
管理集群中所有RegionServer
管理集群中所有RegionServer,包括RegionServer中Region的負(fù)載均衡宫患、RegionServer的宕機(jī)恢復(fù)以及Region的遷移等
-
清理過期日志以及文件
Master會(huì)每隔一段時(shí)間檢查HDFS中HLog是否過期刊懈、HFile是否已經(jīng)被刪除,并在過期之后將其刪除娃闲。
RegionServer
RegionServer主要用來響應(yīng)用戶的IO請(qǐng)求虚汛,是HBase中最核心的模塊,由WAL(HLog)皇帮、BlockCache以及多個(gè)Region構(gòu)成卷哩。
-
WAL(HLog)
HLog在HBase中有兩個(gè)核心作用——其一,用于實(shí)現(xiàn)數(shù)據(jù)的高可靠性属拾,HBase數(shù)據(jù)隨機(jī)寫入時(shí)将谊,并非直接寫入HFile數(shù)據(jù)文件,而是先寫入緩存渐白,再異步刷新落盤尊浓。為了防止緩存數(shù)據(jù)丟失,數(shù)據(jù)寫入緩存之前需要首先順序?qū)懭際Log纯衍,這樣栋齿,即使緩存數(shù)據(jù)丟失,仍然可以通過HLog日志恢復(fù)襟诸;其二褒颈,用于實(shí)現(xiàn)HBase集群間主從復(fù)制,通過回放主集群推送過來的HLog日志實(shí)現(xiàn)主從復(fù)制励堡。
-
BlockCache
HBase系統(tǒng)中的讀緩存谷丸。客戶端從磁盤讀取數(shù)據(jù)之后通常會(huì)將數(shù)據(jù)緩存到系統(tǒng)內(nèi)存中应结,后續(xù)訪問同一行數(shù)據(jù)可以直接從內(nèi)存中獲取而不需要訪問磁盤刨疼。
- LRUBlockCache
- BucketCache
-
Region
數(shù)據(jù)表的一個(gè)分片,當(dāng)數(shù)據(jù)表大小超過一定閾值就會(huì)“水平切分”鹅龄,分裂為兩個(gè)Region揩慕。Region是集群負(fù)載均衡的基本單位。通常一張表的Region會(huì)分布在整個(gè)集群的多臺(tái)RegionServer上扮休,一個(gè)RegionServer上會(huì)管理多個(gè)Region迎卤,當(dāng)然,這些Region一般來自不同的數(shù)據(jù)表玷坠。
一個(gè)Region由一個(gè)或者多個(gè)Store構(gòu)成蜗搔,Store的個(gè)數(shù)取決于表中列簇(column family)的個(gè)數(shù)劲藐,多少個(gè)列簇就有多少個(gè)Store。HBase中樟凄,每個(gè)列簇的數(shù)據(jù)都集中存放在一起形成一個(gè)存儲(chǔ)單元Store聘芜,因此建議將具有相同IO特性的數(shù)據(jù)設(shè)置在同一個(gè)列簇中。-
Store
每個(gè)Store由一個(gè)MemStore和一個(gè)或多個(gè)HFile組成缝龄。
-
MemStore
MemStore稱為寫緩存汰现,用戶寫入數(shù)據(jù)時(shí)首先會(huì)寫到MemStore,當(dāng)MemStore寫滿之后(緩存數(shù)據(jù)超過閾值叔壤,默認(rèn)128M)系統(tǒng)會(huì)異步地將數(shù)據(jù)flush成一個(gè)HFile文件瞎饲。
-
HFile
隨著數(shù)據(jù)不斷寫入,HFile文件會(huì)越來越多炼绘,當(dāng)HFile文件數(shù)超過一定閾值之后系統(tǒng)將會(huì)執(zhí)行Compact操作嗅战,將這些小文件通過一定策略合并成一個(gè)或多個(gè)大文件
-
-
HDFS
HBase底層依賴HDFS組件存儲(chǔ)實(shí)際數(shù)據(jù),包括用戶數(shù)據(jù)文件饭望、HLog日志文件等最終都會(huì)寫入HDFS落盤仗哨。HDFS是Hadoop生態(tài)圈內(nèi)最成熟的組件之一,數(shù)據(jù)默認(rèn)三副本存儲(chǔ)策略可以有效保證數(shù)據(jù)的高可靠性铅辞。HBase內(nèi)部封裝了一個(gè)名為DFSClient的HDFS客戶端組件厌漂,負(fù)責(zé)對(duì)HDFS的實(shí)際數(shù)據(jù)進(jìn)行讀寫訪問。
數(shù)據(jù)模型
table(表)
表斟珊,一個(gè)表包含多行數(shù)
row(行)
行苇倡,一行數(shù)據(jù)包含一個(gè)唯一標(biāo)識(shí)rowkey、多個(gè)column以及對(duì)應(yīng)的值囤踩。在HBase中旨椒,一張表中所有row都按照rowkey的字典序由小到大排序
timestamp(時(shí)間戳)
時(shí)間戳,每個(gè)cell在寫入HBase的時(shí)候都會(huì)默認(rèn)分配一個(gè)時(shí)間戳作為該cell的版本堵漱,當(dāng)然综慎,用戶也可以在寫入的時(shí)候自帶時(shí)間戳。HBase支持多版本特性勤庐,即同一rowkey示惊、column下可以有多個(gè)value存在,這些value使用timestamp作為版本號(hào)愉镰,版本越大米罚,表示數(shù)據(jù)越新。
cell(具體value)
單元格丈探,由五元組(row, column, timestamp,type, value)組成的結(jié)構(gòu)录择,其中type表示Put/Delete這樣的操作類型,timestamp代表這個(gè)cell的版本。這個(gè)結(jié)構(gòu)在數(shù)據(jù)庫(kù)中實(shí)際是以KV結(jié)構(gòu)存儲(chǔ)的隘竭,其中(row, column,timestamp, type)是K塘秦,value字段對(duì)應(yīng)KV結(jié)構(gòu)的V
column(列)
列,與關(guān)系型數(shù)據(jù)庫(kù)中的列不同货裹,HBase中的column由column family(列簇)以及qualifier(列名)兩部分組成嗤形,兩者中間使用":"相連精偿。比如contents:html弧圆,其中contents為列簇,html為列簇下具體的一列笔咽。column family在表創(chuàng)建的時(shí)候需要指定搔预,用戶不能隨意增減。一個(gè)column family下可以設(shè)置任意多個(gè)qualifier叶组,因此可以理解為HBase中的列可以動(dòng)態(tài)增加拯田,理論上甚至可以擴(kuò)展到上百萬(wàn)列
數(shù)據(jù)結(jié)構(gòu)
跳躍表
跳躍表(SkipList)是一種能高效實(shí)現(xiàn)插入、刪除甩十、查找的內(nèi)存數(shù)據(jù)結(jié)構(gòu)船庇,這些操作的期望復(fù)雜度都是O(logN)。與紅黑樹以及其他的二分查找樹相比侣监,跳躍表的優(yōu)勢(shì)在于實(shí)現(xiàn)簡(jiǎn)單鸭轮,而且在并發(fā)場(chǎng)景下加鎖粒度更小,從而可以實(shí)現(xiàn)更高的并發(fā)性橄霉。正因?yàn)檫@些優(yōu)點(diǎn)窃爷,跳躍表廣泛使用于KV數(shù)據(jù)庫(kù)中,諸如Redis姓蜂、LevelDB按厘、HBase都把跳躍表作為一種維護(hù)有序數(shù)據(jù)集合的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)。
多路歸并
先看一個(gè)簡(jiǎn)單的問題:現(xiàn)在有K個(gè)文件钱慢,其中第i個(gè)文件內(nèi)部存儲(chǔ)有Ni個(gè)正整數(shù)(這些整數(shù)在文件內(nèi)按照從小到大的順序存儲(chǔ))逮京,如何設(shè)計(jì)一個(gè)算法將K個(gè)有序文件合并成一個(gè)大的有序文件?在排序算法中束莫,有一類排序算法叫做歸并排序懒棉,里面就有大家熟知的兩路歸并實(shí)現(xiàn)。現(xiàn)在相當(dāng)于K路歸并麦箍,因此可以拓展一下漓藕,思路類似。對(duì)每個(gè)文件設(shè)計(jì)一個(gè)指針挟裂,取出K個(gè)指針中數(shù)值最小的一個(gè)享钞,然后把最小的那個(gè)指針后移,接著繼續(xù)找K個(gè)指針中數(shù)值最小的一個(gè),繼續(xù)后移指針……直到N個(gè)文件全部讀完為止
LSM樹
LSM樹本質(zhì)上和B+樹一樣栗竖,是一種磁盤數(shù)據(jù)的索引結(jié)構(gòu)暑脆。但和B+樹不同的是,LSM樹的索引對(duì)寫入請(qǐng)求更友好狐肢。因?yàn)闊o論是何種寫入請(qǐng)求添吗,LSM樹都會(huì)將寫入操作處理為一次順序?qū)懀鳫DFS擅長(zhǎng)的正是順序?qū)懀ㄇ襀DFS不支持隨機(jī)寫)份名,因此基于HDFS實(shí)現(xiàn)的HBase采用LSM樹作為索引是一種很合適的選擇碟联。LSM樹的索引一般由兩部分組成,一部分是內(nèi)存部分僵腺,一部分是磁盤部分鲤孵。內(nèi)存部分一般采用跳躍表來維護(hù)一個(gè)有序的KeyValue集合。磁盤部分一般由多個(gè)內(nèi)部KeyValue有序的文件組成
布隆過濾器
在HBase 1.x版本中辰如,用戶可以對(duì)某些列設(shè)置不同類型的布隆過濾器普监,共有3種類型。
? NONE:關(guān)閉布隆過濾器功能琉兜。
? ROW:按照rowkey來計(jì)算布隆過濾器的二進(jìn)制串并存儲(chǔ)凯正。Get查詢的時(shí)候,必須帶rowkey豌蟋,所以用戶可以在建表時(shí)默認(rèn)把布隆過濾器設(shè)置為ROW類型廊散。
? ROWCOL:按照rowkey+family+qualifier這3個(gè)字段拼出byte[]來計(jì)算布隆過濾器值并存儲(chǔ)。如果在查詢的時(shí)候夺饲,Get能指定rowkey奸汇、family、qualifier這3個(gè)字段往声,則肯定可以通過布隆過濾器提升性能擂找。但是如果在查詢的時(shí)候,Get中缺少rowkey浩销、family贯涎、qualifier中任何一個(gè)字段,則無法通過布隆過濾器提升性能慢洋,因?yàn)橛?jì)算布隆過濾器的Key不確定塘雳。
Compaction
Minor Compaction
是指選取部分小的、相鄰的HFile普筹,將它們合并成一個(gè)更大的HFile
Major Compaction
是指將一個(gè)Store中所有的HFile合并成一個(gè)HFile败明,這個(gè)過程還會(huì)完全清理三類無意義數(shù)據(jù):被刪除的數(shù)據(jù)、TTL過期數(shù)據(jù)太防、版本號(hào)超過設(shè)定版本號(hào)的數(shù)據(jù)妻顶。
RegionServer核心模塊
HLog
HBase中系統(tǒng)故障恢復(fù)以及主從復(fù)制都基于HLog實(shí)現(xiàn)。默認(rèn)情況下,所有寫入操作(寫入讳嘱、更新以及刪除)的數(shù)據(jù)都先以追加形式寫入HLog幔嗦,再寫入MemStore。大多數(shù)情況下沥潭,HLog并不會(huì)被讀取邀泉,但如果RegionServer在某些異常情況下發(fā)生宕機(jī),此時(shí)已經(jīng)寫入MemStore中但尚未f lush到磁盤的數(shù)據(jù)就會(huì)丟失钝鸽,需要回放HLog補(bǔ)救丟失的數(shù)據(jù)汇恤。此外,HBase主從復(fù)制需要主集群將HLog日志發(fā)送給從集群寞埠,從集群在本地執(zhí)行回放操作屁置,完成集群之間的數(shù)據(jù)復(fù)制焊夸。
-
HLog文件存儲(chǔ)
/hbase/WALs存儲(chǔ)當(dāng)前還未過期的日志仁连;/hbase/oldWALs存儲(chǔ)已經(jīng)過期的日志
-
HLog生命周期
-
HLog構(gòu)建
HBase的任何寫入(更新、刪除)操作都會(huì)先將記錄追加寫入到HLog文件中阱穗。
-
HLog滾動(dòng)
HBase后臺(tái)啟動(dòng)一個(gè)線程饭冬,每隔一段時(shí)間(由參數(shù)'hbase.regionserver. logroll.period'決定,默認(rèn)1小時(shí))進(jìn)行日志滾動(dòng)揪阶。日志滾動(dòng)會(huì)新建一個(gè)新的日志文件昌抠,接收新的日志數(shù)據(jù)。日志滾動(dòng)機(jī)制主要是為了方便過期日志數(shù)據(jù)能夠以文件的形式直接刪除鲁僚。
-
HLog失效
寫入數(shù)據(jù)一旦從MemStore中落盤炊苫,對(duì)應(yīng)的日志數(shù)據(jù)就會(huì)失效。為了方便處理冰沙,HBase中日志失效刪除總是以文件為單位執(zhí)行侨艾。查看某個(gè)HLog文件是否失效只需確認(rèn)該HLog文件中所有日志記錄對(duì)應(yīng)的數(shù)據(jù)是否已經(jīng)完成落盤,如果日志中所有日志記錄已經(jīng)落盤拓挥,則可以認(rèn)為該日志文件失效唠梨。一旦日志文件失效,就會(huì)從WALs文件夾移動(dòng)到oldWALs文件夾侥啤。注意此時(shí)HLog并沒有被系統(tǒng)刪除当叭。
-
HLog刪除
Master后臺(tái)會(huì)啟動(dòng)一個(gè)線程,每隔一段時(shí)間(參數(shù)'hbase.master.cleaner. interval'盖灸,默認(rèn)1分鐘)檢查一次文件夾oldWALs下的所有失效日志文件蚁鳖,確認(rèn)是否可以刪除,確認(rèn)可以刪除之后執(zhí)行刪除操作赁炎。確認(rèn)條件主要有兩個(gè):
?該HLog文件是否還在參與主從復(fù)制醉箕。對(duì)于使用HLog進(jìn)行主從復(fù)制的業(yè)務(wù),需要繼續(xù)確認(rèn)是否該HLog還在應(yīng)用于主從復(fù)制。
?該HLog文件是否已經(jīng)在OldWALs目錄中存在10分鐘琅攘。為了更加靈活地管理HLog生命周期垮庐,系統(tǒng)提供了參數(shù)設(shè)置日志文件的TTL(參數(shù)'hbase.master.logcleaner.ttl',默認(rèn)10分鐘)坞琴,默認(rèn)情況下oldWALs里面的HLog文件最多可以再保存10分鐘哨查。
-
HLog邏輯結(jié)構(gòu)圖
MemStore
-
MSLAB內(nèi)存管理
為了優(yōu)化這種內(nèi)存碎片可能導(dǎo)致的Full GC,HBase借鑒了線程本地分配緩存(Thread-Local Allocation Buffer剧辐,TLAB)的內(nèi)存管理方式寒亥,通過順序化分配內(nèi)存、內(nèi)存數(shù)據(jù)分塊等特性使得內(nèi)存碎片更加粗粒度荧关,有效改善Full GC情況溉奕。具體實(shí)現(xiàn)步驟如下:
1)每個(gè)MemStore會(huì)實(shí)例化得到一個(gè)MemStoreLAB對(duì)象。
2)MemStoreLAB會(huì)申請(qǐng)一個(gè)2M大小的Chunk數(shù)組忍啤,同時(shí)維護(hù)一個(gè)Chunk偏移量加勤,該偏移量初始值為0。
3)當(dāng)一個(gè)KeyValue值插入MemStore后同波,MemStoreLAB會(huì)首先通過KeyValue.getBuffer()取得data數(shù)組鳄梅,并將data數(shù)組復(fù)制到Chunk數(shù)組中,之后再將Chunk偏移量往前移動(dòng)data.length未檩。4)當(dāng)前Chunk滿了之后戴尸,再調(diào)用new byte[2 * 1024 * 1024]申請(qǐng)一個(gè)新的Chunk。這種內(nèi)存管理方式稱為MemStore本地分配緩存(MemStore-Local Allocation Buffer冤狡,MSLAB)孙蒙。
這是因?yàn)镸emStore會(huì)在將數(shù)據(jù)寫入內(nèi)存時(shí)首先申請(qǐng)2M的Chunk,再將實(shí)際數(shù)據(jù)寫入申請(qǐng)的Chunk中悲雳。這種內(nèi)存管理方式挎峦,使得f lush之后殘留的內(nèi)存碎片更加粗粒度,極大降低Full GC的觸發(fā)頻率怜奖。 -
MemStore Chunk Pool
經(jīng)過MSLAB優(yōu)化之后浑测,系統(tǒng)因?yàn)镸emStore內(nèi)存碎片觸發(fā)的Full GC次數(shù)會(huì)明顯降低。然而這樣的內(nèi)存管理模式并不完美歪玲,還存在一些“小問題”迁央。比如一旦一個(gè)Chunk寫滿之后,系統(tǒng)會(huì)重新申請(qǐng)一個(gè)新的Chunk滥崩,新建Chunk對(duì)象會(huì)在JVM新生代申請(qǐng)新內(nèi)存岖圈,如果申請(qǐng)比較頻繁會(huì)導(dǎo)致JVM新生代Eden區(qū)滿掉,觸發(fā)YGC钙皮。試想如果這些Chunk能夠被循環(huán)利用蜂科,系統(tǒng)就不需要申請(qǐng)新的Chunk顽决,這樣就會(huì)使得YGC頻率降低,晉升到老年代的Chunk就會(huì)減少导匣,CMS GC發(fā)生的頻率也會(huì)降低才菠。這就是MemStore Chunk Pool的核心思想,具體實(shí)現(xiàn)步驟如下:
1)系統(tǒng)創(chuàng)建一個(gè)Chunk Pool來管理所有未被引用的Chunk贡定,這些Chunk就不會(huì)再被JVM當(dāng)作垃圾回收赋访。
2)如果一個(gè)Chunk沒有再被引用,將其放入Chunk Pool缓待。
3)如果當(dāng)前Chunk Pool已經(jīng)達(dá)到了容量最大值蚓耽,就不會(huì)再接納新的Chunk。
4)如果需要申請(qǐng)新的Chunk來存儲(chǔ)KeyValue旋炒,首先從ChunkPool中獲取步悠,如果能夠獲取得到就重復(fù)利用,否則就重新申請(qǐng)一個(gè)新的Chunk瘫镇。 -
相關(guān)配置項(xiàng)
HBase中MSLAB功能默認(rèn)是開啟的鼎兽,默認(rèn)的ChunkSize是2M,也可以通過參數(shù)"hbase.hregion.memstore.mslab.chunksize"進(jìn)行設(shè)置汇四,建議保持默認(rèn)值接奈。Chunk Pool功能默認(rèn)是關(guān)閉的,需要配置參數(shù)"hbase.hregion.memstore.chunkpool.maxsize"為大于0的值才能開啟通孽,該值默認(rèn)是0。"hbase.hregion.memstore.chunkpool.maxsize"取值為[0,1]睁壁,表示整個(gè)MemStore分配給Chunk Pool的總大小為hbase.hregion.memstore.chunkpool. maxsize * MemstoreSize背苦。另一個(gè)相關(guān)參數(shù)"hbase.hregion.memstore.chunkpool.initialsize"取值為[0,1],表示初始化時(shí)申請(qǐng)多少個(gè)Chunk放到Pool里面潘明,默認(rèn)是0行剂,表示初始化時(shí)不申請(qǐng)內(nèi)存。
HFile
-
HFile邏輯結(jié)構(gòu)
-
Scanned Block部分
顧名思義钳降,表示順序掃描HFile時(shí)所有的數(shù)據(jù)塊將會(huì)被讀取厚宰。這個(gè)部分包含3種數(shù)據(jù)塊:DataBlock,Leaf Index Block以及Bloom Block遂填。其中DataBlock中存儲(chǔ)用戶的KeyValue數(shù)據(jù)铲觉,Leaf Index Block中存儲(chǔ)索引樹的葉子節(jié)點(diǎn)數(shù)據(jù),Bloom Block中存儲(chǔ)布隆過濾器相關(guān)數(shù)據(jù)吓坚。
-
Non-scanned Block部分
示在HFile順序掃描的時(shí)候數(shù)據(jù)不會(huì)被讀取撵幽,主要包括Meta Block和IntermediateLevel Data Index Blocks兩部分。
-
Load-on-open部分
這部分?jǐn)?shù)據(jù)會(huì)在RegionServer打開HFile時(shí)直接加載到內(nèi)存中礁击,包括FileInfo盐杂、布隆過濾器MetaBlock逗载、Root Data Index和Meta IndexBlock。
-
Trailer部分
這部分主要記錄了HFile的版本信息链烈、其他各個(gè)部分的偏移值和尋址信息厉斟。
-
-
HFile物理結(jié)構(gòu)
實(shí)際上,HFile文件由各種不同類型的Block(數(shù)據(jù)塊)構(gòu)成强衡,雖然這些Block的類型不同捏膨,但卻擁有相同的數(shù)據(jù)結(jié)構(gòu)。Block的大小可以在創(chuàng)建表列簇的時(shí)候通過參數(shù)blocksize=>'65535'指定食侮,默認(rèn)為64K号涯。通常來講,大號(hào)的Block有利于大規(guī)模的順序掃描锯七,而小號(hào)的Block更有利于隨機(jī)查詢链快。因此用戶在設(shè)置blocksize時(shí)需要根據(jù)業(yè)務(wù)查詢特征進(jìn)行權(quán)衡,默認(rèn)64K是一個(gè)相對(duì)折中的大小眉尸。HFile中所有Block都擁有相同的數(shù)據(jù)結(jié)構(gòu)域蜗,HBase將所有Block統(tǒng)一抽象為HFile-Block。HFileBlock支持兩種類型噪猾,一種類型含有checksum霉祸,另一種不含有checksum。
-
HFileBlock結(jié)構(gòu)
HFileBlock主要包含兩部分:BlockHeader和BlockData袱蜡。其中BlockHeader主要存儲(chǔ)Block相關(guān)元數(shù)據(jù)丝蹭,BlockData用來存儲(chǔ)具體數(shù)據(jù)。Block元數(shù)據(jù)中最核心的字段是BlockType字段坪蚁,表示該Block的類型奔穿,HBase中定義了8種BlockType,每種BlockType對(duì)應(yīng)的Block都存儲(chǔ)不同的內(nèi)容敏晤,有的存儲(chǔ)用戶數(shù)據(jù)贱田,有的存儲(chǔ)索引數(shù)據(jù),有的存儲(chǔ)元數(shù)據(jù)(meta)。對(duì)于任意一種類型的HFileBlock,都擁有相同結(jié)構(gòu)的BlockHeader扳抽,但是BlockData結(jié)構(gòu)卻不盡相同。
HFileBlock類型
-
-
HFile基礎(chǔ)Block說明
-
Trailer Block
Trailer Block主要記錄了HFile的版本信息耗拓、各個(gè)部分的偏移值和尋址信息。
Trailer Block數(shù)據(jù)結(jié)構(gòu)
-
重要字段
-
Version
HBase中version包含majorVersion和minorVersion兩部分扶平,前者決定了HFile的主版本——V1帆离、V2還是V3;后者在主版本確定的基礎(chǔ)上決定是否支持一些微小修正结澄,比如是否支持checksum等哥谷。不同的版本使用不同的文件解析器對(duì)HFile進(jìn)行讀取解析岸夯。HBase會(huì)根據(jù)version信息計(jì)算Trailer Block的大小(不同version的Trailer Block大小不同)们妥,再根據(jù)Trailer Block大小加載整個(gè)HFileTrailer Block到內(nèi)存中猜扮。
-
LoadOnOpenDataOffset
表示load-on-open Section在整個(gè)HFile文件中的偏移量
-
LoadOnOpenDataSize
load-on-open Section的大小
-
-
Data Block
-
數(shù)據(jù)結(jié)構(gòu)
KeyValue由4個(gè)部分構(gòu)成,分別為Key Length监婶、ValueLength旅赢、Key和Value。其中惑惶,Key Length和Value Length是兩個(gè)固定長(zhǎng)度的數(shù)值煮盼,Value是用戶寫入的實(shí)際數(shù)據(jù),Key是一個(gè)復(fù)合結(jié)構(gòu)带污,由多個(gè)部分構(gòu)成:Rowkey僵控、Column Family、Column Qualif ier鱼冀、TimeStamp以及KeyType报破。其中,KeyType有四種類型千绪,分別是Put充易、Delete、DeleteColumn和DeleteFamily荸型。
-
-
Bloom Index Block
整個(gè)HFile中僅有一個(gè)Bloom Index Block數(shù)據(jù)塊盹靴,位于load-on-open部分。
數(shù)據(jù)結(jié)構(gòu)
-
重要字段
-
Bloom Index Entry
Bloom Index Entry對(duì)應(yīng)每一個(gè)Bloom Block的索引項(xiàng)帆疟,作為索引分別指向scanned block部分的Bloom Block鹉究,Bloom Block中實(shí)際存儲(chǔ)了對(duì)應(yīng)的位數(shù)組。Bloom Index Entry的結(jié)構(gòu)見圖中間部分踪宠,其中BlockKey是一個(gè)非常關(guān)鍵的字段,表示該Index Entry指向的Bloom Block中第一個(gè)執(zhí)行Hash映射的Key妈嘹。BlockOffset表示對(duì)應(yīng)Bloom Block在HFile中的偏移量柳琢。
因此,一次get請(qǐng)求根據(jù)布隆過濾器進(jìn)行過濾查找需要執(zhí)行以下三步操作:
1)首先根據(jù)待查找Key在Bloom Index Block所有的索引項(xiàng)中根據(jù)BlockKey進(jìn)行二分查找润脸,定位到對(duì)應(yīng)的Bloom IndexEntry柬脸。
2)再根據(jù)Bloom Index Entry中BlockOffset以及BlockOndiskSize加載該Key對(duì)應(yīng)的位數(shù)組。
3)對(duì)Key進(jìn)行Hash映射毙驯,根據(jù)映射的結(jié)果在位數(shù)組中查看是否所有位都為1倒堕,如果不是,表示該文件中肯定不存在該Key爆价,否則有可能存在垦巴。
-
Bloom Meta Block
Bloom Block
-
Root Index Block
- 數(shù)據(jù)結(jié)構(gòu)
IntermediateIndex Block
Leaf Index Block
-
BlockCache
BlockCache是RegionServer級(jí)別的媳搪,一個(gè)RegionServer只有一個(gè)BlockCache,在RegionServer啟動(dòng)時(shí)完成BlockCache的初始化工作骤宣。到目前為止秦爆,HBase先后實(shí)現(xiàn)了3種BlockCache方案,LRUBlockCache是最早的實(shí)現(xiàn)方案憔披,也是默認(rèn)的實(shí)現(xiàn)方案等限;HBase 0.92版本實(shí)現(xiàn)了第二種方案SlabCache,參見HBASE-4027芬膝;HBase 0.96之后官方提供了另一種可選方案BucketCache望门,參見HBASE-7404。
-
LRUBlockCache
LRUBlockCache是HBase目前默認(rèn)的BlockCache機(jī)制锰霜,實(shí)現(xiàn)相對(duì)比較簡(jiǎn)單筹误。它使用一個(gè)ConcurrentHashMap管理BlockKey到Block的映射關(guān)系,緩存Block只需要將BlockKey和對(duì)應(yīng)的Block放入該HashMap中锈遥,查詢緩存就根據(jù)BlockKey從HashMap中獲取即可纫事。同時(shí),該方案采用嚴(yán)格的LRU淘汰算法所灸,當(dāng)Block Cache總量達(dá)到一定閾值之后就會(huì)啟動(dòng)淘汰機(jī)制丽惶,最近最少使用的Block會(huì)被置換出來。
-
緩存分層策略
HBase采用了緩存分層設(shè)計(jì)爬立,將整個(gè)BlockCache分為三個(gè)部分:single-access钾唬、multi-access和in-memory,分別占到整個(gè)BlockCache大小的25%侠驯、50%抡秆、25%。在一次隨機(jī)讀中吟策,一個(gè)Block從HDFS中加載出來之后首先放入single-access區(qū)儒士,后續(xù)如果有多次請(qǐng)求訪問到這個(gè)Block,就會(huì)將這個(gè)Block移到multi-access區(qū)檩坚。而in-memory區(qū)表示數(shù)據(jù)可以常駐內(nèi)存着撩,一般用來存放訪問頻繁、量小的數(shù)據(jù)匾委,比如元數(shù)據(jù)拖叙,用戶可以在建表的時(shí)候設(shè)置列簇屬性IN_MEMORY=true,設(shè)置之后該列簇的Block在從磁盤中加載出來之后會(huì)直接放入in-memory區(qū)赂乐。
-
single-access(總大小25%)
在一次隨機(jī)讀中薯鳍,一個(gè)Block從HDFS中加載出來之后首先放入single-access區(qū)。
multi-access(總大小50%)
-
in-memory(總大小25%)
需要注意的是挨措,設(shè)置IN_MEMORY=true并不意味著數(shù)據(jù)在寫入時(shí)就會(huì)被放到in-memory區(qū)挖滤,而是和其他BlockCache區(qū)一樣崩溪,只有從磁盤中加載出Block之后才會(huì)放入該區(qū)。另外壶辜,進(jìn)入in-memory區(qū)的Block并不意味著會(huì)一直存在于該區(qū)悯舟,仍會(huì)基于LRU淘汰算法在空間不足的情況下淘汰最近最不活躍的一些Block。因?yàn)镠Base系統(tǒng)元數(shù)據(jù)(hbase:meta砸民,hbase:namespace等表)都存放在in-memory區(qū)抵怎,因此對(duì)于很多業(yè)務(wù)表來說,設(shè)置數(shù)據(jù)屬性IN_MEMORY=true時(shí)需要非常謹(jǐn)慎岭参,一定要確保此列簇?cái)?shù)據(jù)量很小且訪問頻繁反惕,否則可能會(huì)將hbase:meta等元數(shù)據(jù)擠出內(nèi)存,嚴(yán)重影響所有業(yè)務(wù)性能演侯。
-
-
方案缺陷
LRUBlockCache方案使用JVM提供的HashMap管理緩存姿染,簡(jiǎn)單有效。但隨著數(shù)據(jù)從single-access區(qū)晉升到multi-access區(qū)或長(zhǎng)時(shí)間停留在single-access區(qū)秒际,對(duì)應(yīng)的內(nèi)存對(duì)象會(huì)從young區(qū)晉升到old區(qū),晉升到old區(qū)的Block被淘汰后會(huì)變?yōu)閮?nèi)存垃圾娄徊,最終由CMS回收(Conccurent Mark Sweep,一種標(biāo)記清除算法)寄锐,顯然這種算法會(huì)帶來大量的內(nèi)存碎片兵多,碎片空間一直累計(jì)就會(huì)產(chǎn)生臭名昭著的Full GC。尤其在大內(nèi)存條件下橄仆,一次Full GC很可能會(huì)持續(xù)較長(zhǎng)時(shí)間剩膘,甚至達(dá)到分鐘級(jí)別。Full GC會(huì)將整個(gè)進(jìn)程暫停盆顾,稱為stop-the-world暫停(STW)怠褐,因此長(zhǎng)時(shí)間Full GC必然會(huì)極大影響業(yè)務(wù)的正常讀寫請(qǐng)求。正因?yàn)樵摲桨赣羞@樣的弊端您宪,之后相繼出現(xiàn)了SlabCache方案和BucketCache方案惫搏。
-
-
SlabCache
為了解決LRUBlockCache方案中因JVM垃圾回收導(dǎo)致的服務(wù)中斷問題,SlabCache方案提出使用Java NIO DirectByteBuffer技術(shù)實(shí)現(xiàn)堆外內(nèi)存存儲(chǔ)蚕涤,不再由JVM管理數(shù)據(jù)內(nèi)存。默認(rèn)情況下铣猩,系統(tǒng)在初始化的時(shí)候會(huì)分配兩個(gè)緩存區(qū)揖铜,分別占整個(gè)BlockCache大小的80%和20%,每個(gè)緩存區(qū)分別存儲(chǔ)固定大小的Block达皿,其中前者主要存儲(chǔ)小于等于64K的Block天吓,后者存儲(chǔ)小于等于128K的Block贿肩,如果一個(gè)Block太大就會(huì)導(dǎo)致兩個(gè)區(qū)都無法緩存龄寞。和LRUBlockCache相同溜哮,SlabCache也使用Least-Recently-Used算法淘汰過期的Block。和LRUBlockCache不同的是述吸,SlabCache淘汰Block時(shí)只需要將對(duì)應(yīng)的BufferByte標(biāo)記為空閑,后續(xù)cache對(duì)其上的內(nèi)存直接進(jìn)行覆蓋即可。
- <=64K Block Cache(總大小80%)
- <=128K Block Cache(總大小20%)
-
DoubleBlockCache
不同表不同列簇設(shè)置的BlockSize都可能不同衅金,很顯然,默認(rèn)只能存儲(chǔ)小于等于128KB Block的SlabCache方案不能滿足部分用戶場(chǎng)景。比如瞒渠,用戶設(shè)置BlockSize=256K,簡(jiǎn)單使用SlabCache方案就不能達(dá)到緩存這部分Block的目的窍箍。因此HBase在實(shí)際實(shí)現(xiàn)中將SlabCache和LRUBlockCache搭配使用,稱為DoubleBlockCache祷蝌。在一次隨機(jī)讀中,一個(gè)Block從HDFS中加載出來之后會(huì)在兩個(gè)Cache中分別存儲(chǔ)一份。緩存讀時(shí)首先在LRUBlockCache中查找,如果Cache Miss再在SlabCache中查找癣疟,此時(shí)如果命中,則將該Block放入LRUBlockCache中。
經(jīng)過實(shí)際測(cè)試淤击,DoubleBlockCache方案有很多弊端。比如印机,SlabCache中固定大小內(nèi)存設(shè)置會(huì)導(dǎo)致實(shí)際內(nèi)存使用率比較低奶是,而且使用LRUBlockCache緩存Block依然會(huì)因?yàn)镴VM GC產(chǎn)生大量?jī)?nèi)存碎片腐魂。因此在HBase 0.98版本之后,已經(jīng)不建議使用該方案。 -
BucketCache
BucketCache通過不同配置方式可以工作在三種模式下:heap育叁,offheap和file豌骏。heap模式表示這些Bucket是從JVMHeap中申請(qǐng)的;offheap模式使用DirectByteBuffer技術(shù)實(shí)現(xiàn)堆外內(nèi)存存儲(chǔ)管理躁倒;file模式使用類似SSD的存儲(chǔ)介質(zhì)來緩存Data Block纬凤。無論工作在哪種模式下,BucketCache都會(huì)申請(qǐng)?jiān)S多帶有固定大小標(biāo)簽的Bucket拇舀,和SlabCache一樣,一種Bucket存儲(chǔ)一種指定BlockSize的Data Block,但和SlabCache不同的是搏嗡,BucketCache會(huì)在初始化的時(shí)候申請(qǐng)14種不同大小的Bucket蔚润,而且如果某一種Bucket空間不足,系統(tǒng)會(huì)從其他Bucket空間借用內(nèi)存使用货徙,因此不會(huì)出現(xiàn)內(nèi)存使用率低的情況左权。
-
內(nèi)存結(jié)構(gòu)
圖所示為BucketCache的內(nèi)存組織形式,圖中上半部分是邏輯組織結(jié)構(gòu)痴颊,下半部分是對(duì)應(yīng)的物理組織結(jié)構(gòu)赏迟。HBase啟動(dòng)之后會(huì)在內(nèi)存中申請(qǐng)大量的Bucket,每個(gè)Bucket的大小默認(rèn)為2MB蠢棱。每個(gè)Bucket會(huì)有一個(gè)baseoffset變量和一個(gè)size標(biāo)簽,其中baseoffset變量表示這個(gè)Bucket在實(shí)際物理空間中的起始地址刺下,因此Block的物理地址就可以通過baseoffset和該Block在Bucket的偏移量唯一確定惕鼓;size標(biāo)簽表示這個(gè)Bucket可以存放的Block大小价淌,比如圖中左側(cè)Bucket的size標(biāo)簽為65KB淌喻,表示可以存放64KB的Block伤塌,右側(cè)Bucket的size標(biāo)簽為129KB真屯,表示可以存放128KB的Block缸托。
BucketSizeInfo
-
BucketAllocator類
1)HBase會(huì)根據(jù)每個(gè)Bucket的size標(biāo)簽對(duì)Bucket進(jìn)行分類廊勃,相同size標(biāo)簽的Bucket由同一個(gè)BucketSizeInfo管理皮迟,如圖所示,左側(cè)存放64KB Block的Bucket由65KB BucketSizeInfo管理,右側(cè)存放128KB Block的Bucket由129KBBucketSizeInfo管理∷儆拢可見诈闺,BucketSize大小總會(huì)比Block本身大1KB晃危,這是因?yàn)锽lock本身并不是嚴(yán)格固定大小的叙赚,總會(huì)大那么一點(diǎn),比如64K的Block總是會(huì)比64K大一些僚饭。
2)HBase在啟動(dòng)的時(shí)候就決定了size標(biāo)簽的分類震叮,默認(rèn)標(biāo)簽有(4+1)K,(8+1)K鳍鸵,(16+1)K...(48+1)K苇瓣,(56+1)K,(64+1)K偿乖,(96+1)K...(512+1)K击罪。而且系統(tǒng)會(huì)首先從小到大遍歷一次所有size標(biāo)簽,為每種size標(biāo)簽分配一個(gè)Bucket贪薪,最后所有剩余的Bucket都分配最大的size標(biāo)簽媳禁,默認(rèn)分配 (512+1)K。
3)Bucket的size標(biāo)簽可以動(dòng)態(tài)調(diào)整画切,比如64K的Block數(shù)目比較多竣稽,65K的Bucket用完了以后,其他size標(biāo)簽的完全空閑的Bucket可以轉(zhuǎn)換成為65K的Bucket霍弹,但是會(huì)至少保留一個(gè)該size的Bucket毫别。 -
BucketCache中Block緩存寫入、讀取流程
-
名詞說明
-
RAMCache
RAMCache是一個(gè)存儲(chǔ)blockKey和Block對(duì)應(yīng)關(guān)系的HashMap
-
WriteThead
WriteThead是整個(gè)Block寫入的中心樞紐典格,主要負(fù)責(zé)異步地將Block寫入到內(nèi)存空間
-
BucketAllocator
BucketAllocator主要實(shí)現(xiàn)對(duì)Bucket的組織管理岛宦,為Block分配內(nèi)存空間。
-
BackingMap
BackingMap也是一個(gè)HashMap钝计,用來存儲(chǔ)blockKey與對(duì)應(yīng)物理內(nèi)存偏移量的映射關(guān)系恋博,并且根據(jù)blockKey定位具體的Block。圖中實(shí)線表示Block寫入流程私恬,虛線表示Block緩存讀取流程债沮。
-
IOEngine
IOEngine是具體的內(nèi)存管理模塊,將Block數(shù)據(jù)寫入對(duì)應(yīng)地址的內(nèi)存空間本鸣。
-
-
寫入流程
1)將Block寫入RAMCache疫衩。實(shí)際實(shí)現(xiàn)中,HBase設(shè)置了多個(gè)RAMCache荣德,系統(tǒng)首先會(huì)根據(jù)blockKey進(jìn)行hash闷煤,根據(jù)hash結(jié)果將Block分配到對(duì)應(yīng)的RAMCache中童芹。
2)WriteThead從RAMCache中取出所有的Block。和RAMCache相同鲤拿,HBase會(huì)同時(shí)啟動(dòng)多個(gè)WriteThead并發(fā)地執(zhí)行異步寫入假褪,每個(gè)WriteThead對(duì)應(yīng)一個(gè)RAMCache。
3)每個(gè)WriteThead會(huì)遍歷RAMCache中所有Block近顷,分別調(diào)用bucketAllocator為這些Block分配內(nèi)存空間生音。
4)BucketAllocator會(huì)選擇與Block大小對(duì)應(yīng)的Bucket進(jìn)行存放,并且返回對(duì)應(yīng)的物理地址偏移量offset窒升。
5)WriteThead將Block以及分配好的物理地址偏移量傳給IOEngine模塊缀遍,執(zhí)行具體的內(nèi)存寫入操作。
6)寫入成功后饱须,將blockKey與對(duì)應(yīng)物理內(nèi)存偏移量的映射關(guān)系寫入BackingMap中域醇,方便后續(xù)查找時(shí)根據(jù)blockKey直接定位。 -
讀取流程
1)首先從RAMCache中查找蓉媳。對(duì)于還沒有來得及寫入Bucket的緩存Block譬挚,一定存儲(chǔ)在RAMCache中。
2)如果在RAMCache中沒有找到酪呻,再根據(jù)blockKey在BackingMap中找到對(duì)應(yīng)的物理偏移地址量offset殴瘦。
3)根據(jù)物理偏移地址offset直接從內(nèi)存中查找對(duì)應(yīng)的Block數(shù)據(jù)。
-
-
配置使用
-
工作模式
<property
<name>hbase.bucketcache.ioengine</name>
<value>heap</value>
</property> -
bucketcache大小
<property
<name>hbase.bucketcache.size</name>
<value>0.4</value>
</property> -
示例
-
offheap模式
<property>
<name>hbase.bucketcache.ioengine</name>
<value>offheap</value>
</property>
<property
<name>hbase.bucketcache.size</name>
<value>0.4</value>
</property> -
file模式
<property>
<name>hbase.bucketcache.ioengine</name>
<value>file</value>
</property>
//bucketcache緩存空間大小号杠,單位為MB
<property>
<name>hbase.bucketcache.size</name>
<value>10 * 1024</value>
</property>
<property>
<name>hbase.bucketcache.persistent.path</name>
<value>file:/cache_path</value>
</property>
-
-
-
-
CombinedBlock-Cache
實(shí)際實(shí)現(xiàn)中,HBase將BucketCache和LRUBlockCache搭配使用丰歌,稱為CombinedBlock-Cache姨蟋。和DoubleBlockCache不同,系統(tǒng)在LRUBlockCache中主要存儲(chǔ)Index Block和BloomBlock立帖,而將Data Block存儲(chǔ)在BucketCache中眼溶。因此一次隨機(jī)讀需要先在LRUBlockCache中查到對(duì)應(yīng)的Index Block,然后再到BucketCache查找對(duì)應(yīng)Data Block晓勇。BucketCache通過更加合理的設(shè)計(jì)修正了SlabCache的弊端堂飞,極大降低了JVM GC對(duì)業(yè)務(wù)請(qǐng)求的實(shí)際影響,但其也存在一些問題绑咱。比如绰筛,使用堆外內(nèi)存會(huì)存在拷貝內(nèi)存的問題,在一定程度上會(huì)影響讀寫性能描融。當(dāng)然铝噩,在之后的2.0版本中這個(gè)問題得到了解決,參見HBASE-11425窿克。
讀寫流程
寫入流程
-
客戶端處理階段
客戶端將用戶的寫入請(qǐng)求進(jìn)行預(yù)處理骏庸,并根據(jù)集群元數(shù)據(jù)定位寫入數(shù)據(jù)所在的RegionServer毛甲,將請(qǐng)求發(fā)送給對(duì)應(yīng)的RegionServer。
-
本地緩沖區(qū)暫存
用戶提交put請(qǐng)求后具被,HBase客戶端會(huì)將寫入的數(shù)據(jù)添加到本地緩沖區(qū)中玻募,符合一定條件就會(huì)通過AsyncProcess異步批量提交。HBase默認(rèn)設(shè)置autoflush=true一姿,表示put請(qǐng)求直接會(huì)提交給服務(wù)器進(jìn)行處理七咧;用戶可以設(shè)置autoflush=false,這樣啸蜜,put請(qǐng)求會(huì)首先放到本地緩沖區(qū)坑雅,等到本地緩沖區(qū)大小超過一定閾值(默認(rèn)為2M,可以通過配置文件配置)之后才會(huì)提交衬横。很顯然裹粤,后者使用批量提交請(qǐng)求,可以極大地提升寫入吞吐量蜂林,但是因?yàn)闆]有保護(hù)機(jī)制遥诉,如果客戶端崩潰,會(huì)導(dǎo)致部分已經(jīng)提交的數(shù)據(jù)丟失噪叙。
-
查找RegionServer
提交之前矮锈,HBase會(huì)在元數(shù)據(jù)表hbase:meta中根據(jù)rowkey找到它們歸屬的RegionServer,這個(gè)定位的過程是通過HConnection的locateRegion方法完成的睁蕾。如果是批量請(qǐng)求苞笨,還會(huì)把這些rowkey按照HRegionLocation分組,不同分組的請(qǐng)求意味著發(fā)送到不同的RegionServer子眶,因此每個(gè)分組對(duì)應(yīng)一次RPC請(qǐng)求瀑凝。
客戶端根據(jù)寫入的表以及rowkey在元數(shù)據(jù)緩存中查找,如果能夠查找出該rowkey所在的RegionServer以及Region臭杰,就可以直接發(fā)送寫入請(qǐng)求(攜帶Region信息)到目標(biāo)RegionServer粤咪。
如果客戶端緩存中沒有查到對(duì)應(yīng)的rowkey信息,需要首先到ZooKeeper上/hbase-root/meta-region-server節(jié)點(diǎn)查找HBase元數(shù)據(jù)表所在的RegionServer渴杆。向hbase:meta所在的RegionServer發(fā)送查詢請(qǐng)求寥枝,在元數(shù)據(jù)表中查找rowkey所在的RegionServer以及Region信息〈沤保客戶端接收到返回結(jié)果之后會(huì)將結(jié)果緩存到本地囊拜,以備下次使用。
客戶端根據(jù)rowkey相關(guān)元數(shù)據(jù)信息將寫入請(qǐng)求發(fā)送給目標(biāo)RegionServer比搭,Region Server接收到請(qǐng)求之后會(huì)解析出具體的Region信息艾疟,查到對(duì)應(yīng)的Region對(duì)象,并將數(shù)據(jù)寫入目標(biāo)Region的MemStore中。 -
發(fā)送數(shù)據(jù)到RegionServer
HBase會(huì)為每個(gè)HRegionLocation構(gòu)造一個(gè)遠(yuǎn)程RPC請(qǐng)求MultiServerCallable蔽莱,并通過rpcCallerFactory. newCaller()執(zhí)行調(diào)用弟疆。將請(qǐng)求經(jīng)過Protobuf序列化后發(fā)送給對(duì)應(yīng)的RegionServer。
-
-
Region寫入階段
服務(wù)器端RegionServer接收到客戶端的寫入請(qǐng)求后盗冷,首先會(huì)反序列化為put對(duì)象怠苔,然后執(zhí)行各種檢查操作,比如檢查Region是否是只讀仪糖、MemStore大小是否超過blockingMemstoreSize等柑司。檢查完成之后,執(zhí)行一系列核心操作
示意圖
-
Acquire locks
HBase中使用行鎖保證對(duì)同一行數(shù)據(jù)的更新都是互斥操作锅劝,用以保證更新的原子性攒驰,要么更新成功,要么更新失敗故爵。
-
Update LATEST_TIMESTAMP timestamps
更新所有待寫入(更新)KeyValue的時(shí)間戳為當(dāng)前系統(tǒng)時(shí)間玻粪。
-
Build WAL edit
HBase使用WAL機(jī)制保證數(shù)據(jù)可靠性,即首先寫日志再寫緩存诬垂,即使發(fā)生宕機(jī)劲室,也可以通過恢復(fù)HLog還原出原始數(shù)據(jù)。該步驟就是在內(nèi)存中構(gòu)建WALEdit對(duì)象结窘,為了保證Region級(jí)別事務(wù)的寫入原子性很洋,一次寫入操作中所有KeyValue會(huì)構(gòu)建成一條WALEdit記錄。
-
Append WALEdit To WAL
將步驟3中構(gòu)造在內(nèi)存中的WALEdit記錄順序?qū)懭際Log中隧枫,此時(shí)不需要執(zhí)行sync操作喉磁。當(dāng)前版本的HBase使用了disruptor實(shí)現(xiàn)了高效的生產(chǎn)者消費(fèi)者隊(duì)列,來實(shí)現(xiàn)WAL的追加寫入操作官脓。
-
Write back to MemStore
寫入WAL之后再將數(shù)據(jù)寫入MemStore线定。
-
Release row locks
釋放行鎖。
-
Sync wal
HLog真正sync到HDFS确买,在釋放行鎖之后執(zhí)行sync操作是為了盡量減少持鎖時(shí)間,提升寫性能纱皆。如果sync失敗湾趾,執(zhí)行回滾操作將MemStore中已經(jīng)寫入的數(shù)據(jù)移除。
-
Advance mvcc
此時(shí)該線程的更新操作才會(huì)對(duì)其他讀請(qǐng)求可見派草,更新才實(shí)際生效搀缠。
-
HLog持久化等級(jí)
HBase可以通過設(shè)置HLog的持久化等級(jí)決定是否開啟HLog機(jī)制以及HLog的落盤方式。
用戶可以通過客戶端設(shè)置HLog持久化等級(jí)近迁,代碼如下:
put.setDurability(Durability.SYNC_WAL );-
SKIP_WAL
只寫緩存艺普,不寫HLog日志。因?yàn)橹粚憙?nèi)存,因此這種方式可以極大地提升寫入性能歧譬,但是數(shù)據(jù)有丟失的風(fēng)險(xiǎn)岸浑。在實(shí)際應(yīng)用過程中并不建議設(shè)置此等級(jí),除非確認(rèn)不要求數(shù)據(jù)的可靠性瑰步。
-
ASYNC_WAL
異步將數(shù)據(jù)寫入HLog日志中矢洲。
-
SYNC_WAL
同步將數(shù)據(jù)寫入日志文件中,需要注意的是缩焦,數(shù)據(jù)只是被寫入文件系統(tǒng)中读虏,并沒有真正落盤。HDFSFlush策略詳見HADOOP-6313袁滥。
-
FSYNC_WAL
同步將數(shù)據(jù)寫入日志文件并強(qiáng)制落盤盖桥。這是最嚴(yán)格的日志寫入等級(jí),可以保證數(shù)據(jù)不會(huì)丟失题翻,但是性能相對(duì)比較差揩徊。
-
USER_DEFAULT
如果用戶沒有指定持久化等級(jí),默認(rèn)HBase使用SYNC_WAL等級(jí)持久化數(shù)據(jù)藐握。
-
-
MemStore Flush階段
當(dāng)Region中MemStore容量超過一定閾值靴拱,系統(tǒng)會(huì)異步執(zhí)行flush操作,將內(nèi)存中的數(shù)據(jù)寫入文件猾普,形成HFile袜炕。
-
觸發(fā)條件
-
MemStore級(jí)別限制
當(dāng)Region中任意一個(gè)MemStore的大小達(dá)到了上限(hbase.hregion.memstore.flush.size,默認(rèn)128MB)初家,會(huì)觸發(fā)MemStore刷新偎窘。
-
Region級(jí)別限制
當(dāng)Region中所有MemStore的大小總和達(dá)到了上限(hbase.hregion.memstore.block.multiplier *hbase.hregion.memstore.flush.size),會(huì)觸發(fā)MemStore刷新溜在。
-
RegionServer級(jí)別限制
當(dāng)RegionServer中MemStore的大小總和超過低水位閾值hbase.regionserver.global.memstore.size.lower.limit*hbase.regionserver.global.memstore.size陌知,RegionServer開始強(qiáng)制執(zhí)行flush,先f(wàn)lush MemStore最大的Region掖肋,再flush次大的仆葡,依次執(zhí)行。如果此時(shí)寫入吞吐量依然很高志笼,導(dǎo)致總MemStore大小超過高水位閾值hbase.regionserver.global.memstore.size沿盅,RegionServer會(huì)阻塞更新并強(qiáng)制執(zhí)行flush,直至總MemStore大小下降到低水位閾值纫溃。
當(dāng)一個(gè)RegionServer中HLog數(shù)量達(dá)到上限(可通過參數(shù)hbase.regionserver.maxlogs配置)時(shí)腰涧,系統(tǒng)會(huì)選取最早的HLog對(duì)應(yīng)的一個(gè)或多個(gè)Region進(jìn)行f lush。 -
HBase級(jí)別限制
默認(rèn)周期為1小時(shí)紊浩,確保MemStore不會(huì)長(zhǎng)時(shí)間沒有持久化窖铡。為避免所有的MemStore在同一時(shí)間都進(jìn)行flush而導(dǎo)致的問題疗锐,定期的f lush操作有一定時(shí)間的隨機(jī)延時(shí)。
-
手動(dòng)觸發(fā)
用戶可以通過shell命令flush 'tablename'或者flush 'regionname'分別對(duì)一個(gè)表或者一個(gè)Region進(jìn)行flush费彼。
-
-
執(zhí)行流程
為了減少flush過程對(duì)讀寫的影響滑臊,HBase采用了類似于兩階段提交的方式,將整個(gè)flush過程分為三個(gè)階段敌买。
-
prepare階段
遍歷當(dāng)前Region中的所有MemStore简珠,將MemStore中當(dāng)前數(shù)據(jù)集CellSkipListSet(內(nèi)部實(shí)現(xiàn)采用ConcurrentSkipListMap)做一個(gè)快照snapshot,然后再新建一個(gè)CellSkipListSet接收新的數(shù)據(jù)寫入虹钮。prepare階段需要添加updateLock對(duì)寫請(qǐng)求阻塞聋庵,結(jié)束之后會(huì)釋放該鎖。因?yàn)榇穗A段沒有任何費(fèi)時(shí)操作芙粱,因此持鎖時(shí)間很短祭玉。
-
flush階段
遍歷所有MemStore,將prepare階段生成的snapshot持久化為臨時(shí)文件春畔,臨時(shí)文件會(huì)統(tǒng)一放到目錄.tmp下脱货。這個(gè)過程因?yàn)樯婕按疟PIO操作,因此相對(duì)比較耗時(shí)律姨。
-
commit階段
遍歷所有的MemStore振峻,將flush階段生成的臨時(shí)文件移到指定的ColumnFamily目錄下,針對(duì)HFile生成對(duì)應(yīng)的storefile和Reader择份,把storefile添加到Store的storef iles列表中扣孟,最后再清空prepare階段生成的snapshot。
-
注意
在當(dāng)前大部分HBase1.x的Release中荣赶,上述prepare階段存在一個(gè)問題(HBASE-21738):在使用updateLock鎖寫的過程中凤价,使用了ConcurrentSkipListMap#size()來統(tǒng)計(jì)MemStore的cell個(gè)數(shù),而ConcurrentSkipListMap為了保證寫入刪除操作的高并發(fā)拔创,對(duì)size()接口采用實(shí)時(shí)遍歷的方式實(shí)現(xiàn)利诺,其時(shí)間復(fù)雜度為O(N)。正因?yàn)镃oncurrent SkipListMap#size()這個(gè)耗時(shí)操作剩燥,可能會(huì)在f lush階段造成較長(zhǎng)時(shí)間阻塞慢逾,嚴(yán)重拉高p999延遲。新版本已經(jīng)修復(fù)該Bug灭红,建議用戶升級(jí)到1.5.0或1.4.10(包括)以上版本侣滩。
-
-
生成HFile
HFile依次由Scanned Block、Non-scanned Block比伏、Load-on-open以及Trailer四個(gè)部分組成。
-
Scanned Block
這部分主要存儲(chǔ)真實(shí)的KV數(shù)據(jù)疆导,包括DataBlock赁项、Leaf Index Block和Bloom Block。
-
Non-scanned Block
這部分主要存儲(chǔ)Meta Block,這種Block大多數(shù)情況下可以不用關(guān)心悠菜。
-
Load-on-open
主要存儲(chǔ)HFile元數(shù)據(jù)信息舰攒,包括索引根節(jié)點(diǎn)、布隆過濾器元數(shù)據(jù)等,在RegionServer打開HFile就會(huì)加載到內(nèi)存,作為查詢的入口峡谊。
-
Trailer
存儲(chǔ)Load-on-open和Scanned Block在HFile文件中的偏移量熊锭、文件大小(未壓縮)绰垂、壓縮算法、存儲(chǔ)KV個(gè)數(shù)以及HFile版本等基本信息。Trailer部分的大小是固定的蒂秘。
-
-
MemStore Flush對(duì)業(yè)務(wù)影響
在實(shí)踐過程中,f lush操作的不同觸發(fā)方式對(duì)用戶請(qǐng)求影響的程度不盡相同淘太。正常情況下姻僧,大部分MemStore Flush操作都不會(huì)對(duì)業(yè)務(wù)讀寫產(chǎn)生太大影響。比如系統(tǒng)定期刷新MemStore蒲牧、手動(dòng)執(zhí)行f lush操作撇贺、觸發(fā)MemStore級(jí)別限制、觸發(fā)HLog數(shù)量限制以及觸發(fā)Region級(jí)別限制等冰抢,這幾種場(chǎng)景只會(huì)阻塞對(duì)應(yīng)Region上的寫請(qǐng)求松嘶,且阻塞時(shí)間較短。然而晒屎,一旦觸發(fā)RegionServer級(jí)別限制導(dǎo)致f lush喘蟆,就會(huì)對(duì)用戶請(qǐng)求產(chǎn)生較大的影響。在這種情況下鼓鲁,系統(tǒng)會(huì)阻塞所有落在該RegionServer上的寫入操作蕴轨,直至MemStore中數(shù)據(jù)量降低到配置閾值內(nèi)。
-
讀取流程
和寫流程相比骇吭,HBase讀數(shù)據(jù)的流程更加復(fù)雜橙弱。主要基于兩個(gè)方面的原因:一是因?yàn)镠Base一次范圍查詢可能會(huì)涉及多個(gè)Region、多塊緩存甚至多個(gè)數(shù)據(jù)存儲(chǔ)文件燥狰;二是因?yàn)镠Base中更新操作以及刪除操作的實(shí)現(xiàn)都很簡(jiǎn)單棘脐,更新操作并沒有更新原有數(shù)據(jù),而是使用時(shí)間戳屬性實(shí)現(xiàn)了多版本龙致;刪除操作也并沒有真正刪除原有數(shù)據(jù)蛀缝,只是插入了一條標(biāo)記為"deleted"標(biāo)簽的數(shù)據(jù),而真正的數(shù)據(jù)刪除發(fā)生在系統(tǒng)異步執(zhí)行Major Compact的時(shí)候目代。很顯然屈梁,這種實(shí)現(xiàn)思路大大簡(jiǎn)化了數(shù)據(jù)更新嗤练、刪除流程,但是對(duì)于數(shù)據(jù)讀取來說卻意味著套上了層層枷鎖:讀取過程需要根據(jù)版本進(jìn)行過濾在讶,對(duì)已經(jīng)標(biāo)記刪除的數(shù)據(jù)也要進(jìn)行過濾煞抬。
-
Client-Server讀取交互邏輯
Client首先會(huì)從ZooKeeper中獲取元數(shù)據(jù)hbase:meta表所在的RegionServer,然后根據(jù)待讀寫rowkey發(fā)送請(qǐng)求到元數(shù)據(jù)所在RegionServer构哺,獲取數(shù)據(jù)所在的目標(biāo)RegionServer和Region(并將這部分元數(shù)據(jù)信息緩存到本地)革答,最后將請(qǐng)求進(jìn)行封裝發(fā)送到目標(biāo)RegionServer進(jìn)行處理。
-
Server端Scan框架體系
從宏觀視角來看曙强,一次scan可能會(huì)同時(shí)掃描一張表的多個(gè)Region残拐,對(duì)于這種掃描,客戶端會(huì)根據(jù)hbase:meta元數(shù)據(jù)將掃描的起始區(qū)間[startKey, stopKey)進(jìn)行切分旗扑,切分成多個(gè)互相獨(dú)立的查詢子區(qū)間蹦骑,每個(gè)子區(qū)間對(duì)應(yīng)一個(gè)Region。比如當(dāng)前表有3個(gè)Region臀防,Region的起始區(qū)間分別為:["a", "c")眠菇,["c", "e"),["e", "g")袱衷,客戶端設(shè)置scan的掃描區(qū)間為["b", "f")捎废。因?yàn)閽呙鑵^(qū)間明顯跨越了多個(gè)Region,需要進(jìn)行切分致燥,按照Region區(qū)間切分后的子區(qū)間為["b", "c")登疗,["c", "e"),["e", "f ")嫌蚤。HBase中每個(gè)Region都是一個(gè)獨(dú)立的存儲(chǔ)引擎辐益,因此客戶端可以將每個(gè)子區(qū)間請(qǐng)求分別發(fā)送給對(duì)應(yīng)的Region進(jìn)行處理。下文會(huì)聚焦于單個(gè)Region處理scan請(qǐng)求的核心流程脱吱。RegionServer接收到客戶端的get/scan請(qǐng)求之后做了兩件事情:首先構(gòu)建scanner iterator體系智政;然后執(zhí)行next函數(shù)獲取KeyValue,并對(duì)其進(jìn)行條件過濾箱蝠。
-
構(gòu)建Scanner Iterator體系
Scanner的核心體系包括三層Scanner:RegionScanner续捂,StoreScanner,MemStoreScanner和StoreFileScanner宦搬。三者是層級(jí)的關(guān)系:
一個(gè)RegionScanner由多個(gè)StoreScanner構(gòu)成牙瓢。一張表由多少個(gè)列簇組成,就有多少個(gè)StoreScanner间校,每個(gè)StoreScanner負(fù)責(zé)對(duì)應(yīng)Store的數(shù)據(jù)查找矾克。
一個(gè)StoreScanner由MemStoreScanner和StoreFileScanner構(gòu)成。每個(gè)Store的數(shù)據(jù)由內(nèi)存中的MemStore和磁盤上的StoreFile文件組成憔足。相對(duì)應(yīng)的胁附,StoreScanner會(huì)為當(dāng)前該Store中每個(gè)HFile構(gòu)造一個(gè)StoreFileScanner差购,用于實(shí)際執(zhí)行對(duì)應(yīng)文件的檢索。同時(shí)汉嗽,會(huì)為對(duì)應(yīng)MemStore構(gòu)造一個(gè)MemStoreScanner,用于執(zhí)行該Store中MemStore的數(shù)據(jù)檢索找蜜。 執(zhí)行next函數(shù)獲取KeyValue并對(duì)其進(jìn)行條件過濾
-
過濾淘汰不符合查詢條件的HFile
從HFile中讀取待查找Key
Meta表
HBase一張表的數(shù)據(jù)是由多個(gè)Region構(gòu)成饼暑,而這些Region是分布在整個(gè)集群的RegionServer上的。那么客戶端在做任何數(shù)據(jù)操作時(shí)洗做,都要先確定數(shù)據(jù)在哪個(gè)Region上弓叛,然后再根據(jù)Region的RegionServer信息,去對(duì)應(yīng)的RegionServer上讀取數(shù)據(jù)诚纸。因此撰筷,HBase系統(tǒng)內(nèi)部設(shè)計(jì)了一張?zhí)厥獾谋怼猦base:meta表,專門用來存放整個(gè)集群所有的Region信息畦徘。hbase:meta中的hbase指的是namespace毕籽,HBase容許針對(duì)不同的業(yè)務(wù)設(shè)計(jì)不同的namespace,系統(tǒng)表采用統(tǒng)一的namespace井辆,即hbase关筒;meta指的是hbase這個(gè)namespace下的表名。
數(shù)據(jù)結(jié)構(gòu)
- info:regioninfo
該列對(duì)應(yīng)的Value主要存儲(chǔ)4個(gè)信息杯缺,即EncodedName蒸播、RegionName、Region的StartRow萍肆、Region的StopRow袍榆。
-
info:seqnumDuringOpen
該列對(duì)應(yīng)的Value主要存儲(chǔ)Region打開時(shí)的sequenceId。
info:server
該列對(duì)應(yīng)的Value主要存儲(chǔ)Region落在哪個(gè)RegionServer上塘揣。
-
info:serverstartcode
該列對(duì)應(yīng)的Value主要存儲(chǔ)所在RegionServer的啟動(dòng)Timestamp包雀。
客戶端定位Region方式
HBase客戶端有一個(gè)叫做MetaCache的緩存,在調(diào)用HBaseAPI時(shí)勿负,客戶端會(huì)先去MetaCache中找到業(yè)務(wù)rowkey所在的Region馏艾,這個(gè)Region可能有以下三種情況:
?Region信息為空,說明MetaCache中沒有這個(gè)rowkey所在Region的任何Cache奴愉。此時(shí)直接用上述查詢語(yǔ)句去hbase:meta表中Reversed Scan即可琅摩,注意首次查找時(shí),需要先讀取ZooKeeper的/hbase/meta-region-server這個(gè)ZNode锭硼,以便確定hbase:meta表所在的RegionServer房资。在hbase:meta表中找到業(yè)務(wù)rowkey所在的Region之后,將(regionStartRow, region)這樣的二元組信息存放在一個(gè)MetaCache中檀头。這種情況極少出現(xiàn)轰异,一般發(fā)生在HBase客戶端到服務(wù)端連接第一次建立后的少數(shù)幾個(gè)請(qǐng)求內(nèi)岖沛,所以并不會(huì)對(duì)HBase服務(wù)端造成巨大壓力。
?Region信息不為空搭独,但是調(diào)用RPC請(qǐng)求對(duì)應(yīng)RegionServer后發(fā)現(xiàn)Region并不在這個(gè)RegionServer上婴削。這說明MetaCache信息過期了,同樣直接ReversedScan hbase:meta表牙肝,找到正確的Region并緩存唉俗。通常,某些Region在兩個(gè)RegionServer之間移動(dòng)后會(huì)發(fā)生這種情況配椭。但事實(shí)上虫溜,無論是RegionServer宕機(jī)導(dǎo)致Region移動(dòng),還是由于Balance導(dǎo)致Region移動(dòng)股缸,發(fā)生的幾率都極小衡楞。而且,也只會(huì)對(duì)Region移動(dòng)后的極少數(shù)請(qǐng)求產(chǎn)生影響敦姻,這些請(qǐng)求只需要通過HBase客戶端自動(dòng)重試locatemeta即可成功瘾境。
?Region信息不為空,且調(diào)用RPC請(qǐng)求到對(duì)應(yīng)RegionSsrver后镰惦,發(fā)現(xiàn)是正確的RegionServer寄雀。絕大部分的請(qǐng)求都屬于這種情況,也是代價(jià)極小的方案陨献。
負(fù)載均衡
Region遷移
作為一個(gè)分布式系統(tǒng)盒犹,分片遷移是最基礎(chǔ)的核心功能。集群負(fù)載均衡眨业、故障恢復(fù)等功能都是建立在分片遷移的基礎(chǔ)之上的急膀。比如集群負(fù)載均衡,可以簡(jiǎn)單理解為集群中所有節(jié)點(diǎn)上的分片數(shù)目保持相同龄捡。實(shí)際執(zhí)行分片遷移時(shí)可以分為兩個(gè)步驟:第一步卓嫂,根據(jù)負(fù)載均衡策略制定分片遷移計(jì)劃;第二步聘殖,根據(jù)遷移計(jì)劃執(zhí)行分片的實(shí)際遷移晨雳。HBase系統(tǒng)中,分片遷移就是Region遷移奸腺。和其他很多分布式系統(tǒng)不同餐禁,HBase中Region遷移是一個(gè)非常輕量級(jí)的操作。所謂輕量級(jí)突照,是因?yàn)镠Base的數(shù)據(jù)實(shí)際存儲(chǔ)在HDFS上帮非,不需要獨(dú)立進(jìn)行管理,因而Region在遷移的過程中不需要遷移實(shí)際數(shù)據(jù),只要將讀寫服務(wù)遷移即可末盔。
-
Region狀態(tài)
其中筑舅,SPLITTING、SPLIT和SPLITTING_NEW 3個(gè)狀態(tài)是Region分裂過程中的狀態(tài)陨舱,MERGING翠拣、MERGED和MERGING_NEW 3個(gè)狀態(tài)是Region合并過程中的狀態(tài),這6個(gè)狀態(tài)會(huì)在接下來兩節(jié)詳細(xì)講解游盲。本節(jié)重點(diǎn)關(guān)注OFFLINE心剥、OPENING、OPEN背桐、FAILED_OPEN、CLOSING蝉揍、CLOSED以及FAILED_CLOSE這7個(gè)狀態(tài)链峭。
-
unassign階段
Master生成事件M_ZK_REGION_CLOSING并更新到ZooKeeper組件,同時(shí)將本地內(nèi)存中該Region的狀態(tài)修改為PENDING_CLOSE又沾。
Master通過RPC發(fā)送close命令給擁有該Region的RegionServer弊仪,令其關(guān)閉該Region。
RegionServer接收到Master發(fā)送過來的命令后杖刷,生成一個(gè)RS_ZK_REGION_CLOSING事件励饵,更新到ZooKeeper。
Master監(jiān)聽到ZooKeeper節(jié)點(diǎn)變動(dòng)后滑燃,更新內(nèi)存中Region的狀態(tài)為CLOSING役听。
RegionServer執(zhí)行Region關(guān)閉操作。如果該Region正在執(zhí)行flush或者Compaction表窘,等待操作完成典予;否則將該Region下的所有MemStore強(qiáng)制flush,然后關(guān)閉Region相關(guān)的服務(wù)乐严。
關(guān)閉完成后生成事件RS_ZK_REGION_CLOSED瘤袖,更新到ZooKeeper。Master監(jiān)聽到ZooKeeper節(jié)點(diǎn)變動(dòng)后昂验,更新該Region的狀態(tài)為CLOSED捂敌。 -
assign階段
Master生成事件M_ZK_REGION_OFFLINE并更新到ZooKeeper組件,同時(shí)將本地內(nèi)存中該Region的狀態(tài)修改為PENDING_OPEN既琴。
Master通過RPC發(fā)送open命令給擁有該Region的RegionServer占婉,令其打開該Region。
RegionServer接收到Master發(fā)送過來的命令后甫恩,生成一個(gè)RS_ZK_REGION_OPENING事件锐涯,更新到ZooKeeper。
Master監(jiān)聽到ZooKeeper節(jié)點(diǎn)變動(dòng)后填物,更新內(nèi)存中Region的狀態(tài)為OPENING纹腌。
RegionServer執(zhí)行Region打開操作霎终,初始化相應(yīng)的服務(wù)。
打開完成之后生成事件RS_ZK_REGION_OPENED升薯,更新到ZooKeeper莱褒,Master監(jiān)聽到ZooKeeper節(jié)點(diǎn)變動(dòng)后,更新該Region的狀態(tài)為OPEN涎劈。
Region合并
在線合并Region是HBase非常重要的功能之一广凸。相比Region分裂,在線合并Region的使用場(chǎng)景比較有限蛛枚,最典型的一個(gè)應(yīng)用場(chǎng)景是谅海,在某些業(yè)務(wù)中本來接收寫入的Region在之后的很長(zhǎng)時(shí)間都不再接收任何寫入,而且Region上的數(shù)據(jù)因?yàn)門TL過期被刪除蹦浦。這種場(chǎng)景下的Region實(shí)際上沒有任何存在的意義扭吁,稱為空閑Region。一旦集群中空閑Region很多盲镶,就會(huì)導(dǎo)致集群管理運(yùn)維成本增加侥袜。此時(shí),可以使用在線合并功能將這些Region與相鄰的Region合并溉贿,減少集群中空閑Region的個(gè)數(shù)枫吧。
-
合并流程
客戶端發(fā)送merge請(qǐng)求給Master
Master將待合并的所有Region都move到同一個(gè)RegionServer上
Master發(fā)送merge請(qǐng)求給該RegionServer。
RegionServer啟動(dòng)一個(gè)本地事務(wù)執(zhí)行merge操作宇色。
merge操作將待合并的兩個(gè)Region下線九杂,并將兩個(gè)Region的文件進(jìn)行合并。
將這兩個(gè)Region從hbase:meta中刪除宣蠕,并將新生成的Region添加到hbase:meta中尼酿。
將新生成的Region上線。
Region分裂
Region分裂是HBase最核心的功能之一植影,是實(shí)現(xiàn)分布式可擴(kuò)展性的基礎(chǔ)裳擎。HBase中,Region分裂有多種觸發(fā)策略可以配置思币,一旦觸發(fā)鹿响,HBase會(huì)尋找分裂點(diǎn),然后執(zhí)行真正的分裂操作谷饿。
-
分裂策略
-
ConstantSizeRegionSplitPolicy
0.94版本之前默認(rèn)分裂策略惶我。表示一個(gè)Region中最大Store的大小超過設(shè)置閾值(hbase.hregion.max.filesize)之后會(huì)觸發(fā)分裂。ConstantSizeRegionSplitPolicy最簡(jiǎn)單博投,但是在生產(chǎn)線上這種分裂策略卻有相當(dāng)大的弊端——分裂策略對(duì)于大表和小表沒有明顯的區(qū)分绸贡。閾值(hbase.hregion.max.filesize)設(shè)置較大對(duì)大表比較友好,但是小表就有可能不會(huì)觸發(fā)分裂,極端情況下可能就只有1個(gè)Region听怕,這對(duì)業(yè)務(wù)來說并不是什么好事捧挺。如果閾值設(shè)置較小則對(duì)小表友好,但一個(gè)大表就會(huì)在整個(gè)集群產(chǎn)生大量的Region尿瞭,對(duì)于集群的管理闽烙、資源使用來說都不是一件好事。
-
IncreasingToUpperBoundRegionSplitPolicy
0.94版本~2.0版本默認(rèn)分裂策略声搁。這種分裂策略總體來看和ConstantSizeRegionSplitPolicy思路相同黑竞,一個(gè)Region中最大Store大小超過設(shè)置閾值就會(huì)觸發(fā)分裂。但是這個(gè)閾值并不像ConstantSizeRegionSplitPolicy是一個(gè)固定的值疏旨,而是在一定條件下不斷調(diào)整很魂,調(diào)整后的閾值大小和Region所屬表在當(dāng)前RegionServer上的Region個(gè)數(shù)有關(guān)系,調(diào)整后的閾值等于(#regions) *(#regions) * (#regions) * flushsize * 2檐涝,當(dāng)然閾值并不會(huì)無限增大遏匆,最大值為用戶設(shè)置的MaxRegionFileSize。這種分裂策略很好地彌補(bǔ)了ConstantSizeRegionSplitPolicy的短板骤铃,能夠自適應(yīng)大表和小表,而且在集群規(guī)模較大的場(chǎng)景下坷剧,對(duì)很多大表來說表現(xiàn)很優(yōu)秀惰爬。然而,這種策略并不完美惫企,比如在大集群場(chǎng)景下撕瞧,很多小表就會(huì)產(chǎn)生大量小Region,分散在整個(gè)集群中狞尔。
-
SteppingSplitPolicy
2.0版本默認(rèn)分裂策略丛版。這種分裂策略的分裂閾值也發(fā)生了變化,相比IncreasingToUpperBoundRegionSplitPolicy簡(jiǎn)單了一些偏序,分裂閾值大小和待分裂Region所屬表在當(dāng)前RegionServer上的Region個(gè)數(shù)有關(guān)系页畦,如果Region個(gè)數(shù)等于1,分裂閾值為f lush size * 2研儒,否則為MaxRegionFileSize豫缨。這種分裂策略對(duì)于大集群中的大表、小表會(huì)比IncreasingToUpperBoundRegionSplitPolicy更加友好端朵,小表不會(huì)再產(chǎn)生大量的小Region好芭。
-