利用多线程批Put方式压测HBase

本文介绍了在HBase上进行性能和稳定性压测的重要性,提供了一个JavaDemo,展示了如何利用多线程进行HBase的批Put操作,以模拟高并发场景。代码中涉及Kerberos认证,反转时间戳避免数据热点,并通过调整列数量和批处理大小来适应不同的压测需求。在压测过程中,作者强调了正确配置RSGroup以实现硬件级资源隔离的重要性,以及在压测中遇到的HDFS容量限制问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

利用多线程批Put方式压测HBase

背景

在正式上生产之前,一定要对集群的组件做稳定性和性能压测,这是常识。这种压测当然不能指望那些只会鼠标点几下网页并经常指责前端页面样式有bug的测试去做。。。这种稍微有点技术含量的事情,她们其实有心无力。。。

众所周知,表的列用百万为基本单位,行号用10亿为基本单位计数时,才配得上用HBase。。。所以HBase的实际应用场景大部分是大宽表【SQL Boy们手写SQL的那种几百个字段的Hive表叫宽表没啥问题,但是不够资格叫大宽表】。做性能压测绝对不能只用几十个字段或者几百w行数据,这种RDBMS都能轻松应付的场景不配用HBase。。。要做稳定性压测,一定是以压爆HBase集群为目的的,正式上线前压爆了去做一些jvm调优,总比上了生产环境天天爆库强一点。。。

当然有时候不一定是为了压爆集群,还可能是对HBase的协处理器做一些技术调研,例如:RSGroup硬件级资源隔离。这种情况更多是观察在大数据量场景下,协处理器是否会出错。

Demo

上一个简单的Demo。读者可以参照着改参数适应自己公司的场景。

pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>zhiyong_study</artifactId>
        <groupId>com.zhiyong</groupId>
        <version>1.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>hbase_study</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <hbase-client.version>2.1.10</hbase-client.version>
        <hbase.version>2.1.10</hbase.version>
        <hbase-common.version>2.1.10</hbase-common.version>
        <hbase-server.version>2.1.10</hbase-server.version>
        <lombok-version>1.18.24</lombok-version>
        <encoding>UTF-8</encoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-client</artifactId>
            <version>${hbase-client.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>org.glassfish</groupId>
                    <artifactId>javax.el</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

<!--        损坏-->
<!--        <dependency>-->
<!--            <groupId>org.apache.hbase</groupId>-->
<!--            <artifactId>hbase</artifactId>-->
<!--            <version>${hbase.version}</version>-->
<!--            <exclusions>-->
<!--                <exclusion>-->
<!--                    <groupId>org.glassfish</groupId>-->
<!--                    <artifactId>javax.el</artifactId>-->
<!--                </exclusion>-->
<!--            </exclusions>-->
<!--        </dependency>-->
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-common</artifactId>
            <version>${hbase-common.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>org.glassfish</groupId>
                    <artifactId>javax.el</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-server</artifactId>
            <version>${hbase-server.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>org.glassfish</groupId>
                    <artifactId>javax.el</artifactId>
                </exclusion>
            </exclusions>
        </dependency>


        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>



    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

需要排除org.glassfish的依赖,否则HBase的Maven依赖会爆红!!!如果公司用的是CDP,当然要改成服务器的组件对应的版本!!!

Java

采用多线程的方式,一个Jar包就可以模拟出所需的并发。当网络的带宽不够时,还是需要多个node上同时启动来减少网络的影响。

package com.zhiyong;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.client.HBaseAdmin;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.security.UserGroupInformation;

import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.UUID;

/**
 * @program: zhiyong_study
 * @description: HBase的put压力测试
 * @author: zhiyong
 * @create: 2023-04-02 20:29
 **/
public class HBasePutPressTest implements Runnable{
    private Thread thread;

    @Override
    public void run() {
        long timeStart = System.currentTimeMillis();//开始时间
        long timeEnd = 0;
        org.apache.hadoop.hbase.client.Connection connection = null;
        Configuration configuration = HBaseConfiguration.create();
        configuration.set("hbase.zookeeper.quorum", "192.168.88.101:2181,192.168.88.102:2181,192.168.88.103:2181");
        configuration.set("zookeeper.znode.parent", "/hbase");
        configuration.set("hbase.client.retries.number", "3");
        configuration.set("hbase.security.authentication", "kerberos");
        configuration.set("keytab.file", "D:/krb/zhiyong.keytab");//Win路径
//        configuration.set("keytab.file","/krb/zhiyong.keytab");//Linux路径
        configuration.set("kerberos.principal", "zhiyong");
        configuration.set("hbase.master.kerberos.principal", "hbase/_HOST@ZHIYONG.COM");
        configuration.set("hbase.regionserver.kerberos.principal", "hbase/_HOST@ZHIYONG.COM");
        configuration.set("hadoop.security.authentication", "kerberos");
        System.setProperty("java.security.krb5.conf", "D:/krb/krb5.conf");//Win路径
//        System.setProperty("java.security.krb5.conf","/krb/krb5.conf");//Linux路径

        UserGroupInformation.setConfiguration(configuration);

        try {
            UserGroupInformation ugi = UserGroupInformation.loginUserFromKeytabAndReturnUGI("zhiyong", "D:/krb/zhiyong.keytab");//Win路径
//          UserGroupInformation ugi = UserGroupInformation.loginUserFromKeytabAndReturnUGI("zhiyong","/krb/zhiyong.keytab");//Linux路径
            UserGroupInformation.setLoginUser(ugi);
            System.out.println("Kerberos->HBase认证成功");

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

        try {
            HBaseAdmin.available(configuration);
            connection = ConnectionFactory.createConnection(configuration);

            timeEnd = System.currentTimeMillis();
            System.out.println("构建connection成功");
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("构建connection耗时:" + (timeEnd - timeStart) + "ms");

        Table table = null;


        try {
            table = connection.getTable(TableName.valueOf("db_lzy:tb1"));
            System.out.println("获取到HBase表:db_lzy:tb1");
        } catch (Exception e) {
            e.printStackTrace();
        }

        String rowkey = "";
        StringBuilder strb = new StringBuilder();
        Long rowCounter = 0L;
        LinkedHashMap<String, String> map = new LinkedHashMap<>();

        //一次put10000列
        for (int i = 0; i < 10000; i++) {
            map.put("col" + i, UUID.randomUUID().toString() + UUID.randomUUID().toString() + UUID.randomUUID().toString());
        }

        //批put
        for (int i = 0; i < 100000; i++) {
            LinkedList<Put> puts = new LinkedList<>();

            for (int j = 0; j < 1000; j++) {
                strb.delete(0, strb.length());
                strb.append(String.valueOf(System.currentTimeMillis()));
                strb.reverse();
                rowkey = strb.toString();
                strb.delete(0, strb.length());
                Put put = new Put(rowkey.getBytes());

                for (String col : map.keySet()) {
                    put.addColumn("f1".getBytes(), col.getBytes(), map.get(col).toString().getBytes());
                }

                puts.add(put);
            }

            try {
                table.put(puts);
                rowCounter++;
                System.out.println("插入第" + rowCounter + "批次成功");
            } catch (Exception e) {
                e.printStackTrace();
            }


        }

        System.out.println("插入批次数:" + rowCounter);

        try {
            table.close();
        } catch (Exception e){
            System.out.println("关闭Table失败");
        }
    }

    public void start(){
        if (null==thread){
            thread=new Thread();
            thread.start();
        }
    }

    public static void main(String[] args) {
        System.out.println("启动");
        int threadCounter = 20;

        for (int i = 0; i < threadCounter; i++) {
            HBasePutPressTest test = new HBasePutPressTest();
            test.start();
        }

    }
}

过Kerberos认证的模式会比较麻烦,如果没有Kerberos认证,会简单不少。

大体上就是反转时间戳做rowkey,防止出现数据热点。每条数据1个列族,10000个列,每10000条数据做一个Batch put批处理,这是我们的生产环境常用的方式。一般很少有单条put的情况,这样玩容易让HBase频繁GC进而OOM或者别的问题宕掉,这是严厉禁止的做法。

笔者这里是受限于网络带宽不足的问题,20线程并发,实际还是要多台node并行跑这个Jar包。

结果

以当时的压测结果来看,RSGroup配置好后,HBase可以把大批量数据正确地存到指定分组的Region Server中,没有出现差错。期间压爆了几个node,但是整体对外服务依旧可用。最终挂服务器跑了一天多,把集群的HDFS接近写满的时候,由于HDFS先抗不住压力挂掉了,才把整个HBase集群压爆了!

至于RSGroup硬件级资源隔离,是另一个故事了。

转载请注明出处:https://lizhiyong.blog.csdn.net/article/details/129918165
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值