2022-09-05 HBase的Shell操作、API操作、优化

一、Shell操作

1. 进入hbaseshell客户端操作界面

# 于linux终端中敲命令
hbase shell

2. 帮助命令:help

(1) 查看hbase中可以使用的命令 

help

(2) 查看某个命令的使用帮助文档

# cmd_name: 命令名称
help 'cmd_name'

3. namespace相关命令

(1) 创建namespace

# 创建namespace
create_namespace 'namespace_name'

# 创建namespace的同时,指定namespace的属性值
create_namespace 'ns1', {'property_name1'=>'property_value1', 'property_name2'=>'property_value2', ...}

(2) 增加、修改或者删除namespace的属性

# 增添、修改namespace的属性
alter_namespace 'namespace_name', {METHOD=>'set', 'property_name1'=>'property_value1', 'property_name2'=>'property_value2', ...}

# 删除属性
alter_namespace 'namespace_name', {METHOD=>'unset', NAME=>'property_name1', NAME=>'property_name2', ...}

# 可以同时将增添、修改、删除属性的命令写在一起
alter_namespace 'namespace_name', {METHOD=>'set', 'property_name1'=>'property_value1', 'property_name2'=>'property_value2', ...}, {METHOD=>'unset', NAME=>'property_name1', NAME=>'property_name2', ...}, ...

(3) 查看namespace的详细描述信息

describe_namespace 'namespace_name'

(4) 查看namespace下创建的表

list_namespace_tables 'namespace_name'

(5) 查看所有创建的namespace

list_namespace

(6) 删除namespace

# 需要注意的是,如果namespace中有表的话,需要先把表删除后,才能删除该namespace
drop_namespace 'namespace_name'

4. 表相关命令

指定表的格式为:namespace_name:table_name;namespace_name不是必须的,如果未指定namespace_name,那么HBase默认会去default默认命名空间中寻找表。

(1) DDL

a. 创建表

# 创建表,不指定列族属性
create 'namespace_name:table_name', {NAME=>'column_famliy1'}, {NAME=>'column_famliy2'}, ...
# 简化后的写法
create 'namespace_name:table_name', 'column_famliy1', 'column_famliy2', ...



# 创建表,指定列族属性
# VERSIONS: 指定该列族中每列的数据会在HBase中保留几个版本(使用时间戳来指定每个数据的版本)
create 'namespace_name:table_name', {NAME=>'column_famliy1', VERSIONS=>num1}, {NAME=>'column_famliy2', VERSIONS=>num2}, ...

b. 添加新列族、修改列族属性、删除列族

# 添加列族,不指定列族属性
alter 'namespace_name:table_name', 'column_famliy1', 'column_famliy2', ...
# 添加列族,指定列族属性 或者 修改原有列族属性
alter 'namespace_name:table_name', {NAME=>'column_famliy1', VERSIONS=>num1}, {NAME=>'column_famliy2', VERSIONS=>num2},...
# 当只操作一个列族时,简写为:
alter 'namespace_name:table_name', NAME=>'column_famliy', VERSIONS=>num



# 删除列族
alter 'namespace_name:table_name', {METHOD=>'delete', NAME=>'column_famliy1'}, {METHOD=>'delete', NAME=>'column_famliy2'}, ...
# 当只需要删除单个列族时候,简写为
alter 'namespace_name:table_name', 'delete'=>'column_famliy'

c. 查看表的详细描述信息

describe 'namespace_name:table_name'

d. 列出用户创建的表

# 列出用户创建的所有表
list

# 从用户创建的所有表中使用正则表达式过滤出指定的表
list 'namespace_name_regx:table_name_regx'

e. 删除表

# 删除表需要两步走:1. 使表无效;2. 删表;

# 使单个表无效
disable 'namespace_name:table_name'
# 删除单个表
drop 'namespace_name:table_name'

# 使用正则表达式,批量使表无效
disable_all 'namespace_name_regx:table_name_regx'
# 使用正则表达式,批量删除表
drop_all 'namespace_name_regx:table_name_regx'

# 如果表已经无效,想让它再变为有效,使用命令:
enable 'namespace_name:table_name'
enable_all 'namespace_name_regx:table_name_regx'

# 判断表是否有效
is_enabled 'namespace_name:table_name'
is_disabled 'namespace_name:table_name'

f. 判断表是否存在

# 判断表是否存在(是否删除)
exists 'namespace_name:table_name'

(2) DML

a. 向表中添加、修改数据

# 添加或者修改数据,时间戳越大代表数据越新,HBase读取最新的数据
# 不指定时间戳
put 'namespace_name_table_name', 'row_key', 'column_famliy:column_name', 'value'
# 指定时间戳
put 'namespace_name_table_name', 'row_key', 'column_famliy:column_name', 'value', timestamp

# 向表中放入数值类型(默认放入的是Long类型)
put 'namespace_name_table_name', 'row_key', 'column_famliy:column_name', Bytes.toBytes(num)[, timestamp]

b. 查看表中某条数据

# 获取表中一行记录的所有列数据
get 'namespace_name:table_name', 'row_key'

# 获取表中一行记录的一个列的数据
get 'namespace_name:table_name', 'row_key', {COLUMN=>'column_famliy:column', VERSIONS=>num, TIMESTAMP=>timestamp}

# 获取表中一行记录的某些列数据
get 'namespace_name:table_name', 'row_key', {COLUMNS=>['column_famliy:column', 'column_famliy:column', ...]}
# 简写为:
get 'namespace_name:table_name', 'row_key', ['column_famliy:column', 'column_famliy:column', ...]

# 对要获取的表中数据加上一些限制
# 获取num个版本、指定时间戳的指定列数据
get 'namespace_name:table_name', 'row_key', {COLUMNS=>['column_famliy:column', 'column_famliy:column, ...'], TIMESTAMP=>timestamp, VERSIONS=>num}
# 获取num个版本、指定时间戳范围内的指定列数据
get 'namespace_name:table_name', 'row_key', {COLUMNS=>['column_famliy:column', 'column_famliy:column', ...], TIMERANGE=>[timestamp1, timestamp2], VERSIONS=>num}


# 默认情况下获取表中数据使用的是字符串格式,但是当表中数据存储的时候是数值时,使用字符串格式无法查看,toInt查看Int类型、toLong查看Long类型
# 解决方案如下:
get 'namespace_name:table_name', 'row_key', 'column_famliy:column:toInt'

c. 扫描表中数据

# 扫描一张表中的所有数据
scan 'namespace_name:table_name'

# 扫描一张表中某个时间戳范围、指定数量(num)、指定列的数据
scan 'namespace_name:table_name', {COLUMNS=>['column_famliy:column', 'column_famliy:column', ...], TIMERANGE=>[timestamp1, timestamp2], LIMIT=>num}

# 扫描一张表中某个行键范围、指定数量(num)、指定列的数据
# 扫描范围为:前包后不包
# 假设某一行键为'abc','abc!'(前缀为abc的最小字符串) < 'abc' < 'abc~'(前缀为abc的最大字符串)
scan 'namespace_name:table_name', {COLUMNS=>['column_famliy:column', 'column_famliy:column', ...], STARTROW=>'row_key1', STOPROW=>'row_key2', LIMIT=>num}

# 默认情况下获取表中数据使用的是字符串格式,但是当表中数据存储的时候是数值时,使用字符串格式无法查看,toInt查看Int类型、toLong查看Long类型
# 解决方案如下:
scan 'namespace_name:table_name', {COLUMNS=>['column_famliy:column:toInt', 'column_famliy:column:toLong', ...]


# 扫描一张表的原始数据,也就是存储在HFile中的数据
# 扫描num个版本的原始数据
scan 'namespace_name:table_name', {RAW=>true, VERSIONS=>num}

d. 删除表中一行数据的某个版本

# 删除表中某行记录某列的最新版本的数据,如果有多个版本,那么次新版本的列数据将会被使用
delete 'namespace_name:table_name', 'row_key', 'column_famliy:column'

# 删除表中某行记录某列的指定版本数据,如果有多个版本,那么删除后的最新版本的列数据将会被使用
delete 'namespace_name:table_name', 'row_key', 'column_famlit:column', timestamp

# 底层会向HFile中添加一个删除操作的记录,该记录使用被删除数据的时间戳,操作类型是DELETE类型

f. 删除表中一行数据的所有版本

# 删除表中整整一行记录
# 底层会向HFile中追加列族数量条的删除记录,表示删除了所有版本的数据,每条删除记录的类型都是:DELETE_FAMLIY
deleteall 'namespace_name:table_name', 'row_key'

# 删除表中某条记录中指定列的所有版本的数据
# 底层会向HFile中追加一条删除记录,指示删除了哪个列,删除记录的类型为:DELETE_COLUMN
deleteall 'namespace_name:table_name', 'row_key', 'column_famliy:column'

# 删除表中某条记录中指定列的指定版本的数据]
# 底层的处理逻辑同delete
deleteall 'namespace_name:table_name', 'row_key', 'column_famliy:column', timestamp

g. 清空表中所有数据,直接就是物理删除

truncate 'namespace_name:table_name'

二、API

官方API文档

Admin实例负责DDL操作,Table实例负责DML操作;这两个示例都需要从Connection实例中获取;Connection是一个重量级实现且线程安全,打开了就不要随意关闭,而Admin、Table是轻量级实现,随开随关。

1. 引入依赖

	<dependency>
		<groupId>org.apache.hbase</groupId>
		<artifactId>hbase-server</artifactId>
		<version>2.0.5</version>
	</dependency>
	<dependency>
		<groupId>org.apache.hbase</groupId>
		<artifactId>hbase-client</artifactId>
		<version>2.0.5</version>
	</dependency>

2. Connection实例的获取与关闭

(1) 获取

    public static Connection getConnection() throws IOException {
        Configuration conf = HBaseConfiguration.create();
        // 配置Zookeeper的连接地址,如果hadoop101不行就连接hadoop102,之间使用逗号(,)分割
        conf.set("hbase.zookeeper.quorum", "hadoop101,hadoop102,hadoop103");
        return ConnectionFactory.createConnection(conf);
    }

(2) 关闭

    public static void closeConnection(Connection connection){
        if (connection != null){
            try {
                // 关闭connection对象
                // 切记,要等到确定不使用的时候再关,因为Connection是一个重量级实现
                connection.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

3. DDL

(1) 创建namespace

    public static void createNamespace(Connection connection, String namespaceName){
        Admin admin = null;
        try {
            admin = connection.getAdmin();
            NamespaceDescriptor.Builder builder = NamespaceDescriptor.create(namespaceName);
            // 为要创建的namespace添加属性
            builder.addConfiguration("greet", "hello world!");
            admin.createNamespace(builder.build());
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (admin != null){
                try {
                    admin.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

(2) 判断表是否存在

    public static boolean exists(Connection connection, String namespaceName, String tableName){
        Admin admin = null;
        try {
            admin = connection.getAdmin();
            return admin.tableExists(TableName.valueOf(namespaceName, tableName));
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }finally {
            if (admin != null){
                try {
                    admin.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

(3) 创建表

    public static void createTable(Connection connection, String namespaceName, String tableName, String[] columnFamilies, int versions){
        // 判断表是否存在,存在就不重复创建
        if (exists(connection, namespaceName, tableName)){
            return;
        }
        Admin admin = null;
        try {
            admin = connection.getAdmin();
            TableDescriptorBuilder tableDescriptorBuilder = TableDescriptorBuilder.newBuilder(TableName.valueOf(namespaceName, tableName));
            tableDescriptorBuilder.setColumnFamilies(Arrays.stream(columnFamilies).map(s -> {
                // 使用了HBase提供的Bytes工具类
                ColumnFamilyDescriptorBuilder columnFamilyDescriptorBuilder = ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(s));
                // 设置列族保留多少个版本的列数据
                columnFamilyDescriptorBuilder.setMaxVersions(versions);
                return columnFamilyDescriptorBuilder.build();
            }).collect(Collectors.toList()));
            // 创建表
            admin.createTable(tableDescriptorBuilder.build());
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (admin != null){
                try {
                    admin.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

(4) 删除表

    public static void dropTable(Connection connection, String namespaceName, String tableName){
        // 判断表是否存在,如果不存在,就不执行删除操作
        if (!exists(connection, namespaceName, tableName)){
            return;
        }
        Admin admin = null;
        try {
            admin = connection.getAdmin();
            // 让表变为disable状态
            admin.disableTable(TableName.valueOf(namespaceName, tableName));
            // 删除表
            admin.deleteTable(TableName.valueOf(namespaceName, tableName));
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (admin != null){
                try {
                    admin.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

(5) 删除namespace

    public static void dropNamespace(Connection connection, String namespaceName){
        Admin admin = null;
        try {
            admin = connection.getAdmin();
            NamespaceDescriptor[] namespaceDescriptors = admin.listNamespaceDescriptors();
            if (Arrays.stream(namespaceDescriptors).anyMatch(namespaceDescriptor -> namespaceDescriptor.getName().equalsIgnoreCase(namespaceName))){
                // 判断要删除的namespace存在
                TableName[] tableNames = admin.listTableNames(Pattern.compile(namespaceName + ":.*"));
                if (tableNames.length != 0){
                    // 若namespace下有表,先删表
                    Arrays.stream(tableNames).forEach(tableName -> dropTable(connection, namespaceName, tableName.getQualifierAsString()));
                }
                // 删除没有表的namespace
                admin.deleteNamespace(namespaceName);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (admin != null){
                try {
                    admin.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

4. DML

(1) put数据/修改数据

    public static void putData(Connection connection, String namespaceName, String tableName, String rowKey, String columnFamily, String column, Object value){
        Table table = null;
        try {
            // 获取Table对象
            table = connection.getTable(TableName.valueOf(namespaceName, tableName));

            // 获取Put对象,该对象封装了多个列数据
            Put put = new Put(Bytes.toBytes(rowKey));

            // 封装列数据到put对象中
            // 等价于:put 'columnFamily:tableName', 'rowKey', 'value'
            put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(column), Bytes.toBytes(value.toString()));

            // 添加数据
            table.put(put);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (table != null){
                try {
                    table.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

(2) get一行记录

    public static void getRecord(Connection connection, String namespaceName, String tableName, String rowKey){
        Table table = null;
        try {
            table = connection.getTable(TableName.valueOf(namespaceName, tableName));

            // 创建Get对象,用来get数据
            Get get = new Get(Bytes.toBytes(rowKey));
            // 也可以通过调用Get对象的addxxx方法,指定获取的具体列等信息

            // 获取一条记录的列数据的集和
            Result result = table.get(get);
            String outputParttern = "{0} : {1} : {2} : {3}";
            for (Cell cell : result.rawCells()) {
                // 遍历列数据
                String rowKy = Bytes.toString(CellUtil.copyRow(cell));
                String columnFamily = Bytes.toString(CellUtil.cloneFamily(cell));
                String column = Bytes.toString(CellUtil.cloneQualifier(cell));
                String value = Bytes.toString(CellUtil.cloneValue(cell));
                System.out.println(MessageFormat.format(outputParttern, rowKy, columnFamily, column, value));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (table != null){
                try {
                    table.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

(3) scan表内范围记录

    public static void scanRecord(Connection connection, String namespace, String tableName, String startRow, String stopRow){
        Table table = null;
        try {
            table = connection.getTable(TableName.valueOf(namespace, tableName));

            // 创建Scan对象
            Scan scan = new Scan(Bytes.toBytes(startRow), Bytes.toBytes(stopRow));
            // 可以通过Scan对象的addxxx方法,来设定检索哪些列(默认检索出所有列)、最多检索几条记录等等条件;

            String outputParttern = "{0} : {1} : {2} : {3}";
            ResultScanner tableScanner = table.getScanner(scan);
            for (Result result : tableScanner) {
                // 遍历每一行表中的记录
                for (Cell cell : result.rawCells()) {
                    // 遍历列数据
                    String rowKey = Bytes.toString(CellUtil.copyRow(cell));
                    String columnFamily = Bytes.toString(CellUtil.cloneFamily(cell));
                    String column = Bytes.toString(CellUtil.cloneQualifier(cell));
                    String value = Bytes.toString(CellUtil.cloneValue(cell));
                    System.out.println(MessageFormat.format(outputParttern, rowKey, columnFamily, column, value));
                }
                System.out.println("===============================");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (table != null){
                try {
                    table.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

(4) 删除记录

    public static void delete(Connection connection, String namespace, String tableName, String rowKey){
        Table table = null;
        try {
            table = connection.getTable(TableName.valueOf(namespace, tableName));
            
            // 创建Delete对象,默认删除表中一整行记录的所有版本数据
            Delete delete = new Delete(Bytes.toBytes(rowKey));
            
            // 可以通过Delete对象的addxxx方法,来决定删除列族、列、整行记录的所有数据或者某一个版本的数据
            
            table.delete(delete);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (table != null){
                try {
                    table.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

三、优化

1. 预分区(提前划分RegionRegion是针对表来说的)

# 创建表的时候预先指定分区
# 分区为:[-∞, 'region1')、['region1', 'region2')、...
create 'namespace_name:table_name', {NAME=>'column_family1', VERSIONS=>num1}, {NAME=>'column_family2', VERSIONS=>num2}, ..., SPLITS=>['region1', 'region2', ...]

# 类似于SPLITS=>['region1', 'region2', ...],只不过是把分区内容写入了文件中
# 寻找文件的方法有两种:(1)相对路径,相对于启动hbase shell的路径;(2) 绝对路径
create 'namespace_name:table_name', {NAME=>'column_family1', VERSIONS=>num1}, {NAME=>'column_family2', VERSIONS=>num2}, ..., SPLITS_FILE=>'split_file_path'

# count为分区数量,分区为:[-∞, '11111111')、['11111111', '22222222')、...
# 具体每个Region的范围划分如何,由count数量来影响每个区域的范围
create 'namespace_name:table_name', {NAME=>'column_family1', VERSIONS=>num1}, {NAME=>'column_family2', VERSIONS=>num2}, ..., {NUMREGIONS=>count, SPLITALGO=>'HexStringSplit'}

2. Row Key设计思想

假设要提前划分50个分区,利用某种算法,求出可以唯一确定表中记录的字段值的集合(Row Key)所代表的值,用这个值取余分区数,将取余的结果拼接到Row Key的最前端,根据这个来设计分区范围,例如:

[-∞, '01!')

['01!', '02!')

......

3. 基础优化配置(habse-site.xml)

配置项解释
zookeeper.session.timeoutZookeeper与RegionServer之间的心跳间隔时间;默认为90000毫秒(90s)。
hbase.regionserver.handler.count默认值为30;用来响应客户端请求要开启的线程数量,当客户端请求频繁时,需要提高该值。
hbase.hregion.majorcompactionMajor Compaction的周期时间,默认配置为604800000秒(7天);如果想要禁用自动Major Compaction,设置该值为0即可。
hbase.hregion.max.filesize默认值为:10737418240(10GB);用来配置HFile的最大大小。
hbase.client.write.buffer默认值为:2097152bytes(2M);客户端缓存,用来接收hbase查询的数据,例如:某一次查询的数据量大小为10m,那么客户端需要远程通信5次才能完全读取所有查询的数据。据此,可以通过调大该值,来减少远程通信次数过多的性能花销。
hbase.client.scanner.cachingscan的时候,并不会将所有数据一次性读取到内存中,而是会分批次读取,该配置项用来配置一个批次读取多少条数据到内存中。
hfile.block.cache.size读缓存(Block Cache)最多占用JVM内存的比例,默认值为0.4
hbase.regionserver.global.memstore.size一个RegionServer中所有写缓存(MemStore)占用JVM内存的最大比例,默认值为0.4

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值