Hadoop生態圈(十):Hbase

目錄

1 Hbase概述

1.1 什麼是Hbase

1.2 Hbase特點

1.3 HBase架構

1.4 HBase中的角色

1.4.1 HMaster

1.4.2 RegionServer

1.4.3 其他組件

2 Hbase安裝

2.1 環境準備

2.2 下載安裝

2.3 Hbase服務的啓動

2.4 查看Hbase的WEBUI界面

3 Hbase Shell操作

3.1 基本操作

3.2 表的操作

4 Hbase數據結構

4.1 RowKey

4.2 Column Family

4.3 Time Stamp

4.4 命名空間

5 Hbase原理

6 HbaseAPI操作

6.1 HbaseAPI

7 Hbase優化

7.1 高可用(high available)

7.2 RowKey設計

7.3預分區

7.4 內存優化


1 Hbase概述

1.1 什麼是Hbase

HBase的原型是Google的BigTable論文,受到了該論文思想的啓發,目前作爲Hadoop的子項目來開發維護。

官方網站:http://hbase.apache.org

-- 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等幾個組組成,下面來介紹一下幾個組的相關功能:

1Client

Client包含了訪問Hbase的接口,另外Client還維護了對應的cache來加速Hbase的訪問,比如cache的.META.元數據的信息。

2Zookeeper

HBase通過Zookeeper來做master的高可用、RegionServer的監控、元數據的入口以及集羣配置的維護等工作。具體工作如下:

通過Zoopkeeper來保證集羣中只有1個master在運行,如果master異常,會通過競爭機制產生新的master提供服務

通過Zoopkeeper來監控RegionServer的狀態,當RegionSevrer有異常的時候,通過回調的形式通知Master RegionServer上下線的信息(就是使用zk完成的服務器上下線機制

通過Zoopkeeper存儲元數據的統一入口地址

3Hmaster

master節點的主要職責如下:

(1)爲RegionServer分配Region
(2)維護整個集羣的負載均衡
(3)維護集羣的元數據信息
(4)發現失效的Region,並將失效的Region分配到正常的RegionServer上
(5)當RegionSever失效的時候,協調對應Hlog的拆分

4HregionServer

HregionServer直接對接用戶的讀寫請求,是真正的“幹活”的節點。它的功能概括如下:
(1)管理master爲其分配的Region
(2)處理來自客戶端的讀寫請求
(3)負責和底層HDFS的交互,存儲數據到HDFS
(4)負責Region變大以後的拆分
(5)負責Storefile的合併工作

5HDFS

HDFS爲Hbase提供最終的底層數據存儲服務,同時爲HBase提供高可用(Hlog存在HDFS)的支持,具體功能概括如下:
(1)提供元數據和表數據的底層分佈式存儲服務
(2)數據多副本,保證高可靠和高可用性

1.4 HBase中的角色

1.4.1 HMaster

功能

  1. ​監控RegionServer
  2. 處理RegionServer故障轉移
  3. 處理元數據的變更
  4. 處理region的分配或轉移
  5. 在空閒時間進行數據的負載均衡
  6. 通過Zookeeper發佈元數據的位置給客戶端

1.4.2 RegionServer

功能

  1. 負責存儲HBase的實際數據
  2. 處理分配給它的Region
  3. 刷新緩存到HDFS
  4. 維護Hlog(每個regionserver都有一個Hlog)
  5. 執行壓縮
  6. 負責處理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中的行,只有三種方式:

  1. 通過單個RowKey訪問
  2. 通過RowKey的range
  3. 全表掃描

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內存就可以了,如果因爲框架佔用內存過高導致系統內存不足,框架一樣會被系統服務拖死。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章