HBase是一個開源的非關係型分佈式數據庫(NoSQL),是Hadoop項目的一部分,運行於HDFS文件系統之上, (Hadoop Database )。主要應用場景是實時隨機讀寫超大規模的數據。java
HBase 內部管理的文件所有都是存儲在 HDFS 當中的 ,由於是分佈式的存儲,因此表都是邏輯上的結構,物理上的存儲分佈在多個RegionServer上,而且須要一個主節點HMaster進行控制mysql
**Region是分佈式存儲的最小單元,存儲在實際機器上,一個 Region 是由一個或多個 Store 組成。**每個 Store 其實就是一個列族。每一個Store 又是由一個 memStore 和 0 個或者多個 storeFile 組成。memStore 是存儲在內存中,storeFile 是存儲在 HDFS 中,有時候也稱做 HFile。數據都會先寫入memStore,一旦 memStore 超過給的的最大值以後,HBase 就會將memStore 持久化爲 storeFile。算法
HBase架構中只有一個Master節點,稱HMaster,還有多臺RegionServer成爲HRegionServer,每一個RegionServer包含多個Region。sql
client訪問hbase上數據的過程並不須要Master參與(尋址訪問Zookeeper和RegionServer,數據讀寫訪問RegionServer),Master僅僅維護Table和Region的元數據信息,負載很低。shell
RegionServer主要管理兩種文件,一是Hlog(預寫日誌 write-ahead log WAL),二是region(實際的數據文件)。當用戶向hbase請求寫入put數據時,首先就要寫入Hlog實現的預寫日誌當中,當hbase服務器崩潰時,hlog能夠回滾尚未持久化的數據,以便後續從WAL中恢復數據。 當數據寫入預寫日誌後,數據便會存放當region中的memStore中,同時還會檢查memStore是否寫滿,若是寫滿,就會被請求刷寫到磁盤中。數據庫
通常步驟:vim
1.聯繫zookeeper,找出meta表(元數據表)所在rs(regionserver) /hbase/meta-region-server
數組
2.定位row key,找到對應region server 3.緩存信息在本地(再次查找數據時,不須要通過1,2步驟)緩存
4.聯繫RegionServerbash
5.HRegionServer負責open HRegion對象,爲每一個列族建立Store對象,Store包含多個StoreFile實例,他們是對HFile的輕量級封裝。每一個Store還對應了一個MemStore,用於內存存儲數據。
HDFS構建了一個分佈式文件系統,HBase則是在其基礎之上構建一個非關係型數據庫,HBase的全部操做最終仍是會基於HDFS,映射到HDFS之上,全部邏輯上的操做都是在HDFS之上進行。
配置hbase-site.xml
位於 ${HBase-Dir}/conf/hbase-site.xml
單擊版配置:
<configuration>
<property>
<name>hbase.rootdir</name>
<value>file:///tmp/hbase-${user.name}/hbase</value>
</property>
</configuration>
複製代碼
僞分佈式配置:
<property>
<name>hbase.rootdir</name>
<value>hdfs://localhost:9000/hbase</value>
</property>
<property>
<name>hbase.cluster.distributed</name>
<value>true</value>
</property>
</configuration>
複製代碼
hbase.rootdir:該參數制定了HReion服務器的位置,即數據存放的位置。主要端口號要和Hadoop相應配置一致。
hbase.cluster.distributed:HBase的運行模式。false是單機模式,true是分佈式模式。若爲false, HBase和Zookeeper會運行在同一個JVM裏面。默認爲false.
設置環境變量
修改HBase下的conf目錄中的hbase-env.sh文件
export JAVA_HOME=/usr/lib/jvm/java-8-oracle
export HBASE_MANAGES_ZK=true
複製代碼
爲方便使用,將HBase下的bin命令放在系統的/etc/profile下:
sudo vim /etc/profile
# set hbase path
export HBASE_HOME=/opt/hbase-1.2.6
export PATH=$PATH:/opt/hbase-1.2.6/bin
複製代碼
在hbase的bin下執行腳本
./start-hbase.sh
./stop-hbase.sh
複製代碼
HBase基於HDFS,所以必須先啓動HDFS
啓動HBase實例以後,使用 ./hbase shell鏈接HBase實例到shell
命令 quit
退出HBase Shell 而且斷開和集羣的鏈接
將其當作管理多個表的一個Schema便可,如同mysql中的database
create_namespace 'test'
複製代碼
默認狀況下,是在系統的namespace下建表,在自建的namepace下建表使用namespace名:表名
status:查看HBase狀態
create:建表,指定表名與列族名,全部字段都要用' '包含起來,第一個爲表名,剩餘都是列族名
hbase(main):001:0> create 'test', 'cf'
複製代碼
hbase(main):002:0> list 'test'
複製代碼
disable 'test'
drop 'test'
複製代碼
exists 'test'
複製代碼
put 'test', 'row1', 'cf:a', 'yasuo'
put 'test', 'row2', 'cf:b', 'lsl'
複製代碼
重複put列會直接覆蓋
get 'test', 'row'
複製代碼
scan 'test'
複製代碼
在設計rowkey時,有大量連續編號的rowkey。這就會致使大量rowkey相近的記錄集中在個別region裏,也就是集中在一臺或幾臺regionServer當中。當client檢索記錄時,對個別region訪問過多,這時此region所在的主機過載,就致使熱點問題的出現。 也就是說大量訪問集羣中的某一臺PC致使該PC過載,致使負載不均衡
解決方法:
加鹽
在rowkey的前面分配隨機數,當給rowkey隨機前綴後,它就能分佈到不一樣的region中,這裏的前綴應該和你想要數據分散的不一樣的region的數量有關。
引用例子: www.shiyanlou.com/courses/37/…
咱們去電信公司打印電話詳單也就是通話記錄。對於通話記錄來講,每一個人每個月可能都有不少通話記錄,而使用電信的用戶也是億計。這種信息,咱們就能存入hbase當中。
對於通話記錄,咱們有什麼信息須要保存呢?首先,確定應該有主叫和被叫,而後有主叫被叫之間的通話時長,以及通話時間。除此以外,還應該有主叫的位置信息,和被叫的位置信息。
由此,咱們的通話記錄表須要記錄的信息就出來了:主叫、被叫、時長、時間、主叫位置、被叫位置。
咱們該如何來設計一張hbase表呢?
首先,hbase表是依靠rowkey來定位的,咱們應該將盡量多的將查詢的信息編入rowkey當中。hbase的元數據表mate表就給咱們了一個很好的示例。它包括了namespace,表名,startKey,時間戳,計算出來的碼(用於分散數據)。
因此,當咱們設計通話記錄的rowkey時,須要將能惟一肯定該條記錄的數據編入rowkey當中。便是須要將主叫、被叫、時間編入。
以下所示:
17765657979 18688887777 201806121502 #主叫,被叫,時間 複製代碼
可是咱們可否將咱們設計的rowkey真正應用呢?
固然是能夠的,可是熱點問題便會隨之而來。
例如你的電話是以177開頭,電信的hbase集羣有500臺,你的數據就只可能被存入一臺或者兩臺機器的region當中,當你須要打印本身的通話記錄時,就只有一臺機器爲你服務。而如果你的數據均勻分散到500機器中,就是整個集羣爲你服務。二者之間效率速度差了不止一個數量級。
注意:因爲咱們的regionServer就只有一臺,沒有集羣環境,因此咱們只介紹方法和理論操做,不提供實際結果 複製代碼
由於咱們設定整個hbase集羣有500臺,因此咱們隨機在0-499之間中隨機數字,添加到rowkey首部。
以下所示:
12 17765657979 18688887777 201806121502 #隨機數,主叫,被叫,時間 複製代碼
在插入數據時,判斷首部隨機數字,選擇對應的region存入,因爲rowkey首部數字隨機,因此數據也將隨機分佈到不一樣的regionServer中。這樣就能很好的避免熱點問題了。`
對於大量重複數據須要統計的時候,利用hash的性質進行一次預處理,將hashcode相同的數據交給同一臺PC,這樣PC能進行快速計算同時避免進一步合併,即不是將數據均分,而是依據已有數據的性質作一次運算,根據結果分配數據以達到算力分配最優。
引用例子: www.shiyanlou.com/courses/37/…
例如,咱們有一個10TB的大文件存在分佈式文件系統上,存的是100億行字符串,而且字符串無序排列,如今咱們要統計該文件中重複的字符串。
假設,咱們能夠調用100臺機器來計算該文件。
那麼,如今咱們須要怎樣經過哈希函數來統計重複字符串呢。
首先,咱們須要將這一百臺機器分別從0-99標好號,而後咱們在分佈式文件系統中一行行讀取文件(多臺機器並行讀取),經過哈希函數計算hashcode,將計算出的hashcode模以100,根據模出來的值,將該行存入對應的機器中。
根據哈希函數的性質,咱們很容易看出,相同的字符串會存入相同的機器中。
而後咱們就能並行100臺機器,各自分別計算相應的數據,大大加加快統計的速度。
注意:這10TB文件並非均分紅100GB,分給100臺機器,而是這10TB文件中不一樣字符串的種類,均分到100臺機器中。若是還嫌單個機器處理的數據過大,能夠按照一樣的方法,在一臺機器中並行多個進程,處理數據。 複製代碼
布隆過濾器是一種利用hash算法節約空間的存儲方式,最基本的以每個位(bit)爲基本單位,bit是信號量,有0,1兩種狀態,所以能夠根據須要過濾某些數據(如IP黑名單),直接存ip過於耗費內存,所以不存ip,而是根據hash進行映射得到hashcode,根據hashcode對信號量的位置取模變換爲0或1,即以時間換空間。但由於基於hash,可能兩個不一樣的數據獲得同一個hashcode,所以可能會出現數據誤判狀況。
引用例子: www.shiyanlou.com/courses/37/…
首先,咱們應當知道,hash是內存中使用的經典數據結構。
當咱們須要判讀一個元素是否在一個集合當中時,咱們能夠用哈希表來判斷。在集合較小的狀況下,hash是可行並且高效的。
然而數據量以PT計的大數據場景中,不少時候,hash便力有未逮。這是由於在海量數據下hash要佔據鉅額內存空間,這遠遠超過咱們可以提供的內存大小。
例如在黑名單過濾當中,咱們有100億的網站黑名單url須要過濾,假設一個url是64bytes。若是咱們用hash表來作,那麼咱們至少須要6400億字節即640G的內存空間(實際所需空間還遠大於此),空間消耗巨大,必需要多個服務器來同時分攤內存。
然而咱們是否能用更加精簡的結構來作這件事呢?布隆過濾器就是這樣一個高度節省空間的結構,而且其時間也遠超通常算法,可是布隆過濾器存在必定的失誤率,例如在網頁URL黑名單過濾中,布隆過濾器毫不會將黑名單中網頁查錯,可是有可能將正常的網頁URL斷定爲黑名單當中的,它的失誤能夠說是寧肯錯殺,不可放過。不過布隆過濾器的失誤率能夠調節,下面咱們會詳細介紹。
布隆過濾器實際就是一種集合。假設咱們有一個數組,它的長度爲L,從0-(L-1)位置上,存儲的不是一個字符串或者整數,而是一個bit,這意味它的取值只能爲0或1.
例如咱們實現以下的一個數組:
int[] array = new int[1000]; 複製代碼
該數組中有1000個int類型的元素,而每個int由有4個byte組成,一個byte又由8個bit組成,因此一個int就由32個bit所組成。
因此咱們申請含1000整數類型的數組,它就包含32000個bit位。
可是咱們若是想將第20001個bit位描黑,將其改成1,咱們須要怎樣作呢。
首先咱們的須要定位,這第20001個bit位於哪一個整數,接着咱們須要定位該bit位於該整數的第幾個bit位。
int index = 20001; int intIndex = index/32; int bitIndex = index%32; 複製代碼
而後咱們就將其描黑:
array[intIndex] =(array[intIndex] | (1 << bitIndex)); 複製代碼
這段代碼可能有些同窗不能理解,下面咱們詳細解釋一下。 咱們的數組是0-999的整型數組,可是每一個位置上實際保存的是32個bit,咱們的intIndex就是表明第幾個整數,定位到625,而bitIndex定位到第625個整數中的第幾個bit,爲1。
因此,咱們須要描黑的位置是第625號元素的第1個bit。
而
(1 << bitIndex)
表明將1左移到1位置,便是表明只有1位置爲1,而其餘位置爲零。就至關於得到了這樣一串二進制數:00000000 00000000 00000000 00000001 複製代碼
而後咱們將這樣的二進制數與array[indIndex]進行邏輯或運算,就能將第625號元素的第一個bit描黑變1,最後咱們將描黑後的元素賦值給array[indIndex],完成運算。
這裏咱們採用的是int類型的數組,若是咱們想更加節省空間,咱們就能建立long類型的數組,這樣申請1000個數組空間,咱們就能獲得64000個bit。
而後咱們還能進一步擴展,將數組作成矩陣:
long[][] map = new long[1000][1000]; 複製代碼
有了這些基礎之後,咱們如何設計黑名單問題呢?
假設咱們已經有了一個擁有m個bit的數組,而後咱們將一個URL經過哈希函數計算出hashcode,而後
hashcode%m
將對應位置描黑。然而這還不是真正的布隆過濾器,真正的布隆過濾器是經過多個哈希函數對一個URL進行計算,獲得hashcode,而後在對不一樣位置的bit進行描黑。
注意:布隆過濾器採用的多個哈希函數必須是相互獨立的,前面咱們已經介紹瞭如何經過一個哈希函數構造多個獨立哈希函數的方法。 複製代碼
當咱們將一個URL經過n個哈希函數,獲得hashcode,模以m,再將對應位置描黑以後。咱們能夠說該URL已經進入咱們的布隆過濾器當中了。
接下來,咱們將黑名單中全部URL都經過哈希函數,進入布隆過濾器中(布隆過濾器並不真正存儲實際的URL)。
對於一個新的URL,咱們要查詢其是否在黑名單中,咱們便經過一樣的n個哈希函數,計算出n個位置,而後咱們查詢這n個位置是否都被描黑,若是都被描黑,咱們就說該URL在黑名單當中。若是n個位置但凡是有一個不爲黑,咱們就說該URL不在該黑名單中。
注意:咱們的數組不該該太小,否則極可能數組中的大多數位置都被描黑,這很容易將正常的URL斷定爲不合法的,這也是布隆過濾器的失誤率來源。 複製代碼
參考:
實驗樓
知乎
<<Hadoop權威指南 第四版>>