Hbase系列-2、Hbase基础

Hbase系列-2、Hbase基础,第1张

Hbase系列-2、Hbase基础

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

数据系列文章目录

Hbase的官网

目录
  • Hbase的基本简介
    • Hbase的基本介绍
    • Hbase的应用场景
    • Hbase的发展历程
    • Hbase的特点
    • Hbase与RDBMS、HDFS 、Hive的区别
  • Hbase的表模型
  • Hbase的相关 *** 作-命令式
    • Hbase的基础 *** 作命令
    • Hbase高级shell管理命令
  • Hbase的相关 *** 作_JavaAPI方式
    • 需求说明
    • 准备工作
    • 需求一: 使用java代码创建表
    • 需求二: 往表中插入一条数据
    • 需求三:查看一条数据
    • 需求四:删除一条数据
    • 需求五:删除表
    • 需求六:导入数据
    • 需求七:查询数据 查询2020年6月份所有用户的用水量 : C1:RECORD_DATE

Hbase的基本简介 Hbase的基本介绍
  • NoSQL是一个通用术语,泛指一个数据库并不是使用SQL作为主要语言的非关系型数据库
  • Hbase是BigTable的开源java版本。是建立在HDFS之上,提供高可靠性、高性能、列存储、可伸缩、实时读写NoSQL的数据库系统
  • Hbase仅能通过主键(row key)和主键的range来检索数据,仅支持单行事务
  • 主要用来存储结构化和半结构化的松散数据
  • Hbase查询数据功能很简单,不支持join等复杂 *** 作,不支持复杂的事务(行级的事务),从技术上来说,Hbase更像是一个「数据
    存储」而不是「数据库」,因为Hbase缺少RDBMS中的许多特性,例如带类型的列、二级索引以及高级查询语言等
  • Hbase中支持的数据类型:byte[]
  • 与Hadoop一样,Hbase目标主要依靠横向扩展,通过不断增加廉价的商用服务器,来增加存储和处理能力,例如,把集群从10个节点扩展到20个节点,存储能力和处理能力都会加倍
  • Hbase中的表一般有这样的特点
    大:一个表可以有上十亿行,上百万列
    面向列:面向列(族)的存储和权限控制,列(族)独立检索
    稀疏:对于为空(null)的列,并不占用存储空间,因此,表可以设计的非常稀疏
Hbase的应用场景
  • 对象存储

不少的头条类、新闻类的新闻、网页、图片存储在Hbase之中, 一些病毒公司的病毒库也是存储在Hbase中

  • 时序数据

Hbase之上有openTSDB模块, 可以满足时序类场景的需求

  • 推荐画像

用户画像, 是一个比较大的稀疏矩阵, 蚂蚁金服的风控就是构建在Hbase之上

  • 时空数据

主要是轨迹, 气象网格之类, 滴滴打车的轨迹数据主要存在Hbase之中, 另外在所有大一点的数据量的车联网企业, 数据也是存储
在Hbase

  • CubeDb OLAP

kylin 一个cube分析工具, 底层的数据就是存储在Hbase之中, 不少客户自己基于离线计算构建cube存储在hbase之中, 满足在线报表查询的需求

  • 消息/订单

在电信领域、银行领域, 不少的订单查询底层的存储, 另外不少通信、消息同步的应用构建Hbase之上

  • Feeds流

典型的应用就是xx朋友圈类型的应用, 用户可以随时发布新内容, 评论、点赞

  • NewSQL

之上有Phoenix的插件, 可以满足二级索引, SQL的查询, 对接传统数据需要SQL非事务的需求

  • 其他

存储爬虫数据
海量数据备份
短网址
…………

Hbase的发展历程

Hbase的特点
  • 强一致性读/写: Hbase不是“最终一致的”数据存储 , 它非常适合于诸如高速计数器聚合等任务

  • 自动分块: Hbase表通过Region分布在集群上,随着数据的增长,区域被自动拆分和重新分布

  • 自动RegionServer故障转移

  • Hadoop/HDFS集成: Hbase支持HDFS开箱即用作为其分布式文件系统

  • MapReduce : Hbase通过MapReduce支持大规模并行处理,将Hbase用作源和接收器

  • Java Client API: Hbase支持易于使用的 Java API 进行编程访问

  • Thrift/REST API

  • 块缓存和布隆过滤器 : Hbase支持块Cache和Bloom过滤器进行大容量查询优化

  • 运行管理: Hbase为业务洞察和JMX度量提供内置网页。

Hbase与RDBMS、HDFS 、Hive的区别


Hbase的表模型

在Hbase中,数据存储在具有行和列的表中。这是看起来关系数据库(RDBMS)一样,但将Hbase表看成是多个维度的Map结构更容易理解


术语:

  • 表(Table) : Hbase中数据都是以表形式来组织的, Hbase中的表由多个行组成
  • 行键(row key):

Hbase中的行有一个rowkey(行键)和 一个或者多个列组成, 列的值与rowkey、列相关联
行在存储是按行键的字典序排序
行键的设计非常重要, 尽量让相关的行存储在一起

  • 列(Column): Hbase中的列有列族(column family) 和列限定符(列名)(Column Qualifier)组成

表示如下 : 列族名:列限定符 例如: C1:USER_ID C1:SEX

  • 列族(Column Family):

出于性能原因, 列族将一组列及其值组织在一起
每个列族都有一组存储属性: 例如 是否应该换成在内存中, 数据如何被压缩等
表中的每一行都有相同的列族, 但在列族中不存储任何内容
所有的列族的数据全部都存储在一块(文件系统HDFS)
Hbase官方建议所有的列族保持一样的列, 并且将同一类的列放在一个列族中

  • 列标识符(Column Qualifier)

列族中包含一个个的列限定符, 这样可以为存储的数据提供索引
列族在创建表的时候是固定的, 但列限定符是不做限制的
不同的列可能会存在不同的列标识符

  • 单元格(Cell): 单元格是行、列族和列限定符的组合,包含一个值和一个时间戳, 数据以二进制存储
  • 版本号(verson num): 每条数据都会有版本号的概念

每条数据都可以有多个版本号, 默认值为系统时间戳, 类型为Long

  • 时间戳(timeStamp): 每个数据都会有时间戳的概念

在向Hbase插入更新数据的时候, Hbase默认会将当前 *** 作的时间记录下来, 当然也可以人为指定时间
不同版本的数据按照时间倒序排序, 即最新的数据排在最前面

Hbase的相关 *** 作-命令式

我们可以以shell的方式来维护和管理Hbase。例如:执行建表语句、执行增删改查 *** 作等等

Hbase的基础 *** 作命令

1、进入Hbase客户端命令 *** 作界面

bin/hbase shell

2、查看帮助命令

hbase(main):001:0> help

3、查看当前数据库中有那些表

hbase(main):002:0> list

4、创建一张表

hbase(main):010:0> create 'user', 'info', 'data' 或者
hbase(main):010:0> create 'user', {NAME => 'info', VERSIONS => '3'},{NAME
=> 'data'}

5、添加数据 *** 作

向user表中插入信息,row key为rk0001,列族info中添加name列标示符,值为zhangsan
 hbase(main):011:0> put 'user', 'rk0001', 'info:name', 'zhangsan'

向user表中插入信息,row key为rk0001,列族info中添加gender列标示符,值为female 
hbase(main):012:0> put 'user', 'rk0001', 'info:gender', 'female'

向user表中插入信息,row key为rk0001,列族info中添加age列标示符,值为20 
hbase(main):013:0> put 'user', 'rk0001', 'info:age', 20

向user表中插入信息,row key为rk0001,列族data中添加pic列标示符,值为picture 
hbase(main):014:0> put 'user', 'rk0001', 'data:pic', 'picture

6、查询数据 *** 作

6.1、通过rowkey进行查询
获取user表中row key为rk0001的所有信息
hbase(main):015:0> get 'user', 'rk0001'

6.2	、查看rowkey下面的某个列族的信息
获取user表中row key为rk0001,info列族的所有信息
hbase(main):016:0> get 'user', 'rk0001', 'info'

6.3	、查看rowkey指定列族指定字段的值
获取user表中row key为rk0001,info列族的name、age列标示符的信息
hbase(main):017:0> get 'user', 'rk0001', 'info:name', 'info:age’

6.4	、查看rowkey指定多个列族的信息
获取user表中row key为rk0001,info、data列族的信息

hbase(main):018:0> get 'user', 'rk0001', 'info', 'data' 

或者你也可以这样写

hbase(main):019:0> get 'user', 'rk0001', {COLUMN => ['info', 'data']}

或者你也可以这样写,也行

hbase(main):020:0> get 'user', 'rk0001', {COLUMN => ['info:name', 'data:pic’]}

6.5	、指定rowkey与列值查询
获取user表中row key为rk0001,cell的值为zhangsan的信息
hbase(main):030:0> get 'user', 'rk0001', {FILTER => "ValueFilter(=, 'binary:zhangsan')"}

6.6、查询所有数据 : 查询user表中的所有信息
scan 'user'
scan 'user' , {FORMATTER => 'toString’}
scan 'user' , {LIMIT => 3,FORMATTER => 'toString’} 

6.7、列族查询: 查询user表中列族为info的信息
scan 'user', {COLUMNS => 'info'}
scan 'user', {COLUMNS => 'info', RAW => true, VERSIONS => 5} scan 'user', {COLUMNS => 'info', RAW => true, VERSIONS => 3}

6.8	、多列族查询: 查询user表中列族为info和data的信息
scan 'user', {COLUMNS => ['info', 'data']}
scan 'user', {COLUMNS => ['info:name', 'data:pic']}

6.9	、指定列族与某个列名查询
查询user表中列族为info、列标示符为name的信息
scan 'user', {COLUMNS => 'info:name’}

6.10、指定列族与列名以及限定版本查询
查询user表中列族为info、列标示符为name的信息,并且版本最新的5个
scan 'user', {COLUMNS => 'info:name', VERSIONS => 5}

6.11、指定多个列族与按照数据值模糊查询
查询user表中列族为info和data且列标示符中含有a字符的信息
scan 'user', {COLUMNS => ['info', 'data'], FILTER => "(QualifierFilter(=,'substring:a'))"}

6.12、rowkey的范围值查询
查询user表中列族为info,rk范围是[rk0001, rk0003)的数据
scan 'user', {COLUMNS => 'info', STARTROW => 'rk0001', ENDROW => 'rk0003'}

6.13、指定rowkey模糊查询
查询user表中row key以rk字符开头的
scan 'user',{FILTER=>"PrefixFilter('rk')"}

6.14、指定数据范围值查询 查询user表中指定范围的数据
scan 'user', {TIMERANGE => [1392368783980, 1392380169184]}

过滤器的查询地址:
http://hbase.apache.org/2.2/devapidocs/index.html

7、更新数据 *** 作

1、更新数据值
更新 *** 作同插入 *** 作一模一样,只不过有数据就更新,没数据就添加

2、更新版本号
将user表的f1列族版本号改为5
hbase(main):050:0> alter 'user', NAME => 'info', VERSIONS => 5

8、删除数据以及删除表 *** 作

1、指定rowkey以及列名进行删除
删除user表row key为rk0001,列标示符为info:name的数据
hbase(main):045:0> delete ‘user’, ‘rk0001’, ‘info:name’

2、指定rowkey,删除一整行数据hbase(main):045:0>deleteall ‘user’, 'rk0001’
注意:
(1)deleteall 是在 hbase 2.0版本后出现的, 在2.0版本之前, 只需要使用delete这个命令即可完成所有的删除数据工作,
(2)delete删除数据时候, 只会删除最新版本的数据, 而deleteall 直接将对应数据的所有的历史版本全部删除

3、删除一个列族: alter ‘user’, NAME => ‘info’, METHOD => 'delete’ 或 alter
‘user’, ‘delete’ => ‘info’

4、清空表数据 hbase(main):017:0> truncate ‘user’

5、删除表 首先需要先让该表为disable状态,使用命令: hbase(main):049:0> disable ‘user’
然后才能drop这个表,使用命令:hbase(main):050:0> drop ‘user’ (注意:如果直接drop表,会报错:Drop
the named table. Table must first be disabled)

9、统计一张表有多少行数据

hbase(main):053:0> count ‘user’

Hbase高级shell管理命令
  • status 显示服务器状态 “例如:hbase(main):058:0> status ‘node01’
  • whoami : 显示Hbase当前用户,例如: hbase> whoami
  • list : 显示当前所有的表
  • count: 统计指定表的记录数,例如: hbase> count ‘user’
  • describe : 展示表结构信息
  • exists: 检查表是否存在,适用于表量特别多的情况
  • is_enabled、is_disabled: 检查表是否启用或禁用
  • alter : 该命令可以改变表和列族的模式,
    例如: 为当前表增加列族: hbase> alter ‘user’, NAME => ‘CF2’, VERSIONS => 2
    为当前表删除列族: hbase(main):002:0> alter ‘user’, ‘delete’ => ‘CF2’
  • disable/enable : 禁用一张表/启用一张表
  • drop : 删除一张表,记得在删除表之前必须先禁用
  • truncate : 禁用表-删除表-创建表
Hbase的相关 *** 作_JavaAPI方式 需求说明
  • 某某自来水公司,需要存储大量的缴费明细数据。以下截取了缴费明细的一部分内容

  • 因为缴费明细的数据记录非常庞大,该公司的信息部门决定使用Hbase来存储这些数据。并且,他们希望能够 通过Java程序来访问这些数据。
准备工作

创建IDEA Maven 项目

  • 导入相关pom依赖
 
        
            aliyun
            http://maven.aliyun.com/nexus/content/groups/public/
            
                true
            
            
                false
                never
            
        
    

    
        
            org.apache.hbase
            hbase-client
            2.1.0
        
        
            commons-io
            commons-io
            2.6
        
        
            junit
            junit
            4.12
            test
        
        
            org.testng
            testng
            6.14.3
            test
        
    
    
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                3.1
                
                    1.8
                    1.8
                
            
        
    
需求一: 使用java代码创建表
  • 创建一个名为WATER_BILL的表,包含一个列蔟C1。
  // 1) 如何创建表: WATER_BILL 列族为C1 和 C2
    @Test
    public void test01() throws Exception {
        //1. 创建Java 连接hbase的连接对象Configuration conf = HbaseConfiguration.create();
        conf.set("hbase.zookeeper.quorum", "node1:2181,node2:2181,node3:2181");
        Connection hbaseConn = ConnectionFactory.createConnection(conf);
        //2. 根据连接对象获取相关的管理对象: admin |  Table Admin admin = hbaseConn.getAdmin();
        //3     . 执行相关的 *** 作:
        //3.1: 判断目标表是否存在, 如果不存在 才创建表 : 如果存在返回true 如果不存在 false
        TableName tableName = TableName.valueOf("WATER_BILL");
        boolean flag = admin.tableExists(tableName);
        if (!flag) {    // 说明表不存在:
            //3.3: 创建表的构建器
            TableDescriptorBuilder descriptorBuilder = TableDescriptorBuilder.newBuilder(tableName);
            List families = new ArrayList<>();
            families.add(ColumnFamilyDescriptorBuilder.newBuilder("C1".getBytes()).build());
            //families.add(ColumnFamilyDescriptorBuilder.newBuilder("C2".getBytes()).build());
            //3.4: 在表构建器中添加 表的列族信息
            descriptorBuilder.setColumnFamilies(families);
            //3.5: 通过表构建器, 构建表信息对象
            TableDescriptor desc = descriptorBuilder.build();
            //3.2: 执行创建表的 *** 作
            admin.createTable(desc);

        }
        //4. 释放资源admin.close(); hbaseConn.close();
    }
需求二: 往表中插入一条数据
 @Test
    public void test02() throws Exception {
        //1. 根据连接工厂构建hbase的连接对象
        Configuration conf = HbaseConfiguration.create(); conf.set("hbase.zookeeper.quorum", "node1:2181,node2:2181,node3:2181"); Connection hbaseConn = ConnectionFactory.createConnection(conf);
        //2. 根据连接对象, 获取相关的管理对象: admin table
        Table table = hbaseConn.getTable(TableName.valueOf("WATER_BILL"));
        //3. 执行相关的 *** 作
        Put put = new Put("4944191".getBytes());
        put.addColumn("C1".getBytes(), "name".getBytes(), " 登 卫 红 ".getBytes()); put.addColumn("C1".getBytes(), "address".getBytes(), "贵州省铜仁市德江县7单元267室".getBytes()); put.addColumn("C2".getBytes(), "sex".getBytes(), "男".getBytes());
        table.put(put);
        //4. 释放资源
        table.close(); hbaseConn.close();
    }
  • 优化 *** 作: 抽取公共方法
  private Connection hbaseConn;
    private Admin admin;
    private Table table;
    private String tableName = "WATER_BILL";

    @Before
    public void before() throws Exception {
        //1. 根据连接工厂构建hbase的连接对象
        Configuration conf = HbaseConfiguration.create();
        conf.set("hbase.zookeeper.quorum", "node1:2181,node2:2181,node3:2181");
        hbaseConn = ConnectionFactory.createConnection(conf);
        //2. 根据连接对象, 获取相关的管理对象: admin table
        admin = hbaseConn.getAdmin();
        table = hbaseConn.getTable(TableName.valueOf(tableName));

    }

    @After
    public void after() throws Exception {
        //4. 释放资源
        table.close();
        admin.close();
        hbaseConn.close();
    }
需求三:查看一条数据
 //6. 查询rowkey 为 9990611 的数据
    @Test
    public void test06() throws Exception {
        //3. 执行相关的 *** 作
        //3.1: 执行查询
        Get get = new Get("9990611".getBytes());
        Result result = table.get(get);
        //3.2: 处理结果集
        List cellList = result.listCells(); // 获取 这一行所有的单元格
        for (Cell cell : cellList) { // 单元格: rowkey + 列族+ 列名+ 列值
            byte[] rowkeyBytes = CellUtil.cloneRow(cell);
            byte[] familyBytes = CellUtil.cloneFamily(cell);
            byte[] qualifierBytes = CellUtil.cloneQualifier(cell);
            byte[] valueBytes = CellUtil.clonevalue(cell);
            String rowkey = Bytes.toString(rowkeyBytes);
            String family = Bytes.toString(familyBytes);
            String qualifier = Bytes.toString(qualifierBytes);
            if (qualifier.equals("NUM_CURRENT") || qualifier.equals("NUM_PREVIOUS") || qualifier.equals("TOTAL_MONEY") || qualifier.equals("NUM_USAGE")) {
                Double value2 = Bytes.toDouble(valueBytes);
                System.out.println("rowkey为:" + rowkey + "; family:" + family + "; qualifier:" + qualifier + "; value:" + value2);
            } else {
                String value1 = Bytes.toString(valueBytes);
                System.out.println("rowkey为:" + rowkey + "; family:" + family + "; qualifier:" + qualifier + "; value:" + value1);
            }
        }
    }
需求四:删除一条数据
 //3. 删除数据: 4944191
    @Test
    public void test03() throws Exception {
        //3. 执行相关 *** 作:
        Delete delete = new Delete("4944191".getBytes());
        delete.addFamily("C2".getBytes());
        delete.addColumn("C1".getBytes(), "name".getBytes());
        table.delete(delete);
    }
需求五:删除表
//4. 删除表:
    @Test
    public void test04() throws Exception {
        //3. 执行相关 *** 作
        if (admin.isTableEnabled(TableName.valueOf(tableName))) {
            admin.disableTable(TableName.valueOf(tableName));
        }
        admin.deleteTable(TableName.valueOf(tableName));
    }
需求六:导入数据
  • 在资料中,有一份10W的抄表数据文件,我们需要将这里面的数据导入到Hbase中

在Hbase中,有一个import的MapReduce作业,可以专门用来将数据文件导入到Hbase中

  • 用法
hbase org.apache.hadoop.hbase.mapreduce.import 表名 HDFS数据文件路径

开始导入:

  1. 将资料中数据文件上传到Linux中
  2. 再将文件上传到hdfs中
hadoop fs -mkdir -p /water_bill/output_ept_10W
hadoop fs -put part-m-00000_10w /water_bill/output_ept_10W

执行命令:

hbase org.apache.hadoop.hbase.mapreduce.import WATER_BILL /water_bill/output_ept_10W

注意: 一定要启动yarn集群

hbase org.apache.hadoop.hbase.mapreduce.Export WATER_BILL /water_bill/output_ept_10W_export
需求七:查询数据 查询2020年6月份所有用户的用水量 : C1:RECORD_DATE
 @Test
    public void test05() throws Exception {
        //3. 执行相关的 *** 作: scan 扫描
        Scan scan = new Scan();
        //3.1: 封装过滤条件
        SingleColumnValueFilter singleColumnValueFilter1 =
                new SingleColumnValueFilter("C1".getBytes(), "RECORD_DATE".getBytes(), CompareOperator.GREATER_OR_EQUAL, new BinaryComparator("2020-06- 01".getBytes()));
        SingleColumnValueFilter singleColumnValueFilter2 =
                new SingleColumnValueFilter("C1".getBytes(), "RECORD_DATE".getBytes(), CompareOperator.LESS, new BinaryComparator("2020-07-01".getBytes()));
        FilterList filterList = new FilterList();
        filterList.addFilter(singleColumnValueFilter1);
        filterList.addFilter(singleColumnValueFilter2);
        scan.setFilter(filterList);
        //3.2: 执行扫描 *** 作 返回多行结果数据
        ResultScanner results = table.getScanner(scan);
        //3.3: 处理结果集
        for (Result result : results) {
            List cellList = result.listCells();  // 获取这一行所有的单元格
            for (Cell cell : cellList) { // 单元格: rowkey + 列族+ 列名+ 列值
                byte[] rowkeyBytes = CellUtil.cloneRow(cell);
                byte[] familyBytes = CellUtil.cloneFamily(cell);
                byte[] qualifierBytes = CellUtil.cloneQualifier(cell);
                byte[] valueBytes = CellUtil.clonevalue(cell);
                String rowkey = Bytes.toString(rowkeyBytes);
                String family = Bytes.toString(familyBytes);
                String qualifier = Bytes.toString(qualifierBytes);

                if (qualifier.equals("NUM_CURRENT") || qualifier.equals("NUM_PREVIOUS") || qualifier.equals("TOTAL_MONEY") || qualifier.equals("NUM_USAGE")) {
                    Double value = Bytes.toDouble(valueBytes);
                    System.out.println("rowkey为:" + rowkey + "; family:" + family + "; qualifier:" + qualifier + "; value:" + value);
                } else {
                    String value = Bytes.toString(valueBytes);
                    System.out.println("rowkey为:" + rowkey + "; family:" + family + "; qualifier:" + qualifier + "; value:" + value);
                }
            }
            System.out.println("	");
        }
    }

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5695866.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存