一、背景:
Hbase是一个列式存储,nosql类型的数据库,类似mongodb。
我们都知道Hbase是一个Nosql的分布式存储数据引擎,它可以支持千万级别的QPS写入,但是有时候我们需要批量的删除他的数据, 目前似乎没有提供批量删除的方法,只有一个单行删除的命令:deleteall 'tablename', rowkey
二、删除方法:
方法一:通过写 shell 脚本,从 hbase shell 查出需要删除的 rowkey ,拼成删除命令(deleteall 'tablename', rowkey),写到文本 del_temp.txt ;然后执行 hbase shell del_temp.txt
方法二:通过建映射表,比如在 hive 建一个映射表,然后写 hql 查出需要删除的 rowkey ,拼成删除命令(deleteall 'tablename', rowkey),写到文本 del_temp.txt ;然后执行 hbase shell del_temp.txt
方法三:通过python/ java 代码,调用 hbase 的单行删除方法,写个循环删除
方法四:通过建映射表,比如在 hive 建一个映射表,将 需要保留的数据 通过 hql 查询出来,备份到 hive 的一张临时表 ;然后 在hbase shell 里面 执行 truncate 'tablename' ,清空 hbase 的数据; 最后 在将数据 从 hive 的备份表 读出来,插入 映射表,写回 hbase;
总结:
前面3种方法,本质上都是在 hbase 中逐行删除;而第4种方法,是利用hive的map reduce倒数据。对于不熟悉 hbase 和 java 语法的开发人员而言,使用2/4方法,是灵活性最好的。
以第二种删除方法为例:
1、hive 建映射表:
CREATE EXTERNAL TABLE edw.test_turboway_hbase(
`keyid` string COMMENT 'from deserializer',
`title` string COMMENT 'from deserializer',
`bizdate` string COMMENT 'from deserializer',
`loginid` string COMMENT 'from deserializer')
ROW FORMAT SERDE
'org.apache.hadoop.hive.hbase.HBaseSerDe'
STORED BY
'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES (
'hbase.columns.mapping'=':key,cf:title,cf:bizdate,cf:loginid',
'serialization.format'='1')
TBLPROPERTIES (
'COLUMN_STATS_ACCURATE'='false',
'hbase.table.name'='test_turboway',
'numFiles'='0',
'numRows'='-1',
'rawDataSize'='-1',
'totalSize'='0',
'transient_lastDdlTime'='1543297130')
2、记录hive查询要删除的数据,拼成命令:
hive -e
"select concat('deleteall \'test_turboway\',\'',keyid,'\'')
from edw.test_turboway_hbase
where loginid = '20181122'"
> del_temp.txt && echo 'exit' >> del_temp.txt
3、hbase shell 执行删除命令
hbase shell del_temp.txt > del.log
二、
由于从kafka消费过来写入hbase的数据量有点大,导致关联的外部hive表使用性能有点差,因此决定把已经导入hive的dwd层后的数据进行删除,删除脚本如下:
del_date=$1
filename=$(pwd)/rowkey.csv
beeline
--incremental=true
--showHeader=false
--outputformat=dsv
--delimiterForDSV=$'\t'
-e
"select concat('deleteall \'default:monitorData\',\'',row_key,'\'')
from dw_ods.ods_api_msp_plan_point
where row_key like '%${del_date}'
">${filename}
sed -i '$d' ${filename}
hbase shell ${filename} > del.log
以上脚本删除的逻辑是首先通过外部关联表查询出需要删除的rowkey,由于这个表的rowkey后缀是日期,因此通过模糊日期查询出需要删除的rowkey写入一个文件,由于写入文件后最后一行是无关的信息,因此这里需要用sed命令进行删除。
脚本编辑好后,写入del_hbase.sh文件,最后执行sh del_hbase.sh yyyyMMdd进行删除指定日期的数据
三、方法三:比如我们要删除10号到13号的数据
我们可以利用Hbase的API先scan一定时间戳范围内的数据,然后在批量的删除,具体实现的代码如下:
/**
* 根据时间戳范围删除hbase的数据;
*/
object HbaseUtil {
def main(args: Array[String]): Unit = {
val s = System.currentTimeMillis()
val conf = HBaseConfiguration.create()
conf.set("hbase.zookeeper.quorum", PropertiesScalaUtils.loadProperties("zk_hbase")) //zk的地址;
conf.set("hbase.zookeeper.property.clientPort", PropertiesScalaUtils.loadProperties("zk_port"))
conf.set("hbase.master", PropertiesScalaUtils.loadProperties("hbase_master"))
conf.set("hbase.defaults.for.version.skip", "true")
conf.set("hhbase.rootdir", PropertiesScalaUtils.loadProperties("hbase_rootdir"))
conf.set("zookeeper.znode.parent", PropertiesScalaUtils.loadProperties("zookeeper_znode_parent"))
val connection = ConnectionFactory.createConnection(conf)
val table = connection.getTable(TableName.valueOf(PropertiesScalaUtils.loadProperties("hbase_table")))
val scan = new Scan()
scan.setTimeRange(args(0).toLong,args(1).toLong)
val rs = table.getScanner(scan)
val it = rs.iterator()
val list = new util.ArrayList[Delete]()
while (it.hasNext){
val delete = it.next()
val d = new Delete(delete.getRow)
list.add(d)
}
if(list.size()>0){
println("一共有多少条数据:"+list.size())
println("开始删除--------------------")
table.delete(list)
println("删除完成--------------------")
}else{
println("没有数据--------------------")
}
table.close()
connection.close()
val e = System.currentTimeMillis()
println("总共用时:"+ (e-s) +"毫秒")
}
}
这么写虽然能实现对指定时间戳范围内的数据进行删除,但是这么写就没有问题吗,是有问题的,这样删除的效率是非常低的,因为需要先把需要删除的查出来放到一个list里面,然后在进行批量的删除,这个方法对于数据量小的时候是可以的,数据量大的话,就会非常的慢,一定要根据自己的实际情况而定
这两种方法还是推荐使用第一张设置TTL,让其自动过期,如果非要删除段时间范围内的,可以用第二种但是要考虑性能问题.
————————————————