內含面試|一文搞懂HBase的基本原理

本文會對HBase的基本原理進行剖析,經過本文你能夠了解到:css

  • CAP理論
  • NoSQL出現的緣由
  • HBase的特色及使用場景
  • HBase的數據模型和基本原理
  • 客戶端API的基本使用
  • 易混淆知識點面試總結

舒適提示:本文內容較長,若是以爲有用,建議收藏。另外記得分享、點贊、在看,素質三連哦!html

從BigTable提及

HBase是在谷歌BigTable的基礎之上進行開源實現的,是一個高可靠、高性能、面向列、可伸縮的分佈式數據庫,能夠用來存儲非結構化和半結構化的稀疏數據。HBase支持超大規模數據存儲,能夠經過水平擴展的方式處理超過10億行數據和百萬列元素組成的數據表。java

BigTable是一個分佈式存儲系統,利用谷歌提出的MapReduce分佈式並行計算模型來處理海量數據,使用谷歌分佈式文件系統GFS做爲底層的數據存儲,並採用Chubby提供協同服務管理,具有普遍的應用型、可擴展性、高可用性及高性能性等特色。關於BigTable與HBase的對比,見下表:node

依賴 BigTbale HBase
數據存儲 GFS HDFS
數據處理 MapReduce Hadoop的MapReduce
協同服務 Chubby Zookeeper

CAP理論

2000年,Berkerly大學有位Eric Brewer教授提出了一個CAP理論,在2002年,麻省理工學院的Seth Gilbert(賽斯·吉爾伯特)Nancy Lynch(南希·林奇)發表了布魯爾猜測的證實,證實了CAP理論的正確性。所謂CAP理論,是指對於一個分佈式計算系統來講,不可能同時知足如下三點:面試

  • 一致性(Consistency)

    等同於全部節點訪問同一份最新的數據副本。即任何一個讀操做老是可以讀到以前完成的寫操做的結果,也就是說,在分佈式環境中,不一樣節點訪問的數據是一致的。算法

  • 可用性(Availability)

    每次請求都能獲取到非錯的響應——可是不保證獲取的數據爲最新數據。即快速獲取數據,能夠在肯定的時間內返回操做結果。數據庫

  • 分區容錯性(Partition tolerance)

    以實際效果而言,分區至關於對通訊的時限要求。系統若是不能在時限內達成數據一致性,就意味着發生了分區的狀況,必須就當前操做在C和A之間作出選擇。即指當出現網絡分區時(系統中的一部分節點沒法與其餘的節點進行通訊),分離的系統也可以正常運行,便可靠性。編程

在這裏插入圖片描述

如上圖所示:一個分佈式的系統不可能同時知足一致性、可用性和分區容錯性,最多同時知足兩個。當處理CAP的問題時,能夠有一下幾個選擇:數組

  • 知足CA,不知足P。將全部與事務相關的內容都放在同一個機器上,這樣會影響系統的可擴展性。傳統的關係型數據庫。如MySQL、SQL Server 、PostgresSQL等都採用了此種設計原則。
  • 知足AP,不知足C。不知足一致性(C),即容許系統返回不一致的數據。其實,對於WEB2.0的網站而言,更加關注的是服務是否可用,而不是一致性。好比你發了一篇博客或者寫一篇微博,你的一部分朋友立馬看到了這篇文章或者微博,另外一部分朋友卻要等一段時間以後才能刷出這篇文章或者微博。雖然有延時,可是對於一個娛樂性質的Web 2.0網站而言,這幾分鐘的延時並不重要,不會影響用戶體驗。相反,當發佈一篇文章或微博時,不可以當即發佈(不知足可用性),用戶對此確定不爽。因此呢,對於WEB2.0的網站而言,可用性和分區容錯性的優先級要高於數據一致性,固然,並無徹底放棄一致性,而是最終的一致性(有延時)。如Dynamo、Cassandra、CouchDB等NoSQL數據庫採用了此原則。
  • 知足CP,不知足A。強調一致性性(C)和分區容錯性(P),放棄可用性性(A)。當出現網絡分區時,受影響的服務須要等待數據一致,在等待期間沒法對外提供服務。如Neo4J、HBase 、MongoDB、Redis等採用了此種設計原則。

爲何出現NoSQL

所謂NoSQL,即Not Only SQL的縮寫,意思是不僅是SQL。上面提到的CAP理論正是NoSQL的設計原則。那麼,爲何會興起NoSQL數據庫呢?由於WEB2.0以及大數據時代的到來,關係型數據庫愈來愈不能知足需求。大數據、物聯網、移動互聯網和雲計算的發展,使得非結構化的數據比例高達90%以上,關係型數據庫因爲模型不靈活以及擴展水平較差,在面對大數據時,暴露出了愈來愈多的缺陷。由此NoSQL數據庫應運而生,更好地知足了大數據時代及WEB2.0的需求。緩存

面對WEB2.0以及大數據的挑戰,關係型數據庫在如下幾個方面表現欠佳:

  • 對於海量數據的處理性能較差

    WEB2.0時代,尤爲是移動互聯網的發展,UGC(用戶生成內容,User Generated Content)以及PGC(公衆生成內容,Public Generated Content)佔據了咱們的平常。現現在,自媒體發展遍地開花,幾乎每一個人都成了內容的創造者,好比博文、評論、意見、新聞消息、視頻等等,不一而足。可見,這些數據產生的速度之快,數據量之大。好比微博、公衆號、抑或是淘寶,在一分鐘內產生的數據可能就會很是的驚人,面對這些千萬級、億級的數據記錄,關係型數據庫的查詢效率顯然是不能接受的。

  • 沒法知足高併發需求

    WEB1.0時代,大部分是靜態網頁(即提供什麼就看什麼),從而在大規模用戶訪問時,能夠實現較好的響應能力。可是,在WEB2.0時代,強調的是用戶的交互性(用戶創造內容),全部信息都須要事實動態生成,會形成高併發的數據庫訪問,可能每秒上萬次的讀寫請求,對於不少關係型數據庫而言,這顯示是難以承受的。

  • 沒法知足擴展性和高可用性的需求

    在當今娛樂至死的時代,熱點問題(吸引人眼球,知足獵奇心理)會引來一窩蜂的流量,好比微博曝出某明星出軌,熱搜榜會迅速引來大批用戶圍觀(俗稱吃瓜羣衆),從而產生大量的互動交流(蹭熱點),這些都會形成數據庫的讀寫負荷急劇增長,從而須要數據庫可以在短期內迅速提高性能以應對突發需求(畢竟宕機會很是影響戶體驗)。可是關係型數據庫一般難以水平擴展,不可以像網頁服務器和應用服務器那樣簡單地經過增長更多的硬件和服務節點來擴展性能和負載能力。

綜上,NoSQL數據庫應運而生,是IT發展的必然。

HBase的特色及使用場景

特色

  • 強一致性讀寫

HBase 不是 最終一致性(eventually consistent) 數據存儲. 這讓它很適合高速計數聚合類任務

  • 自動分片(Automatic sharding)

HBase 表經過region分佈在集羣中。數據增加時,region會自動分割並從新分佈

  • RegionServer 自動故障轉移
  • Hadoop/HDFS 集成

    HBase 支持本機外HDFS 做爲它的分佈式文件系統

  • MapReduce集成

    HBase 經過MapReduce支持大併發處理, HBase 能夠同時作源(Source)和匯(Sink)

  • Java 客戶端 API

    HBase 支持易於使用的 Java API 進行編程訪問

  • Thrift/REST API

支持Thrift 和 REST 的方式訪問HBase

  • Block Cache 和 布隆過濾器(Bloom Filter)

HBase支持 Block Cache 和 布隆過濾器進行查詢優化,提高查詢性能

  • 運維管理

HBase提供內置的用於運維的網頁和JMX 指標

使用場景

HBase並不適合全部場景

首先,數據量方面 。確信有足夠多數據,若是有上億或十億行數據,至少單表數據量超過千萬,HBase會是一個很好的選擇。 若是隻有上千或上百萬行,用傳統的RDBMS多是更好的選擇。

其次,關係型數據庫特性方面。確信能夠不依賴全部RDBMS的額外特性 (如列數據類型、二級索引、事務、高級查詢語言等) 。一個創建在RDBMS上應用,並不能經過簡單的改變JDBC驅動就能遷移到HBase,須要一次徹底的從新設計。

再次,硬件方面。 確信你有足夠硬件。好比因爲HDFS 的默認副本是3,因此通常至少5個數據節點纔可以發揮其特性,另外 還要加上一個 NameNode節點。

最後,數據分析方面。數據分析是HBase的弱項,由於對於HBase乃至整個NoSQL生態圈來講,基本上都是不支持表關聯的。若是主要需求是數據分析,好比作報表,顯然HBase是不太合適的。

HBase的數據模型

基本術語

HBase是一個稀疏、多維、持久化存儲的映射表,採用的row key、列族、列限定符合時間戳進行索引,每一個cell的值都是字節數組byte[]。瞭解HBase須要先知道下面的一些概念:

  • Namespace

    Namespace,即命名空間,是表的邏輯分組,相似於關係型數據庫管理系統的database。HBase存在兩個預約義的特殊的命名空間:hbasedefault,其中hbase屬於系統命名空間,用來存儲HBase的內部的表。default屬於默認的命名空間,即若是建表時不指定命名空間,則默認使用default。

  • 由行和列組成,列劃分爲若干個列族

  • row key是未解釋的字節數組,在HBase內部,row key是按字典排序由低到高存儲在表中的。每一個HBase的表由若干行組成,每一個行由行鍵(row key)標識。能夠利用這一特性,將常常一塊兒讀取的行存儲在一塊兒。

  • 列族

    HBase中,列是由列族進行組織的。一個列族全部列成員是有着相同的前綴,好比,列courses:historycourses:math都是 列族 courses的成員。冒號(:)是列族的分隔符,用來區分前綴和列名。列族必須在表創建的時候聲明,而列則能夠在使用時進行聲明。另外,存儲在一個列族中的全部數據,一般都具備相同的數據類型,這能夠極大提升數據的壓縮率。在物理上,一個的列族成員在文件系統上都是存儲在一塊兒。

  • 列族裏面的數據經過列限定符來定位。列一般不須要在建立表時就去定義,也不須要在不一樣行之間保持一致。列沒有明確的數據類型,老是被視爲字節數組byte[]。

  • cell

    單元格,即經過row key、列族、列肯定的具體存儲的數據。單元格中存儲的數據也沒有明確的數據類型,總被視爲字節數組byte[]。另外,每一個單元格的數據是多版本的,每一個版本會對應一個時間戳。

  • 時間戳

    因爲HBase的表數據是具備版本的,這些版本是經過時間戳進行標識的。每次對一個單元格進行修改或刪除時,HBase會自動爲其生成並存儲一個時間戳。一個單元格的不一樣版本是根據時間戳降序的順序進行存儲的,即優先讀取最新的數據。

    關於HBase的數據模型,詳見下圖:

在這裏插入圖片描述

概念模型

在HBase概念模型中,一個表能夠被看作是一個稀疏的、多維的映射關係,以下圖所示:

在這裏插入圖片描述

如上表所示:

該表包含兩行數據,分別爲com.cnn.wwwcom.example.www;

三個列族,分別爲:contents, anchorpeople

對於第一行數據(對應的row key爲com.cnn.www),列族anchor包含兩列:anchor:cssnsi.comanchor:my.look.ca;列族contents包含一列:contents:html;

對於第一行數據(對應的row key爲com.cnn.www),包含5個版本的數據

對於第二行數據(對應的row key爲com.example.www),包含1個版本的數據

上表中能夠經過一個四維座標定位一個單元格數據:[row key,列族,列,時間戳],好比[com.cnn.www,contents,contents:html,t6]

物理模型

從概念模型上看,HBase的表是稀疏的。在物理存儲的時候,是按照列族進行存儲的。一個列限定符(column_family:column_qualifier)能夠被隨時添加到已經存在的列族上。

在這裏插入圖片描述

從物理模型上看,概念模型中存在的空單元格是不會被存儲的。好比要訪問contents:html,時間戳爲t8,則不會返回值。值得注意的是,若是訪問數據時沒有指定時間戳,則默認訪問最新版本的數據,由於數據是按照版本時間戳降序排列的。

如上表:若是訪問行com.cnn.www,列contents:html,在沒有指定時間戳的狀況下,則返回t6對應的數據;同理若是訪問anchor:cnnsi.com,則返回t9對應的數據。

HBase的原理及運行機制

總體架構

經過上面的描述,應該對HBase有了必定的瞭解。如今咱們在來看一下HBase的宏觀架構,以下圖:

在這裏插入圖片描述

咱們先從宏觀的角度看一下HBase的總體架構。從HBase的部署架構上來講,HBase有兩種服務器:Master服務器RegionServer服務器。通常一個HBase集羣有一個Master服務器和幾個RegionServer服務器。

Master服務器負責維護表結構信息,實際的數據都存儲在RegionServer服務器上。在HBase的集羣中,客戶端獲取數據由客戶端直連RegionServer的,因此你會發現Master掛掉以後你依然能夠查詢數據,可是不能建立新的表了。

  • Master

咱們都知道,在Hadoop採用的是master-slave架構,即namenode節點爲主節點,datanode節點爲從節點。namenode節點對於hadoop集羣而言相當重要,若是namenode節點掛了,那麼整個集羣也就癱瘓了。

可是,在HBase集羣中,Master服務的做用並無那麼的重要。雖然是Master節點,其實並非一個leader的角色。Master服務更像是一個‘打雜’的,相似於一個輔助者的角色。由於當咱們鏈接HBase集羣時,客戶端會直接從Zookeeper中獲取RegionServer的地址,而後從RegionServer中獲取想要的數據,不須要通過Master節點。除此以外,當咱們向HBase表中插入數據刪除數據等操做時,也都是直接跟RegionServer交互的,不須要Master服務參與。

那麼,Master服務有什麼做用呢?Master只負責各類協調工做,好比建表刪表移動Region合併等操做。這些操做有一個共性的問題:就是須要跨RegionServer。因此,HBase就將這些工做分配給了Master服務。這種結構的好處是大大下降了集羣對Master的依賴。而Master節點通常只有一個到兩個,一旦宕機,若是集羣對Master的依賴度很大,那麼就會產生單點故障問題。在HBase中即便Master宕機了,集羣依然能夠正常地運行,依然能夠存儲和刪除數據。

  • RegionServer

RegionServer就是存放Region的容器,直觀上說就是服務器上的一個服務。RegionServer是真正存儲數據的節點,最終存儲在分佈式文件系統HDFS。當客戶端從ZooKeeper獲取RegionServer的地址後,它會直接從RegionServer獲取數據。對於HBase集羣而言,其重要性要比Master服務大。

  • Zookeeper

RegionServer很是依賴ZooKeeper服務,ZooKeeper在HBase中扮演的角色相似一個管家。ZooKeeper管理了HBase全部RegionServer的信息,包括具體的數據段存放在哪一個RegionServer上。客戶端每次與HBase鏈接,其實都是先與ZooKeeper通訊,查詢出哪一個RegionServer須要鏈接,而後再鏈接RegionServer。

咱們能夠經過zkCli訪問hbase節點的數據,經過下面命名能夠獲取hbase:meta表的信息:

[zk: localhost:2181(CONNECTED) 17] get /hbase/meta-region-server

簡單總結Zookeeper在HBase集羣中的做用以下:對於服務端,是實現集羣協調與控制的重要依賴。對於客戶端,是查詢與操做數據必不可少的一部分

須要注意的是:當Master服務掛掉時,依然能夠進行能讀能寫操做;可是把ZooKeeper一旦掛掉,就不能讀取數據了,由於讀取數據所須要的元數據表hbase:meata的位置存儲在ZooKeeper上。可見zookeeper對於HBase而言是相當重要的。

  • Region

Region就是一段數據的集合。HBase中的表通常擁有一個到多個Region。Region不能跨服務器,一個RegionServer上有一個或者多個Region。當開始建立表時,數據量小的時候,一個Region足以存儲全部數據,等到數據量逐漸增長,會拆分爲多個region;當HBase在進行負載均衡的時候,也有可能會從一臺RegionServer上把Region移動到另外一臺RegionServer上。Region是存儲在HDFS的,它的全部數據存取操做都是調用了HDFS的客戶端接口來實現的。一個Region就至關於關係型數據庫中分區表的一個分區。

微觀架構

上一小節對HBase的總體架構進行了說明,接下來再看一下內部細節,以下圖所示:展現了一臺RegionServer的內部架構。

在這裏插入圖片描述

如上圖所示:一個RegionServer能夠存儲多個region,Region至關於一個數據分片。每個Region都有起
始rowkey和結束rowkey,表明了它所存儲的row範圍。在一個region內部,包括多個store,其中一個store對應一個列族,每一個store的內部又包含一個MemStore,主要負責數據排序,等超過必定閾值以後將MemStore的數據刷到HFile文件,HFile文件時最終存儲數據的地方。

值得注意的是:一臺RegionServer共用一個WAL(Write-Ahead Log)預寫日誌,若是開啓了WAL,那麼當寫數據時會先寫進WAL,能夠起到容錯做用。WAL是一個保險機制,數據在寫到Memstore以前,先被寫到WAL了。這樣當故障恢復的時候能夠從WAL中恢復數據。另外,每一個Store都有一個MemStore,用於數據排序。一臺RegionServer也只有一個BlockCache,用於讀數據是進行緩存。

  • WAL預寫日誌

Write Ahead Log (WAL)會記錄HBase中的全部數據,WAL起到容錯恢復的做用,並非必須的選項。在HDFS上,WAL的默認路徑是/hbase/WALs/,用戶能夠經過hbase.wal.dir進行配置。

WAL默認是開啓的,若是關閉,可使用下面的命令Mutation.setDurability(Durability.SKIP_WAL)。WAL支持異步和同步的寫入方式,異步方式經過調用下面的方法Mutation.setDurability(Durability.ASYNC_WAL)。同步方式經過調用下面的方法:Mutation.setDurability(Durability.SYNC_WAL),其中同步方式是默認的方式。

關於異步WAL,當有Put、Delete、Append操做時,並不會當即觸發同步數據。而是要等到必定的時間間隔,該時間間隔能夠經過參數hbase.regionserver.optionallogflushinterval進行設定,默認是1000ms。

  • MemStore

每一個Store中有一個MemStore實例。數據寫入WAL以後就會被放入MemStore。MemStore是內存的存儲對象,只有當MemStore滿了的時候纔會將數據刷寫(flush)到HFile中。

爲了讓數據順序存儲從而提升讀取效率,HBase使用了LSM樹結構來存儲數據。數據會先在Memstore中
整理成LSM樹,最後再刷寫到HFile上。

關於MemStore,很容易讓人混淆。數據在被刷到HFile以前,已經被存儲到了HDFS的WAL上了,那麼爲何還要在放入MemStore呢?其實很簡單,咱們都知道HDFS是不能修改的,而HBase的數據又是按照Row Key進行排序的,其實這個排序的過程就是在MemStore中進行的。值得注意的是:MemStore的做用不是爲了加快寫速度,而是爲了對Row Key進行排序。

  • HFile

HFile是數據存儲的實際載體,咱們建立的全部表、列等數據都存儲在HFile裏面。當Memstore達到必定閥值,或者達到了刷寫時間間隔閥值的時候,HBaes會被這個Memstore的內容刷寫到HDFS系統上,稱爲一個存儲在硬盤上的HFile文件。至此,咱們數據真正地被持久化到硬盤上。

Region的定位

在開始講解HBase的數據讀寫流程以前,先來看一下Region是怎麼定位的。咱們知道Region是HBase很是重要的一個概念,Region存儲在RegionServer中,那麼客戶端在讀寫操做時是如何定位到所須要的region呢?關於這個問題,老版本的HBase與新版本的HBase有所不一樣。

老版本HBase(0.96.0以前)

老版本的HBase採用的是爲三層查詢架構,以下圖所示:

在這裏插入圖片描述

如上圖:第一層定位是Zookeeper中的節點數據,記錄了-ROOT-表的位置信息;

第二層-ROOT-表記錄了.META.region位置信息,-ROOT-表只有一個region,經過-ROOT-表能夠訪問.META.表中的數據

第三層.META.表,記錄了用戶數據表的region位置信息,.META.表能夠有多個region。

整個查詢步驟以下:

第一步:用戶經過查找zk(ZooKeeper)的/hbase/root-regionserver節點來知道-ROOT-表的RegionServer位置。

第二步:訪問-ROOT-表,查找所須要的數據表的元數據信息存在哪一個.META.表上,這個.META.表在哪一個RegionServer上。

第四步:訪問.META.表來看你要查詢的行鍵在什麼Region範圍裏面。

第五步:鏈接具體的數據所在的RegionServer,這個一步纔開始在很正的查詢數據。

新版本HBase

老版本的HBase尋址存在不少弊端,在新版本中進行了改進。採用的是二級尋址的方式,僅僅使用 hbase:meta表來定位region,那麼 從哪裏獲取hbase:meta的信息呢,答案是zookeeper。在zookeeper中存在一個/hbase/meta-region-server節點,能夠獲取hbase:meta表的位置信息,而後經過hbase:meta表查詢所須要數據所在的region位置信息。

整個查詢步驟以下:

第一步:客戶端先經過ZooKeeper的/hbase/meta-region-server節點查詢hbase:meta表的位置。

第二步:客戶端鏈接hbase:meta表所在的RegionServer。hbase:meta表存儲了全部Region的行鍵範圍信息,經過這個表就能夠查詢出你要存取的rowkey屬於哪一個Region的範圍裏面,以及這個Region屬於哪一個
RegionServer。

第三步:獲取這些信息後,客戶端就能夠直連擁有你要存取的rowkey的RegionServer,並直接對其操做。

第四步:客戶端會把meta信息緩存起來,下次操做就不須要進行以上加載hbase:meta的步驟了。

客戶端API基本使用

public class Example {

  private static final String TABLE_NAME = "MY_TABLE_NAME_TOO";
  private static final String CF_DEFAULT = "DEFAULT_COLUMN_FAMILY";

  public static void createOrOverwrite(Admin admin, HTableDescriptor table) throws IOException {
    if (admin.tableExists(table.getTableName())) {
      admin.disableTable(table.getTableName());
      admin.deleteTable(table.getTableName());
    }
    admin.createTable(table);
  }

  public static void createSchemaTables(Configuration config) throws IOException {
    try (Connection connection = ConnectionFactory.createConnection(config);
         Admin admin = connection.getAdmin()) {

      HTableDescriptor table = new HTableDescriptor(TableName.valueOf(TABLE_NAME));
      table.addFamily(new HColumnDescriptor(CF_DEFAULT).setCompressionType(Algorithm.NONE));

      System.out.print("Creating table. ");
      createOrOverwrite(admin, table);
      System.out.println(" Done.");
    }
  }

  public static void modifySchema (Configuration config) throws IOException {
    try (Connection connection = ConnectionFactory.createConnection(config);
         Admin admin = connection.getAdmin()) {

      TableName tableName = TableName.valueOf(TABLE_NAME);
      if (!admin.tableExists(tableName)) {
        System.out.println("Table does not exist.");
        System.exit(-1);
      }

      HTableDescriptor table = admin.getTableDescriptor(tableName);

      // 更新table
      HColumnDescriptor newColumn = new HColumnDescriptor("NEWCF");
      newColumn.setCompactionCompressionType(Algorithm.GZ);
      newColumn.setMaxVersions(HConstants.ALL_VERSIONS);
      admin.addColumn(tableName, newColumn);

      // 更新column family
      HColumnDescriptor existingColumn = new HColumnDescriptor(CF_DEFAULT);
      existingColumn.setCompactionCompressionType(Algorithm.GZ);
      existingColumn.setMaxVersions(HConstants.ALL_VERSIONS);
      table.modifyFamily(existingColumn);
      admin.modifyTable(tableName, table);

      // 禁用table
      admin.disableTable(tableName);

      // 刪除column family
      admin.deleteColumn(tableName, CF_DEFAULT.getBytes("UTF-8"));

      // 刪除表,首先要禁用表
      admin.deleteTable(tableName);
    }
  }

  public static void main(String... args) throws IOException {
    Configuration config = HBaseConfiguration.create();

    config.addResource(new Path(System.getenv("HBASE_CONF_DIR"), "hbase-site.xml"));
    config.addResource(new Path(System.getenv("HADOOP_CONF_DIR"), "core-site.xml"));
    createSchemaTables(config);
    modifySchema(config);
  }
}

易混淆知識點總結

Q1:MemStore的做用是什麼?

在HBase中,一個表能夠有多個列族,一個列族在物理上是存儲在一塊兒的,一個列族會對應一個store,在store的內部會存在一個MemStore,其做用並非爲了提高讀寫速度,而是爲了對RowKey進行排序。咱們知道,HBase的數據是存儲在HDFS上的,而HDFS是不支持修改的,HBase爲了按RowKey進行排序,首先會將數據寫入MemStore,數據會先在Memstore中整理成LSM樹,最後再刷寫到HFile上。

總之一句話:Memstore的實現目的不是加速數據寫入或讀取,而是維持數據結構。

Q2:讀取數據時會先從MemStore讀取嗎?

MemStore的做用是爲了按RowKey進行排序,其做用不是爲了提高讀取速度的。讀取數據的時候是有專門的緩存叫BlockCache,若是開啓了BlockCache,就是先讀BlockCache,而後纔是讀HFile+Memstore的數據。

Q3:BlockCache有什麼用?

塊緩存(BlockCache)使用內存來記錄數據,適用於提高讀取性能。當開啓了塊緩存後,HBase會優先從塊緩存中查詢是否有記錄,若是沒有才去檢索存儲在硬盤上的HFile。

值得注意的是,一個RegionServer只有一個BlockCache。BlockCache不是數據存儲的必須組成部分,只是用來優化讀取性能的。

BlockCache的基本原理是:在讀請求到HBase以後,會先嚐試查詢BlockCache,若是獲取不到所需的數據,就去HFile和Memstore中去獲取。若是獲取到了,則在返回數據的同時把Block塊緩存到BlockCache中。

Q4:HBase是怎麼刪除數據的?

HBase刪除記錄並非真的刪除了數據,而是標識了一個墓碑標記(tombstone marker),把這個版本連同以前的版本都標記爲不可見了。這是爲了性能着想,這樣HBase就能夠按期去清理這些已經被刪除的記錄,而不用每次都進行刪除操做。所謂按期清理,就是按照必定時間週期在HBase作自動合併(compaction,HBase整理存儲文件時的一個操做,會把多個文件塊合併成一個文件)。這樣刪除操做對於HBase的性能影響被降到了最低,即使是在很高的併發負載下大量刪除記錄也是OK的。

合併操做分爲兩種:Minor CompactionMajor Compaction

其中Minor Compaction是將Store多個HFile合併爲一個HFile。在這個過程當中達到TTL的數據會被移除,可是被手動刪除的數據不會被移除。這種合併觸發頻率較高。

Major Compaction合併Store中的全部HFile爲一個HFile。在這個過程當中被手動刪除的數據會被真正地移除。同時被刪除的還有單元格內超過MaxVersions的版本數據。這種合併觸發頻率較低,默認爲7天一次。不過因爲Major Compaction消耗的性能較大,通常建議手動控制MajorCompaction的時機。

須要注意的是:Major Compaction刪除的是那些帶墓碑標記的數據,而Minor Compaction合併的時候直接會忽略過時數據文件,因此過時的這些文件會在Minor Compaction的時候就被刪除。

Q5:爲何HBase具備高性能的讀寫能力?

由於HBase使用了一種LSM的存儲結構,在LSM樹的實現方式中,會在數據存儲以前先對數據進行排序。LSM樹是Google BigTable和HBase的基本存儲算法,它是傳統關係型數據庫的B+樹的改進。算法的核心在於儘可能保證數據是順序存儲到磁盤上的,而且會有頻率地對數據進行整理,確保其順序性。

LSM樹就是一堆小樹,在內存中的小樹即memstore,每次flush,內存中的memstore變成磁盤上一個新的storefile。這種批量的讀寫操做使得HBase的性能較高。

Q6:Store與列簇是什麼關係?

Region是HBase的核心模塊,而Store則是Region的核心模塊。每一個Store對應了表中的一個列族存儲。每一個Store包含一個MemStore和若干個HFile。

Q7:WAL是RegionServer共享的,仍是Region級別共享的?

在HBase中,每一個RegionServer只須要維護一個WAL,全部Region對象共用一個WAL,而不是每一個Region都維護一個WAL。這種方式對於多個Region的更新操做所發生的的日誌修改,只須要不斷地追加到單個日誌文件中,不須要同時打開並寫入多個日誌文件,這樣能夠減小磁盤尋址次數,提升寫性能。

可是這種方式也存在一個缺點,若是RegionServer發生故障,爲了恢復其上的Region對象,須要將RegionServer上的WAL按照其所屬的Region對象進行拆分,而後分發到其餘RegionServer上執行恢復操做。

Q8:Master掛掉以後,還能查詢數據嗎?

能夠的。Master服務主要負責表和Region的管理工做。主要做用有:

  • 管理用戶對錶的增長、刪除、修改操做
  • 實現不一樣RegionServer以前的負載均衡
  • Region的分裂與合併
  • 對發生故障的RegionServer的Region進行遷移

客戶端訪問HBase時,不須要Master的參與,只須要鏈接zookeeper獲取hbase:meta地址,而後直連RegionServer進行數據讀寫操做,Master僅僅維護表和Region的元數據信息,負載很小。可是Master節點也不能長時間的宕機。

總結

本文首先從谷歌的BigTable提及,而後介紹了CAP相關理論,並分析了NoSQL出現的緣由。接着對HBase的數據模型進行了剖析,而後詳細描述了HBase的原理和運行機制。最後給出了客戶端API的基本使用,並對常見的、易混淆的知識點進行了解釋。

公衆號『大數據技術與數倉』,回覆『資料』領取大數據資料包
相關文章
相關標籤/搜索