【问题标题】:HBase bulk delete as "complete bulk load"HBase 批量删除为“完成批量加载”
【发布时间】:2016-09-02 12:56:43
【问题描述】:

我想删除 HBase 表中的 3 亿行。我可以使用 HBase API 并发送一批 Delete 对象。不过恐怕要花很多时间。

以前的代码就是这种情况,我想插入数百万行。我没有使用 HBase API 并发送一批 Put,而是使用 Map Reduce 作业,它发出 RowKey / Put 作为值并使用HFileOutputFormat2.configureIncrementalLoad(job, table, regionLocator) 设置我的 Reducer,以便它直接写入输出,准备好由@987654322 快速加载@(完成批量加载)。它要快得多(5 分钟而不是 3 小时)。

所以我想对批量删除做同样的事情。

但是,我似乎无法将这种技术与 Delete 一起使用,因为 HFileOutputFormat2 尝试为 KeyValuePut (PutSortReducer) 配置 Reducer,但 Delete 不存在任何内容。

我的第一个问题是为什么没有“DeleteSortReducer”来为 Delete 启用完整的批量加载技术?它只是缺少一些东西,还没有完成吗?还是有更深层次的理由证明这一点?

第二个问题,有点相关:如果我复制/粘贴 PutSortReducer 的代码,将其修改为 Delete 并将其作为我工作的 Reducer 传递,它会起作用吗? HBase 完整的批量加载会产生充满墓碑的 HFile 吗?

例子:

public class DeleteSortReducer extends
        Reducer<ImmutableBytesWritable, Delete, ImmutableBytesWritable, KeyValue> {

    @Override
    protected void reduce(
            ImmutableBytesWritable row,
            java.lang.Iterable<Delete> deletes,
            Reducer<ImmutableBytesWritable, Delete,
                    ImmutableBytesWritable, KeyValue>.Context context)
            throws java.io.IOException, InterruptedException
    {
        // although reduce() is called per-row, handle pathological case
        long threshold = context.getConfiguration().getLong(
                "putsortreducer.row.threshold", 1L * (1<<30));
        Iterator<Delete> iter = deletes.iterator();
        while (iter.hasNext()) {
            TreeSet<KeyValue> map = new TreeSet<KeyValue>(KeyValue.COMPARATOR);
            long curSize = 0;
            // stop at the end or the RAM threshold
            while (iter.hasNext() && curSize < threshold) {
                Delete d = iter.next();
                for (List<Cell> cells: d.getFamilyCellMap().values()) {
                    for (Cell cell: cells) {
                        KeyValue kv = KeyValueUtil.ensureKeyValue(cell);
                        map.add(kv);
                        curSize += kv.heapSize();
                    }
                }
            }
            context.setStatus("Read " + map.size() + " entries of " + map.getClass()
                    + "(" + StringUtils.humanReadableInt(curSize) + ")");
            int index = 0;
            for (KeyValue kv : map) {
                context.write(row, kv);
                if (++index % 100 == 0)
                    context.setStatus("Wrote " + index);
            }

            // if we have more entries to process
            if (iter.hasNext()) {
                // force flush because we cannot guarantee intra-row sorted order
                context.write(null, null);
            }
        }
    }
}

【问题讨论】:

  • 你从上面的程序有什么发现?您是否尝试过/找到其他方法?如果是的话,它们是什么

标签: hbase


【解决方案1】:

首先,简单介绍一下删除操作在 HBase 中是如何工作的。在删除命令上,HBase 将数据标记为已删除,并将有关它的信息写入 HFile。实际上,数据并没有从磁盘中删除,并且存储中存在两条记录:数据和删除标记。只有在压缩后,数据才会从磁盘存储中删除。

所有这些信息都表示为KeyValue。对于表示数据的 KeyValue,KeyValue.Type 等于 Put。对于删除标记,KeyValue.Type 设置为以下值之一:DeleteDeleteColumnDeleteFamilyDeleteFamilyVersion

在您的情况下,您可以通过为 KeyValue.Type 创建具有特殊值的 KeyValue 来实现批量删除。例如,如果你想删除唯一的一列,你应该创建一个KeyValue,使用构造函数

KeyValue(byte[] row, byte[] family, byte[] qualifier, long timestamp, KeyValue.Type type)

// example 

KeyValue kv = new KeyValue(row, family, qualifier, time, KeyValue.Type.DeleteColumn)

第一个问题的答案你不需要专门的DeleteSortReducer,你应该为KeyValue配置一个reducer。对于第二个问题,答案是否定的。

【讨论】:

  • 我的 Mapper 是否应该发出类似的东西:byte[] rowkey = ...; KeyValue kv = new KeyValue(rowkey, System.currentTimeMillis(), KeyValue.Type.Delete);
  • 是的,如果您想删除整行。如果您只想删除一个列或列族,您应该使用 DeleteColumn、DeleteFamily 修饰符。
  • 如果我们删除该行的所有单元格(为每个单元格编写一个 DeleteColumn 类型的 KeyValue),它就可以工作。但是如果我尝试删除整行(只为整行写入一个KeyValue,类型等于Delete),它会失败。
猜你喜欢
  • 2016-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多