海量列式非關聯式資料庫HBase 架構,shell與API

wangheng1409 發表於 2021-09-14
資料庫 HBase

HBase的特點:

  • 海量儲存: 底層基於HDFS儲存海量資料
  • 列式儲存:HBase表的資料是基於列族進行儲存的,一個列族包含若干列
  • 極易擴充套件:底層依賴HDFS,當磁碟空間不足的時候,只需要動態增加DataNode服務節點就可以
  • 高併發:支援高併發的讀寫請求
  • 稀疏:稀疏主要是針對HBase列的靈活性,在列族中,你可以指定任意多的列,在列資料為空的情 況下,是不會佔用儲存空間的。
  • 資料的多版本:HBase表中的資料可以有多個版本值,預設情況下是根據版本號去區分,版本號就 是插入資料的時間戳
  • 資料型別單一:所有的資料在HBase中是以位元組陣列進行儲存

HBase的應用場景:

  HBase適合海量明細資料的儲存,並且後期需要有很好的查詢效能(單表超千萬、上億, 且併發要求高)

HBase資料模型:

海量列式非關聯式資料庫HBase 架構,shell與API

 

HBase整體架構:

海量列式非關聯式資料庫HBase 架構,shell與API

 

 

 Zookeeper

  • 實現了HMaster的高可用
  • 儲存了HBase的後設資料資訊,是所有HBase表的定址入口
  • 對HMaster和HRegionServer實現了監控

HMaster(Master)

  • 為HRegionServer分配Region 維護整個叢集的負載均衡
  • 維護叢集的後設資料資訊
  • 發現失效的Region,並將失效的Region分配到正常的HRegionServer上

HRegionServer(RegionServer)

  • 負責管理Region 接受客戶端的讀寫資料請求
  • 切分在執行過程中變大的Region

Region

  • 每個HRegion由多個Store構成, 每個Store儲存一個列族(Columns Family),表有幾個列族,則有幾個Store,
  • 每個Store由一個MemStore和多個StoreFile組成,MemStore是Store在記憶體中的內容,寫到檔案 後就是StoreFile。
  • StoreFile底層是以HFile的格式儲存

HBase shell 基本操作:

入口:hbase shell

hbase(main):001:0> create 'lagou', 'base_info', 'extra_info'
或者(Hbase建表必須指定列族資訊)
create 'lagou', {NAME => 'base_info', VERSIONS => '3'},{NAME =>
'extra_info',VERSIONS => '3'}
VERSIONS 是指此單元格內的資料可以保留最近的 3 個版本

新增資料操作:

向lagou表中插入資訊,row key為 rk1,列族base_info中新增name列標示符,值為wang
put 'lagou', 'rk1', 'base_info:name', 'wang'

向lagou表中插入資訊,row key為rk1,列族base_info中新增age列標示符,值為30
put 'lagou', 'rk1', 'base_info:age', 30
向lagou表中插入資訊,row key為rk1,列族extra_info中新增address列標示符,值為shanghai put 'lagou', 'rk1', 'extra_info:address', 'shanghai'

查詢,更新,刪除:

獲取表中row key為rk1的所有資訊
get 'lagou', 'rk1'
獲取lagou表中row key為rk1,base_info列族的所有資訊
get 'lagou', 'rk1', 'base_info'
獲取表中row key為rk1,base_info列族的name、age列標示符的資訊
get 'lagou', 'rk1', 'base_info:name', 'base_info:age'

獲取lagou表中row key為rk1,base_info、extra_info列族的資訊
hbase(main):010:0> get 'lagou', 'rk1', 'base_info', 'extra_info'
或者
hbase(main):011:0> get 'lagou', 'rk1', {COLUMN => ['base_info', 'extra_info']}
或者
hbase(main):012:0> get 'lagou', 'rk1', {COLUMN => ['base_info:name',
'extra_info:address']}

獲取表中row key為rk1,cell的值為wang的資訊
get 'lagou', 'rk1', {FILTER => "ValueFilter(=,
'binary:wang')"}

獲取表中row key為rk1,列標示符中含有a的資訊
get 'lagou', 'rk1', {FILTER => "
(QualifierFilter(=,'substring:a'))"}

查詢lagou表中的所有資訊:
 scan 'lagou'

查詢表中列族為 base_info 的資訊:
hbase(main):001:0> scan 'lagou', {COLUMNS => 'base_info'}
hbase(main):002:0> scan 'lagou', {COLUMNS => 'base_info', RAW => true, VERSIONS
=> 3}
## Scan時可以設定是否開啟Raw模式,開啟Raw模式會返回包括已新增刪除標記但是未實際刪除的資料
## VERSIONS指定查詢的最大版本數

指定多個列族與按照資料值模糊查詢:
查詢lagou表中列族為 base_info 和 extra_info且列標示符中含有a字元的資訊

hbase(main):001:0> scan 'lagou', {COLUMNS => ['base_info', 'extra_info'], FILTER
=> "(QualifierFilter(=,'substring:a'))"}

rowkey的範圍值查詢(非常重要)
查詢lagou表中列族為base_info,rk範圍是[rk1, rk3)的資料(rowkey底層儲存是字典序)
按rowkey順序儲存。
scan 'lagou', {COLUMNS => 'base_info', STARTROW => 'rk1',
ENDROW => 'rk3'}

查詢lagou表中row key以rk字元開頭的
hbase(main):001:0> scan 'lagou',{FILTER=>"PrefixFilter('rk')"}

 更新資料值:
把lagou表中rowkey為rk1的base_info列族下的列name修改為liang
put 'lagou', 'rk1', 'base_info:name', 'liang'


刪除資料和表:
刪除lagou表row key為rk1,列標示符為 base_info:name 的資料
> delete 'lagou', 'rk1', 'base_info:name'

指定rowkey,列名以及時間戳資訊進行刪除
刪除lagou表row key為rk1,列標示符為base_info:name的資料
delete 'lagou', 'rk1', 'base_info:name',1600660619655

刪除 base_info 列族
 alter 'lagou', 'delete' => 'base_info'

刪除lagou表資料
truncate 'lagou'

刪除lagou表
#先disable 再drop
hbase(main):036:0> disable 'lagou'
hbase(main):037:0> drop 'lagou'
#如果不進行disable,直接drop會報錯
ERROR: Table user is enabled. Disable it first.

HBase JAVA  API:

<dependencies>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.14.3</version>
<scope>test</scope>
</dependency>
</dependencies>

建立連線:

package com.lagou.hbase.client;

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.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;

public class HbaseClientDemo {
    Configuration conf = null;
    Connection conn = null;

    @Before
    public void init() throws IOException {
        //獲取一個配置檔案物件
        conf = HBaseConfiguration.create();

        conf.set("hbase.zookeeper.quorum", "linux121,linux122");
        conf.set("hbase.zookeeper.property.clientPort", "2181");
        //通過conf獲取到hbase叢集的連線
        conn = ConnectionFactory.createConnection(conf);
    }

    //釋放連線
    @After
    public void realse() {
        if (conn != null) {
            try {
                conn.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

 

 

建立表:

 //建立一張hbase表
    @Test
    public void createTable() throws IOException {
        //獲取HbaseAdmin物件用來建立表
        HBaseAdmin admin = (HBaseAdmin) conn.getAdmin();
        //建立Htabledesc描述器,表描述器
        final HTableDescriptor worker = new HTableDescriptor(TableName.valueOf("worker"));
        //指定列族
        worker.addFamily(new HColumnDescriptor("info"));
        admin.createTable(worker);
        System.out.println("worker表建立成功!!");
    }

 

插入資料:

  //插入一條資料
    @Test
    public void putData() throws IOException {
        //需要獲取一個table物件
        final Table worker = conn.getTable(TableName.valueOf("worker"));

        //準備put物件
        final Put put = new Put(Bytes.toBytes("110"));//指定rowkey

        put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("addr"), Bytes.toBytes("beijing"));
        //插入資料,引數型別是put
        worker.put(put);
        //準備list<puts>,可以執行批量插入
        //關閉table物件
        worker.close();
        System.out.println("插入資料到worker表成功!!");
    }

 

查詢資料:

//查詢資料
    @Test
    public void getData() throws IOException {
        //準備table物件
        final Table worker = conn.getTable(TableName.valueOf("worker"));
        //準備get物件
        final Get get = new Get(Bytes.toBytes("110"));
        //指定查詢某個列族或者列
        get.addFamily(Bytes.toBytes("info"));
        //執行查詢
        final Result result = worker.get(get);
        //獲取到result中所有cell物件
        final Cell[] cells = result.rawCells();
        //遍歷列印
        for (Cell cell : cells) {
            final String rowkey = Bytes.toString(CellUtil.cloneRow(cell));
            final String f = Bytes.toString(CellUtil.cloneFamily(cell));
            final String column = Bytes.toString(CellUtil.cloneQualifier(cell));
            final String value = Bytes.toString(CellUtil.cloneValue(cell));

            System.out.println("rowkey-->" + rowkey + "--;cf-->" + f + "---;column--->" + column + "--;value-->" + value);
        }
        worker.close();
    }

刪除資料:

   //刪除一條資料
    @Test
    public void deleteData() throws IOException {
        //需要獲取一個table物件
        final Table worker = conn.getTable(TableName.valueOf("worker"));

        //準備delete物件
        final Delete delete = new Delete(Bytes.toBytes("110"));
//執行刪除
        worker.delete(delete);
        //關閉table物件
        worker.close();
        System.out.println("刪除資料成功!!");
    }

 

 

通過Scan全表掃描:

/**
* 全表掃描
*/
@Test
public void scanAllData() throws IOException {
HTable teacher = (HTable) conn.getTable(TableName.valueOf("teacher"));
Scan scan = new Scan();
ResultScanner resultScanner = teacher.getScanner(scan);
for (Result result : resultScanner) {
Cell[] cells = result.rawCells();//獲取改行的所有cell物件
for (Cell cell : cells) {
//通過cell獲取rowkey,cf,column,value
String cf = Bytes.toString(CellUtil.cloneFamily(cell));
String column = Bytes.toString(CellUtil.cloneQualifier(cell));
String value = Bytes.toString(CellUtil.cloneValue(cell));
String rowkey = Bytes.toString(CellUtil.cloneRow(cell));
System.out.println(rowkey + "----" + cf + "--" + column + "---"
+ value);
}
}
teacher.close();

}

通過startRowKey和endRowKey進行掃描:

//指定scan 開始rowkey和結束rowkey,這種查詢方式建議使用,指定開始和結束rowkey區間避免全表掃描
@Test
public void scanStartEndData() throws IOException {
    //準備table物件
    final Table worker = conn.getTable(TableName.valueOf("worker"));
    //準備scan物件
    final Scan scan = new Scan();
    //指定查詢的rowkey區間,rowkey在hbase中是以字典序排序
    scan.setStartRow(Bytes.toBytes("001"));
    scan.setStopRow(Bytes.toBytes("004"));
    //執行掃描
    final ResultScanner resultScanner = worker.getScanner(scan);
    for (Result result : resultScanner) {
        //獲取到result中所有cell物件
        final Cell[] cells = result.rawCells();
        //遍歷列印
        for (Cell cell : cells) {
            final String rowkey = Bytes.toString(CellUtil.cloneRow(cell));
            final String f = Bytes.toString(CellUtil.cloneFamily(cell));
            final String column = Bytes.toString(CellUtil.cloneQualifier(cell));
            final String value = Bytes.toString(CellUtil.cloneValue(cell));
            System.out.println("rowkey-->" + rowkey + "--;cf-->" + f + ";column--->" + column + "--;value-->" + value);
        }
    }

    worker.close();
}