liudehaos

还历史以真诚,还生命以过程。 ——余秋雨
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

HBase学习(二) 基本命令 Java api

Posted on 2022-06-23 15:55  liudehaos  阅读(187)  评论(0编辑  收藏  举报

一、Hbase shell

1、Region信息观察

创建表指定命名空间

在创建表的时候可以选择创建到bigdata17这个namespace中,如何实现呢? 使用这种格式即可:‘命名空间名称:表名’ 针对default这个命名空间,在使用的时候可以省略不写

create 'bigdata17:t1','info','level'

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

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 查看一下

画图带同学理解

2、预分region解决热点问题

row设计的一个关键点是查询维度

(在建表的时候根据具体的查询业务 设计rowkey 预拆分)

在默认的拆分策略中 ,region的大小达到一定的阈值以后才会进行拆分,并且拆分的region在同一个regionserver中 ,只有达到负载均衡的时机时才会进行region重分配!并且开始如果有大量的数据进行插入操作,那么并发就会集中在单个RS中, 形成热点问题,所以如果有并发插入的时候尽量避免热点问题 ,应当预划分 Region的rowkeyRange范围 ,在建表的时候就指定预region范围

查看命令使用(指定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、总结(写一个文档总结回顾)

4、日志查看

演示不启动hdfs 就启动hbase

日志目录:
/usr/local/soft/hbase-1.7.1/logs

image-20220609225923182

start-all.sh发现HMaster没启动,hbase shell客户端也可以正常访问

再启动hbase就好了

5、scan进阶使用

查看所有的命名空间

list_namespace

查看某个命名空间下的所有表

list_namespace_tables 'default'

修改命名空间,设置一个属性

alter_namespace 'bigdata17',{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

在已有的值后面追加值

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

6、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=>'60'}

7、插入时间指定时间戳

put 'teacher','tid0007','cf2:job','bigdata17',1654845442790

画图理解这个操作在实际生产的作用

8、delete(只能删除一个单元格,不能删除列簇)

删除某一列

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

9、deleteall(删除不了某个列簇,但是可以删除多个单元格)

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

deleteall 'teacher','tid0006'

删除单元格

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

10、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'

11、获取region的分割点,清除数据,快照

获取region的分割点

get_splits 'tb_split'

清除表数据

truncate 'teacher'

拍摄快照

snapshot 'tb_split','tb_split_20220610'

列出所有快照

list_table_snapshots 'tb_split'

再添加一些数据

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

恢复快照(先禁用)

disable 'tb_split'
restore_snapshot 'tb_split_20220610'
enable 'tb_split'

12 修饰词

1、修饰词
# 语法
scan '表名', {COLUMNS => [ '列族名1:列名1', '列族名1:列名2', ...]}

# 示例
scan 'tbl_user', {COLUMNS => [ 'info:id', 'info:age']}
2、TIMESTAMP 指定时间戳
# 语法
scan '表名',{TIMERANGE=>[timestamp1, timestamp2]}

# 示例
scan 'tbl_user',{TIMERANGE=>[1551938004321, 1551938036450]}
3、VERSIONS

默认情况下一个列只能存储一个数据,后面如果修改数据就会将原来的覆盖掉,可以通过指定VERSIONS时HBase一列能存储多个值。

create 'tbl_test', 'columnFamily1'
describe 'tbl_test'

# 修改列族版本号
alter 'tbl_test', { NAME=>'columnFamily1', VERSIONS=>3 }

put 'tbl_test', 'rowKey1', 'columnFamily1:column1', 'value1'
put 'tbl_test', 'rowKey1', 'columnFamily1:column1', 'value2'
put 'tbl_test', 'rowKey1', 'columnFamily1:column1', 'value3'

# 默认返回最新的一条数据
get 'tbl_test','rowKey1','columnFamily1:column1'

# 返回3个
get 'tbl_test','rowKey1',{COLUMN=>'columnFamily1:column1', VERSIONS=>3}
# 返回2个
get 'tbl_test','rowKey1',{COLUMN=>'columnFamily1:column1', VERSIONS=>2}
4、STARTROW

ROWKEY起始行。会先根据这个key定位到region,再向后扫描

# 语法
scan '表名', { STARTROW => '行键名'}

# 示例
scan 'tbl_user', { STARTROW => 'vbirdbest'}
5、STOPROW :截止到STOPROW行,STOPROW行之前的数据,不包括STOPROW这行数据
# 语法
scan '表名', { STOPROW => '行键名'}

# 示例
scan 'tbl_user', { STOPROW => 'xiaoming'}
6、LIMIT 返回的行数
# 语法
scan '表名', { LIMIT => 行数}

# 示例
scan 'tbl_user', { LIMIT => 2 }

13 FILTER条件过滤器

过滤器之间可以使用AND、OR连接多个过滤器。

1、ValueFilter 值过滤器
# 语法:binary 等于某个值
scan '', FILTER=>"ValueFilter(=,'binary:')"
# 语法 substring:包含某个值
scan '表名', FILTER=>"ValueFilter(=,'substring:列值')"

# 示例
scan 'tbl_user', FILTER=>"ValueFilter(=, 'binary:26')"
scan 'tbl_user', FILTER=>"ValueFilter(=, 'substring:6')"
2、ColumnPrefixFilter 列名前缀过滤器
# 语法 substring:包含某个值
scan '表名', FILTER=>"ColumnPrefixFilter('列名前缀')"

# 示例
scan 'tbl_user', FILTER=>"ColumnPrefixFilter('birth')"
# 通过括号、AND和OR的条件组合多个过滤器
scan 'tbl_user', FILTER=>"ColumnPrefixFilter('birth') AND ValueFilter(=,'substring:26')"
3、rowKey字典排序

Table中的所有行都是按照row key的字典排序的

image-20220608234458949

二、JAVA API

pom文件 依赖

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-client</artifactId>
            <version>1.7.1</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>

 

Java操作
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
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.ArrayList;

/**
 *      思考:
 *          1、如何将所有的学生信息查询出来?有几种方式?
 *          2、加入我给一个条件查询,你会不会呢?比如说:查姓王,年龄24以下的,理科的,女生
 *          3、我们今天学习了预分region,如何用代码去创建呢?
 *          4、如何设置过期时间呢?
 *          5、hbase的读写流程是什么样子呢?你可以自己画出来吗?
 *          6、hbase中涉及到的数据结构有哪些?
 *          7、二级索引了解一下
 *          8、如何通过代码实现二级索引,有什么好处?
 */

public class ClintToHbase {
    private HConnection conn;
    private HBaseAdmin hAdmin;

    @Before
    public void connect() {
        try {
            //1、获取Hadoop的相关配置环境
            Configuration conf = new Configuration();

            //2、获取zookeeper的配置
            conf.set("hbase.zookeeper.quorum", "master:2181,node1:2181,node2:2181");
            //获取与Hbase的连接,这个连接是将来可以用户获取hbase表的
            conn = HConnectionManager.createConnection(conf);

            //将来我们要对表做DDL相关操作,而对表的操作在hbase架构中是有HMaster
            hAdmin = new HBaseAdmin(conf);

            System.out.println("建立连接成功:" + conn + ", HMaster获取成功:" + hAdmin);

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

    /**
     * 在HBase中创建表
     * <p>
     * create 'students','info'
     */
    @Test
    public void createTable() {
        try {
            //使用HTableDescriptor类创建一个表对象
            HTableDescriptor students = new HTableDescriptor("students");

            //在创建表的时候,至少指定一个列簇
            HColumnDescriptor info = new HColumnDescriptor("info");


            //将列簇添加到表中
            students.addFamily(info);
            //真正的执行,是由HMaster
            //hAdmin
            hAdmin.createTable(students);
            System.out.println(Bytes.toString(students.getName()) + "表 创建成功。。。");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 删除Hbase中的表
     */
    @Test
    public void deleteTable() {
        //1、判断一下要删除的表是否存在
        try {
            if (hAdmin.tableExists("students")) {
                //如果表存在,就可以去删除
                //直接删除的话,是删除不了的
                //先禁用表
                hAdmin.disableTable("students");
                hAdmin.deleteTable("students");
                System.out.println(conn.getTable("students").getName() + "表 成功被删除....");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 往表中添加一条数据
     * <p>
     * put 'students','1001','info:name','xiaohu'
     */
    @Test
    public void putData() {
        try {
            //1、获取到表的实例
            HTableInterface students = conn.getTable("students");

            //创建一个Put实例,并且给一个行键(rowkey)
            Put put = new Put("1001".getBytes());

            //将这个行键对应的值,添加列簇,列,具体的值
            put.add("info".getBytes(), "name".getBytes(), "xiaohu".getBytes());

            //表的实例将数据添加到表中
            students.put(put);

            String s = put.toString();
            System.out.println(s);

            System.out.println(students.getName() + "表 成功插入一条数据");

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

    /**
     * 获取hbase中的一条数据
     * <p>
     * get 'students','1001'
     */
    @Test
    public void getData() {
        try {
            //1、获取到表的实例
            HTableInterface students = conn.getTable("students");
            //获取get对象,通过rowkey获取
            Get get = new Get("1001".getBytes());
            //获取到了行键对应的信息
            String rowkey = Bytes.toString(get.getRow());
//            System.out.println(s);

            //表的实例调用get方法,返回的是一个结果集
            Result result = students.get(get);

            //调用getValue()方法获取到一个字节数组中的一个结果集
            String name = Bytes.toString(result.getValue("info".getBytes(), "name".getBytes()));
            System.out.println("查询结束,查询结果如下:");
            System.out.println(rowkey + ":" + name);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 一次性往表中添加多条数据
     *
     * students.txt
     *
     */
    @Test
    public void putAll(){
        try {
            //获取表的实例
            HTableInterface students = conn.getTable("students");
            //创建一个List集合对象
            ArrayList<Put> puts = new ArrayList<>();

            //读取数据 --- IO
            BufferedReader br = new BufferedReader(new FileReader("E:\\projects\\IdeaProjects\\hadoop-bigdata17\\hadoop-hbase\\data\\students.txt"));

            String line = null;
            while ((line=br.readLine())!=null){
                String[] split = line.split(",");
                String id = split[0];
                String name = split[1];
                String age = split[2];
                String gender = split[3];
                String clazz = split[4];

                //把每一行组成一个put对象
                Put put = new Put(id.getBytes());

                //为一行添加多列

                put.add("info".getBytes(),"name".getBytes(),name.getBytes());
                put.add("info".getBytes(),"age".getBytes(),age.getBytes());
                put.add("info".getBytes(),"gender".getBytes(),gender.getBytes());
                put.add("info".getBytes(),"clazz".getBytes(),clazz.getBytes());

                //每一行组成一个put对象后添加到集合中
                //put 'students',id,'info:name',name
                //put 'students',id,'info:age',age
                //put 'students',id,'info:gender',gender
                //put 'students',id,'info:clazz',clazz
                puts.add(put);
            }

            //调用表的put方法,将集合数据添加到表中
            students.put(puts);
            System.out.println("学生信息表添加完毕!!");

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


    @After
    public void close() {
        if (conn != null) {
            try {
                conn.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println("conn连接已经关闭.....");
        }

        if (hAdmin != null) {
            try {
                hAdmin.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println("HMaster已经关闭......");
        }
    }
}