HBase学习笔记(三)

1、HBase Java编程

1.1 需求与数据集

自来水公司存储大量数据,数据集格式示例如下

用户id

姓名

用户地址

性别

缴费时间

表示数(本次)

表示数(上次)

用量(立方)

合计金额

查表日期

最迟缴费日期

4944191

登卫红

贵州省铜仁市德江县7单元267室

2020-05-10

308.1

283.1

25

150

2020-04-25

2020-06-09

用HBase存储,希望用java程序访问

1.2 准备

(1)创建IDEA Maven项目

groupid:cn.btks, artifactId: habase_op

(2)导入包依赖

    <repositories><!-- 代码库 -->
        <repository>
            <id>aliyun</id>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
                <updatePolicy>never</updatePolicy>
            </snapshots>
        </repository>
    </repositories>

    <dependencies>
        <!--        Hbase的java客户端-->
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-client</artifactId>
            <version>2.1.0</version>
        </dependency>
        <!--操作文件的-->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
            <!--   做单元测试的     -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>6.14.3</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <target>1.8</target>
                    <source>1.8</source>
                </configuration>
            </plugin>
        </plugins>
    </build>

(3)复制HBase的Hadoop配置文件,可以从hbase服务器中下载

所在目录及文件

/export/server/hbase-2.1.0/conf/hbase-site.xml

/export/server/hadoop-3.1.4/etc/hadoop/core-site.xml

还有一个日志配置文件

log4j.peopertis

拷贝到resource中

(4)创建包和类

1.3 创建Hbase链接和admin管理对象

/**
 * 1.    使用HbaseConfiguration.create()创建Hbase配置
 * 2.    使用ConnectionFactory.createConnection()创建Hbase连接
 * 3.    要创建表,需要基于Hbase连接获取admin管理对象
 * 4.    使用admin.close、connection.close关闭连接
 */
public class TableAdminTest {
    private Connection connection;
    private Admin admin;
    @BeforeTest
    public void beforeTest() throws IOException {
        //从项目目录加载配置
        Configuration configuration = HBaseConfiguration.create();
        //使用连接的工厂创建链接
        connection = ConnectionFactory.createConnection(configuration);
        //创建表,需要和HMaster打交道,需要有一个admin对象
        admin = connection.getAdmin();


    }
    
    
    @AfterTest
    public void afterTest() throws IOException {
        //关闭
        admin.close();
        connection.close();
        
    }
}

使用Java代码创建表


    @Test
    public void createTableTest() throws IOException {
        //1.    判断表是否存在
            //a)    存在,则退出
        TableName tableName = TableName.valueOf("WATER_BILL");
        if (admin.tableExists(tableName)) {
            System.out.println("表已存在");
            return;
        }
        //构建表
        //2.    使用TableDescriptorBuilder.newBuilder构建表描述构建器,描述列簇、属性
        TableDescriptorBuilder tableDescriptorBuilder = TableDescriptorBuilder.newBuilder(tableName);
        //3.    使用ColumnFamilyDescriptorBuilder.newBuilder构建列蔟描述构建器
        //使用Hbase下的工具类,将常见类型转换为byte数组或将数字转换常见类型
        ColumnFamilyDescriptorBuilder columnFamilyDescriptorBuilder = ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("C1"));
        //4.    构建列蔟描述,构建表描述
        ColumnFamilyDescriptor cfDes = columnFamilyDescriptorBuilder.build();
        //建立表和列簇的关联
        tableDescriptorBuilder.setColumnFamily(cfDes);
        //5.    创建表
        TableDescriptor tableDescriptor = tableDescriptorBuilder.build();
        admin.createTable(tableDescriptor);

    }

小结

  • 创建Hbase链接

  • 创建admin对象

  • 创建表

  • 调用tableExists判断表是否存在

  • 在hbase创建表,构建表描述器TableDescriptor,列簇描述器ColumnFamilyDescriptor

  • 将列簇描述器添加到表描述器中

  • 使用admin.createTable创建表

Connection creation is a heavy-weight operation. Connection implementations are thread-safe,

HTable:An implementation of {@link Table}. Used to communicate with a single HBase table.
* Lightweight. Get as needed and just close when done.

1.4 删除表

    @Test
    public void  dropTable() throws IOException {
//        1.    判断表是否存在
        TableName tableName = TableName.valueOf("WATER_BILL");
        if (admin.tableExists(tableName)) {
//            2.    如果存在,则禁用表
            admin.disableTable(tableName);
            //        3.    再删除表
            admin.deleteTable(tableName);
        }
    }
在Hbase中所有的数据都是以Byte[]的形式存储的,所以将java的数据类型进行转换
经常会用到一个工具类:Bytes(hbase包下的Bytes工具类)
字符串、整数、浮点数-->Byte[]
Byte[]-->字符串、整数、浮点数

1.5 往表里面插入一条数据

表中插入一个行,该行只包含一个列。

ROWKEY

姓名(列名:NAME)

4944191

张可

package cn.btks.hbase.data.api_test;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

import java.io.IOException;

public class DataOpTest {
    private Connection connection;
    private TableName tableName = TableName.valueOf("WATER_BILL");

    @BeforeTest
    public void beforeTest() throws IOException {
        //从项目目录加载配置
        Configuration configuration = HBaseConfiguration.create();
        //使用连接的工厂创建链接,重量级对象,不能经常创建
        connection = ConnectionFactory.createConnection(configuration);

    }
    @Test
    public void putTest() throws IOException {
        //1.使用Hbase连接获取Htable
        Table table = connection.getTable(tableName);
        //2.构建ROWKEY、列蔟名、列名
        String rowKey = "4944191";
        String columnFamily = "C1";
        String columnName = "NAME";
        //3.构建Put对象(对应put命令)
        Put put = new Put(Bytes.toBytes(rowKey));
        //4.添加姓名列
        put.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(columnName),Bytes.toBytes("登卫红"));
        //5.使用Htable表对象执行put操作
        table.put(put);
        // 6.关闭Htable表对象
        //HTable是一个轻量级的对象,可以经常创建,他是一个非线程安全,及时关闭
        table.close();

    }

    @AfterTest
    public void afterTest() throws IOException {
        //关闭
        connection.close();

    }
}
# 查看数据
scan "WATER_BILL",{FORMATTER=>'toString'}

插入其他列

列名

说明

ADDRESS

用户地址

贵州省铜仁市德江县7单元267室

SEX

性别

PAY_DATE

缴费时间

2020-05-10

NUM_CURRENT

表示数(本次)

308.1

NUM_PREVIOUS

表示数(上次)

283.1

NUM_USAGE

用量(立方)

25

TOTAL_MONEY

合计金额

150

RECORD_DATE

查表日期

2020-04-25

LATEST_DATE

最迟缴费日期

2020-06-09

String columnNameAddress = "ADDRESS";
String columnNameSex = "SEX";
String columnNamePayDate = "PAY_DATE";
String columnNameNumCurrent = "NUM_CURRENT";
String columnNameNumPrevious = "NUM_PREVIOUS";
String columnNameNumUsage = "NUM_USAGE";
String columnNameTotalMoney = "TOTAL_MONEY";
String columnNameRecordDate = "RECORD_DATE";
String columnNameLatestDate = "LATEST_DATE";

put.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(columnNameAddress),Bytes.toBytes("贵州省铜仁市德江县7单元267室"));
put.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(columnNameSex),Bytes.toBytes("男"));
put.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(columnNamePayDate),Bytes.toBytes("2020-05-10"));
put.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(columnNameNumCurrent),Bytes.toBytes("308.1"));
put.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(columnNameNumPrevious),Bytes.toBytes("283.1"));
put.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(columnNameNumUsage),Bytes.toBytes("25"));
put.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(columnNameTotalMoney),Bytes.toBytes("150"));
put.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(columnNameRecordDate),Bytes.toBytes("2020-04-25"));
put.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(columnNameLatestDate),Bytes.toBytes("2020-06-09"));

总结,先获取Table对象,这个对象要和HRegionServer节点连接。所以将来HRegionServer负载比较高

Hbase 的connection对象是一个重量级对象,避免频繁创建,将来spark也需要注意,它是线程安全的

table是一个轻量级的,用完就close,非线程安全的。

1.6 查看一条数据

查询rowkey为4944191的所有列的数据,并打印出来。

@Test
    public void getTest() throws IOException {
        //实现步骤:
        //1.使用Hbase连接获取Htable
        Table table = connection.getTable(tableName);
        //2. 使用rowkey构建Get对象
        Get get = new Get(Bytes.toBytes("4944191"));
        //3. 执行get请求
        Result result = table.get(get);
        //4. 获取所有单元格
        List<Cell> cells = result.listCells();
        //5. 打印rowkey
        byte[] rowKey = result.getRow();
        System.out.println(Bytes.toString(rowKey));
        //6. 迭代单元格列表
        for (Cell cell : cells) {
            //将字节数组转化为字符串
            //获取列簇的名称
            String cf = Bytes.toString(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength());
            //获取列的名称
            String columnName = Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getFamilyLength());
            //获取值
            String value = Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
            System.out.println(cf+":"+columnName+"-->"+value);
        }
        //7. 关闭表
        table.close();
    }

1.7 删除一条数据

删除rowkey为4944191的整条数据。

 @Test
    public void deleteTest() throws IOException {
        //
        //实现步骤:
        //1. 获取HTable对象
        Table table = connection.getTable(tableName);
        //2. 根据rowkey构建delete对象
        Delete delete = new Delete(Bytes.toBytes("4944191"));
        //3. 执行delete请求
        table.delete(delete);
        //4. 关闭表
        table.close();
    }

代码执行前后对比

1.8 导入测试数据

导入10万条抄表测试数据导入到hbase中

import Job,在Hbase中有一个MR程序,可以专门用来导入数据文件到HBase中

用法:

hbase org.apache.hadoop.hbase.mapreduce.Import 表名 HDFS数据文件路径

步骤和代码

# 1、在HDFS中创建一个文件夹,保存需要导入的数据
hadoop fs -mkdir -p /water_bill/output_ept_10W
#2、上传文件
hadoop fs -put part-m-00000_10w /water_bill/output_ept_10W

数据上传之前

数据上传之后

启动yarn

start-yarn.sh

导入数据

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

可以查看到启动一个作业

查看导入数据量

count "WATER_BILL"

导出数据工具

hbase org.apache.hadoop.hbase.mapreduce.Export WATER_BILL /water_bill/output_ept_10W_export

1.9 查询数据,过滤

在Java API中,使用scan + filter来实现过滤查询。查询从2020年6月1日到2020年6月30日的所有抄表数据。

创建类

package cn.btks.hbase.data.api_test;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CompareOperator;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.filter.BinaryComparator;
import org.apache.hadoop.hbase.filter.FilterList;
import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
import org.apache.hadoop.hbase.util.Bytes;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

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

public class ScanFilterTest {

    private Connection connection;
    private TableName tableName = TableName.valueOf("WATER_BILL");

    @BeforeTest
    public void beforeTest() throws IOException {
        //从项目目录加载配置
        Configuration configuration = HBaseConfiguration.create();
        //使用连接的工厂创建链接,重量级对象,不能经常创建
        connection = ConnectionFactory.createConnection(configuration);

    }
    @Test
    public void scanFilterTest() throws IOException {
        //1.    获取表
        Table table = connection.getTable(tableName);
        //2.    构建scan请求对象
        Scan scan = new Scan();
        //3.    构建两个过滤器
            //a)    构建两个日期范围过滤器(注意此处请使用RECORD_DATE——抄表日期比较
        SingleColumnValueFilter startFilter = new SingleColumnValueFilter(
                Bytes.toBytes("C1"), Bytes.toBytes("RECORD_DATE"),
                CompareOperator.GREATER_OR_EQUAL,
                new BinaryComparator(Bytes.toBytes("2020-06-01")));
        SingleColumnValueFilter endFilter = new SingleColumnValueFilter(
                Bytes.toBytes("C1"), Bytes.toBytes("RECORD_DATE"),
                CompareOperator.LESS_OR_EQUAL,
                new BinaryComparator(Bytes.toBytes("2020-06-30")));
        //b)    构建过滤器列表
        FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ALL, startFilter, endFilter);
        //4.    执行scan扫描请求
        scan.setFilter(filterList);
        ResultScanner resultScanner = table.getScanner(scan);
        //5.    迭代打印result
        Iterator<Result> iterator = resultScanner.iterator();
        while (iterator.hasNext()){
            Result result = iterator.next();
            //4. 获取所有单元格
            List<Cell> cells = result.listCells();
            byte[] rowKey = result.getRow();
            System.out.println(Bytes.toString(rowKey));
            //6. 迭代单元格列表
            for (Cell cell : cells) {
                //将字节数组转化为字符串
                //获取列簇的名称
                String cf = Bytes.toString(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength());
                //获取列的名称
                String columnName = Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength());
                //获取值
                String value = Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
                System.out.println(cf+":"+columnName+"-->"+value);
            }
        }
        //7.    关闭ResultScanner(这玩意把转换成一个个的类似get的操作,注意要关闭释放资源)
        resultScanner.close();
        //8.    关闭表
        table.close();

    }

    @AfterTest
    public void afterTest() throws IOException {
        //关闭
        connection.close();

    }
}

乱码问题,因为前面我们的代码,在打印所有的列时,都是使用字符串打印的,Hbase中如果存储的是int、double,那么有可能就会乱码了。

要解决的话,我们可以根据列来判断,使用哪种方式转换字节码。如下:

NUM_CURRENT

NUM_PREVIOUS

NUM_USAGE

TOTAL_MONEY

这4列使用double类型展示,其他的使用string类型展示。修改以下代码,对值的字段进行判断。

//获取值
String value = null;
//NUM_CURRENT
//NUM_PREVIOUS
//NUM_USAGE
//TOTAL_MONEY
if ("NUM_CURRENT".equals(columnName)
        ||"NUM_PREVIOUS".equals(columnName)
        ||"NUM_USAGE".equals(columnName)
        ||"TOTAL_MONEY".equals(columnName)){
    value = Bytes.toDouble(cell.getValueArray())/100000000+"亿";
}else{
    value = Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
}

2Hbase高可用

2.1 高可用介绍

base集群只有一个Master,一旦master出现故障,hbase将不再可用(创建表、删除表、查看表的元数据)

2.1搭建Hbase高可用

在hbase的conf中创建backup-masters文件

## 进入目录 
cd /export/server/hbase-2.1.0/conf
# 创建文件
touch backup-masters

node2.btks.cnnode3.btks.cn 添加到文件中

vim backup-master
添加内容
node2.btks.cn
node3.btks.cn

分发到其他节点

scp backup-masters root@node2:$PWD
scp backup-masters root@node3:$PWD

重启hbase

stop-hbase.sh
start-hbase.sh

查看结果

2.3实验切换

当前状态

杀死节点1的Hmaster的进程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天码村

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值