HBase學習的第四天--HBase的進階與API

shmil發表於2024-08-16

HBase進階與API

一、Hbase shell

1、Region資訊觀察

建立表指定名稱空間

在建立表的時候可以選擇建立到bigdata17這個namespace中,如何實現呢?
使用這種格式即可:‘名稱空間名稱:表名’
針對default這個名稱空間,在使用的時候可以省略不寫

create 'hbase01:t1','info'

image-20220609214828043

此時使用list檢視所有的表

image-20220609214843582

如果只想檢視bigdata17這個名稱空間中的表,可以使用命令list_namespace_tables

list_namespace_tables 'n1'

image-20220609214907399

檢視region中的某列簇資料

hbase hfile -p -f /hbase/data/default/tbl_user/92994712513a45baaa12b72117dda5e5/info/d84e2013791845968917d876e2b438a5


# 行鍵的設計在hbase中有三大設計原則:唯一性 長度不宜過長 雜湊性
put 'students_test1','1500100001','info:name','施笑槐'
put 'students_test1','1500100001','info:age','22'
put 'students_test1','1500100001','info:gender','女'
put 'students_test1','1500100001','info:clazz','文科六班'
put 'students_test1','1500100002','info:name','呂金鵬'
put 'students_test1','1500100002','info:age','24'
put 'students_test1','1500100002','info:gender','男'
put 'students_test1','1500100002','info:clazz','文科六班'
put 'students_test1','1500100003','info:name','單樂蕊'
put 'students_test1','1500100003','info:age','22'
put 'students_test1','1500100003','info:gender','女'
put 'students_test1','1500100003','info:clazz','理科六班'
put 'students_test1','1500100004','info:name','葛德曜'
put 'students_test1','1500100004','info:age','24'
put 'students_test1','1500100004','info:gender','男'
put 'students_test1','1500100004','info:clazz','理科三班'

1500100005,宣谷芹,22,女,理科五班

put 'students_test1','1500100005','info:name','宣谷芹'
put 'students_test1','1500100005','info:age','22'
put 'students_test1','1500100005','info:gender','女'
put 'students_test1','1500100005','info:clazz','理科五班'
重新整理資料:flush 'tb'
合併資料:major_compact 'tb'

1.1 檢視錶的所有region

list_regions '表名'

image-20220609215203693

1.2 強制將表切分出來一個region

# 切分的行鍵是包括在下一行裡的
split '表名','行鍵'

image-20220609215654881

但是在頁面上可以看到三個:過一會會自動的把原來的刪除

image-20220609215721140

1.2 檢視某一行在哪個region中

locate_region '表名','行鍵'

image-20220609215929647

可以hbase hfile -p -f xxxx 檢視一下

檢視命令使用(指定4個切割點,就會有5個region)

help 'create'

image-20220609221719260

create 'tb_split','cf',SPLITS => ['e','h','l','r']
list_regions 'tb_split'

image-20220609222140125

新增資料試試

put 'tb_split','c001','cf:name','first'
put 'tb_split','f001','cf:name','second'
put 'tb_split','z001','cf:name','last'

hbase hfile -p --f xxxx 檢視資料

如果沒有資料,因為資料還在記憶體中,需要手動重新整理記憶體到HDFS中,以HFile的形式儲存

3、日誌檢視

演示不啟動hdfs 就啟動hbase

日誌目錄:
/usr/local/soft/hbase-1.7.1/logs

image-20220609225923182

start-all.sh發現HMaster沒啟動,hbase shell客戶端也可以正常訪問

再啟動hbase就好了

4、scan進階使用

檢視所有的名稱空間

list_namespace

檢視某個名稱空間下的所有表

list_namespace_tables 'default'

修改名稱空間,設定一個屬性

alter_namespace 'bigdata25',{METHOD=>'set','author'=>'wyh'}

檢視名稱空間屬性

describe_namespace 'bigdata17'

刪除一個屬性

alter_namespace 'bigdata17',{METHOD=>'unset', NAME=>'author'}

刪除一個名稱空間

drop_namespace 'bigdata17'

建立一張表

create 'teacher','cf'

新增資料

put 'teacher','tid0001','cf:tid',1
put 'teacher','tid0002','cf:tid',2
put 'teacher','tid0003','cf:tid',3
put 'teacher','tid0004','cf:tid',4
put 'teacher','tid0005','cf:tid',5
put 'teacher','tid0006','cf:tid',6

顯示三行資料

scan 'teacher',{LIMIT=>3}
put 'teacher','tid00001','cf:name','wyh'
scan 'teacher',{LIMIT=>3}

image-20220609232359610

從後查三行

scan 'teacher',{LIMIT=>3,REVERSED=>true}

image-20220609232457186

檢視包含指定列的行

scan 'teacher',{LIMIT=>3,COLUMNS=>['cf:name']}

image-20220609232755396

簡化寫法:

scan 'teacher',LIMIT=>3,COLUMNS=>['cf:name']

在已有的值後面追加值

append 'teacher','tid0001','cf:name','123'

5、get進階使用

簡單使用,獲取某一行資料

get 'teacher','tid0001'

獲取某一行的某個列簇

get 'teacher','tid0001','cf'

獲取某一行的某一列(屬性 )

get 'teacher','tid0001','cf:name'

可以新增一個列簇資料測試

檢視歷史版本

1、修改表可以儲存多個版本

alter 'teacher',NAME=>'cf',VERSIONS=>3

2、put四次相同rowkey和列的資料

put 'teacher','tid0001','cf:name','xiaohu1'
put 'teacher','tid0001','cf:name','xiaohu2'
put 'teacher','tid0001','cf:name','xiaohu3'
put 'teacher','tid0001','cf:name','xiaohu4'

3、檢視歷史資料,預設是最新的

get 'teacher','tid0001',COLUMN=>'cf:name',VERSIONS=>2

修改列簇的過期時間 TTL單位是秒,這個時間是與插入的時間比較,而不是現在開始60s

alter 'teacher',{NAME=>'cf2',TTL=>'10'}

6、插入時間指定時間戳

put 'teacher','tid0007','info:clazz','bigdata30',1718356919312

資料時間:資料產生那一刻的時間

事務時間(操作時間):接收到資料並處理的那一刻時間

7、delete(只能刪除一個單元格,不能刪除列簇)

刪除某一列

delete 'teacher','tid0004','cf:tid'

8、deleteall(刪除不了某個列簇,但是可以刪除多個單元格)

刪除一行,如果不指定列簇,刪除的是一行中的所有列簇

deleteall 'teacher','tid0006'

刪除單元格

deleteall 'teacher','tid0006','cf:name','cf2:job'

9、incr和counter

統計表有多少行(統計的是行鍵的個數)

count 'teacher'

新建一個自增的一列

incr 'teacher','tid0001','cf:cnt',1

每操作一次,自增1

incr 'teacher','tid0001','cf:cnt',1
incr 'teacher','tid0001','cf:cnt',10
incr 'teacher','tid0001','cf:cnt',100

image-20220610000847703

配合counter取出資料,只能取incr欄位

get_counter 'teacher','tid0001','cf:cnt'

10、獲取region的分割點,清除資料,快照

獲取region的分割點

get_splits 'tb_split'

清除表資料

truncate 'teacher'

拍攝快照

snapshot 'teacher','teacher_20240614'

列出所有快照

list_table_snapshots 'tb_split'

再新增一些資料

put 'tb_split','k001','cf:name','wyh'

恢復快照(先禁用)

disable 'tb_split'
restore_snapshot 'teacher_20240614'
enable 'tb_split'

二、JAVA API

pom檔案

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.hbase/hbase-client -->
<dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-client</artifactId>
    <version>2.2.7</version>
</dependency>

        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
            <version>2.7.6</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-common</artifactId>
            <version>2.7.6</version>
        </dependency>
package com.hbase.base;

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.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;

public class HBaseAPI {
    private Connection conn;
    private Admin admin;

    /**
     * 在所有Test方法執行之前執行
     */
    @Before
    public void getConnection() {
        try {
            //1、獲取hbase叢集的配置檔案物件
            //0.90.0之前舊版本的寫法:
//        HBaseConfiguration conf = new HBaseConfiguration();
            //新版本的寫法:呼叫靜態方法public static Configuration create()
            Configuration conf = HBaseConfiguration.create();

            Properties prop = new Properties();
            prop.load(new BufferedReader(new FileReader("src/main/resources/hbase.properties")));

            String zk = (String)prop.get("hbase.zookeeper.quorum");

            //2、因為hbase的資料都有一條後設資料,而後設資料也儲存在一張表中,這張後設資料表也有後設資料,儲存在zookeeper中
            //配置檔案要設定你自己的zookeeper叢集
            conf.set("hbase.zookeeper.quorum",zk); //前提時windows中配置了hosts對映

            //3、獲取資料庫的連線物件
            conn = ConnectionFactory.createConnection(conf);

            //4、獲取資料庫操作物件
//            HBaseAdmin hBaseAdmin = new HBaseAdmin(conn);
            //新版本的做法
            admin = conn.getAdmin(); //使用連線物件獲取資料庫操作物件
            System.out.println("資料庫連線物件獲取成功!!" + conn);
            System.out.println("資料庫操作物件獲取成功!!" + admin);
            System.out.println("==========================================");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 需求1:  1、如何建立一張表
     * create 'students','info'  必須要有表名和列簇的名
     */
    @Test
    public void createOneTable() {
        try {
            //先將表名封裝成一個TableName的物件
            TableName tn = TableName.valueOf("students2");
            //HTableDescriptor類已經在2.0.0版本之後就過時了,並且在3.0.0之後完全被刪除
            //新版本不能使用這個類
//        HTableDescriptor students = new HTableDescriptor(tn);
            //新版本使用 TableDescriptorBuilder 類來建立並獲取表描述器物件
            //public static TableDescriptorBuilder newBuilder(final TableName name)
            TableDescriptorBuilder students = TableDescriptorBuilder.newBuilder(tn);

            //舊版本建立列簇描述器物件
//        HColumnDescriptor info = new HColumnDescriptor("info");
            //新版本中ColumnFamilyDescriptorBuilder.of(String).
            ColumnFamilyDescriptor info = ColumnFamilyDescriptorBuilder.of("info");

            //將列簇與表進行關聯
            //舊版本中表描述器呼叫addColumnFamily方法將列簇描述器新增到表中
//        students.addColumnFamily(info);
            //新版本中使用setColumnFamily
            students.setColumnFamily(info);

            //呼叫方法,建立表
            // createTable(TableDescriptor desc)
            //             TableDescriptorBuilder
            admin.createTable(students.build());
            System.out.println(tn + "表建立成功!!!");
        } catch (Exception e) {
            System.out.println("表建立失敗!!");
            e.printStackTrace();
        }
    }

    /**
     * 需求:2、如何刪除一張表
     * disable 'students'
     * drop 'students'
     */
    @Test
    public void dropOneTable() {
        try {
            //先將表名封裝成一個TableName的物件
            TableName tn = TableName.valueOf("tb_split2");

            //判斷表是否存在
            if (admin.tableExists(tn)) {
                //先禁用表
                admin.disableTable(tn);

                //使用admin物件呼叫方法刪除表
                //void deleteTable(TableName tableName)
                admin.deleteTable(tn);

                System.out.println(tn + "表成功被刪除");
            } else {
                System.out.println(tn + "表不存在!!");
            }


        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 需求:3、如何向一張表中新增一條資料
     * put 'students','1001','info:name','小虎'
     */
    @Test
    public void putOneDataToTable() {
        try {
            //先將表名封裝成一個TableName的物件
            TableName tn = TableName.valueOf("students");

            //判斷表是否存在
            if (admin.tableExists(tn)) {
                //獲取表物件
                Table students = conn.getTable(tn);
                //建立Put物件
//                Put put = new Put("1001".getBytes());//行鍵的位元組陣列形式
//                //對put物件進行設定,新增列簇,列名和列值
//                put.addColumn("info".getBytes(),"name".getBytes(),"小虎".getBytes());

                //hbase自帶的一個工具類Bytes,可以將字串轉位元組陣列
                Put put = new Put(Bytes.toBytes("1001"));//行鍵的位元組陣列形式
                //對put物件進行設定,新增列簇,列名和列值  以前的寫法
//                put.addColumn(Bytes.toBytes("info"),Bytes.toBytes("name"),Bytes.toBytes("小虎"));
                //public Put add(Cell cell) 新版本另外一種設定put物件的方式
                //Cell 是一個介面,無法被例項化,使用實現類KeyValue來建立物件
                //KeyValue(final byte [] row, final byte [] family, final byte [] qualifier, final byte [] value)
                put.add(new KeyValue(Bytes.toBytes("1001"),
                        Bytes.toBytes("info"),
                        Bytes.toBytes("age"),
                        Bytes.toBytes(18)));

                //void put(Put put)
                //需要先將我們新增的列資料封裝成一個Put物件
                students.put(put);

            } else {
                System.out.println(tn + "表不存在!!");
            }


        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 需求:4、如何向一張表中同時新增一批資料
     */
    @Test
    public void putMoreDataToTable() {
        BufferedReader br = null;
        try {
            //先將表名封裝成一個TableName的物件
            TableName tn = TableName.valueOf("students2");
            //建立字元輸入緩衝流
            br = new BufferedReader(new FileReader("data/students.csv"));

            String[] colNameArray = {"", "name", "age", "gender", "clazz"};

            //判斷表是否存在
            if (admin.tableExists(tn)) {
                //獲取表物件
                Table students = conn.getTable(tn);
                //迴圈讀取資料
                String line = null;
                while ((line = br.readLine()) != null) {
                    String[] info = line.split(",");
                    byte[] rowKey = Bytes.toBytes(info[0]);
                    //建立這一行的Put物件
                    Put put = new Put(rowKey);
                    //第一列作為行鍵唯一標識,從第二列開始,每一列都要被封裝成Put物件
                    for (int i = 1; i < info.length; i++) {
                        byte[] colName = Bytes.toBytes(info[i]);
                        put.addColumn(Bytes.toBytes("info"), Bytes.toBytes(colNameArray[i]), Bytes.toBytes(info[i]));
                        //新增該列資料
                        students.put(put);
                    }
                }

                System.out.println("學生表資料新增完畢!!!!");
            } else {
                System.out.println(tn + "表不存在!!");
            }


        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 需求:5、如何獲取一條資料
     * get 'students','1500101000'
     */
    @Test
    public void getOneData() {
        try {
            //將表名封裝TableName物件
            TableName sd = TableName.valueOf("students");

            //獲取表的物件
            Table students = conn.getTable(sd);

            //傳入行鍵的位元組陣列的形式
            Get get = new Get(Bytes.toBytes("1500101000"));

            //default Result get(Get get)
            Result result = students.get(get);
//            System.out.println(result);
            /**
             *  Result類中的常用方法:
             *      getRow() : 獲取行鍵的位元組陣列形式
             *      getValue(byte [] family, byte [] qualifier): 根據列簇和列名,獲取列值的位元組陣列形式
             *      List<Cell> listCells():獲取所有單元格,單元格中儲存了行鍵,列簇,列名,版本號(時間戳),列值
             */
            String id = Bytes.toString(result.getRow());
            //在已經知道列名的前提之下獲取對應的列值
//            String name = Bytes.toString(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("name")));
//            String age = Bytes.toString(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("age")));
//            String gender = Bytes.toString(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("gender")));
//            String clazz = Bytes.toString(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("clazz")));
//            System.out.println("學號:"+id+",姓名:"+name+",年齡:"+age+",性別:"+gender+",班級:"+clazz);
            //當不清楚列名的時候該如何獲取呢?
            //獲取一行中的所有單元格組合的集合
            List<Cell> cells = result.listCells(); //獲取的結果值的順序是hbase中儲存排序後的順序
            //遍歷集合得到每個單元格,獲取每個列值
            /**
             * hbase中除了提供一個Bytes工具類給我們使用以外,還提供了另外的一個工具類:CellUtil
             * 該工具類主要的操作物件是Cell類的物件
             */
            //遍歷方式1:普通for迴圈遍歷
            //遍歷方式2:增強for迴圈遍歷
//            for (Cell cell : cells) {
//                String colName = Bytes.toString(CellUtil.cloneQualifier(cell));
//                String colValue = Bytes.toString(CellUtil.cloneValue(cell));
//                System.out.println(colName + ":" + colValue);
//            }
            //遍歷方式3:forEach + lambda表示式
//            cells.forEach(e -> {
//                String colName = Bytes.toString(CellUtil.cloneQualifier(e));
//                String colValue = Bytes.toString(CellUtil.cloneValue(e));
//                System.out.println(colName + ":" + colValue);
//            });
            //遍歷方式4:jdk1.8新特性遍歷,轉流處理
            cells.stream().map(e -> {
                String colName = Bytes.toString(CellUtil.cloneQualifier(e));
                String colValue = Bytes.toString(CellUtil.cloneValue(e));
//                System.out.println(colName + ":" + colValue);
                return colName + ":" + colValue;
            }).forEach(System.out::println);


        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 6、如果獲取一批資料 第一種方式
     */
    @Test
    public void getMoreData() {
        try {
            //將表名封裝TableName物件
            TableName sd = TableName.valueOf("students");

            //獲取表的物件
            Table students = conn.getTable(sd);

            //建立List集合,儲存多個Get物件
            //1500100001 ~ 1500101000
            ArrayList<Get> gets = new ArrayList<>();
            for (int i = 1500100001; i <= 1500101000; i++) {
                gets.add(new Get(Bytes.toBytes(String.valueOf(i))));
            }

            //default Result[] get(List<Get> gets)
            Result[] results = students.get(gets);
            //1、先遍歷results得到每一個result(每一行)
            //2、遍歷每一個result中的每一列
            for (Result result : results) {
                List<Cell> cells = result.listCells();
                if (cells != null) {
                    cells.stream().map(cell -> {
                        String colName = Bytes.toString(CellUtil.cloneQualifier(cell));
                        String colValue = Bytes.toString(CellUtil.cloneValue(cell));
//                System.out.println(colName + ":" + colValue);
                        return colName + ":" + colValue;
                    }).forEach(System.out::println);
                    System.out.println("-----------------");
                } else {
                    System.out.println("是空");
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 7、如果獲取一批資料 第二種方式
     * scan 'students' 預設情況下獲取表所有資料
     * scan 'students',LIMIT=>3
     */
    @Test
    public void ScanData() {
        try {
            TableName sd = TableName.valueOf("students");
            Table students = conn.getTable(sd);
            //建立Scan物件
            Scan scan = new Scan(); //查詢表中的所有行
            //設定開始行和結束行
            //舊版本寫法
//            scan.setStartRow(Bytes.toBytes("1500100001"));
//            scan.setStopRow(Bytes.toBytes("1500100004"));
            //新版本寫法
//            scan.withStartRow(Bytes.toBytes("1500100001"));
//            scan.withStopRow(Bytes.toBytes("1500100004"), true);

            //設定取前幾行
            scan.setLimit(10);

            //default ResultScanner getScanner(Scan scan)
            ResultScanner resultScanner = students.getScanner(scan);
            //透過觀察原始碼發現,可以先獲取迭代器物件
            Iterator<Result> iterator = resultScanner.iterator();
            while (iterator.hasNext()) {
                Result result = iterator.next();
//                String id = Bytes.toString(result.getRow());
                //在已經知道列名的前提之下獲取對應的列值
//                String name = Bytes.toString(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("name")));
//                String age = Bytes.toString(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("age")));
//                String gender = Bytes.toString(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("gender")));
//                String clazz = Bytes.toString(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("clazz")));
//                System.out.println("學號:" + id + ",姓名:" + name + ",年齡:" + age + ",性別:" + gender + ",班級:" + clazz);
                printResult(result);
                System.out.println("-----------------");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 需求:7、如何建立預分region表
     * 將來對於某一個RegionServer而言,可能會接收大量的併發請求,超出了該RegionServer承受的壓力,有可能會導致該子節點崩潰
     * 如果大量的併發請求查詢的資料是多種多種多樣的,只不過巧合的是都在一個RegionServer上管理
     * 解決的思路:分散查詢的資料到不同的RegionServer上,這樣請求也會隨著被分散到不同的RegionServer上,就達到了減輕某一個RegionServer壓力過大情況,解決了單點故障的問題
     */
    @Test
    public void createPreviewTable(){
        try {
            //先將表名封裝成TableName物件
            TableName tb = TableName.valueOf("tb_split2");

            //建立表描述器物件
            TableDescriptorBuilder tbSplit2 = TableDescriptorBuilder.newBuilder(tb);

            //建立列簇描述器物件
            ColumnFamilyDescriptor info = ColumnFamilyDescriptorBuilder.of("info");

            //將列簇新增到表中
            tbSplit2.setColumnFamily(info);

            //建立分割鍵的二維陣列
            byte[][] splitKeys = {
                    Bytes.toBytes("f"),
                    Bytes.toBytes("k"),
                    Bytes.toBytes("p")
            };

            //呼叫方法建立表
//            admin.createTable(tbSplit2.build());
            //呼叫另外一個方法,傳入表描述器的同時,傳入分割點,建立預分region表
            //void createTable(TableDescriptor desc, byte[][] splitKeys)
            admin.createTable(tbSplit2.build(),splitKeys);
            System.out.println("預分region表建立成功!!!");
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public void printResult(Result result) {
        String id = Bytes.toString(result.getRow());
        //在已經知道列名的前提之下獲取對應的列值
        String name = Bytes.toString(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("name")));
        String age = Bytes.toString(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("age")));
        String gender = Bytes.toString(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("gender")));
        String clazz = Bytes.toString(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("clazz")));
        System.out.println("學號:" + id + ",姓名:" + name + ",年齡:" + age + ",性別:" + gender + ",班級:" + clazz);
//        System.out.println("-----------------");
    }




    /**
     * 釋放Before建立的資源,在每個Test之後執行
     */
    @After
    public void closeSource() {
        try {
            if (admin != null) {
                admin.close();
            }

            if (conn != null) {
                conn.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


}

相關文章