Hbase大批量写的实践

概述

最近在做全量库手机号的MD5和SHA256,从130号段到199号段。差不多有140亿的数据量,其中md5是70亿,SHA256也是70亿。如何让这140亿的手机号批量写入到Hbase中去,达到效率最高不丢数据。且出现异常,可以自行修复。


设计思路
任务拆分

将70亿是手机号,按照号段进行拆分,平均1000w个手机号为一个任务单元。开启多线程去处理每个任务单元

预分区 + Rowkey设计

为了让Hbase可以大批量写入,同时解决数据热点问题。那么对Hbase进行rowkey和预分区设计必不可好。

我们有5台服务器,平均每台服务器8块硬盘。为了让每块硬盘发挥其最大的写优势。我们每台服务器使用5个分区。那么总共是25个分区。

为了让手机号可以均匀的落到25个分区中去,需要对rowkey进行设计。首先我们的业务需求是根据md5或者sha256反向去查询手机号明文,rowkey必须为md5或者sha256。其中md5的长度为32位,SHA256的长度为64位。rowkey设计要越短越好,我们把SHA256密文,在进行一次MD5加密。这样长度就保持统一。

rowkey = 分区编号 + md5

其中分区编号是根据md5的值hash过后取模得到的。

线程设计

具体到一个任务单位元1000w的手机号。那么要想对这1000w的手机号进行快速写入。那么客户端必须采用批量写入。研究Hbase的客户端批量写入,采用Hbase提供的 BufferedMutator。

为了达到写入效率最大化,那么我们每个线程共享一个Hbase connection连接 和 BufferedMutator。在每次批量写入以后进行统一的flush操作。


细节处理
  1. 每个线程中的Hbase Connection 只被初始化一次。初始化有一个状态阈值,当初始化成功以后,将该阈值置为true。当Hbase connection出现异常以后,将该阈值置为false。这样在下次调用的时候,将重新获取链接。保证Hbase在批量写的时候,出现超时异常以后,任然可以正常写入
  2. BufferedMutator的使用尤为重要。BufferedMutator是hbase新API中提供的一个批量写入操作。如果使用不好导致提交线程暴涨,最后出现无法申请栈异常等错误。根本原因是BufferedMutator没法立刻关闭自己,而占用了本地线程。所以我们在使用BufferedMutator的时候,也是一个线程只有一个BufferedMutator,避免每次去创建BufferedMutator而导致资源浪费。
  3. 异常处理尤为重要,因为在批量写的过程中,Hbase自身也需要进行合并分裂等操作,这样就会产生异常,当产生异常的时候,我们保证程序不中断,且不丢数据。所以在异常处理中我们可以循环调用写入操作,把核心的逻辑都封装到写入操作中。
核心代码
    private void insertHbase(HBaseOperator hBaseOperator, List<Long> mobiles) {
        Connection connection = null;
        BufferedMutator bufferedMutator = null;
        List<Put> puts = new ArrayList<>(mobiles.size() * 2);
        try {
            connection = hBaseOperator.getConnection();
            bufferedMutator = hBaseOperator.getBufferedMutator(connection, HbaseConstant.TABLE_NAME);
            for (Long mobile : mobiles) {
                byte[] rowKeyByMD5 = RowKey.getRowKeyByMD5(String.valueOf(mobile));
                byte[] rowKeyBySHA256 = RowKey.getRowKeyBySHA256(String.valueOf(mobile));
                Put putMD5 = new Put(rowKeyByMD5);
                putMD5.addColumn(Bytes.toBytes(HbaseConstant.FAMILY_NAME), Bytes.toBytes(HbaseConstant.QUALIFIER), Bytes.toBytes(mobile));
                Put putSHA256 = new Put(rowKeyBySHA256);
                putSHA256.addColumn(Bytes.toBytes(HbaseConstant.FAMILY_NAME), Bytes.toBytes(HbaseConstant.QUALIFIER), Bytes.toBytes(mobile));
                puts.add(putMD5);
                puts.add(putSHA256);
            }
            hBaseOperator.puts(bufferedMutator, puts);
            bufferedMutator.flush();
            mobiles.clear();
            puts = null;
        } catch (Exception e) {
            e.printStackTrace();
            try {
                if (bufferedMutator != null) {
                    bufferedMutator.close();
                    bufferedMutator = null;
                }
                if (connection != null) {
                    connection.close();
                    connection = null;
                }
            } catch (IOException e1) {
                e1.printStackTrace();
            }

            try {
                Thread.sleep(2 * 1000);
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }

            String day = DateUtil.getTime("yyyy-MM-dd hh:mm:ss");
            String threadName = Thread.currentThread().getName();
            System.out.println(day + "thread-name: " + threadName + ",出现异常,重新连接重跑...." + e.getMessage());
            puts = null;
            // 设置当前初始化状态为false,重新获取链接
            hBaseOperator.setConnectionFlag(false);
            hBaseOperator.setBufferedMutatoFlag(false);
            insertHbase(hBaseOperator, mobiles);
        }
    }
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值