文章目录

  • 写在前面
  • 关键点
  • 实现代码
  • 尾记


写在前面

其实下文内容更适合在Spark中作为任务去执行,为了讲解,我先单独拎出来了,使用场景的话其实也很明显,就是大规模将数据写入HBase中。

关键点

  1. 大数据组件服务不可用(如断点、磁盘爆炸等)暂时不是本文内容所考虑的内容。
  2. 首先,使用HBase1.0以上版本才支持的BufferedMutator,对HBase执行异步写入操作,使用mutate(List<? extends Mutation> var1)去执行写入操作。
    建议手动设定writeBufferSize参数,使用spark时设置为10 * 1024 * 1024(10MB),普通java程序设置为50 * 1024 * 1024(50MB),不宜过大。
  3. 加入容错机制,即BufferedMutator.ExceptionListener,防止在批量插入时因为触发Region Split或Region Blance等导致Region的短暂下线的相关异常,在接收到NotServingRegionException等异常时会进行一次重试。在重试之前需要将线程sleep几秒,不放心的话可以sleep几十秒,也是问题不大的。
    因为在组件可用的情况下,亿级数据一次split的操作在毫秒级的时间内就能完成,所以仅仅是一次重试便能在很大程度上减少插入数据时丢失数据的情况,并且对速度几乎无影响。
  4. 不要一次性插入几百万条数据,不仅内存吃不消而且插入速度也异常缓慢,个人实践下来每个批次3000-5000左右的数据量最佳。

实现代码

下面是具体代码,截取部分内容,其中关于HBase的连接和写入的表与列簇都需要自行修改。。。

//HBase连接
        Configuration configuration = HBaseConfiguration.create();
        configuration.set("zookeeper.znode.parent", "127.0.0.1");
        configuration.set("hbase.zookeeper.property.clientPort", "2181");
        configuration.set("hbase.zookeeper.quorum", "hbase-unsecure");
        Connection connection = null;
        BufferedMutator table = null;
        try {
            //异常处理
            final BufferedMutator.ExceptionListener listener = (e, mutator) -> {
                String failTime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now());
                System.out.println("fail time :" + failTime + " ,insert data fail,cause:" + e.getCause(0) + ",failed num:" + e.getNumExceptions());
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
                //重试
                String retryTime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now());
                System.out.println("its time to retry:" + retryTime);
                for (int i = 0; i < e.getNumExceptions(); i++) {
                    org.apache.hadoop.hbase.client.Row row = null;
                    try {
                        row = e.getRow(i);
                        mutator.mutate((Put) row);
                    } catch (IOException ex) {
                        System.out.println("insert data fail,please check hbase status and row info : " + row);
                    }
                }
            };
            //表名自行修改
            BufferedMutatorParams htConfig = new BufferedMutatorParams(TableName.valueOf("wangelai")).writeBufferSize(10 * 1024 * 1024).listener(listener);
            connection = ConnectionFactory.createConnection(configuration);
            table = connection.getBufferedMutator(htConfig);
            int count = 0;
            List<Put> puts = new ArrayList<>(2048);
            //模拟插入数据
            for (int i = 0; i < 10000000; i++) {
                Put p = new Put(Bytes.toBytes(String.valueOf(i)));
                p.addColumn(Bytes.toBytes("default"), Bytes.toBytes("id"), Bytes.toBytes(i));
                puts.add(p);
                count++;
                if (count % 3000 == 0) {
                    System.out.println("count:" + count);
                    table.mutate(puts);
                    puts = new ArrayList<>(2048);
                }
            }
            // 提交最后的内容
            System.out.println("Total count:" + count);
            table.mutate(puts);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (table != null) {
                try {
                    table.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }