如何在HBase中扫描和删除数百万行

时间:2018-09-28 10:11:19

标签: java hadoop hbase

发生了什么
由于系统中的错误,上个月的所有数据均已损坏。因此,我们必须手动删除并重新输入这些记录。基本上,我想删除在一定时间内插入的所有行。但是,我发现很难在HBase中扫描和删除数百万行。

可能的解决方案
我发现了两种批量删除的方法:
第一个是设置TTL,以便系统将自动删除所有过时的记录。但是我想保留上个月之前插入的记录,因此该解决方案对我不起作用。

第二个选项是使用Java API编写客户端:

 public static void deleteTimeRange(String tableName, Long minTime, Long maxTime) {
    Table table = null;
    Connection connection = null;

    try {
        Scan scan = new Scan();
        scan.setTimeRange(minTime, maxTime);
        connection = HBaseOperator.getHbaseConnection();
        table = connection.getTable(TableName.valueOf(tableName));
        ResultScanner rs = table.getScanner(scan);

        List<Delete> list = getDeleteList(rs);
        if (list.size() > 0) {

            table.delete(list);
        }
    } catch (Exception e) {
        e.printStackTrace();

    } finally {
        if (null != table) {
            try {
                table.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if (connection != null) {
            try {
                connection.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

private static List<Delete> getDeleteList(ResultScanner rs) {

    List<Delete> list = new ArrayList<>();
    try {

        for (Result r : rs) {
            Delete d = new Delete(r.getRow());
            list.add(d);
        }
    } finally {
        rs.close();
    }
    return list;
}

但是在这种方法中,所有记录都存储在ResultScanner rs中,因此堆大小将很大。如果程序崩溃了,则必须从头开始。
那么,有没有更好的方法可以达到目标呢?

2 个答案:

答案 0 :(得分:2)

不知道您的表中要处理多少个“百万”,但简单的事情是不要尝试一次将它们全部放入List中,而是通过更易于管理的步骤来完成使用.next(n)函数。像这样:

for (Result row : rs.next(numRows))
{
Delete del = new Delete(row.getRow());
...
}

这样,您可以通过RPC参数控制通过单个numRows从服务器返回多少行。确保它足够大,以免与服务器进行过多往返,但同时又不要太大以至于无法杀死您的堆。您还可以使用BufferedMutator一次对多个Delete进行操作。

希望这会有所帮助。

答案 1 :(得分:0)

我建议进行两项改进:

  1. 使用BufferedMutator批量删除,它完全满足您的需要–保留突变的内部缓冲区,并在缓冲区填满时将其刷新到HBase,因此您不必担心保留自己的列表,调整大小并冲洗它。
  2. 改善扫描:
    • 使用KeyOnlyFilter –因为您不需要这些值,所以无需检索它们
    • 使用scan.setCacheBlocks(false)-由于您执行全表扫描,因此在区域服务器上缓存所有块没有多大意义
    • 调整scan.setCaching(N)scan.setBatch(N) – N将取决于密钥的大小,您应该在缓存更多和所需的内存之间保持平衡;但由于您只转移密钥,我想N可能会很大。

这是您代码的更新版本:

public static void deleteTimeRange(String tableName, Long minTime, Long maxTime) {
    try (Connection connection = HBaseOperator.getHbaseConnection();
         final Table table = connection.getTable(TableName.valueOf(tableName));
         final BufferedMutator mutator = connection.getBufferedMutator(TableName.valueOf(tableName))) {

        Scan scan = new Scan();
        scan.setTimeRange(minTime, maxTime);
        scan.setFilter(new KeyOnlyFilter());
        scan.setCaching(1000);
        scan.setBatch(1000);
        scan.setCacheBlocks(false);
        try (ResultScanner rs = table.getScanner(scan)) {
            for (Result result : rs) {
                mutator.mutate(new Delete(result.getRow()));
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

请注意“尝试资源”的使用-如果您忽略了这一点,请确保.close() mutatorrstableconnection