1. 什麼是HBase
HBase的原型是Google的BigTable
論文,受到了該論文思想的啓發,目前作爲Hadoop的頂級項目來開發維護,用於支持結構化的數據存儲。
HBase是一個高可靠性
、高性能
、面向列
、可伸縮
的分佈式存儲
系統,利用HBASE技術可在廉價PC Server上搭建起大規模結構化存儲集羣。
HBase的目標是存儲
並處理
大型的數據,更具體來說是僅需使用普通的硬件配置,就能夠處理由成千上萬的行和列所組成的大型數據。【非大勿用】
HBase是Google Bigtable的開源實現,但是也有很多不同之處。比如:Google Bigtable利用GFS作爲其文件存儲系統,HBase利用Hadoop HDFS作爲其文件存儲系統;Google運行MAPREDUCE來處理Bigtable中的海量數據,HBase同樣利用Hadoop MapReduce來處理HBase中的海量數據;Google Bigtable利用Chubby作爲協同服務,HBase利用Zookeeper作爲對應。
簡單粗暴的總結:就是一款NoSQL
數據庫,面向列存儲
,用於存儲處理海量數據。
核心在於它是一個存數據的地方,可是在此之前學習過了HDFS和Mysql,那HBase爲什麼還會出現呢?
2. 爲什麼會有HBase
先說一下Mysql,我們都知道Mysql是一個關係型數據庫,平時開發使用的非常頻繁。一個網站或者系統最核心的表就是用戶表,而當用戶表的數據達到幾千萬甚至幾億級別的時候,對單條數據的檢索將會耗費數秒甚至分鐘級別。實際的清空可能更加複雜不堪。
看下邊一張表:
假如根據id=1查詢出來這條數據對應的用戶姓名,很簡單,會給我們返回zhangsan。但是,當我們查的時候,想一下,查名字的時候age和email會不會被查出來?答案是肯定的,Mysql的數據存儲是以行爲單位的,面向行存儲。那問題就出現了,我只需要找出zhangsan的名字,卻需要查詢一整行的數據,如果列非常多,那麼查詢效率可想而知了。
查詢的操作速度會受到以下兩個因素的制約:
- 表被併發的插入、編輯以及刪除操作。
- 查詢語句通常不是簡單的對一個表進行操作,有可能是多個表關聯後的複雜查詢,甚至有可能是group by或者order by操作,此時,性能下降很明顯。
如果一張表的列過多
,會影響查詢效率,我們稱這樣表爲寬表。怎麼優化呢,拆開來,豎直拆分
:
這樣的情況下,我們要查找username的時候只需要查找user_basic表,沒有多餘的字段,查詢效率就會很快。如果一張表的行過多
,會影響查詢效率,我們將這樣的表稱之爲高表
,可以採用水平拆表
的方式提高效率:
這種水平拆分應用比較多的 場景就是日誌表
,日誌信息每天產生很多,可以按月
進行水平拆分,這樣就實現了高表變矮。
ok,這種拆分方式貌似可以解決寬表和高表的問題,但是如果有一天公司的業務變了,比如原來沒有微信,現在有了微信,需要加入用戶的微信字段。這時候需要改變表的結構信息,該怎麼辦?最簡單的想法是多加一列,像這樣:
多考慮一下就知道這樣做很不妥帖,比如說有些早期用戶沒有微信,這一列是設置默認值還是採取其他的做法就得權衡一下。如果需要擴展很多的列出來,而且不是所有的用戶都有這些屬性,那麼拓展起來就更加複雜了。
這時候,想到了JSON格式的字符串,這是一種以字符串的形式表示的對象(將若干可選擇填寫信息彙總),而且屬性字段可以動態拓展,於是有了下邊這種做法,兩種做法加以對比:
ok,這樣存儲數據它不挺好的嘛,HBase出來幹嘛??Mysql有一點,數據達到一定的閾值(一般是500W),無論怎麼優化,它都無法達到高性能的發揮。而大數據領域的數據,動輒PB級,這種存儲應用明顯是不能很好的滿足需求的。針對上邊的問題,HBase都有很好的解決方案~~
3. HBase怎麼實現的
先不說爲什麼用,接着上邊說到的幾個問題:高表寬表,數據列動態擴展,把提到的幾個解決辦法:水平垂直切分
,列擴展方法
雜糅在一起。
有這麼一張表,怕它又寬又高,又會動態擴展列,那麼在設計之初,就把這個表給他拆開,爲了列的動態拓展
,直接存儲JSON格式:
這樣就解決了寬表問題,高表怎麼辦呢?一個表的兩部分,各存一部分行:
解決了高表,寬表,動態擴展列的問題,如果還要進一步提高性能怎麼辦?Mysql->Redis !!! 緩存啊!
查詢出來的數據放入到緩存中,下一次查詢直接從緩存中拿數據。插入數據怎麼辦呢?也可以這樣理解,我把要插入的數據放進緩存中,再也不用管了,直接由數據庫從緩存拿數據插入到數據庫。此時程序不需要等待數據插入成功,提高了並行工作的效率。
可是這樣做有了很大的風險,服務器宕機的話,緩存中的數據沒來得及插入到數據庫中,那不就丟數據了嘛。參考Redis的持久化策略,可以給插入數據這個操作添加一個操作日誌,用於持久化插入操作,宕機重啓後從日誌恢復。
這樣設計架構就變成了這個樣子:
上邊這種解決方式,實際上就是HBase實現的大致思路,詳細的內容會在後邊慢慢說。
簡單粗暴總結:HBase就是一個面向列存儲的非關係型數據庫
。兩者的區別主要是:
HBase是的存儲時基於HDFS
的,HDFS有着高容錯性的特點,被設計用來部署在低廉的硬件上,而且它提供高吞吐量以訪問應用程序的數據,基於Hadoop意味着HBase與生俱來的超強的擴展性
和吞吐量
。
HBase採用的時key/value
的存儲方式,這意味着,即使隨着數據量的增大,也幾乎不會導致查詢性能的下降。HBase又是一個面向列
存儲的數據庫,當表的字段很多時,可以把其中幾個字段獨立出來放在一部分機器上,而另外幾個字段放到另一部分機器上,充分分散了負載的壓力。如此複雜的存儲結構和分佈式的存儲方式,帶來的代價就是:即便是存儲很少的數據,也不會很快。
HBase並不是足夠快,而是數據量很大的時候它慢的不明顯。什麼時候使用HBase呢,主要是以下兩種情況:
- 單表數據量超過千萬,而且併發量很大;
- 數據分析需求較弱,或者不需要那麼實時靈活。
4. HBase簡介
官方網站:http://hbase.apache.org
HBase的原型是Google的BigTable論文,受到了該論文思想的啓發,目前作爲Apache的頂級項目來開發維護,用於支持結構化的數據存儲。
HBase 特點
- 海量存儲
Hbase適合存儲PB級別的海量數據,在PB級別的數據以及採用廉價PC存儲的情況下,能在幾十到百毫秒內返回數據。這與Hbase的極易擴展性息息相關。正是因爲Hbase良好的擴展性,才爲海量數據的存儲提供了便利。
- 列式存儲
這裏的列式存儲其實說的是
列族
存儲,Hbase是根據列族
來存儲數據的。列族下面可以有非常多的列,列族在創建表的時候就必須指定。
- 極易擴展
Hbase的擴展性主要體現在兩個方面,一個是基於上層處理能力(RegionServer)的擴展,一個是基於存儲的擴展(HDFS)。
通過橫向添加RegionSever的機器,進行水平擴展,提升Hbase上層的處理能力,提升Hbsae服務更多Region的能力。
備註:RegionServer的作用是管理region、承接業務的訪問,這個後面會詳細的介紹通過橫向添加Datanode的機器,進行存儲層擴容,提升Hbase的數據存儲能力和提升後端存儲的讀寫能力。
- 高併發
由於目前大部分使用Hbase的架構,都是採用的廉價PC,因此單個IO的延遲其實並不小,一般在幾十到上百ms之間。這裏說的高併發,主要是在併發的情況下,Hbase的單個IO延遲下降並不多。能獲得高併發、低延遲的服務。
- 稀疏
稀疏主要是針對Hbase列的靈活性,在列族中,你可以指定任意多的列,在列數據爲空的情況下,是不會佔用存儲空間的。
HBase邏輯結構
先從邏輯思維上對HBase的存儲有個大致的理解:
- Table(表):
一個表由一個或者多個`列族構成。數據的屬性。比如:name、age、TTL(超時時間)等等都在列族裏邊定義。定義完列族的表是個空表,只有添加了數據行以後,表纔有數據。
- Column Family(列族):
在HBase裏,可以將多個列
組合
成一個列族。建表的時候不用創建列,因爲列是可增減變化
的,非常靈活。唯一需要確定的就是列族
,也就是說一個表有幾個列族是一開始就定好的。此外表的很多屬性,比如數據過期時間、數據塊緩存以及是否使用壓縮等都是定義在列族上的,而不是定義在表上或者列上。這一點與以往的關係型數據庫有很大的差別。列族存在的意義是
:HBase會把相同列族的列儘量放在同一臺機器上,所以說想把某幾個列放在一臺服務器上,只需要給他們定義相同的列族。
- Row(行):
一個行包含多個列,這些列通過列族來分類。行中的數據所屬的列族從該表所定義的列族中選取,不能選擇這個表中不存在的列族。由於HBase是一個
面向列存儲的數據庫
,所以一個行中的數據可以分佈在不同的服務器上
。
- RowKey(行鍵):
rowkey和MySQL數據庫的主鍵比起來簡單很多,rowkey
必須要有
,如果用戶不指定的話,會有默認的。rowkey完全是由用戶指定的一串不重複的字符串,另外,rowkey按照字典排序
。一個rowkey對應的是一行數據!!!
- Region:
Region就是一段數據的
集合
。之前提到過高表的概念,把高表進行水平切分,假設分成兩部分,那麼這就形成了兩個Region。注意一下Region的幾個特性:Region
不能
跨服務器,一個RegionServer可以有多個Region。
數據量小的時候,一個Region可以存儲所有的數據;但是當數據量大的時候,HBase會拆分
Region。
當HBase在進行負載均衡
的時候,也有可能從一臺RegionServer上把Region移動到另一服務器的RegionServer上。
Region是基於HDFS的,它的所有數據存取操作都是調用HDFS客戶端完成的。
- RegionServer:
RegionServer就是
存放Region的容器
,直觀上說就是服務器上的一個服務。負責管理維護Region。
HBase物理存儲
以上是一個基本的邏輯結構,底層的物理存儲結構纔是重中之重的內容,看下圖,並且將嘗試換個角度解釋上邊的幾個概念:
- NameSpace:
命名空間,類似於關係型數據庫的
DatabBase
概念,每個命名空間下有多個表。HBase有兩個自帶的命名空間,分別是hbase
和default
,hbase 中存放的是 HBase 內置的表,default 表是用戶默認使用的命名空間。
- Row:
HBase 表中的每行數據都由一個
RowKey
和多個Column
(列)組成,數據是按照 RowKey 的字典順序存儲的,並且查詢數據時只能根據 RowKey 進行檢索
,所以 RowKey 的設計十分重要。
- Column :
HBase 中的每個列都由 Column Family(列族)和 Column Qualifier(列限定符)進行限 定,例如 info:name,info:age。建表時,只需指明列族,而列限定符無需預先定義。
- TimeStamp:
時間戳,用於標識數據的不同版本(version),每條數據寫入時,如果不指定時間戳,系統會自動爲其加上該字段,其值爲寫入 HBase 的時間。並且讀取數據的時候一般只拿出數據的Type符合,時間戳最新的數據。HBase中通過Type來標識數據是否可用。
因爲HBase是基於HDFS的而HDFS是可以增刪查而不支持改的
。
- Cell:
單元格,由{rowkey, column Family:column Qualifier, time Stamp} 唯一確定的單元。cell 中的數據是
沒有
類型的,全部是字節碼
形式存儲。
HBase與關係型數據庫的對比
傳統關係型數據庫的表結構圖如下:
其中每行都是不可分割的,也正是體現了數據庫第一範式的原子性,也就是說三個列必須在一起,而且要被存儲在同一臺服務器上,甚至是同一個文件裏面。
HBase的表架構如圖所示:
HBase的每一個行都是離散的,因爲列族的存在,所以一個行裏面不同的列甚至被分配到了不同的服務器上。行的概念被減弱到了一個抽象的存在。在實體上,把多個列定義爲一個行的關鍵詞rowkey,也就是行這個概念在HBase中的唯一體現。
HBase的存儲語句中必須·精確·的寫出要將數據存放到哪個單元格,單元格由表:列族:行:列來唯一確定。用人話說就是要寫清楚數據要被存儲在哪個表的哪個列族的哪個行的哪個列。如果一行有10列,那麼存儲一行的數據就需要寫明10行的語句。
HBase架構解析
1. 宏觀圖
從這張圖上可以看到是一個HBase集羣由一個Master
(也可配置成多個,搞成HA)和多個RegionServer
組成,之後再詳細介紹RegionServer的架構。上面的圖示說明了HBase的服務器角色構成,下邊給出具體的介紹:
- Master:
負責啓動的時候分配
Region
到具體的RegionServer
,執行各種管理操作,比如Region的分割
與合併
。在HBase中,master的角色地位比其他類型的集羣弱很多。數據的讀寫操作與他沒有關係,它掛了之後,集羣照樣運行。具體的原因後邊後詳細介紹。但是master也不能宕機太久,有很多必要的操作,比如創建表、修改列族配置主要是DDL
,以及更重要的分割
與合併
都需要它的操作。
- RegionServer:
RegionServer就是一臺機器,在它上邊有多個
region
。我們讀寫的數據就存儲在Region
中。
- Region:
它是表拆分出來的一部分,HBase是一個會自動切片的數據庫。當數據庫過高時,就會進行拆分。
- HDFS:
HBase的數據存儲是基於HDFS的,它是真正承載數據的載體。
- Zookeeper:
在本集羣中負責存儲
hbase:meata
的位置存儲信息,客戶端在寫數據時需要先讀取元數據信息。
2. RegionServer
在宏觀架構圖的最後一個RegionServer
中可以看到 ,它的內部是多個Region的集合:
現在我們放大一下這個RegionServer的內部架構:
- 一個WAL:
跟HDFS中的edits文件存在的意義一樣
WAL時
Write-Ahead Log
的縮寫,翻譯爲預寫入日誌
。從名字大概也能猜出它的作用,當操作到達Region的時候,HBase先把操作寫入到WAL中,然後把數據放入到基於內存實現的MemStore
中,等到一定的時機再把數據刷寫
形成HFile文件,存儲到HDFS上。WAL是一個保險機制,數據在寫到MemStore之前,先寫到WAL中,這樣如果在刷寫過程中出現事故,可以從WAL恢復數據。
- 多個Region:
Region已經多次提到了,它就時是數據庫的一部分,每一個Region都有起始的rowkey和結束的rowkey,代表了它存儲的row的範圍。
我們再放大Region的內部結構:
從圖中可以看的出來,一個Region包含 多個Store:一個Region有多個Store,一個Store就是對應一個列族的數據
,如圖就有三個列族
。再從最後一個Store中我們又可以看出,Store是由MemStore和HFile
組成的。
3. WAL
預寫入日誌就是設計來解決宕機之後的操作恢復問題的,WAL是保存在HDFS上的持久化文件。數據到達Region的時候,先寫入WAL,然後被加載到MemStore中。這樣就算Region宕機了,操作沒來得及執行持久化,也可以再重啓的時候從WAL加載操作並執行。
- 如何啓用WAL?
WAL是默認開啓的,也可以手動關閉它,這樣增刪改操作會快一點。但是這樣做犧牲的是數據的安全性,所以不建議關閉。
關閉方法:
Mutation.setDurability(Durability.SKIP_WAL)
- 異步寫入WAL
如果不惜通過關閉WAL來提高性能的話,還可以考慮一下折中的方案:異步寫入WAL。
正常情況下,客戶端提交的put、delete、append操作來到Region的時候,先調用HDFS的客戶端寫到WAL中。哪怕只有一個改動,也會調用HDFS的接口來寫入數據。可以想象到,這種方式儘可能的保證了數據的安全性,代價這種方式頻繁的消耗資源。
如果不想關閉WAL,又不想每次都耗費那麼大的資源,每次改動都調用HDFS客戶端,可以選擇異步
的方式寫入WAL:
Mutation.setDurability(Durability.ASYNC_WAL)
這樣設定以後,Region會等到條件滿足的時候纔將操作寫到WAL。這裏的條件指的是間隔多久,寫一次,默認的時間間隔是1s。
如果異步寫入數據的時候出錯了怎麼辦呢?比如客戶端的操作現在在Region內存中,由於時間間隔未到1s,操作還沒來得及寫入到WAL,Region掛了(邪門不?就差那麼一丟丟不到1s)。出錯了是沒有任何事務可以保證的。
- WAL滾動
之前學習過MapReduce的shuffle
機制,所以猜得到WAL是一個喚醒的滾動日誌數據結構
,因爲這種結構不會導致佔用的空間持續變大,而且寫入效率也最高。
通過wal日誌切換,這樣可以避免產生單獨的過大的wal日誌文件,這樣可以方便後續的日誌清理(可以將過期日誌文件直接刪除)另外如果需要使用日誌進行恢復時,也可以同時解析多個小的日誌文件,縮短恢復所需時間。
WAL的檢查間隔由hbase.regionserver.logroll.period定義,默認值是一個小時。檢查的內容是把當前WAL中的操作跟實際持久化到HDFS上的操作做比較,看哪些操作已經被持久化了,如果已經被持久化了,該WAL就會被移動到HDFS上的.oldlogs文件夾下。
一個WAL實例包含多個WAL文件。WAL文件的最大數量可以手動通過參數配置。
其它的觸發滾動的條件是:
- WAL的大小超過了一定的閾值。
- WAL文件所在的HDFS文件塊快要滿了。
- WAL歸檔和刪除
歸檔:WAL創建出來的文件都會放在/hbase/.log下,在WAL文件被定爲歸檔時,文件會被移動到/hbase/.oldlogs下
刪除:判斷:是否此WAL文件不再需要,是否沒有被其他引用指向這個WAL文件
會引用此文件的服務:
- TTL進程:該進程會保證WAL文件一直存活直到達到hbase.master.logcleaner.ttl定義的超時時間(默認10分鐘)爲止。
- 備份(replication)機制:如果你開啓了HBase的備份機制,那麼HBase要保證備份集羣已經完全不需要這個WAL文件了,纔會刪除這個WAL文件。這裏提到的replication不是文件的備份數,而是0.90版本加入的特性,這個特性用於把一個集羣的數據實時備份到另外一個集羣。如果你的手頭就一個集羣,可以不用考慮這個因素。
只有當該WAL文件沒有被以上兩種情況引用的時候,纔會被系統徹底清除掉
4. Store
解釋完了WAL,放大一下Store的內部架構:
Store有兩個重要的部分:
- MemStore:
每個Store都有一個MemStore實例。數據寫入到WAL之後就會被放入MemStore中。MemStore是內存的存儲對象,只有到達一定的時機纔會被刷寫到HFile中去。
- HFile:
在Store中有多個HFile,每次刷寫都會形成一個HFile文件落盤在HDFS上。HFile直接跟HDFS打交道,它是數據存儲的實體。
這裏提出一點疑問:
客戶端的操作到達Region時,先將數據寫到WAL中,而WAL是存儲在HDFS上的。所以就相當於數據已經持久化了,那麼爲什麼還要從WAL加載到MemStore中,再刷寫形成HFile存到HDFS上呢?
簡單的說就是:數據進入HFile之前就已經被持久化了,爲什麼還要放入MemStore?
這是因爲HDFS支持文件的創建、追加、刪除,但是不能修改
。對於一個數據庫來說,數據的順序是非常重要的。第一次WAL的持久化是爲了保證數據的安全性,無序的。再讀取到MemStore中,是爲了排序後存儲
。所以MemStore的意義在於維持數據按照rowkey的字典序排列
,而不是做一個緩存提高寫入效率。
補一張圖,對比着來看,關於MemStore刷寫:
HBase架構圖解析
從圖中可以看出Hbase是由Client
、Zookeeper
、Master
、HRegionServer
、HDFS
等幾個組件組成,並且如果做DML的操作是不需要關心HMaster的,只需要從ZK中獲得必備meta數據地址,然後從RegionServer
中增刪查數據即可。下面來介紹一下幾個組件的相關功能:
- Client
Client包含了訪問Hbase的接口,另外Client還維護了對應的cache來加速Hbase的訪問,比如cache的.META.元數據的信息。
- Zookeeper
HBase通過Zookeeper來做master的
高可用
、RegionServer的監控
、元數據的入口以及集羣配置的維護等工作。具體工作如下:
通過Zoopkeeper來保證集羣中只有
1個master在運行,如果master異常,會通過競爭機制產生新的master提供服務
通過Zoopkeeper來監控RegionServer的狀態,當RegionSevrer有異常的時候,通過回調的形式通知MasterRegionServer上下線的信息
通過Zoopkeeper存儲元數據
的統一入口地址
- Hmaster
master節點的主要職責如下:
- 爲RegionServer分配Region
- 維護整個集羣的負載均衡
- 維護集羣的元數據信息
- 發現失效的Region,並將失效的Region分配到正常的RegionServer上
- 當RegionSever失效的時候,協調對應Hlog的拆分
- HregionServer
HregionServer直接對接用戶的讀寫請求,是真正的幹活
的節點。它的功能概括如下:
- 管理master爲其分配的Region
- 處理來自客戶端的讀寫請求
- 負責和底層HDFS的交互,存儲數據到HDFS
- 負責Region變大以後的拆分
- 負責Storefile的合併工作
- HDFS
HDFS爲Hbase提供最終的底層數據存儲服務,HBase底層用HFile格式(跟hadoop底層的數據存儲格式類似)將數據存儲到HDFS中,同時爲HBase提供高可用(Hlog存儲在HDFS)的支持,具體功能概括如下:
- 提供元數據和表數據的底層分佈式存儲服務
- 數據多副本,保證的高可靠和高可用性
參考
大數據技術之HBase-李海波
HBase概述