1. 程式人生 > >12.大資料學習之旅——HBase第二天

12.大資料學習之旅——HBase第二天

HBASE完全分散式安裝


實現步驟

  1. 準備三臺虛擬機器,01作為主節點,02、03作為從節點。(把每臺虛擬機器防火牆都關掉,配
    置免密碼登入,配置每臺的主機名和hosts檔案。)
  2. 01節點上安裝和配置:Hadoop+Hbase+JDK+Zookeeper
  3. 02、03節點上安裝和配置:Hbase+JDK+Zookeeper
  4. 修改conf/hbase-env.sh

配置示例:

#修改JAVA_HOME
export JAVA_HOME=xxxx
#修改Zookeeper和Hbase的協調模式,hbase預設使用自帶的zookeeper,如果需要使用外
部zookeeper,需要先關閉。 export HBASE_MANAGES_ZK=false
  1. 修改hbase-site.xml,配置開啟完全分散式模式

配置示例:

<property>
</property>
<name>hbase.cluster.distributed</name>
<value>true</value>
<property>
</property>
#配置Zookeeper的連線地址與埠號
<name>hbase.zookeeper.quorum</
name
>
<value>hadoop01:2181,hadoop02:2181,hadoop03:2181</value> <property> </property>
  1. 配置region伺服器,修改conf/regionservers檔案,每個主機名獨佔一行,hbase啟動或關閉
    時會按照該配置順序啟動或關閉主機中的hbase

配置示例:

hadoop01
hadoop02
hadoop03
  1. 將01節點配置好的hbase通過遠端複製拷貝到02,03節點上
  2. 啟動01,02,03的Zookeeper服務
  3. 啟動01節點的Hadoop
  4. 啟動01節點的Hbase,進入到hbase安裝目錄下的bin目錄
    執行:sh start-hbase.sh
  5. 檢視各節點的java程序是否正確
  6. 通過瀏覽器訪問http://xxxxx:60010來訪問web介面,通過web見面管理hbase
  7. 關閉Hmaster,進入到hbase安裝目錄下的bin目錄
    執行:stop-hbase.sh
  8. 關閉regionserver,進入到hbase安裝目錄下的bin目錄
    執行:sh hbase-daemon.sh stop regionserver

注:HBASE配置檔案說明
hbase-env.sh配置HBase啟動時需要的相關環境變數
hbase-site.xml配置HBase基本配置資訊
HBASE啟動時預設使用hbase-default.xml中的配置,如果需要可以修改hbase-site.xml文
件,此檔案中的配置將會覆蓋hbase-default.xml中的配置
修改配置後要重啟hbase才會起作用

HBASE API

實現步驟:

  1. 匯入開發包將hbase安裝包中lib下包匯入java專案
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.HBaseAdmin;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.junit.Test;

public class TestDemo {
	@Test
	public void createTable() throws Exception{
		//--獲取HBase的環境引數物件
		Configuration conf=HBaseConfiguration.create();
		conf.set("hbase.zookeeper.quorum",
				"hadoop01:2181,hadoop02:2181,hadoop03:2181");
		HBaseAdmin admin=new HBaseAdmin(conf);
		
		HTableDescriptor table=
				 new HTableDescriptor(TableName.valueOf("tb2"));
		
		HColumnDescriptor cf1=new HColumnDescriptor("cf1".getBytes());
		HColumnDescriptor cf2=new HColumnDescriptor("cf2".getBytes());
		//--將指定的列族和表繫結
		table.addFamily(cf1);
		table.addFamily(cf2);
		
		//--執行建表
		admin.createTable(table);
		admin.close();
	}
	
	@Test
	public void insertData() throws Exception{
		Configuration conf=HBaseConfiguration.create();
		//--zk的叢集地址可以只寫一個,通過一個地址找到整個的zk叢集
		//--但風險是如果這臺zk宕機,則無法連線
		conf.set("hbase.zookeeper.quorum","hadoop01:2181");
		
		//--建立表物件,並指定操作的表名
		HTable table=new HTable(conf,"tb1");
		//--建立行物件,並指定行鍵
		Put put=new Put("row1".getBytes());
		//--①參:列族名 ②參:列名  ③參:列值
		//--對應的指令:put 'tb1','row1','cf1:name','tom'
		put.add("cf1".getBytes(),"name".getBytes(),"tom".getBytes());
		put.add("cf1".getBytes(),"age".getBytes(),"30".getBytes());
		put.add("cf2".getBytes(),"city".getBytes(),"bj".getBytes());
		
		//--執行插入
		table.put(put);
		table.close();
	}
	@Test
	public void batchInsert() throws Exception{
		Configuration conf=new Configuration();
		conf.set("hbase.zookeeper.quorum","hadoop01:2181");
		
		HTable table=new HTable(conf,"tb1");
		List<Put> puts=new ArrayList<>();
		for(int i=0;i<100;i++){
			Put put=new Put(("row"+i).getBytes());
			put.add("cf1".getBytes(),"num".getBytes(),(i+"").getBytes());
			//--插入一行資料到HBase表
			//--注意這種寫法是一行一行插入,效能不高
			//table.put(put);
			
			puts.add(put);
		}
		//--執行批量插入,本例中是100行資料一起插入
		//--在生產環境下,都是批量插入
		table.put(puts);
	}
	@Test
	public void getData() throws Exception{
		Configuration conf=new Configuration();
		conf.set("hbase.zookeeper.quorum","hadoop01:2181");
		
		HTable table=new HTable(conf, "tb1");
		//--通過行鍵去指定表讀取資料
		Get get=new Get("row1".getBytes());
		//--執行查詢,把結果集封裝到Result物件
		Result result=table.get(get);
		//--根據指定的列族名和列名獲取值
		byte[] name=result.getValue("cf1".getBytes(),"name".getBytes());
		byte[] age=result.getValue("cf1".getBytes(),"age".getBytes());
		byte[] city=result.getValue("cf2".getBytes(),"city".getBytes());
		System.out.println(new String(name)+":"+new String(age)+":"+new String(city));
		table.close();
	}
	
	@Test
	public void scanTable() throws Exception{
		Configuration conf=new Configuration();
		conf.set("hbase.zookeeper.quorum","hadoop01:2181");
		
		HTable table=new HTable(conf, "tb1");
		//--建立掃描物件,可以通過此物件掃描整表資料
		Scan scan=new Scan();
		//--將整表資料封裝到結果集(包含多行資料)
		//--指定掃描的起始行鍵和終止行鍵,(不包含終止的行鍵)
		//--需要記住
		scan.setStartRow("row10".getBytes());
		scan.setStopRow("row19".getBytes());
		
		ResultScanner rs=table.getScanner(scan);
		//--獲取行資料的迭代器
		Iterator<Result> it=rs.iterator();
		while(it.hasNext()){
			//--每迭代一次,就獲取一行資料
			Result result=it.next();
			byte[] num=result.getValue("cf1".getBytes(),"num".getBytes());
			System.out.println(new String(num));
		}
		table.close();
		
	}
	@Test
	public void  delete() throws Exception{
		Configuration conf=new Configuration();
		conf.set("hbase.zookeeper.quorum","hadoop01:2181");
		HTable table=new HTable(conf, "tb1");
		
		//--根據指定行鍵刪除
		Delete delete=new Delete("row0".getBytes());
		table.delete(delete);
		//--還可以通過List<Delete> 來實現批量刪除
		//--在生產環境下,儘可能實現批量操作
		table.close();
	}
	
	@Test
	public void dropTable() throws Exception{
		Configuration conf=new Configuration();
		conf.set("hbase.zookeeper.quorum","hadoop01:2181");
		HBaseAdmin admin=new HBaseAdmin(conf);
		//--先禁用tb2表
		admin.disableTable("tb2");
		//--再刪除表
		admin.deleteTable("tb2");
		
		admin.close();
	}
}


HBASE過濾器 API

import java.io.IOException;
import java.util.Iterator;


import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.filter.BinaryComparator;
import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.filter.PrefixFilter;
import org.apache.hadoop.hbase.filter.RegexStringComparator;
import org.apache.hadoop.hbase.filter.RowFilter;
import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
import org.junit.Test;

public class FilterDemo {

	@Test
	public void rowFilter() throws Exception{
		Configuration conf=new Configuration();
		conf.set("hbase.zookeeper.quorum","hadoop01:2181");
		
		HTable table=new HTable(conf, "tb1");
		Scan scan=new Scan();
		//--①參:比較規則  有:等於;不等於;大於;大於等於;小於;小於等於
		//--下例匹配的是行鍵中含3的行鍵
		//Filter filter=new RowFilter(CompareOp.EQUAL,
		//		     new RegexStringComparator("^.*3.*$"));
		
		//Filter filter=new RowFilter(CompareOp.LESS_OR_EQUAL,
		//		new BinaryComparator("row30".getBytes()));
		
		//--行鍵字首過濾器
		//Filter filter=new PrefixFilter("row3".getBytes());
		
		//--列值過濾器
		Filter filter=new SingleColumnValueFilter(
				"cf1".getBytes(),"name".getBytes(), 
				CompareOp.EQUAL, "tom".getBytes());
		
		//--繫結過濾器,使其生效
		scan.setFilter(filter);
		//--執行掃描查詢並結合過濾器查詢
		ResultScanner rs=table.getScanner(scan);
		Iterator<Result> it=rs.iterator();
		while(it.hasNext()){
			Result result=it.next();
			byte[] num=result.getValue("cf1".getBytes(),"num".getBytes());
			System.out.println(new String(num));
		}
		table.close();
	}
}

HBase物理儲存原理

Hbase裡的一個Table 在行的方向上分割為多個Hregion。
即HBase中一個表的資料會被劃分成很多的HRegion,HRegion可以動態擴充套件並且HBase保證
HRegion的負載均衡。HRegion實際上是行鍵排序後的按規則分割的連續的儲存空間。
在這裡插入圖片描述
在這裡插入圖片描述
總結:
一張Hbase表,可能有多個HRegion,每個HRegion達到一定大小(預設是10GB)
時,進行分裂。

拆分流程圖:

Hregion是按大小分割的,每個表一開始只有一個Hregion,隨著資料不斷插入表,Hregion不
斷增大,當增大到一個閥值的時候,Hregion就會等分兩個新的Hregion。當table中的行不斷
增多,就會有越來越多的Hregion。
按照現在主流硬體的配置,每個HRegion的大小可以是1~20GB。這個大小由
hbase.hregion.max.filesize指定,預設為10GB。HRegion的拆分和轉移是由
HBase(HMaster)自動完成的,使用者感知不到。

Hregion是 Hbase中分散式儲存和負載均衡的最小單元
在這裡插入圖片描述

HRegion雖然是分散式儲存的最小單元,但並不是儲存的最小單元。
事實上,HRegion由一個或者多個HS tore組成,每個Hstore儲存一個columns family。
每個HStore又由一個memStore和0至多個StoreFile組成。如圖:
StoreFile以HFile格式儲存在HDFS上。
總結:HRegion是分散式的儲存最小單位,StoreFile(Hfile)是儲存最小單位。
在這裡插入圖片描述

Hbase系統架構

Hbase與Hadoop架構圖

在這裡插入圖片描述

HBase架構組成

HBase採用Master/Slave架構搭建叢集,它隸屬於Hadoop生態系統,由以下型別節點組
成:

  1. HMaster節點
  2. HRegionServer節點
  3. ZooKeeper叢集
  4. Hbase的資料儲存於HDFS中,因而涉及到HDFS的NameNode、DataNode
    等。RegionServer和DataNode一般會放在相同的Server上實現資料的本地化(避免或減少
    資料在網路中的傳輸,節省頻寬)。

HMaster節點
5. 管理HRegionServer,實現其負載均衡。
6. 管理和分配HRegion,比如在HRegion split時分配新的HRegion;在HRegionServer退出
時遷移其內的HRegion到其他HRegionServer上。
7. 實現DDL操作(Data Definition Language,namespace和table的增刪改,column
familiy的增刪改等)。
8. 管理namespace和table的元資料(實際儲存在HDFS上)。
9. 許可權控制(ACL).

HRegionServer節點
10. 存放和管理本地HRegion。
11. 讀寫HDFS,管理Table中的資料。
12. Client直接通過HRegionServer讀寫資料(從HMaster中獲取元資料,找到RowKey所在的
HRegion/HRegionServer後)。

ZooKeeper叢集
13. 存放整個 HBase叢集的元資料以及叢集的狀態資訊。以及RS伺服器的執行狀態
14. 實現HMaster主備節點的failover。
下圖更全面展示了Hbase於Hadoop的體系圖:
在這裡插入圖片描述
HBase Client通過RPC方式和HMaster、HRegionServer通訊;一個HRegionServer可以存
放1000個HRegion(1000個數字的由來是來自於Google的Bigtable論文);底層Table資料
儲存於HDFS中,而HRegion所處理的資料儘量和資料所在的DataNode在一起,實現資料的
本地化;資料本地化並不是總能實現,比如在HRegion移動(如因Split)時,需要等下一次
Compact才能繼續回到本地化。

HBASE架構原理詳解

整體架構圖

在這裡插入圖片描述
Hregion
在這裡插入圖片描述
HBase使用RowKey將表水平切割成多個HRegion,從HMaster的角度,每個HRegion都紀錄了它
的StartKey和EndKey,由於RowKey是排序的,因而Client可以通過HMaster快速的定位每個
RowKey在哪個HRegion中。HRegion由HMaster分配到相應的HRegionServer中,然後由
HRegionServer負責HRegion的啟動和管理,和Client的通訊,負責資料的讀(使用HDFS)。每個
HRegionServer可以同時管理1000個左右的HRegion(這個數字怎麼來的?超過1000個會引起性
能問題?這個1000的數字是從BigTable的論文中來的(5 Implementation節):Each tablet
server manages a set of tablets(typically we have somewhere between ten to a thousand tablets per
tablet server))。

HMaster
在這裡插入圖片描述

HMaster沒有單點故障問題,可以啟動多個HMaster,通過ZooKeeper的Master Election機制保證
同時只有一個HMaster處於Active狀態,其他的HMaster則處於熱備份狀態。一般情況下會啟動
兩個HMaster,非Active的HMaster會定期的和Active HMaster通訊以獲取其最新狀態,從而保證
它是實時更新的,因而如果啟動了多個HMaster反而增加了Active HMaster的負擔。前文已經介
紹過了HMaster的主要用於HRegion的分配和管理,DDL(Data Definition Language,既Table的新
建、刪除、修改等)的實現等,既它主要有兩方面的職責:

  1. 協調HRegionServer
    1. 啟動時HRegion的分配,以及負載均衡和修復時HRegion的重新分配。
    2. 監控叢集中所有HRegionServer的狀態(通過Heartbeat和監聽ZooKeeper中的狀態)。
  2. Admin職能
    1. 建立、刪除、修改Table的定義。

ZooKeeper:協調者
在這裡插入圖片描述
ZooKeeper(根據Google的《The Chubby lock service for loosely coupled distributed
System》)為HBase叢集提供協調服務,它管理著HMaster和HRegionServer的狀態
(available/alive等),並且會在它們宕機時通知給HMaster,從而HMaster可以實現HMaster之間
的failover(失敗恢復),或對宕機的HRegionServer中的HRegion集合的修復(將它們分配給其他
的HRegionServer)。ZooKeeper叢集本身使用一致性協議(PAXOS協議,PAXOS演算法的思想是:
在分散式的環境下,如何就某個決議達成一致性的演算法,PAXOS演算法的缺點是存在活鎖問
題,ZK是基於PAXOS實現的Fast PAXOS)保證每個節點狀態的一致性。

How The Components Work Together

ZooKeeper協調叢集所有節點的共享資訊,在HMaster和HRegionServer連線到ZooKeeper後建立
Ephemeral( 臨時)節點,並使用Heartbeat機制維持這個節點的存活狀態,如果某個Ephemeral
節點實效,則HMaster會收到通知,並做相應的處理。
在這裡插入圖片描述
另外,HMaster通過監聽ZooKeeper中的Ephemeral節點(預設:/hbase/rs/*)來監控HRegionServer
的加入和宕機。在第一個HMaster連線到ZooKeeper時會建立Ephemeral節點(預設:/hbasae/master)來表示Active的HMaster,其後加進來的HMaster則監聽該Ephemeral節點,
如果當前Active的HMaster宕機,則該節點消失,因而其他HMaster得到通知,而將自身轉換成
Active的HMaster,在變為Active的HMaster之前,它會建立在/hbase/back-masters/下建立自己
的Ephemeral節點。

HBase的第一次讀寫
在HBase 0.96以前,HBase有兩個特殊的Table:-ROOT-和.META.(如BigTable中的設計),其中-
ROOT- Table的位置儲存在ZooKeeper,它儲存了.META. Table的RegionInfo資訊,並且它只能存
在一個HRegion,而.META. Table則儲存了使用者Table的RegionInfo資訊,它可以被切分成多個
HRegion,因而對第一次訪問使用者Table時,首先從ZooKeeper中讀取-ROOT-Table所在
HRegionServer;然後從該HRegionServer中根據請求的TableName,RowKey讀取.META. Table所
在HRegionServer;最後從該HRegionServer中讀取.META. Table的內容而獲取此次請求需要訪問
的HRegion所在的位置,然後訪問該HRegionSever獲取請求的資料,這需要三次請求才能找到
使用者Table所在的位置,然後第四次請求開始獲取真正的資料。當然為了提升效能,客戶端會
快取-ROOT- Table位置以及-ROOT-/.META. Table的內容。如下圖所示:
在這裡插入圖片描述

可是即使客戶端有快取,在初始階段需要三次請求才能直到使用者Table真正所在的位置也是性
能低下的,而且真的有必要支援那麼多的HRegion嗎?或許對Google這樣的公司來說是需要
的,但是對一般的叢集來說好像並沒有這個必要。在BigTable的論文中說,每行METADATA存
儲1KB左右資料,中等大小的Tablet(HRegion)在128MB左右,3層位置的Schema設計可以支援2^
34個Tablet(HRegion)。即使去掉-ROOT-Table,也還可以支援2^17(131072)個HRegion, 如果每
個HRegion還是128MB,那就是16TB,這個貌似不夠大,但是現在的HRegion的最大大小都會設
置的比較大,比如我們設定了2GB,此時支援的大小則變成了4PB,對一般的叢集來說已經夠

了,因而在HBase 0.96以後去掉了-ROOT-Table,只剩下這個特殊的目錄表叫做Meta
Table(hbase:meta),它儲存了叢集中所有使用者HRegion的位置資訊,而ZooKeeper的節點中
(/hbase/meta-region-server)儲存的則直接是這個Meta Table的位置,並且這個Meta Table如以前
的-ROOT- Table一樣是不可split的。這樣,客戶端在第一次訪問使用者Table的流程就變成了:

  1. 從ZooKeeper(/hbase/meta-region-server)中獲取hbase:meta的位置(HRegionServer的位
    置),快取該位置資訊。
  2. 從HRegionServer中查詢使用者Table對應請求的RowKey所在的HRegionServer,快取該位置信
    息。
  3. 從查詢到HRegionServer中讀取Row。
    從這個過程中,我們發現客戶會快取這些位置資訊,然而第二步它只是快取當前RowKey對應
    的HRegion的位置,因而如果下一個要查的RowKey不在同一個HRegion中,則需要繼續查詢
    hbase:meta所在的HRegion,然而隨著時間的推