HBase批量删除作为“完整批量加载”

3
我想要在一个HBase表中删除3亿行数据。我可以使用HBase API并发送Delete对象的批处理来实现,但我担心这会花费很长时间。
之前我想要插入数百万行数据时,也是这样。但我没有使用HBase API和发送Put的批处理。相反,我使用了一个Map Reduce作业,该作业将RowKey / Put作为值发出,并使用设置了我的Reducer,以便直接编写输出以便快速加载LoadIncrementalHFiles(完整批量加载)。它比原来快得多(5分钟而不是3小时)。
所以我想用同样的方法进行批量删除。
然而,似乎我不能像Put一样使用此技术来进行Delete操作,因为尝试配置KeyValue或Put的Reducer (PutSortReducer),但对于Delete则不存在。
我的第一个问题是:为什么没有“DeleteSortReducer”以启用Delete的完整批量加载技术?这只是缺少的东西,还是有更深层次的原因可以证明?
第二个问题,有点相关:如果我复制/粘贴PutSortReducer的代码,将其调整为Delete,然后将其作为我的作业Reducer传递,它会工作吗?HBase完整的批量加载是否会产生满是墓碑的HFiles?
例子:
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);
            }
        }
    }
}

你从上面的程序中发现了什么?你尝试或发现了其他方法吗?如果有,它们是什么? - Ram Ghadiyaram
1个回答

5
首先,让我们来了解一下在HBase中删除操作是如何工作的。执行删除命令时,HBase会将数据标记为已删除,并将相关信息写入HFile。实际上,数据并未从磁盘中删除,存储器中存在两条记录:数据和删除标记。只有在压缩后,数据才会从磁盘存储中删除。
所有这些信息都表示为KeyValue。对于代表数据的KeyValue,其KeyValue.Type等于Put。对于删除标记KeyValue.Type被设置为以下值之一:DeleteDeleteColumnDeleteFamilyDeleteFamilyVersion
在您的情况下,您可以通过创建带有特殊值的KeyValue来实现批量删除KeyValue.Type。例如,如果您想要删除唯一的一列,则应使用构造函数创建一个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)? - Comencau
1
如果您想删除整行数据,是的。如果您只想删除一列或列系列,您应该使用DeleteColumn、DeleteFamily修饰符。 - Alexander Kuznetsov
如果我们删除行的所有单元格(为每个单元格编写一个类型为DeleteColumn的KeyValue),它可以工作。但是,如果我尝试删除整行(仅使用类型为Delete的一个KeyValue编写整行),则会失败。 - Comencau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接