可能是最易懂的Hbase架構(gòu)原理解析
小史是一個(gè)非科班的程序員,雖然學(xué)的是電子專業(yè),但是通過(guò)自己的努力成功通過(guò)了面試,現(xiàn)在要開(kāi)始迎接新生活了。
今天,小史的姐姐和呂老師一起過(guò)來(lái)看小史,一進(jìn)屋,就有一股難聞的氣味。
可不,小史姐姐走進(jìn)衛(wèi)生間,發(fā)現(xiàn)地下一個(gè)盆子里全是沒(méi)洗的襪子。
小史:當(dāng)然不是,盆里的襪子滿了,就先放到這個(gè)桶里,然后再繼續(xù)裝,等到桶里的襪子滿了,然后才放到洗衣機(jī)里一次洗完,這樣不僅效率高,而且節(jié)省水電費(fèi)。
小史洋洋得意地介紹起自己洗襪子的流程。
小史一聽(tīng)就有點(diǎn)不高興,全世界都黑程序員,沒(méi)想到自己還沒(méi)變成程序員就被自家姐姐黑了。
說(shuō)完就進(jìn)自己房間,把姐姐和呂老師晾在外面。小史姐姐也意識(shí)到不該拿程序員開(kāi)玩笑,但現(xiàn)在也不知道該怎么辦,就看著呂老師。
呂老師走進(jìn)小史的房間。
HBase是啥
小史:別吹了,構(gòu)建在 HDFS 上除了能存儲(chǔ)海量數(shù)據(jù)之外,缺點(diǎn)一大堆,上次你給我介紹的 HDFS 缺點(diǎn)我可沒(méi)忘啊,不支持小文件,不支持并發(fā)寫(xiě),不支持文件隨機(jī)修改,查詢效率也低。
小史仔細(xì)回憶起 HDFS 來(lái):
呂老師:HDFS 確實(shí)有很多缺點(diǎn),但是 HBase 卻是一個(gè)支持***別高并發(fā)寫(xiě)入,支持實(shí)時(shí)查詢,適合存儲(chǔ)稀疏數(shù)據(jù)的分布式數(shù)據(jù)庫(kù)系統(tǒng)。
呂老師:HBase 主要用于大數(shù)據(jù)領(lǐng)域,在這方面,確實(shí)比 MySQL 要厲害得多啊,它和 MySQL 的存儲(chǔ)方式就完全不一樣。MySQL 是行式存儲(chǔ),HBase 是列式存儲(chǔ)。
列式存儲(chǔ)
呂老師:沒(méi)錯(cuò),這就是行式存儲(chǔ)系統(tǒng)存儲(chǔ)稀疏數(shù)據(jù)的問(wèn)題,我們?cè)賮?lái)看看列式存儲(chǔ)如何解決這個(gè)問(wèn)題,它的存儲(chǔ)結(jié)構(gòu)是這樣的:
小史:這個(gè)我看懂了,相當(dāng)于把每一行的每一列拆開(kāi),然后通過(guò) Rowkey 關(guān)聯(lián)起來(lái),Rowkey 相同的這些數(shù)據(jù)其實(shí)就是原來(lái)的一行。
呂老師:你這里只說(shuō)到了一個(gè)好處,由于把一行數(shù)據(jù)變成了這樣的 key-value 的形式。
所以 HBase 可以存儲(chǔ)上百萬(wàn)列,又由于 HBase 基于 HDFS 來(lái)存儲(chǔ),所以 HBase 可以存儲(chǔ)上億行,是一個(gè)真正的海量數(shù)據(jù)庫(kù)。
呂老師:這就是 HBase 的威力呀,還不只如此,其實(shí)很多時(shí)候,我們做 Select 查詢的時(shí)候,只關(guān)注某幾列,比如我現(xiàn)在只關(guān)心大家的工資,傳統(tǒng)的按行存儲(chǔ),要選出所有人的工資是怎么辦的呢?
小史:哦,我大概明白了,原來(lái)是這樣,所以 HBase 的查詢效率也很高,但是我有個(gè)問(wèn)題啊,如果我就要查我的所有信息,這是一行數(shù)據(jù),HBase 查詢起來(lái)是不是反而更慢了呢?
列簇
呂老師:列簇,顧名思義,就是把一些列放在一起咯,在 HBase 中,會(huì)把列簇中的列存儲(chǔ)在一起,比如我們把和工作相關(guān)的 Salary 和 Job 都放在 Work 這個(gè)列簇下。
那么大概是這樣的:
小史:哦,我明白了,這樣的話,一個(gè)列簇中的列會(huì)被一次就拿出來(lái),如果我要查所有列的信息的話,把所有信息都放在一個(gè)列簇就好了。
注意:HBase 中,其實(shí)所有列都是在列簇中,定義表的時(shí)候就需要指定列簇。生產(chǎn)環(huán)境由于性能考慮和數(shù)據(jù)均衡考慮,一般只會(huì)用一個(gè)列簇,最多兩個(gè)列簇。
Rowkey 設(shè)計(jì)
注:當(dāng)然,有些中間件把 SQL 翻譯成 HBase 的查詢規(guī)則,從而支持了 SQL 查 HBase,不在本文討論范圍內(nèi)。
小史:?。窟@和我想象的不一樣啊,如果我想查詢工資比 20w 多的記錄,在 MySQL 中,只要用一條很簡(jiǎn)單的 SQL 就行啊,這在 HBase 中怎么查呢?
呂老師:在 HBase 中,你需要把要查詢的字段巧妙地設(shè)置在 Rowkey 中,一個(gè) Rowkey 你可以理解為一個(gè)字符串,而 HBase 就是根據(jù) Rowkey 來(lái)建立索引的。
不熟悉 B+ 樹(shù)的同學(xué)可以看這篇文章。HBase 的 HFile 底層也是一樣的原理。
呂老師:假設(shè)員工工資 9999w 封頂,查詢的時(shí)候可能根據(jù)員工工資查詢,也可能根據(jù)名字查詢一個(gè)特定的員工,那么 Rowkey 就可以這樣設(shè)計(jì):
注意:以上 Rowkey 是簡(jiǎn)化版設(shè)計(jì),只是為了講清楚范圍查詢。實(shí)際使用中由于 Rowkey 需要考慮散列性,所以可能不會(huì)這么用。后文會(huì)具體探討散列性。
呂老師:HBase 提供了三種查詢方式:
- 全表掃描,Scan。
- 根據(jù)一個(gè) Rowkey 進(jìn)行查詢。
- 根據(jù) Rowkey 過(guò)濾的范圍查詢。
比如你要查工資不少于 20w 的記錄,就可以用范圍查詢,查出從 startRow=0020 到 stopRow=9999 的所有記錄,這是 HBase 直接支持的一種查詢方式哦。
呂老師:這里要注意幾點(diǎn),首先,Rowkey 是按照字符串字典序來(lái)組織成 B+ 樹(shù)的,所以數(shù)字的話需要補(bǔ)齊,不然的話會(huì)出現(xiàn) 123w 小于 20w 的情況,但是補(bǔ)齊的話,你就會(huì)發(fā)現(xiàn) 020w 小于 123w。
小史:哦,明白了,這都很好理解,因?yàn)?Rowkey 是字符串形式,所以肯定是按照字符串順序排序咯。
而且 Rowkey 有點(diǎn)類似于 MySQL 中的主鍵吧,所以保證其唯一性也是可以理解的。
還有就是因?yàn)槊總€(gè) key-value 都包含 Rowkey,所以 Rowkey 越短,越能節(jié)省存儲(chǔ)空間。
注意:如果 Rowkey 復(fù)雜且查詢條件復(fù)雜,HBase 還針對(duì) Rowkey 提供了自定義 Filter,所以只要數(shù)據(jù)在 Rowkey 中有體現(xiàn),能解析,就能根據(jù)自己的條件進(jìn)行查詢。
小史:但是呂老師,我有一個(gè)問(wèn)題啊,之前說(shuō)過(guò) HDFS 不適合存儲(chǔ)小文件,而 HBase 中的一條記錄只有一點(diǎn)點(diǎn)數(shù)據(jù),記錄條數(shù)卻很多,屬于海量小文件,存在 HDFS 中不是內(nèi)存爆炸了嗎?
LSM 三層存儲(chǔ)模型
小史:哦,這就像把盆里的襪子放到桶里一樣。但是呂老師,如果數(shù)據(jù)量大的話,時(shí)間一長(zhǎng),就會(huì)有很多次刷寫(xiě),不就形成了很多個(gè)小文件嗎?這豈不又是海量小文件了?
不記得 HDFS 原理的同學(xué)可以溫習(xí)一下:
呂老師:對(duì)了,HBase 也是使用同樣的思想,其實(shí)這就是 WAL 預(yù)寫(xiě)日志的思想,HBase 也會(huì)將數(shù)據(jù)的操作先寫(xiě)日志,然后存到內(nèi)存,哪天機(jī)器掛了,內(nèi)存丟了,還能從 WAL 日志中將數(shù)據(jù)恢復(fù)。
數(shù)據(jù)修改
小史:不過(guò)呂老師,我還有問(wèn)題啊,我記得 HDFS 是不能隨機(jī)修改文件的,只能追加,那么 HBase 里的數(shù)據(jù)是不是寫(xiě)了之后就不能改也不能刪除呢?
呂老師:刪除同樣是追加一條版本***的記錄,只不過(guò)標(biāo)記這個(gè)數(shù)據(jù)被刪除而已,查詢的時(shí)候,看到版本***的記錄是數(shù)據(jù)刪除,就知道這個(gè)數(shù)據(jù)被刪了。
呂老師:哈,小史,你思考得非常深入,還記得 LSM 的第三層嗎,HBase 會(huì)在合并的時(shí)候,將這些用不到的記錄刪除掉,節(jié)省存儲(chǔ)空間。
呂老師:不全對(duì),其實(shí) HBase 把合并分為兩種:
- 小合并 Minor Compact,這種方式只會(huì)將少數(shù)文件進(jìn)行簡(jiǎn)單合并,不會(huì)進(jìn)行數(shù)據(jù)的清理。
- 大合并 Major Compact,這種方式會(huì)將大部分文件進(jìn)行合并,并且清理數(shù)據(jù)。
呂老師:基本正確,但是你要知道,如果數(shù)據(jù)量大,這個(gè)過(guò)程是非常耗性能的,一般在生產(chǎn)環(huán)境都禁止大合并,否則在正常服務(wù)的時(shí)候突然來(lái)個(gè)大合并,整個(gè)集群可能資源被耗光,沒(méi)法正常服務(wù)。
HBase 架構(gòu)
小史:HBase 的架構(gòu)似乎也是 master-slave 架構(gòu),和 HDFS 有點(diǎn)像,HMaster 是用來(lái)管理集群,HRegionServer 是真正存儲(chǔ)數(shù)據(jù)的地方吧?
呂老師:啊,這塊不太對(duì),HBase 在數(shù)據(jù)查詢和寫(xiě)入的時(shí)候,其實(shí)并不是像 HDFS 那樣詢問(wèn) HMaster。
在 HBase 中,每一張表都會(huì)有元信息,這些信息也是被存儲(chǔ)為 HBase 表,稱為元信息表,也叫 Meta 表,這是一種系統(tǒng)表。
小史:但是這又有個(gè)問(wèn)題,既然 Meta 表也是存儲(chǔ)在 HBase 上,那么 HBase 又如何知道 Meta 表存在哪個(gè) HRegionServer 上呢?這豈不是一個(gè)雞生蛋蛋生雞的問(wèn)題?
呂老師:小史啊,我說(shuō) Meta 表是 HBase 表,是指 Meta 表也是用 Rowkey 和 Value 的鍵值存儲(chǔ),但是我并沒(méi)有說(shuō) Meta 表在 HBase 上啊。
其實(shí) Meta 表不是存儲(chǔ)在 HRegionServer 上,而是存儲(chǔ)在那個(gè)分布式協(xié)調(diào)服務(wù) Zookeeper 上面。
小史:哦,原來(lái)如此,所以 Meta 表其實(shí)是在一個(gè)固定地方讀取,然后根據(jù) Meta 表就知道數(shù)據(jù)在哪個(gè) HRegionServer 上。但是 Zookeeper 又是啥呢?
呂老師:其實(shí) HMaster 的任務(wù)相對(duì)不繁重,但是卻比較重要,它主要是通過(guò)調(diào)整和管理 Region 分布來(lái)實(shí)現(xiàn) HRegionServer 的負(fù)載均衡。
HRegionServer 架構(gòu)
呂老師:其實(shí) Region 是 HBase 在 Rowkey 上的切分,每個(gè) Region 都可以通過(guò) StartKey 和 EndKey 來(lái)確定 Rowkey 的范圍,一個(gè) HRegionServer 上可能會(huì)有多個(gè) Region。
小史:所以說(shuō)數(shù)據(jù)是根據(jù) Rowkey 和一定的哈希規(guī)則,分散到不同的 Region 上面,而 Region 又是屬于某一個(gè) HRegionServer 上的,這個(gè)關(guān)系沒(méi)錯(cuò)吧?
呂老師:沒(méi)錯(cuò),通過(guò)這里其實(shí)可以得出 Rowkey 設(shè)計(jì)的另一個(gè)原則,就是散列性。
Rowkey 的頭幾個(gè)字母,***不要是一樣的,不然會(huì)分布在同一個(gè) HRegionServer 上面,導(dǎo)致這個(gè) HRegionServer 的負(fù)載非常高,累死累活,其他 HRegionServer 卻沒(méi)事干。
一般可以根據(jù)一定規(guī)則算一個(gè)數(shù)據(jù)的摘要,比如 MD5,把 MD5 的頭幾位拼在 Rowkey 的前面。
呂老師:哈哈,名詞沒(méi)講過(guò),原理可都是講過(guò)的喲。比如這個(gè) Store,我們之前說(shuō)過(guò),一個(gè)列簇中的列是存儲(chǔ)在一起的,對(duì)應(yīng)到這里,一個(gè)列簇中的數(shù)據(jù)就是存到一個(gè) Store 中。
呂老師:沒(méi)錯(cuò),這里 StoreFile 只是一個(gè)名字,它是以 HFile 的格式存儲(chǔ)在 HDFS 上,HFile 是一個(gè)存儲(chǔ)格式,在新版本的 HFile 存儲(chǔ)格式中,它就是一個(gè)類似 B+ 樹(shù)的索引形式。
讀取和寫(xiě)入流程
①HBase Client 要寫(xiě)輸入了,先從 Zookeeper 中拿到 Meta 表信息,根據(jù)數(shù)據(jù)的 Rowkey 找到應(yīng)該往哪個(gè) RegionServer 寫(xiě)。
②然后 HBase 會(huì)將數(shù)據(jù)寫(xiě)入對(duì)應(yīng) RegionServer 的內(nèi)存 MemStore 中,同時(shí)記錄操作日志 WAL。
③當(dāng) MemStore 超過(guò)一定閾值,就會(huì)將內(nèi)存 MemStore 中的數(shù)據(jù)刷寫(xiě)到硬盤(pán)上,形成 StoreFile。
④在觸發(fā)了一定條件的時(shí)候,小的 StoreFile 會(huì)進(jìn)行合并,變成大的 StoreFile,有利于 HDFS 存儲(chǔ)。
呂老師:當(dāng)大量 Rowkey 相近的數(shù)據(jù)都被分配到一個(gè) Region 中,導(dǎo)致這個(gè) Region 數(shù)據(jù)過(guò)大的時(shí)候,Region 會(huì)進(jìn)行拆分,HMaster 會(huì)對(duì)拆分后的 Region 重新分配 RegionServer,這是 HMaster 的負(fù)載均衡策略。
①HBase Client 要讀數(shù)據(jù)了,先從 Zookeeper 中拿到 Meta 表信息,根據(jù)要查的 Rowkey 找到對(duì)應(yīng)的數(shù)據(jù)在哪些 RegionServer 上。
②分別在這些 RegionServer 上根據(jù)列簇進(jìn)行 StoreFile 和 MemStore 的查找,得到很多 key-value 結(jié)構(gòu)的數(shù)據(jù)。
③根據(jù)數(shù)據(jù)的版本找到***數(shù)據(jù)進(jìn)行返回。
OLTP 和 OLAP
呂老師:OLTP 應(yīng)用叫聯(lián)機(jī)事務(wù)處理應(yīng)用,就是類似銀行轉(zhuǎn)賬等業(yè)務(wù)的,這類應(yīng)用對(duì)事務(wù)要求比較高。
而 OLAP 應(yīng)用叫聯(lián)機(jī)分析處理應(yīng)用,比如推薦系統(tǒng),是在收集了大量用戶行為后進(jìn)行分析,再得出結(jié)論的應(yīng)用,主要側(cè)重分析,對(duì)事務(wù)要求非常低。
總結(jié)
小史把這次學(xué)習(xí)到的 HBase 的知識(shí)記了下來(lái):
HBase 是列式存儲(chǔ),和 MySQL 的行式存儲(chǔ)不一樣。
HBase 中有列簇概念,同一個(gè)列簇下的列存儲(chǔ)在一起,在 Region 的一個(gè) StoreFile 中。
HBase 是按照 Rowkey 進(jìn)行查找,要查詢的字段要想辦法放到 Rowkey 中。
HBase 內(nèi)部使用 LSM 三層模型進(jìn)行存儲(chǔ),數(shù)據(jù)先寫(xiě)到內(nèi)存的 MemStore 中,內(nèi)存達(dá)到一定閾值再刷寫(xiě)到硬盤(pán) StoreFile 中,再滿足一定條件時(shí),小的 StoreFile 會(huì)合并為大的 StoreFile。
HBase 適合 OLAP 類的應(yīng)用。
學(xué)完 HBase,記完筆記,小史開(kāi)開(kāi)心心地洗襪子去了。