目錄
1 Hbase概述
1.1 什麼是Hbase
HBase的原型是Google的BigTable論文,受到了該論文思想的啓發,目前作爲Hadoop的子項目來開發維護。
-- 2006年Google發表BigTable白皮書
-- 2006年開始開發HBase
-- 2008年北京成功開奧運會,程序員默默地將HBase弄成了Hadoop的子項目
-- 2010年HBase成爲Apache頂級項目
-- 現在很多公司二次開發出了很多發行版本,你也開始使用了。
->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作爲對應。
Zk:ha使用過自動故障轉移;kafka存儲元數據(集羣的元數據+消費者的元數據)
1.2 Hbase特點
1)海量存儲
Hbase適合存儲PB級別的海量數據,在PB級別的數據以及採用廉價PC存儲的情況下,能在幾十到百毫秒內返回數據。這與Hbase的極易擴展性息息相關。正是因爲Hbase良好的擴展性,才爲海量數據的存儲提供了便利。
2)列式存儲
->這裏的列式存儲其實說的是列族存儲,Hbase是根據列族來存儲數據的。列族下面可以有非常多的列,列族在創建表的時候就必須指定。
3)極易擴展
Hbase的擴展性主要體現在兩個方面,一個是基於上層處理能力(RegionServer)的擴展,一個是基於存儲的擴展(HDFS)。
->通過橫向添加RegionSever的機器,進行水平擴展,提升Hbase上層的處理能力,提升Hbsae服務更多Region的能力。
備註:RegionServer的作用是管理region、承接業務的訪問,這個後面會詳細的介紹,通過橫向添加Datanode的機器,進行存儲層擴容,提升Hbase的數據存儲能力和提升後端存儲的讀寫能力。
4)高併發
由於目前大部分使用Hbase的架構,都是採用的廉價PC,因此單個IO的延遲其實並不小,一般在幾十到上百ms之間。這裏說的高併發,主要是在併發的情況下,Hbase的單個IO延遲下降並不多。能獲得高併發、低延遲的服務。
5)稀疏
稀疏主要是針對Hbase列的靈活性,在列族中,你可以指定任意多的列,在列數據爲空的情況下,是不會佔用存儲空間的。
1.3 HBase架構
從圖中可以看出Hbase是由Client、Zookeeper、Master、HRegionServer、HDFS等幾個組件組成,下面來介紹一下幾個組件的相關功能:
1)Client
Client包含了訪問Hbase的接口,另外Client還維護了對應的cache來加速Hbase的訪問,比如cache的.META.元數據的信息。
2)Zookeeper
HBase通過Zookeeper來做master的高可用、RegionServer的監控、元數據的入口以及集羣配置的維護等工作。具體工作如下:
通過Zoopkeeper來保證集羣中只有1個master在運行,如果master異常,會通過競爭機制產生新的master提供服務
通過Zoopkeeper來監控RegionServer的狀態,當RegionSevrer有異常的時候,通過回調的形式通知Master RegionServer上下線的信息(就是使用zk完成的服務器上下線機制)
通過Zoopkeeper存儲元數據的統一入口地址
3)Hmaster
master節點的主要職責如下:
(1)爲RegionServer分配Region
(2)維護整個集羣的負載均衡
(3)維護集羣的元數據信息
(4)發現失效的Region,並將失效的Region分配到正常的RegionServer上
(5)當RegionSever失效的時候,協調對應Hlog的拆分
4)HregionServer
HregionServer直接對接用戶的讀寫請求,是真正的“幹活”的節點。它的功能概括如下:
(1)管理master爲其分配的Region
(2)處理來自客戶端的讀寫請求
(3)負責和底層HDFS的交互,存儲數據到HDFS
(4)負責Region變大以後的拆分
(5)負責Storefile的合併工作
5)HDFS
HDFS爲Hbase提供最終的底層數據存儲服務,同時爲HBase提供高可用(Hlog存在HDFS)的支持,具體功能概括如下:
(1)提供元數據和表數據的底層分佈式存儲服務
(2)數據多副本,保證高可靠和高可用性
1.4 HBase中的角色
1.4.1 HMaster
功能
- 監控RegionServer
- 處理RegionServer故障轉移
- 處理元數據的變更
- 處理region的分配或轉移
- 在空閒時間進行數據的負載均衡
- 通過Zookeeper發佈元數據的位置給客戶端
1.4.2 RegionServer
功能
- 負責存儲HBase的實際數據
- 處理分配給它的Region
- 刷新緩存到HDFS
- 維護Hlog(每個regionserver都有一個Hlog)
- 執行壓縮
- 負責處理Region分片(自動分片)
1.4.3 其他組件
1.Write-Ahead logs(WAL,HLOG&edits日誌)
HBase的修改記錄,當對HBase讀寫數據的時候,數據不是直接寫進磁盤,它會在內存中保留一段時間(時間以及數據量閾值可以設定)。但把數據保存在內存中可能有更高的概率引起數據丟失,爲了解決這個問題,數據會先寫在一個叫做Write-Ahead logfile的文件中,然後再寫入內存中。所以在系統出現故障的時候,數據可以通過這個日誌文件重建。
2.Region
Hbase表的分片,HBase表會根據RowKey值被切分成不同的region存儲在RegionServer中,在一個RegionServer中可以有多個不同的region。
3.Store
HFile存儲在Store中,一個Store對應HBase表中的一個列族。
4.MemStore
顧名思義,就是內存存儲,位於內存中,用來保存當前的數據操作,所以當數據保存在WAL中之後,RegsionServer會在內存中存儲鍵值對。
5.HFile
這是在磁盤上保存原始數據的實際的物理文件,是實際的存儲文件。StoreFile是以HFile的形式存儲在HDFS的。
2 Hbase安裝
2.1 環境準備
(1)zookeeper正常部署:保證zookeeper的正常部署,並啓動zookeeper集羣
(2)Hadoop正常部署:保證Hadoop集羣的正常部署,並啓動hdfs和yarn
2.2 下載安裝
1)下載:
官方網站:http://hbase.apache.org/downloads.html
網盤鏈接:請點這裏 提取碼:cgf7
2)上傳並安裝
(1)將現在的Hbase安裝包上傳到hadoop101節點/opt/modoule/目錄下,配置完成後再分發到其他節點
(2)解壓Hbase到指定目錄:
[root@hadoop101 software]# tar -zxvf hadoop-2.7.2.tar.gz -C /opt/module/
(3)修改conf目錄下的配置文件:
1)修改hbase-env.sh,添加以下內容:
export JAVA_HOME=/opt/module/jdk1.8.0_144
export HBASE_MANAGES_ZK=false
2)hbase-site.xml修改內容如下:
<configuration>
<property>
<name>hbase.rootdir</name>
<value>hdfs://hadoop101:9000/hbase</value>
</property>
<property>
<name>hbase.cluster.distributed</name>
<value>true</value>
</property>
<!-- 0.98後的新變動,之前版本沒有port,默認端口爲60000 -->
<property>
<name>hbase.master.port</name>
<value>16000</value>
</property>
<property>
<name>hbase.zookeeper.quorum</name>
<value>hadoop101:2181,hadoop102:2181,hadoop103:2181</value>
</property>
<property>
<name>hbase.zookeeper.property.dataDir</name>
<value>/opt/module/zookeeper-3.4.10/zkData</value>
</property>
</configuration>
3)修改regionservers:
hadoop101
hadoop102
hadoop103
4)軟連接hadoop配置文件到hbase:
[root@hadoop101 module]$ ln -s /opt/module/hadoop-2.7.2/etc/hadoop/core-site.xml /opt/module/hbase-1.3.1/conf/core-site.xml
[root@hadoop101 module]$ ln -s /opt/module/hadoop-2.7.2/etc/hadoop/hdfs-site.xml /opt/module/hbase-1.3.1/conf/hdfs-site.xml
5)將配置好的Hbase分發到其他節點
2.3 Hbase服務的啓動
1)啓動方式1:
[root@hadoop101 hbase]$ bin/hbase-daemon.sh start master
[root@hadoop101 hbase]$ bin/hbase-daemon.sh start regionserver
可能出現的問題:如果集羣之間的節點時間不同步,會導致regionserver無法啓動,拋出ClockOutOfSyncException異常。
解決方案:
a、同步時間服務
b、屬性:hbase.master.maxclockskew設置更大的值
<property>
<name>hbase.master.maxclockskew</name>
<value>180000</value>
<description>Time difference of regionserver from master</description>
</property>
2)啓動方式2:
[root@hadoop101 hbase]$ bin/start-hbase.sh
[root@hadoop101 hbase]$ bin/stop-hbase.sh //對應的停止服務
2.4 查看Hbase的WEBUI界面
啓動成功後,可以通過“host:port”的方式來訪問HBase管理頁面,例如:http://hadoop101:16010
3 Hbase Shell操作
3.1 基本操作
1.進入HBase客戶端命令行(任意節點均可)
[root@hadoop101 hbase]$ bin/hbase shell
2.查看幫助命令
hbase(main):001:0> help
3.查看當前數據庫中有哪些表
hbase(main):002:0> list
3.2 表的操作
1.創建表
hbase(main):002:0> create 'student','info' (單雙引號無所謂)
2.插入數據到表
hbase(main):003:0> put 'student','1001','info:sex','male'
hbase(main):004:0> put 'student','1001','info:age','18'
hbase(main):005:0> put 'student','1002','info:name','Janna'
hbase(main):006:0> put 'student','1002','info:sex','female'
hbase(main):007:0> put 'student','1002','info:age','20'
3.掃描查看錶數據
hbase(main):008:0> scan 'student'
hbase(main):009:0> scan 'student',{STARTROW => '1001', STOPROW => '1002'}
hbase(main):010:0> scan 'student',{STARTROW => '1001'}
4.查看錶結構
hbase(main):011:0> describe 'student'
5.更新指定字段的數據
hbase(main):012:0> put 'student','1001','info:name','Nick'
hbase(main):013:0> put 'student','1001','info:age','100'
6.查看“指定行”或“指定列族:列”的數據
hbase(main):014:0> get 'student','1001'
hbase(main):015:0> get 'student','1001','info:name'
7.統計表數據行數
hbase(main):021:0> count 'student'
8.刪除數據
刪除某rowkey的全部數據:
hbase(main):016:0> deleteall 'student','1001'
刪除某rowkey的某一列數據:
hbase(main):017:0> delete 'student','1002','info:sex'
9.清空表數據(表結構還在)
hbase(main):018:0> truncate 'student'
提示:清空表的操作順序爲先disable,然後再truncate。
10.刪除表
首先需要先讓該表爲disable狀態:
hbase(main):019:0> disable 'student'
然後才能drop這個表:
hbase(main):020:0> drop 'student'
提示:如果直接drop表,會報錯:ERROR: Table student is enabled. Disable it first.
11.變更表信息
將info列族中的數據存放3個版本:
hbase(main):022:0> alter 'student',{NAME=>'info',VERSIONS=>3}
hbase(main):022:0> get 'student','1001',{COLUMN=>'info:name',VERSIONS=>3}
4 Hbase數據結構
4.1 RowKey
與nosql數據庫們一樣,RowKey是用來檢索記錄的主鍵。訪問HBASE table中的行,只有三種方式:
- 通過單個RowKey訪問
- 通過RowKey的range
- 全表掃描
RowKey行鍵 (RowKey)可以是任意字符串(最大長度是64KB,實際應用中長度一般爲 10-100bytes),在HBASE內部,RowKey保存爲字節數組。存儲時,數據按照RowKey的字典序(byte order)排序存儲。設計RowKey時,要充分利用排序存儲這個特性,將經常一起讀取的行存儲放到一起。(位置相關性)
4.2 Column Family
列族:HBASE表中的每個列,都歸屬於某個列族。列族是表的schema的一部分(而列不是),必須在使用表之前定義。列名都以列族作爲前綴。例如 courses:history,courses:math都屬於courses 這個列族。
4.3 Time Stamp
HBASE 中通過rowkey和columns確定的爲一個存貯單元稱爲cell。cell中的數據是沒有類型的,全部是字節數組形式存貯。每個 cell都保存 着同一份數據的多個版本。版本通過時間戳來索引。時間戳的類型是 64位整型。時間戳可以由HBASE(在數據寫入時自動 )賦值,此時時間戳是精確到毫秒 的當前系統時間。時間戳也可以由客戶顯式賦值。如果應用程序要避免數據版 本衝突,就必須自己生成具有唯一性的時間戳。每個 cell中,不同版本的數據按照時間倒序排序,即最新的數據排在最前面。
爲了避免數據存在過多版本造成的的管理 (包括存貯和索引)負擔,HBASE提供 了兩種數據版本回收方式。一是保存數據的最後n個版本,二是保存最近一段 時間內的版本(比如最近七天)。用戶可以針對每個列族進行設置。
4.4 命名空間
命名空間的結構:
1) Table:表,所有的表都是命名空間的成員,即表必屬於某個命名空間,如果沒有指定,則在default默認的命名空間中。
2) RegionServer group:一個命名空間包含了默認的RegionServer Group。
3) Permission:權限,命名空間能夠讓我們來定義訪問控制列表ACL(Access Control List)。例如,創建表,讀取表,刪除,更新等等操作。
4) Quota:限額,可以強制一個命名空間可包含的region的數量。
5 Hbase原理
可以參考我的另一篇文章:https://blog.csdn.net/jiezou12138/article/details/88677006
6 HbaseAPI操作
首先添加下面的依賴:
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>1.3.1</version>
</dependency>
6.1 HbaseAPI
package com.bigdata;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class HbaseTest {
private Configuration conf;
@Before
public void init() {
conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum", "192.168.1.102");
conf.set("hbase.zookeeper.property.clientPort", "2181");
}
/**
* use_case:判斷Hbase表是否存在
* @throws IOException
*/
@Test
public void testExists() throws IOException {
Connection conn = ConnectionFactory.createConnection(conf);
HBaseAdmin hBaseAdmin = (HBaseAdmin) conn.getAdmin();
boolean exists = hBaseAdmin.tableExists(TableName.valueOf("student"));
System.out.println("=========================>>>" + exists);
hBaseAdmin.close();
conn.close();
}
/**
* use_case:創建Hbase表
*/
@Test
public void testCreateTable() throws IOException {
//1.獲取連接對象
Connection connection = ConnectionFactory.createConnection(conf);
//2.通過連接獲取Hbase的客戶端對象
HBaseAdmin hBaseAdmin = (HBaseAdmin) connection.getAdmin();
//3.創建表描述器
HTableDescriptor student = new HTableDescriptor(TableName.valueOf("student1"));
//4.設置列組描述器
student.addFamily(new HColumnDescriptor("info"));
//5.執行創建操作
hBaseAdmin.createTable(student);
System.out.println("====================>>>student表創建成功了");
hBaseAdmin.close();
connection.close();
}
/**
* use_case:刪除hbase表
*/
@Test
public void testDeleteTable() throws IOException {
//1.獲取連接對象
Connection conn = ConnectionFactory.createConnection(conf);
//2.通過連接對象獲取Hbase的客戶端對象
HBaseAdmin admin = (HBaseAdmin) conn.getAdmin();
//3.先設置表位不可用
admin.disableTable("student1");
//4.刪除表
admin.deleteTable("student1");
admin.close();
conn.close();
}
/**
* use_case:向表中插入一條數據
*/
@Test
public void testInsert() throws IOException {
Connection connection = ConnectionFactory.createConnection(conf);
HBaseAdmin admin = (HBaseAdmin) connection.getAdmin();
//獲取一個表對象
Table student = connection.getTable(TableName.valueOf("student"));
//設置rowkey
Put put = new Put(Bytes.toBytes("1106"));
//設置列組、列、列值
put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("name"), Bytes.toBytes("Rose"));
//執行插入
student.put(put);
student.close();
admin.close();
connection.close();
}
/**
* use_case:向表中插入多條數據
*/
@Test
public void testInserts() throws IOException {
Connection connection = ConnectionFactory.createConnection(conf);
HBaseAdmin admin = (HBaseAdmin) connection.getAdmin();
//獲取一個表對象
Table student = connection.getTable(TableName.valueOf("student"));
List<Put> list = new ArrayList<>();
Put put1 = new Put(Bytes.toBytes("1106"));
put1.addColumn(Bytes.toBytes("info"), Bytes.toBytes("name"), Bytes.toBytes("Rose"));
Put put2 = new Put(Bytes.toBytes("1106"));
put2.addColumn(Bytes.toBytes("info"), Bytes.toBytes("age"), Bytes.toBytes("16"));
Put put3 = new Put(Bytes.toBytes("1106"));
put3.addColumn(Bytes.toBytes("info"), Bytes.toBytes("sex"), Bytes.toBytes("female"));
list.add(put1);
list.add(put2);
list.add(put3);
student.put(list);
student.close();
admin.close();
connection.close();
}
/**
* use_case:刪除一行或多行數據
*/
@Test
public void testDelete() throws IOException {
Connection connection = ConnectionFactory.createConnection(conf);
HBaseAdmin admin = (HBaseAdmin) connection.getAdmin();
Table student = connection.getTable(TableName.valueOf("student"));
Delete delete = new Delete(Bytes.toBytes("1106"));
Delete delete1 = new Delete(Bytes.toBytes("1107"));
List<Delete> list = new ArrayList<>();
list.add(delete);
list.add(delete1);
student.delete(list);
student.close();
admin.close();
connection.close();
}
/**
* use_case:獲取所有數據
*/
@Test
public void testScan() throws IOException {
Connection connection = ConnectionFactory.createConnection(conf);
HBaseAdmin admin = (HBaseAdmin) connection.getAdmin();
Table student = connection.getTable(TableName.valueOf("student"));
ResultScanner scanner = student.getScanner(new Scan());
for (Result result : scanner) {
Cell[] cells = result.rawCells();
for (Cell cell : cells) {
String row = Bytes.toString(CellUtil.cloneRow(cell));
String cf = Bytes.toString(CellUtil.cloneFamily(cell));
String column = Bytes.toString(CellUtil.cloneQualifier(cell));
String value = Bytes.toString(CellUtil.cloneValue(cell));
System.out.println("row:" + row + " column:" + row + ":" + column + " value=" + value);
}
}
student.close();
admin.close();
connection.close();
}
/**
* use_case:獲取某一行的數據,指定列族,列
*/
@Test
public void testGet() throws IOException {
Connection connection = ConnectionFactory.createConnection(conf);
Table student = connection.getTable(TableName.valueOf("student"));
HBaseAdmin admin = (HBaseAdmin) connection.getAdmin();
//創建查詢的get對象
Get get = new Get(Bytes.toBytes("1105"));
//指定要查詢的列族的指定列
//get.addColumn(Bytes.toBytes("info"), Bytes.toBytes("name"));
//Result result = student.get(get);
//查詢指定的列族
get.addFamily(Bytes.toBytes("info"));
Result result = student.get(get);
Cell[] cells = result.rawCells();
for (Cell cell : cells) {
String row = Bytes.toString(CellUtil.cloneRow(cell));
String cf = Bytes.toString(CellUtil.cloneFamily(cell));
String column = Bytes.toString(CellUtil.cloneQualifier(cell));
String value = Bytes.toString(CellUtil.cloneValue(cell));
System.out.println("row:" + row + " column:" + row + ":" + column + " value=" + value);
}
student.close();
admin.close();
connection.close();
}
}
7 Hbase優化
7.1 高可用(high available)
在HBase中HMaster負責監控RegionServer的生命週期,均衡RegionServer的負載,如果HMaster掛掉了,那麼整個HBase集羣將陷入不健康的狀態,並且此時的工作狀態並不會維持太久。所以HBase支持對HMaster的高可用配置。
1.關閉HBase集羣(如果沒有開啓則跳過此步)
[root@hadoop101 hbase-1.3.1]$ bin/stop-hbase.sh
2.在conf目錄下創建backup-masters文件
[root@hadoop101 hbase-1.3.1]$ touch conf/backup-masters
3.在backup-masters文件中配置高可用HMaster節點
[root@hadoop101 hbase-1.3.1]$ echo hadoop103 > conf/backup-masters
4.將backup-masters分發到其他節點
5.啓動集羣並打開頁面測試查看
7.2 RowKey設計
一條數據的唯一標識就是rowkey,那麼這條數據存儲於哪個分區,取決於rowkey處於哪個一個預分區的區間內,設計rowkey的主要目的 ,就是讓數據均勻的分佈於所有的region中,在一定程度上防止數據傾斜。接下來我們就談一談rowkey常用的設計方案。
注意:RowKey如何設計必須結合實際業務場景
設計原則可參考下面兩篇博文:
https://www.cnblogs.com/yuguoshuo/p/6265649.html
https://blog.csdn.net/b6ecl1k7BS8O/article/details/82754169
7.3預分區
每一個region維護着startRowKey與endRowKey,如果加入的數據符合某個region維護的rowKey範圍,則該數據交給這個region維護。那麼依照這個原則,我們可以將數據所要投放的分區提前大致的規劃好,以提高HBase性能。
注意:手動分區(預分區)需要對業務數據量有把控
1.手動設定預分區
hbase> create 'staff1','info',SPLITS => ['1000','2000','3000','4000']
7.4 內存優化
HBase操作過程中需要大量的內存開銷,畢竟Table是可以緩存在內存中的,一般會分配整個可用內存的70%給HBase的Java堆。但是不建議分配非常大的堆內存,因爲GC過程持續太久會導致RegionServer處於長期不可用狀態,一般16~48G內存就可以了,如果因爲框架佔用內存過高導致系統內存不足,框架一樣會被系統服務拖死。