HBase入门学习

在这里插入图片描述

一、概述

Apache HBase 是Hadoop数据库,一个分布式、可伸缩的大数据存储。
当需要对大数据进行随机、实时的读写访问时,请使用Apache HBase™。
该项目的目标是在普通硬件集群上托管非常大的表(数十亿行X数百万列)。Apache HBase是一个开源的、分布式的、版本化的、非关系型数据库,它模仿了谷歌的Bigtable:一个用于结构化数据的分布式存储系统。
正如Bigtable利用谷歌文件系统提供的分布式数据存储一样,HBase在Hadoop和HDFS之上提供了类似Bigtable的功能。
Apache HBase 建立在Hadoop的HDFS之上

1、列存储和行存储

列存储和行存储指的是数据在存储介质中的组织方式

传统关系型数据库(行存储):Oracle、Mysql、DB2、SqlServer等

非关系型数据库(列存储):HBase等
在这里插入图片描述

2、HBase数据模型

在这里插入图片描述

  • 主键:rowkey 获取数据的唯一标示,不可以重复,根据字典顺序自动排序,底层存储bytes[ ]
  • 列簇:Column Family 多个列的集合,通常一个列簇中存放的是一组功能相似或者业务相近的列的集合
  • :Column 列簇中的一个字段,用来存放某一类别的数据
  • 单元格:Cell rowkey+column family+column 定位一个单元格,一个cell有多个版本,默认有1个
  • 多版本:一个单元格有多个数据版本
  • 版本号:系统当前的时间戳,默认会将时间戳最新的cel中的数据返回给用户
3、特点
  • 大:一个表可以有上百亿行,上百万列。
  • Linear and modular scalability:线性和模块化的可扩展性。
  • Strictly consistent reads and writes:严格一致的读写。
  • Automatic and configurable sharding of tables :自动且可配置的表分片
  • Automatic failover support between RegionServers:RegionServers之间的自动故障转移支持。
  • 面向列:面向列表(簇)的存储和权限控制,列(簇)独立检索。
  • 结构稀疏:对于为空(NULL)的列,并不占用存储空间,因此,表可以设计的非常稀疏。
  • 无模式:每一行都有一个可以排序的主键和任意多的列,列可以根据需要动态增加,同一张表中不同的行可以有截然不同的列。
  • 数据多版本:每个单元中的数据可以有多个版本,默认情况下,版本号自动分配,版本号就是单元格插入时的时间戳。
  • 数据类型单一:HBase中的数据在底层存储时都是byte[],可以存放任意类型的数据。

二、基本使用

1、伪分布式集群环境搭建
  • 保证HDFS集群运行正常
  • 保证ZooKeeper集群运行正常
[root@hadoop ~]# jps
83920 SecondaryNameNode
83602 NameNode
83698 DataNode
2548 QuorumPeerMain
  • 安装配置
[root@hadoop ~]# tar -zxf hbase-1.2.4-bin.tar.gz -C /usr

conf/hbase-site.xml

<property>
  <name>hbase.rootdir</name>
  <value>hdfs://主机映射名:9000/hbase</value>
</property>
<property>
  <name>hbase.cluster.distributed</name>
  <value>true</value>
</property>
<property>
  <name>hbase.zookeeper.quorum</name>
  <value>主机映射名</value>
</property>
<property>
  <name>hbase.zookeeper.property.clientPort</name>
  <value>2181</value>
</property>

conf/regionservers

hadoop #主机的映射名
  • 修改环境变量
[root@hadoop hbase-1.2.4]# vi ~/.bashrc
# 将之前的环境变量配置删除 添加如下的环境变量配置
HBASE_MANAGES_ZK=false
# hbase安装目录
HBASE_HOME=/usr/hbase-1.2.4
# hadoop安装目录
HADOOP_HOME=/usr/hadoop-2.6.0
JAVA_HOME=/usr/java/latest
CLASSPATH=.
PATH=$PATH:$JAVA_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin::$HBASE_HOME/bin
export JAVA_HOME
export CLASSPATH
export PATH
export HADOOP_HOME
export HBASE_HOME
export HBASE_MANAGES_ZK
[root@hadoop hbase-1.2.4]# source ~/.bashrc
  • 启动服务
[root@hadoop hbase-1.2.4]# start-hbase.sh
  • 验证HBase服务是否正常
[root@hadoop hbase-1.2.4]# jps
83920 SecondaryNameNode
85104 HRegionServer  # HBase集群的从节点
84963 HMaster  # HBase集群的主节点
83602 NameNode
83698 DataNode
2548 QuorumPeerMain
85516 Jps
2、指令操作
  • 使用客户端指令连接hbase server
[root@hadoop hbase-1.2.4]# hbase shell
  • 在命令窗口使用help指令查看帮助说明
hbase(main):007:0> help "get"  # help "命令"  查看指定命令的帮助说明
hbase(main):007:0> help "general"  # help "命令组"  查看指定命令组下的命令帮助说明
COMMAND GROUPS:
  Group name: general  # 通用指令
  Commands: status, table_help, version, whoami

  Group name: ddl   # 表操作指令
  Commands: alter, alter_async, alter_status, create, describe, disable, disable_all, drop, drop_all, enable, enable_all, exists, get_table, is_disabled, is_enabled, list, locate_region, show_filters

  Group name: namespace  # 类似于mysql的数据库 组织管理表
  Commands: alter_namespace, create_namespace, describe_namespace, drop_namespace, list_namespace, list_namespace_tables

  Group name: dml  # 数据的CRUD
  Commands: append, count, delete, deleteall, get, get_counter, get_splits, incr, put, scan, truncate, truncate_preserve
  • General指令

status

hbase(main):010:0* status
1 active master, 0 backup masters, 1 servers, 0 dead, 2.0000 average load

version

hbase(main):013:0* version
1.2.4, rUnknown, Wed Feb 15 18:58:00 CST 2017

whoami

hbase(main):014:0> whoami
root (auth:SIMPLE)
    groups: root
  • NameSpace指令

Namespace非常类似于mysql中的数据库,是用来组织管理HBase表的,在HBase有一个默认的Namespace叫做default

create_namespace

Examples:
hbase> create_namespace 'ns1'
hbase> create_namespace 'ns1', {'PROPERTY_NAME'=>'PROPERTY_VALUE'}

hbase(main):002:0> create_namespace 'ns1'
0 row(s) in 0.1720 seconds
hbase(main):002:0> create_namespace 'ns2' ,{'author'=>'txb'}
0 row(s) in 0.6490 seconds

describe_namespace

hbase(main):003:0> describe_namespace 'ns2'
DESCRIPTION                                                         
{NAME => 'ns2', author => 'txb'}                                    
1 row(s) in 0.0490 seconds

alter_namespace

hbase(main):022:0* alter_namespace 'ns2',{METHOD=>'set', 'AUTHOR'=>'tianxb'}
0 row(s) in 0.0440 seconds

drop_namespace

hbase(main):026:0> drop_namespace 'ns2'
0 row(s) in 0.0460 seconds

list_namespace

hbase(main):003:0> list_namespace
NAMESPACE                                                                                                                             
default                                                                                                                               
ems                                                                                                                                   
hbase                                                                                                                                 
ns1                                                                                                                                   
txb                                                                                                                                   
5 row(s) in 0.1540 seconds

list_namespace_tables

hbase(main):004:0> list_namespace_tables 'hbase'
TABLE                                                                                                                                 
meta                                                                                                                                  
namespace                                                                                                                             
2 row(s) in 0.1090 seconds
  • DDL指令

表相关的操作
创建表:create

# 1. 语法: create '表名','列簇1',’列簇2‘...
# 2. 语法: create 'namespace:表名',{NAME=>'列簇名',VERSIONS=>Cell允许出现的最多版本}
hbase(main):008:0> create 'ns1:table1' ,'cf1'
0 row(s) in 2.9510 seconds
=> Hbase::Table - ns1:table1

展示列表: list

hbase(main):001:0> list
TABLE                                                               
ems:t_user                                                          
ns1:table1                                                          
t_order                                                             
t_result                                                            
4 row(s) in 1.1120 seconds

修改表:alter

hbase(main):005:0* alter 't_user',NAME=>'cf1',TTL=>1800
Updating all regions with the new schema...
1/1 regions updated.
Done.
0 row(s) in 2.3520 seconds

hbase(main):006:0> describe 't_user'
Table t_user is ENABLED
t_user
COLUMN FAMILIES DESCRIPTION
{NAME => 'cf1', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCOD
ING => 'NONE', TTL => '1800 SECONDS (30 MINUTES)', COMPRESSION => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSI
ZE => '65536', REPLICATION_SCOPE => '0'}
1 row(s) in 0.0340 seconds

hbase(main):008:0* alter 't_user',{NAME=>'cf1',VERSIONS=>3}
Updating all regions with the new schema...
1/1 regions updated.
Done.
0 row(s) in 1.9960 seconds

hbase(main):009:0> describe 't_user'
Table t_user is ENABLED
t_user
COLUMN FAMILIES DESCRIPTION
{NAME => 'cf1', BLOOMFILTER => 'ROW', VERSIONS => '3', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCOD
ING => 'NONE', TTL => '1800 SECONDS (30 MINUTES)', COMPRESSION => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSI
ZE => '65536', REPLICATION_SCOPE => '0'}
1 row(s) in 0.0370 seconds

描述表:describe

hbase(main):002:0> describe 'ns1:table1'
Table ns1:table1 is ENABLED                                         
ns1:table1                                                          
COLUMN FAMILIES DESCRIPTION                                         
{NAME => 'cf1', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 
'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE
', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', BLO
CKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'}  
1 row(s) in 0.5450 seconds

禁用表:disable, disable_all

hbase(main):011:0* disable 't_user'
0 row(s) in 2.3580 seconds

删除表:drop, drop_all

# 删除表时 首先需要禁用表
hbase(main):018:0> drop 't_user'
ERROR: Table t_user is enabled. Disable it first.
Here is some help for this command:
Drop the named table. Table must first be disabled:
  hbase> drop 't1'
  hbase> drop 'ns1:t1'

hbase(main):019:0> disable 't_user'
0 row(s) in 2.2830 seconds

hbase(main):020:0> drop 't_user'
0 row(s) in 1.3090 seconds

启动表:enable, enable_all

hbase(main):017:0* enable 't_user'
0 row(s) in 1.3400 seconds

判断表是否存在:exists

hbase(main):021:0> exists 't_user'
Table t_user does not exist
0 row(s) in 0.0240 seconds

hbase(main):022:0> exists 't_order'
Table t_order does exist
0 row(s) in 0.0240 seconds

是不是:is_disabled, is_enabled

hbase(main):023:0> is_disabled 't_order'
false
0 row(s) in 0.0150 seconds
  • DML指令(重点)
    获得总记录数:count
hbase(main):004:0> count 't_order'
6 row(s) in 0.2490 seconds
=> 6

新增(修改)数据:put

hbase(main):010:0> put 't_student' ,'order100','cf1:name' ,'huawei'
0 row(s) in 0.9320 seconds

hbase(main):011:0> put 't_student' ,'order100','cf1:price','2000'
0 row(s) in 0.0460 seconds

hbase(main):012:0> put 't_student' ,'order100','cf1:count','13'
0 row(s) in 0.0490 seconds

获得数据:get

  hbase> get 'ns1:t1', 'r1'
  hbase> get 't1', 'r1'
  hbase> get 't1', 'r1', {TIMERANGE => [ts1, ts2]}
  hbase> get 't1', 'r1', {COLUMN => 'c1'}
  hbase> get 't1', 'r1', {COLUMN => ['c1', 'c2', 'c3']}
  hbase> get 't1', 'r1', {COLUMN => 'c1', TIMESTAMP => ts1}
  hbase> get 't1', 'r1', {COLUMN => 'c1', TIMERANGE => [ts1, ts2], VERSIONS => 4}
  hbase> get 't1', 'r1', {COLUMN => 'c1', TIMESTAMP => ts1, VERSIONS => 4}
  hbase> get 't1', 'r1', {FILTER => "ValueFilter(=, 'binary:abc')"}
  hbase> get 't1', 'r1', 'c1'
  hbase> get 't1', 'r1', 'c1', 'c2'
  hbase> get 't1', 'r1', ['c1', 'c2']
  hbase> get 't1', 'r1', {COLUMN => 'c1', ATTRIBUTES => {'mykey'=>'myvalue'}}
  hbase> get 't1', 'r1', {COLUMN => 'c1', AUTHORIZATIONS => ['PRIVATE','SECRET']}
  hbase> get 't1', 'r1', {CONSISTENCY => 'TIMELINE'}
  hbase> get 't1', 'r1', {CONSISTENCY => 'TIMELINE', REGION_REPLICA_ID => 1}


hbase(main):018:0> get 't_student','order100'
COLUMN                             CELL                                                                                               
 cf1:count                         timestamp=1571031648959, value=13                                                                  
 cf1:name                          timestamp=1571031620863, value=huawei                                                              
 cf1:price                         timestamp=1571031638523, value=2000                                                                
3 row(s) in 0.1120 seconds

扫描表: scan

hbase(main):020:0> scan 't_student'
ROW                                COLUMN+CELL                                                                                        
 order100                          column=cf1:count, timestamp=1571031648959, value=13                                                
 order100                          column=cf1:name, timestamp=1571031620863, value=huawei                                             
 order100                          column=cf1:price, timestamp=1571031638523, value=2000                                              
1 row(s) in 0.0980 seconds

删除: delete, deleteall

hbase(main):025:0> delete 't_student','order100','cf1:name'
0 row(s) in 0.0350 seconds
hbase(main):029:0> deleteall 't_student','order100'
0 row(s) in 0.0100 seconds

截断表:truncate

截断指的删除表中的所有数据

hbase(main):065:0> truncate 't_order'
truncate            truncate_preserve
hbase(main):065:0> truncate 't_order'
Truncating 't_order' table (it may take a while):
 - Disabling table...
 - Truncating table...
0 row(s) in 3.5150 seconds

hbase(main):066:0> scan 't_order'
ROW                              COLUMN+CELL
0 row(s) in 0.1550 seconds
3、JAVA API

Maven依赖

<dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-client</artifactId>
    <version>1.2.4</version>
</dependency>
<dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-common</artifactId>
    <version>1.2.4</version>
</dependency>
<dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-protocol</artifactId>
    <version>1.2.4</version>
</dependency>
<dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-server</artifactId>
    <version>1.2.4</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

测试代码

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;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

public class HBaseClientTest {

    // 管理员对象(负责DDL操作)
    private Admin admin;

    // 连接对象(负责DML操作)
    private Connection connection;

    @Before
    public void doBefore() throws IOException {
        // 配置对象
        Configuration configuration = HBaseConfiguration.create();
        // 声明HBase的连接参数
        // HBase集群的入口信息 保存在ZK
        configuration.set(HConstants.ZOOKEEPER_QUORUM, "hadoop:2181");
        connection = ConnectionFactory.createConnection(configuration);
        admin = connection.getAdmin();
    }

    /**
     * 创建namespace
     * @throws IOException
     */
    @Test
    public void testCreateNamespace() throws IOException {
        NamespaceDescriptor namespaceDescriptor = NamespaceDescriptor.create("baizhi").addConfiguration("author", "gaozhy").build();
        admin.createNamespace(namespaceDescriptor);
    }

    /**
     * 创建table
     */
    @Test
    public void testCreateTable() throws IOException {
        HTableDescriptor hTableDescriptor = new HTableDescriptor(TableName.valueOf("baizhi:t_user"));
        HColumnDescriptor cf1 = new HColumnDescriptor("cf1");
        cf1.setMaxVersions(5); // cell最多保留5个历史版本
        HColumnDescriptor cf2 = new HColumnDescriptor("cf2");
        cf2.setTimeToLive(3600); // ttl=1hours
        hTableDescriptor.addFamily(cf1);
        hTableDescriptor.addFamily(cf2);
        admin.createTable(hTableDescriptor);
    }

    /**
     * 新增(修改)数据 :
     *      put指令: put 'namespace:table','rowkey','cf1:name','value'
     * @throws IOException
     */
    @Test
    public void testInsert() throws IOException {
        Table table = connection.getTable(TableName.valueOf("baizhi:t_user"));
        // Put put = new Put("user101".getBytes()); // rowkey
        Put put = new Put(Bytes.toBytes("user103")); // HBase为了简化字节操作,提供了工具类 Bytes
        put.addColumn(Bytes.toBytes("cf1"),Bytes.toBytes("name"),Bytes.toBytes("小胖子"));
        table.put(put);
    }

    /**
     * 获得数据:
     *      get指令:get 'namespace:table','rowkey','cf:column'
     */
    @Test
    public void testSelect() throws IOException {
        Table table = connection.getTable(TableName.valueOf("baizhi:t_user"));
        Get get = new Get("user101".getBytes());
        // 查询指定单元格数据
        // get.addColumn("cf1".getBytes(),"name".getBytes());
        // 查指定列簇所有列数据
        // get.addFamily("cf1".getBytes())
        Result result = table.get(get);
        String name = Bytes.toString(result.getValue(Bytes.toBytes("cf1"), Bytes.toBytes("name")));
        System.out.println(name);
    }

    /**
     * 测试删除数据:
     *    delete
     *    deleteall
     */
    @Test
    public void testDelete() throws IOException {
        Table table = connection.getTable(TableName.valueOf("baizhi:t_user"));
        Delete delete = new Delete(Bytes.toBytes("user101"));
        ArrayList<Delete> list = new ArrayList<Delete>();
        list.add(delete);
        table.delete(list);
    }

    /**
     * 扫描表
     *  scan 'namespace:table'
     */
    @Test
    public void testScan() throws IOException {
        Table table = connection.getTable(TableName.valueOf("baizhi:t_user"));
        Scan scan = new Scan();
        // 包含start  不包含stop
        scan.setStartRow(Bytes.toBytes("user101"));
        scan.setStopRow(Bytes.toBytes("user103"));

        ResultScanner rs = table.getScanner(scan);
        Iterator<Result> iterator = rs.iterator();
        while(iterator.hasNext()){
            Result result = iterator.next();
            String rowkey = Bytes.toString(result.getRow());
            String name = Bytes.toString(result.getValue(Bytes.toBytes("cf1"), Bytes.toBytes("name")));
            System.out.println(rowkey + " | " +name);
        }
    }

    @After
    public void doAfter() throws IOException {
        if(admin != null) admin.close();
        if(connection != null) connection.close();
    }
}

三、HBase On MapReduce

在这里插入图片描述
Maven依赖

<dependencies>
    <!--mapreduce + hbase-->
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-common</artifactId>
        <version>2.6.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-hdfs</artifactId>
        <version>2.6.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-mapreduce-client-common</artifactId>
        <version>2.6.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-mapreduce-client-core</artifactId>
        <version>2.6.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-mapreduce-client-jobclient</artifactId>
        <version>2.6.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hbase</groupId>
        <artifactId>hbase-server</artifactId>
        <version>1.2.4</version>
    </dependency>
</dependencies>

测试数据

@Test
    public void testInsertSampleData() throws IOException {
        Table table = connection.getTable(TableName.valueOf("t_order"));
        Put put1 = new Put(Bytes.toBytes("1:20181010153020100"));
        put1.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("money"), Bytes.toBytes(2500.0D));
        put1.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("product"), Bytes.toBytes("p20"));
        put1.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("count"), Bytes.toBytes(1));
        Put put2 = new Put(Bytes.toBytes("2:20180510121011233 "));
        put2.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("money"), Bytes.toBytes(199.0D));
        put2.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("product"), Bytes.toBytes("连衣裙"));
        put2.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("count"), Bytes.toBytes(1));
        Put put3 = new Put(Bytes.toBytes("3:20180612111111111"));
        put3.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("money"), Bytes.toBytes(999.9D));
        put3.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("product"), Bytes.toBytes("小天鹅洗衣机"));
        put3.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("count"), Bytes.toBytes(1));
        Put put4 = new Put(Bytes.toBytes("1:20181212011011111"));
        put4.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("money"), Bytes.toBytes(200.0D));
        put4.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("product"), Bytes.toBytes("搓衣板"));
        put4.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("count"), Bytes.toBytes(1));
        Put put5 = new Put(Bytes.toBytes("1:20190206101010101"));
        put5.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("money"), Bytes.toBytes(10D));
        put5.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("product"), Bytes.toBytes("钢丝球"));
        put5.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("count"), Bytes.toBytes(1));
        Put put6 = new Put(Bytes.toBytes("2:20180306101010101"));
        put6.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("money"), Bytes.toBytes(9.9D));
        put6.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("product"), Bytes.toBytes("丝袜"));
        put6.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("count"), Bytes.toBytes(1));
        ArrayList<Put> puts = new ArrayList<Put>();
        puts.add(put1);
        puts.add(put2);
        puts.add(put3);
        puts.add(put4);
        puts.add(put5);
        puts.add(put6);
        table.put(puts);
    }

创建输入表

@Test
public void testCreateOrderTable() throws IOException {
    boolean exists = admin.tableExists(TableName.valueOf("t_order"));
    if (exists) {
        admin.disableTable(TableName.valueOf("t_order"));
        admin.deleteTable(TableName.valueOf("t_order"));
    }
    HTableDescriptor hTableDescriptor = new HTableDescriptor(TableName.valueOf("t_order"));
    HColumnDescriptor cf1 = new HColumnDescriptor("cf1");
    hTableDescriptor.addFamily(cf1);
    admin.createTable(hTableDescriptor);
}

自定义Mapper

package com.baizhi;

import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.TableInputFormat;
import org.apache.hadoop.hbase.mapreduce.TableMapper;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

import java.io.IOException;

/**
 * @Author:Gaozhy
 */
public class OrderMapper extends TableMapper< Text, DoubleWritable> {
    /**
     * @param key     rowkey
     * @param result   hbase中的一行记录
     * @param context
     * @throws IOException
     * @throws InterruptedException
     */
    @Override
    protected void map(ImmutableBytesWritable key, Result result, Context context) throws IOException, InterruptedException {
        String rowkey = Bytes.toString(key.get());
        String userId = rowkey.split(":")[0];
        double money = Bytes.toDouble(result.getValue(Bytes.toBytes("cf1"), Bytes.toBytes("money")));
        context.write(new Text(userId), new DoubleWritable(money));
    }
}

自定义Reducer

package com.baizhi;

import org.apache.hadoop.hbase.client.Mutation;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.mapreduce.TableReducer;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

import java.io.IOException;
import java.util.Iterator;

public class OrderReducer extends TableReducer<Text, DoubleWritable, NullWritable>{
    /**
     * @param key     userId
     * @param values  本年度的订单金额集合
     * @param context
     * @throws IOException
     * @throws InterruptedException
     */
    @Override
    protected void reduce(Text key, Iterable<DoubleWritable> values, Context context) throws IOException, InterruptedException {
        Double sum = 0.0D;
        Iterator<DoubleWritable> iterator = values.iterator();
        while (iterator.hasNext()) {
            sum += iterator.next().get();
        }
        // 1:2018
        Put put = new Put((key.toString() + ":2018").getBytes());
        put.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("total"), Bytes.toBytes(sum));
        context.write(null, put);
    }
}

自定义初始化类

package com.baizhi;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.filter.*;
import org.apache.hadoop.hbase.mapreduce.TableInputFormat;
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
import org.apache.hadoop.hbase.mapreduce.TableOutputFormat;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;

import java.io.IOException;

public class OrderComputeApplication {

    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {

        Configuration configuration = HBaseConfiguration.create();
        configuration.set(HConstants.ZOOKEEPER_QUORUM, "hadoop:2181");
        Job job = Job.getInstance(configuration, "order compute");
        job.setJarByClass(OrderComputeApplication.class);

        job.setInputFormatClass(TableInputFormat.class);
        job.setOutputFormatClass(TableOutputFormat.class);

        // map任务的初始化
        Scan scan = new Scan();
        // 2018年度的账单统计
        // 正则表达式过滤符合条件的结果:^.*:2018.*$
        RowFilter filter = new RowFilter(CompareFilter.CompareOp.EQUAL, new RegexStringComparator("^.*:2018.*$"));
        scan.setFilter(filter);
        // 3-5步
        TableMapReduceUtil.initTableMapperJob(TableName.valueOf("t_order"), scan, OrderMapper.class, Text.class, DoubleWritable.class, job);
        TableMapReduceUtil.initTableReducerJob("t_result", OrderReducer.class, job);
        job.waitForCompletion(true);
    }
}
  • 本地计算+查看计算结果
@Test
public void testGetOrderTotal() throws IOException {
    Table table = connection.getTable(TableName.valueOf("t_result"));
    Result result = table.get(new Get(Bytes.toBytes("2:2018")));
    double total = Bytes.toDouble(result.getValue(Bytes.toBytes("cf1"), Bytes.toBytes("total")));
    System.out.println("2号用户在2018年的年度消费账单为:"+total);
}

在这里插入图片描述

  • 远程计算

开发完成HBase On MapReduce应用运行在远程的YARN集群中运行

将应用打成JAR包

运行时依赖

应用在YARN集群中运行时需要依赖第三方的JAR包

在这里插入图片描述
解决方案:

将HBase应用依赖的jar包拷贝到share/hadoop/yarn/lib
配置HADOOP_CLASSPATH环境变量

[root@hadoop ~]# vi .bashrc
# 在配置文件的末尾添加如下的第三方依赖的路径
export HADOOP_CLASSPATH=/usr/hbase-1.2.4/lib/*
[root@hadoop ~]# source .bashrc

四、HBase架构详解

在这里插入图片描述
HBase采用Master/Slave架构搭建集群,它隶属于Hadoop生态系统,由一下类型节点组成: HMaster 节点、HRegionServer 节点、 ZooKeeper 集群,而在底层,它将数据存储于HDFS中,因而涉及到HDFS的NameNode、DataNode等,总体结构如下:

HMaster节点用于:

  • 管理HRegionServer,实现其负载均衡。
  • 管理和分配HRegion,比如在HRegion Split时分配新的HRegion;
  • 在HRegionServer退出时迁移其内的HRegion到其他HRegionServer上。
  • 实现DDL操作(Data Definition Language,namespace和table的增删改,column familiy的增删改等)。
  • 管理namespace和table的元数据(实际存储在HDFS上)。
  • 权限控制(ACL)。

ZooKeeper集群用于:

  • 存放整个 HBase集群的元数据以及集群的状态信息。
  • 实现HMaster主从节点的failover

HRegionServer详解
HRegionServer一般和DataNode在同一台机器上运行,实现数据的本地性。HRegionServer包含多个HRegion,由WAL(HLog)、BlockCache、MemStore、HFile组成。

  • WAL即Write Ahead Log,在早期版本中称为HLog,它是HDFS上的一个文件,如其名字所表示的,所有写操作都会先保证将数据写入这个Log文件后,才会真正更新MemStore,最 保证HRegionServer宕机后,我们依然可以从该Log文件中读取数据,Replay所有的操作,而不至于数据丢失。这个Log文件会定期Roll出新的文件而删除旧的文件(那些已持久化到HFile中的Log可以删除)。WAL文件存储在/hbase/WALs/${HRegionServer_Name}的目录中(在0.94之前,存储在/hbase/.logs/目录中),一般一个HRegionServer只有一个WAL实例,也就是说一个HRegionServer的所有WAL写都是串行的(就像log4j的日志写也是串行的),这当然会引起性能问题,因而在HBase 1.0之后,通过HBASE-5699实现了多个WAL并行写(MultiWAL),该实现采用HDFS的多个管道写,以单个HRegion为单位。
  • BlockCache是一个读缓存,即“引用局部性”原理(也应用于CPU,分空间局部性和时间局部性,空间局部性
    是指CPU在某一时刻需要某个数据,那么有很大的概率在一下时刻它需要的数据在其附近;时间局部性是指个数据在被访问过一次后,它有很大的概率在不久的将来会被再次的访问),将数据预读取到内存中,以提升读的性能。HBase中提供两种BlockCache的实现:默认on-heap LruBlockCache和BucketCache(通常是owheap)。通常BucketCache的性能要差于LruBlockCache,然而由于GC的影响,LruBlockCache的延迟会变的不稳定,而BucketCache由于是自己管理BlockCache,而不需要GC,因而它的延迟通常比较稳定,这也是有些时候
    需要选用BucketCache的原因。
  • HRegion是一个Table中的一个Region在一个HRegionServer中的表达。一个Table可以有一个或多个Region,他们可以在一个相同的HRegionServer上,也可以分布在不同的HRegionServer上,一个HRegionServer可以有多个
    HRegion,他们分别属于不同的Table。HRegion由多个Store(HStore)构成,每个HStore对应了一个Table在这个HRegion中的一个Column Family,即每个Column Family就是一个集中的存储单元,因而最好将具有相近IO特性 Column存储在一个Column Family,以实现高效读取(数据局部性原理,可以提高缓存的命中率)。HStore是HBase中存储的核心,它实现了读写HDFS功能,一个HStore由一个MemStore 和0个或多个StoreFile组成。
      MemStore是一个写缓存(In Memory Sorted Buwer),所有数据的写在完成WAL日志写后,会 写入MemStore中,由MemStore根据一定的算法将数据Flush到地层HDFS文件中(HFile),通常每个HRegion中的每个Column Family有一个自己的MemStore。
       HFile(StoreFile) 用于存储HBase的数据(Cell/KeyValue)。在HFile中的数据是按RowKey、Column Family、Column排序,对相同的Cell(即这三个值都一样),则按timestamp倒序排列。

HRegion

HBase使用RowKey将表水平切割成多个HRegion,从HMaster的角度,每个HRegion都纪录了它的StartKey和
EndKey(第一个HRegion的StartKey为空,最后一个HRegion的EndKey为空),由于RowKey是排序的,因而Client可
以通过HMaster快速的定位每个RowKey在哪个HRegion中。HRegion由HMaster分配到相应的HRegionServer中,然后
由HRegionServer负责HRegion的启动和管理,和Client的通信,负责数据的读(使用HDFS)。

五、数据热点

用户读写操作的数据,访问同一个HRegion,造成HRegionServer压力过大,可能会造成HRegionServer不可用

rowkey特点

  • 唯一, 类似于primary key

  • rowkey字典排序

     null ---> bb   aa  ab ac ...
     bb --->cc
     cc--->null    
    
  • 分解后的HRegion(rowkey的范围区间)

rowkey设计原则

在这里插入图片描述
RowKey最大允许为64KB,建议rowkey大小控制100字节以内

  • 唯一值:时间戳

    订单表: user001:20180101141210101
    
  • 哈希散列

    原始值:
        123456 ---> MD5(123456) ---> 3216进制的字符串
        123457
    # 破坏rowkey有序的特点,保证插入数据的随机和负载均衡
    
    String md5 = MD5Hash.getMD5AsHex(Bytes.toBytes(str));
    
  • 翻转reverse

    原始rowkey翻转,将翻转结果作为新的rowkey
      // user10120180101  ---> 101082021resu
      // user10120180110  --->
      
      StringBuilder reverse = new StringBuilder("user10120180101101").reverse();
      System.out.println(reverse);
    
  • 预分区

    根据系统业务特点,提前创建HRegion的范围区间

    # 1个HBase由3个HRegion构成
    # 第一个: null ---> bb
    # 第二个: bb ---> cc
    # 第三个: cc ---> null
    
    # 创建表时指定HRegio的RowKey区间
    hbase> create 't1', 'f1', SPLITS => ['10', '20', '30', '40']
    hbase> create 't1', 'f1', SPLITS_FILE => 'splits.txt'
    
    # 创建预分区的HBase表
    hbase(main):002:0> create 't_split','cf1',SPLITS_FILE=>'/root/splits.txt'
    0 row(s) in 5.0020 seconds
    
    => Hbase::Table - t_split
    

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值