由于需要规范HBase数据表命名,对测试环境的数据进行了重命名操作。
停掉所有服务后执行下面改名操作
# 禁用活跃用户表 disable 'DC_XXL:ACTIVE_USERS' # 创建快照 snapshot 'DC_XXL:ACTIVE_USERS', 'tony_snapshot' # 克隆快照为新的表(使用新名称) clone_snapshot 'tony_snapshot', 'DC_XXL:ACTIVE_USERS_LOG' # 按命名空间查看克隆的表是否存在 list_namespace_tables 'DC_XXL' # 查看快照 list_snapshots # 删除快照 delete_snapshot 'tony_snapshot' # 查看旧表详情 desc 'DC_XXL:ACTIVE_USERS' # 查看新表详情,比较两个表是否一致 desc 'DC_XXL:ACTIVE_USERS_LOG' # 禁用旧表 disable "DC_XXL:ACTIVE_USERS" # 删除旧表 drop 'DC_XXL:ACTIVE_USERS'
然后重新启动测试服务。
操作完后发现测试服务器的存储空间不足,想要删除一些数据占用,使用df -h查看各服务器的磁盘空间占用情况,发现Hadoop存储目录已占用了90%以上的空间,所以顺便使用命令查看一下Hadoop的空间使用情况
# 查看hadoop根目录各文件夹占用空间 hadoop dfs -du -h / # 查看hbase占用空间 hadoop dfs -du -h /hbase
通过命令一层层查看,可以发现主要是hbase占用了存储,而刚刚执行前面的命令操作后,在hbase目录下面发现主要存储空间占用的目录有:data(项目数据表存储目录)与 archive(存档目录),而archive目录一般存储的是备份用的,理论上来说是可以直接删除,所以网上查了一些资料说没问题后,就直接使用 rmr(hadoop dfs -rmr /hbase/archive/data/DC_XXL/ACTIVE_USERS)命令将里面的存档文件删除了,没想到引发了一系列的故障......
过了一会继续查看磁盘空间占用,发现磁盘使用在持续快速增长,很快有的服务器已达到100%
使用 hadoop dfs -du -h /hbase 查看发现,MasterProcWALs 目录突然间多了几十G,经查询这是HBase状态日志目录,进去将它们全部删除,发现磁盘占用还是100%没有变化
继续排查,使用 hadoop dfs -du -h / 发现,原来直接使用rmr删除后,hadoop会将文件移动到 /user/root/.Trash/Current/hbase/ 这个垃圾站目录下面
而这时在查看服务运行日志时,也发现了大量的IO异常
Hbase_thrift.IOError: IOError(message=b'org.apache.hadoop.hbase.client.RetriesExhaustedException: Failed after attempts=16, exceptions: Mon Jul 06 20:01:52 CST 2020, RpcRetryingCaller{globalStartTime=1594036912662, pause=100, maxAttempts=16}, org.apache.hadoop.hbase.NotServingRegionException: org.apache.hadoop.hbase.NotServingRegionException: DC_XXL:ACTIVE_USERS_LOG,4,1593765189585.73133efd2d876b7f7edabc3ab6709cae. is not online on slave2,16020,1594035938293 at org.apache.hadoop.hbase.regionserver.HRegionServer.getRegionByEncodedName(HRegionServer.java:3249) at org.apache.hadoop.hbase.regionserver.HRegionServer.getRegion(HRegionServer.java:3226) at org.apache.hadoop.hbase.regionserver.RSRpcServices.getRegion(RSRpcServices.java:1414) at org.apache.hadoop.hbase.regionserver.RSRpcServices.get(RSRpcServices.java:2429) ......
从日志中可以看到,是由于写入数据时,所要写入的region下线所引发的IO异常,在浏览器中进入HBase web管理器(http://master:16010),可以看到所有节点都是在线的,拉到 Tables 位置,查看对应的数据表状态,会发现该表的Other Regions标识了出现问题的数量。
经过查找资料与思考分析,执行快照操作,HBase会在HDFS中创建一个和unix硬链接相同的存储文件,而执行镜像克隆操作时,实际上新表与旧表共用相同的数据块,新表只在data目录创建新表文件,但并不会复制region,只是通过linkfile定位旧表文件地址。当删除旧表以后,HBase应该会将旧表数据移动到 archive 目录下面,只有在新数据写入并提交compact时,才会数据迁移到新表文件中。所以执行快照和克隆操作是秒级,几百G的数据执行命令后立马结果就出来了。
而我直接删除了备份文件,数据并没有迁移完,导致某些region直接丢失了。
随后将回收站的文件重新移动到 archive 目录下面,使用 hbck 命令修复,发现修复失败,由于是测试数据只需要从kafka中生成最近一周的就可以了,且旧数据占用太多空间需要清理,所以没有继续研究修复,直接将它清除,决定创建新表运行相关服务批量重新生成测试数据。
进入hbase shell中,执行命令
disable 'DC_XXL:ACTIVE_USERS_LOG'
发现命令卡死,直到超时才退出,发现数据表禁用不了也删除不了。
登录 zookeeper ,删除对应的数据
# 连接zookeeper服务 /usr/local/zookeeper/bin/zkCli.sh -server master:2181 # 查看hbase数据表 ls /hbase/table # 删除数据表 rmr /hbase/table/DC_XXL:ACTIVE_USERS_LOG
删除Hadoop存储的数据
# 查看hadoop的dfs文件 hadoop dfs -du -h /hbase/data/DC_XXL # 删除文件 hadoop dfs -rmr /hbase/data/DC_XXL/ACTIVE_USERS_LOG # 清除垃圾站文件 hadoop dfs -rmr /user/root/.Trash/Current/hbase/data/DC_XXL/ACTIVE_USERS_LOG
再次进入hbase shell,输入list已查看不到数据表了,输入命令创建新表
create 'DC_XXL:ACTIVE_USERS_LOG',{NAME=>'c',COMPRESSION=>'lz4',VERSIONS=>1},SPLITS=>['2','4','6','9']
这时直接提示:ERROR: Table already exists 异常信息,经过排查发现原来meta表还存储了数据表以及对应的region信息,需要一一清除干净才行
# 扫描meta表,查找当前数据表相关记录 scan 'hbase:meta',{STARTROW=>'DC_XXL:ACTIVE_USERS_LOG',ENDROW=>'DC_XXL:ACTIVE_USERS_LO~'} # 删除表记录命令(这一条命令必须在shell执行,脚本可能删不干净) deleteall 'hbase:meta','DC_XXL:ACTIVE_USERS_LOG' # 记录量少的话可以手动一条条删除 ......
批量删除 hbase:meta表数据表相关记录脚本
import happybase connect = happybase.Connection(host='master', port=9090, timeout=300000, autoconnect=True, compat='0.98', transport='buffered', protocol='binary') def scan(table, row_prefix=None, columns=None): t = happybase.Table(table, connect) scan = t.scan(row_prefix=row_prefix, columns=columns) data = [] for key, value in scan: result = {row.decode().replace('c:', '').lower(): value[row].decode() for row in value} result['pk'] = key.decode() data.append(result) return data def delete(table, rowkey): t = happybase.Table(table, connect) t.delete(rowkey) if __name__ == '__main__': columns = ['info:server'] row_prefix = 'DC_XXL:ACTIVE_USERS_LOG'.encode() result = scan('hbase:meta', row_prefix, columns=columns) print(len(result)) for row in result: pk = row.get('pk') print(pk) delete('hbase:meta', pk)
脚本执行成功后,需要在shell中执行前面的scan语句检查一次,看看是否全部都删除干净
然后重启hbase服务,进入shell中执行创建数据表语句,这次就可以执行成功了(不重启hbase服务,可能会影响数据表状态,然后产生大量的异常状态记录占用存储空间)
在这次事件中出现了几次错误操作,导致本次故障。由于之前也试过多次改名,并没有出现问题,导致本次操作时过于轻心。没有深入了解快照和克隆快照的底层原理,而网上查到的 archive 相关文章在不了解其背景的情况下,就以为可以直接删除文件,导致异常的发生。当存储空间不足时,没有全面排查了解存储空间丢失原因,就进行一些处理,导致直接恢复删除文件并没有修复问题。而对hbase一些命令的底层运行原理不了解,也造成一些操作上的误判。好在这只是测试环境,数据丢失影响不大,接下来需要深入研究hbase各方面的运行机制,避免这种低级问题的发生。