Table of Contents
背景
HBase 的日常运维中,“误删数据”是每位架构师和运维工程师最不愿面对、却又必须具备应对能力的场景。由于 HBase 采用了基于 LSM-Tree 结构的存储机制,数据的删除并非即时物理抹除,这为数据恢复留下了宝贵的“黄金窗口期”。
数据保护操作
当误删数据发生时候第一要务是进入hbase shell,执行如下命令:
alter 't', { NAME => 'f', KEEP_DELETED_CELLS => TRUE }
如果误删一张表的有多个family里的数据,需要都执行一下:
alter 'tt', { NAME => 'f1', KEEP_DELETED_CELLS => TRUE }, { NAME => 'f2', KEEP_DELETED_CELLS => TRUE }
设置 KEEP_DELETED_CELLS 为 True 的目的在于防止数据被物理删除。这里有必要解释一下HBase清理数据的原理:
- 首先HBase是一个LSM架构,不断发生着数据文件的写入和合并;
- 当删除操作发生时,不会去清理数据文件中的数据,而是写入一个删除标记到新文件中;
- 当某一刻major compaction发生时,在合并文件的同时会根据删除标记清理数据,新合并出来的数据文件不会再有旧数据;
KEEP_DELETED_CELLS 的作用就是在major compaction发生的时候决定要不要清理旧数据,这里需要注意一点,即便 KEEP_DELETED_CELLS 设置为True,数据仍然会因为过期而被清理(HBsae表中的TTL属性)。这个设定无可厚非,既然过期了误删不误删也无所谓了,关于TTL和KEEP_DELETED_CELLS的设置需要提前规划和考虑。TTL和KEEP_DELETED_CELLS差异:
- 创建表时可以指定TTL和KEEP_DELETED_CELLS,TTL的强制性要大于KEEP_DELETED_CELLS;
- 当指定了TTL,那么到TTL指定时间全表数据都会被删除,从hbase上清除;
- 当指定了KEEP_DELETED_CELLS = true[默认值]且时间小于TTL时, 删除[delete]的数据仍然在hbase中,即使flush,也没有删除。使用原生扫描,是可以查到的;
- 当指定了KEEP_DELETED_CELLS = false,同时flush,数据就会被删除,hbase没有保留且原生扫描也查不到数据了;
数据恢复操作
数据恢复的前提数据没有被物理删除,你只需要在查询(Scan)的时候指定raw模式来搜索数据就能看到被删除的数据,你要做就是把数据再写入一次。我们还是以hbase shell为例子进行操作:
1.首先我们准备几行测试数据:
hbase(main):009:0> scan 'testTable2' ROW COLUMN+CELL row001 column=columnFamily1:column1, timestamp=1580948919942, value=value001 row001 column=columnFamily1:column2, timestamp=1580948927665, value=value002 row001 column=columnFamily2:column1, timestamp=1580948939101, value=value021 row001 column=columnFamily2:column2, timestamp=1580948945476, value=value022 row002 column=columnFamily1:column1, timestamp=1580948964943, value=value201 row002 column=columnFamily1:column2, timestamp=1580948972598, value=value202
2.删除row002的数据:
hbase(main):010:0> delete 'testTable2','row002','columnFamily1:column1' 0 row(s) in 0.0640 seconds hbase(main):011:0> delete 'testTable2','row002','columnFamily1:column2' 0 row(s) in 0.0220 seconds hbase(main):012:0> scan 'testTable2' ROW COLUMN+CELL row001 column=columnFamily1:column1, timestamp=1580948919942, value=value001 row001 column=columnFamily1:column2, timestamp=1580948927665, value=value002 row001 column=columnFamily2:column1, timestamp=1580948939101, value=value021 row001 column=columnFamily2:column2, timestamp=1580948945476, value=value022 1 row(s) in 0.0570 seconds
3.scan时可以设置是否开启Raw模式,开启Raw模式会返回包括已添加删除标记但是未实际删除的数据现在我们指定RAW属性,重新scan这张表:
hbase(main):013:0> scan 'testTable2',{RAW=>true}
ROW
row001 column=columnFamily1:column1, timestamp=1580948919942, value=value001
row001 column=columnFamily1:column2, timestamp=1580948927665, value=value002
row001 column=columnFamily2:column1, timestamp=1580948939101, value=value021
row001 column=columnFamily2:column2, timestamp=1580948945476, value=value022
row002 column=columnFamily1:column1, timestamp=1580949067336, type=DeleteColumn
row002 column=columnFamily1:column1, timestamp=1580948964943, value=value201
row002 column=columnFamily1:column2, timestamp=1580949074379, type=DeleteColumn
row002 column=columnFamily1:column2, timestamp=1580948972598, value=value202
不仅能看到原来的数据,还多了一行含有DeleteColum标签的已删除数据。
4.下面你就可以根据自身需要重新put已删除插入数据即可,本文就不再执行操作了,只给出部分示例代码:
Configuration config = HBaseConfiguration.create();
Connection connection = ConnectionFactory.createConnection(config);
Table myTable = connection.getTable(TableName.valueOf("my_table"));
Table myTableBu = connection.getTable(TableName.valueOf("my_table_bu"));
Scan scan = new Scan();
scan.setRaw(true);
scan.setTimeRange(1490004800000L, 1491004910000L);
ResultScanner scanner = myTable.getScanner(scan);
Result result;
do {
result = scanner.next();
if (result != null) {
Put put = new Put(result.getRow());
Arrays.stream(result.rawCells()).forEach(it -> {
try {
if (!CellUtil.isDelete(it)) {
put.add(it);
}
} catch (IOException e) {
log.error("Error adding Cell to Put: " + it);
}
});
myTableBu.put(put);
}
} while (result != null);
myTable.close();
myTableBu.close();
转载请注明:麦童博客 » 如何恢复HBase误删数据(未删除表)