删了HDFS又能怎样?记一次删库不跑路事件

2024-09-03 11:38
文章标签 事件 怎样 hdfs 一次 删库

本文主要是介绍删了HDFS又能怎样?记一次删库不跑路事件,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

    • 一、事发背景
    • 二、应急措施
      • 直观的方案
    • 三、分析过程
      • hdfs文件删除过程
        • BlockManager
        • EditLog
      • hdfs元数据的加载
      • 方案确定
    • 四、灾难重演
    • 五、经验总结

一、事发背景

​ 上个月的某一天,由于集群空间不足,公司有一位权限较高的员工通过跳板机在某个线上集群执行手动清理命令,疯狂地执行hadoop fs -rmr -skipTrash /user/hive/warehouse/xxxxx,突然,不知道是编辑器的问题还是换行问题,命令被截断,命令变成了hadoop fs -rmr -skipTrash /user/hive/warehouse,悲剧此刻开始发生!?

/user/hive/warehouse目录下存储了所有hive表的数据,关联公司多个业务线,一旦丢失,意味着巨大的损失。由于加了-skipTrash参数,意味着删除的数据不会放入回收站而是直接删除,这个参数不加的话很容易挽回,回收站里直接找,可惜逃不了墨菲定律,最担心的事还是发生了。

​ 领导解决不了的事,只有小弟上,看我接下来怎样四两拨千斤。

二、应急措施

运维第一时间找到我,当时说误删了 /user/hive/warehouse目录。当时我也是一震惊,完了完了!凭我仅有的直觉,立马停掉了HDFS集群!后面证明是很明智的选择。

直观的方案

  1. 通过快照恢复?hdfs快照从来没用过,也没创建过快照,所以此路不通
  2. 通过回收站恢复?删除时加了-skipTrash参数,不会放回收站,此路也不通
  3. 删库跑路成为一代传奇人物?嗯,还不错

三、分析过程

想要恢复数据,首先要理解hdfs删除文件的过程,各个文件系统的删除逻辑都不一样,理解了hdfs的删除过程,才知道上面那一条hadoop fs -rmr xxxx究竟干了什么,究竟还有没有救,怎么去救等等问题。

  • hdfs文件删除过程

    下面是hdfs删除路径的方法,源码路径org.apache.hadoop.hdfs.server.namenode.FSNamesystem.delete()

      boolean delete(String src, boolean recursive, boolean logRetryCache)throws IOException {waitForLoadingFSImage();BlocksMapUpdateInfo toRemovedBlocks = null;writeLock();boolean ret = false;try {checkOperation(OperationCategory.WRITE);checkNameNodeSafeMode("Cannot delete " + src);toRemovedBlocks = FSDirDeleteOp.delete(        // @1this, src, recursive, logRetryCache);ret = toRemovedBlocks != null;} catch (AccessControlException e) {logAuditEvent(false, "delete", src);throw e;} finally {writeUnlock();}getEditLog().logSync();if (toRemovedBlocks != null) {removeBlocks(toRemovedBlocks);   // @2}logAuditEvent(true, "delete", src);return ret;}
    

    代码@1里面做了三件比较重要的事

    ①从NameNode维护的的目录树里面删除路径,这也是为什么执行删除操作之后就无法再通过hadoop fs -ls xxx或其它api方式再查看到路径的根本原因

    ②找出被删路径关联的block信息,每个文件包含多个block块,分布在各个DataNode,此时并未真正物理删除DataNode上物理磁盘上的block块

    ③记录删除日志到editlog(这一步也很重要,甚至是后面恢复数据的关键)

    代码@2把将要删除的block信息添加到org.apache.hadoop.hdfs.server.blockmanagement.BlockManager里面维护的InvalidateBlocks对象中,InvalidateBlocks专门用于保存等待删除的数据块副本

    以上步骤并未涉及真正的物理删除的操作

    BlockManager

    BlockManager管理了hdfs block的生命周期并且维护在Hadoop集群中的块相关的信息,包括快的上报、复制、删除、监控、标记等等一系列功能。

    BlockManager中有个方法invalidateWorkForOneNode()专门用于定时删除InvalidateBlocks中存储的待删除的快,此方法会在NameNode启动时在BlockManager的内部线程类ReplicationMonitor定时轮循把要删除的块放入DatanodeDescriptor中的逻辑,方法的调用路径如下:

    org.apache.hadoop.hdfs.server.namenode.NameNode#initialize(Configuration conf) org.apache.hadoop.hdfs.server.namenode.NameNode#startCommonServices(Configuration conf)org.apache.hadoop.hdfs.server.namenode.FSNamesystem#startCommonServices(Configuration conf, HAContext haContext)org.apache.hadoop.hdfs.server.blockmanagement.BlockManager#activate(Configuration conf)org.apache.hadoop.hdfs.server.blockmanagement.BlockManager.ReplicationMonitor#run()org.apache.hadoop.hdfs.server.blockmanagement.BlockManager#computeDatanodeWork()org.apache.hadoop.hdfs.server.blockmanagement.BlockManager#computeDatanodeWorkcomputeInvalidateWork(int nodesToProcess)org.apache.hadoop.hdfs.server.blockmanagement.BlockManager#invalidateWorkForOneNode(DatanodeInfo dn)org.apache.hadoop.hdfs.server.blockmanagement.InvalidateBlocks#invalidateWork(final DatanodeDescriptor dn)
    

    BlockManager维护了InvalidateBlocks,存放了待删除的 block,BlockManager在NameNode启动时会单独启动一个线程,定时把要删除的块信息放入InvalidateBlocks中,每次会从InvalidateBlocks队列中为每个DataNode取出blockInvalidateLimit(由配置项dfs.block.invalidate.limit,默认1000)个块逻辑在BlockManager.computeInvalidateWork()方法里会把要删除的块信息放入DatanodeDescriptor中的invalidateBlocks数组,DatanodeManager再通过DataNode与NameNode心跳时,构建删除块的指令集,NameNode再把指令下发给DataNode,心跳由DatanodeProtocol调用,方法的调用路径如下:

    org.apache.hadoop.hdfs.server.protocol.DatanodeProtocol#sendHeartbeat()org.apache.hadoop.hdfs.server.namenode.NameNodeRpcServer#sendHeartbeat()org.apache.hadoop.hdfs.server.namenode.FSNamesystem#handleHeartbeat()org.apache.hadoop.hdfs.server.blockmanagement.DatanodeManager#handleHeartbeat()
    

    DatanodeManager.handleHeartbeat()中构建删除的指令给DataNode,待NameNode发送,的代码如下:

      /** Handle heartbeat from datanodes. */public DatanodeCommand[] handleHeartbeat(形参略){// ....其余代码逻辑略//check block invalidationBlock[] blks = nodeinfo.getInvalidateBlocks(blockInvalidateLimit);if (blks != null) {cmds.add(new BlockCommand(DatanodeProtocol.DNA_INVALIDATE,blockPoolId, blks));}// ....其余代码逻辑略return new DatanodeCommand[0];}
    

    定时轮循+limit 1000个块删除的特性决定了hdfs删除数据并不会立即真正的执行物理删除,并且一次删除的数量也有限,所以上面的应急措施中立即停止HDFS集群是最明智的选择,虽然有的数据在轮循中已被删除,所以事发后停止HDFS集群越早,被删的数据越少,损失越小!

    EditLog

    恢复数据的另一个关键是EditLog,EditLog记录了hdfs操作的每一条日志记录,包括当然包括删除,我们所熟知的文件操作类型只有增、删、改,但是在HDFS的领域里,远远不止这些操作,我们看看EditLog操作类型的枚举类org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes

    在这里插入图片描述

总计40多种操作类型!打破了人们印象中文件只有增删改读的几种操作,有木有突破你的想象?

EditLog长啥样呢?在hadoop的配置参数dfs.namenode.name.dir可以找到路径

在这里插入图片描述

这里EditLog文件是序列化后的二进制文件不能直接查看,hdfs自带了解析的命令,可以解析成xml明文格式,我们解析一个看看

hdfs oev -i edits_0000000000035854978-0000000000035906741 -o edits.xml

查看文件

在这里插入图片描述

对hdfs的每一个操作都会记录一串RECORD,RECORD里面不同的操作包含的字段属性也不同,但是所有操作都具备的属性是OPCODE,对应上面的枚举类org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes中40多种操作

hdfs元数据的加载

hdfs启动时,NameNode会加载Fsimage,Fsimage记录了hdfs现有的全量的路径信息,启动过程中仅仅加载Fsimage?这句话不完全正确!启动的同时,还会加载未被合并成fsimage的EditLog。关于fsimage具体细节这里不展开。举个栗子:

假设Hadoop 3分钟checkpoint一次生成Fsimage文件,EditLog 1分钟生成一个文件,下面是依次生成的文件:

fsimage_1
editlog_1
editlog_2
editlog_3
fsimage_2
editlog_4
editlog_5

当NameNode启动时,会加载后缀时间戳最大的那个fsimage文件和它后面产生的editlog文件,也就是会加载fsimage_2、editlog_4、editlog_5进NameNode内存。假设我们执行hadoop fs -rmr xxx命令的操作记录在了editlog_5上面,那么,重启NameNode后,我们查看hdfs无法再查看到xxx路径,如果我们把fsimage_2删掉,NameNode则会加载fsimage_1、editlog_1、editlog_2,此时的元数据里面xxx还未被删除,如果此时DataNode未物理删除block,则数据可以恢复,但是editlog_4、editlog_5对应的hdfs操作会丢失。有没有更好的方法呢?

方案确定

方案一:删掉fsimage_2,从上一次checkpoint的地方也就是fsimage_1恢复,我们集群的实际配置,是一个小时生成一次fsimage文件,也就是说,这种恢复方案会导致近一小时hdfs新增的文件全部丢失,集群日工作流的的量2w左右,这一个小时不知道发生了多少事情,可想而知的后果是恢复之后一堆报错,显然不是最好的方案

方案二:修改editlog_5,把删除xxx那条操作改成其它安全的操作类型,这样重启NameNode后,又可以看到这个路径。good idea!就这么干!

四、灾难重演

为了完整的演示一遍恢复过程,我找了个测试环境重新演示一遍。请勿随意在生产环境演示!!!

  • 删除路径

    bash-4.2$ hadoop fs -ls /tmp/user/hive
    Found 1 items
    drwxrwxrwx   - hdfs hdfs          0 2020-08-13 15:02 /tmp/user/hive/warehouse
    bash-4.2$ hadoop fs -rmr -skipTrash /tmp/user/hive/warehouse 
    rmr: DEPRECATED: Please use '-rm -r' instead.
    Deleted /tmp/user/hive/warehouse
    bash-4.2$ 
    bash-4.2$ hadoop fs -ls /tmp/user/hive
    bash-4.2$ 
    
  • 关闭HDFS集群

  • 解析editlog

    找到删除操作时间点范围内所属的editlog文件,解析

    hdfs oev -i edits_0000000000005827628-0000000000005827630 -o editlog.xml
    

    查看editlog.xml,执行删除操作的日志已经记录在里面了

    在这里插入图片描述

  • 替换删除操作

    把OP_DELETE操作替换成比较安全的操作,例如:

    <RECORD><OPCODE>OP_SET_OWNER</OPCODE><DATA><TXID>5827629</TXID><SRC>/tmp/user/hive/warehouse</SRC><USERNAME>hadoop</USERNAME></DATA>
    </RECORD>
    

    在这里插入图片描述

    注意:TXID一定要一样!!!

  • 反解析成editlog

# 反解析更改后的xml文件成editlog
hdfs oev -i editlog.xml -o edits_0000000000005827628-0000000000005827630.tmp -p binary
# 重命名掉之前的editlog
mv edits_0000000000005827628-0000000000005827630 edits_0000000000005827628-0000000000005827630.bak
# 替换反解析后的editlog
mv edits_0000000000005827628-0000000000005827630.tmp edits_0000000000005827628-0000000000005827630
  • 拷贝到JournalNodes

    需要把修改后的editlog考本到所有JournalNode节点的本地editlog存放目录,可在dfs.journalnode.edits.dir找到

    scp edits_0000000000005827628-0000000000005827630 xxx001:/hadoop/hdfs/journal/master/current/
    scp edits_0000000000005827628-0000000000005827630  xxx002:/hadoop/hdfs/journal/master/current/
    scp edits_0000000000005827628-0000000000005827630  xxx003:/hadoop/hdfs/journal/master/current/
    
  • 启动HDFS

    启动hdfs后,查看被删目录是否已恢复

bash-4.2$ hadoop fs -ls /tmp/user/hive
Found 1 items
drwxrwxrwx   - hadoop hdfs          0 2020-08-13 15:41 /tmp/user/hive/warehouse

被删除的**/tmp/user/hive/warehouse**目录已成功恢复!!

五、经验总结

  1. 从恢复程度来看,会有部分block块已被删除,读取时会报block missed之类的错误,但是已经把损失降到了最低,删除的多少取决于误删后停掉hdfs集群的时间,停的越早,损失越小
  2. 从预防方面,权限必须要控制好,由于线上跳板机权限给的过于随意导致了误操作的产生
  3. hdfs删数据的规范,一定不能加-skipTrash做强制删除,放入回收站,多给自己和公司一些挽回的余地
  4. 这是一次宝贵的经历,头一回感受到处理时的心弦紧绷和成功解决后的兴奋激动,事后奶茶已收到,当事人也保住了饭碗,哈哈

这篇关于删了HDFS又能怎样?记一次删库不跑路事件的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1132863

相关文章

HDFS—存储优化(纠删码)

纠删码原理 HDFS 默认情况下,一个文件有3个副本,这样提高了数据的可靠性,但也带来了2倍的冗余开销。 Hadoop3.x 引入了纠删码,采用计算的方式,可以节省约50%左右的存储空间。 此种方式节约了空间,但是会增加 cpu 的计算。 纠删码策略是给具体一个路径设置。所有往此路径下存储的文件,都会执行此策略。 默认只开启对 RS-6-3-1024k

HDFS—集群扩容及缩容

白名单:表示在白名单的主机IP地址可以,用来存储数据。 配置白名单步骤如下: 1)在NameNode节点的/opt/module/hadoop-3.1.4/etc/hadoop目录下分别创建whitelist 和blacklist文件 (1)创建白名单 [lytfly@hadoop102 hadoop]$ vim whitelist 在whitelist中添加如下主机名称,假如集群正常工作的节

禁止平板,iPad长按弹出默认菜单事件

通过监控按下抬起时间差来禁止弹出事件,把以下代码写在要禁止的页面的页面加载事件里面即可     var date;document.addEventListener('touchstart', event => {date = new Date().getTime();});document.addEventListener('touchend', event => {if (new

FreeRTOS内部机制学习03(事件组内部机制)

文章目录 事件组使用的场景事件组的核心以及Set事件API做的事情事件组的特殊之处事件组为什么不关闭中断xEventGroupSetBitsFromISR内部是怎么做的? 事件组使用的场景 学校组织秋游,组长在等待: 张三:我到了 李四:我到了 王五:我到了 组长说:好,大家都到齐了,出发! 秋游回来第二天就要提交一篇心得报告,组长在焦急等待:张三、李四、王五谁先写好就交谁的

【经验交流】修复系统事件查看器启动不能时出现的4201错误

方法1,取得『%SystemRoot%\LogFiles』文件夹和『%SystemRoot%\System32\wbem』文件夹的权限(包括这两个文件夹的所有子文件夹的权限),简单点说,就是使你当前的帐户拥有这两个文件夹以及它们的子文件夹的绝对控制权限。这是最简单的方法,不少老外说,这样一弄,倒是解决了问题。不过对我的系统,没用; 方法2,以不带网络的安全模式启动,运行命令行,输入“ne

BT天堂网站挂马事件后续:“大灰狼”远控木马分析及幕后真凶调查

9月初安全团队披露bt天堂网站挂马事件,该网站被利用IE神洞CVE-2014-6332挂马,如果用户没有打补丁或开启安全软件防护,电脑会自动下载执行大灰狼远控木马程序。 鉴于bt天堂电影下载网站访问量巨大,此次挂马事件受害者甚众,安全团队专门针对该木马进行严密监控,并对其幕后真凶进行了深入调查。 一、“大灰狼”的伪装 以下是10月30日一天内大灰狼远控的木马样本截图,可以看到该木马变种数量不

(function() {})();只执行一次

测试例子: var xx = (function() {     (function() { alert(9) })(); alert(10)     return "yyyy";  })(); 调用: alert(xx); 在调用的时候,你会发现只弹出"yyyy"信息,并不见弹出"10"的信息!这也就是说,这个匿名函数只在立即调用的时候执行一次,这时它已经赋予了给xx变量,也就是只是

argodb自定义函数读取hdfs文件的注意点,避免FileSystem已关闭异常

一、问题描述 一位同学反馈,他写的argo存过中调用了一个自定义函数,函数会加载hdfs上的一个文件,但有些节点会报FileSystem closed异常,同时有时任务会成功,有时会失败。 二、问题分析 argodb的计算引擎是基于spark的定制化引擎,对于自定义函数的调用跟hive on spark的是一致的。udf要通过反射生成实例,然后迭代调用evaluate。通过代码分析,udf在

flume系列之:记录一次flume agent进程被异常oom kill -9的原因定位

flume系列之:记录一次flume agent进程被异常oom kill -9的原因定位 一、背景二、定位问题三、解决方法 一、背景 flume系列之:定位flume没有关闭某个时间点生成的tmp文件的原因,并制定解决方案在博主上面这篇文章的基础上,在机器内存、cpu资源、flume agent资源都足够的情况下,flume agent又出现了tmp文件无法关闭的情况 二、

react笔记 8-19 事件对象、获取dom元素、双向绑定

1、事件对象event 通过事件的event对象获取它的dom元素 run=(event)=>{event.target.style="background:yellowgreen" //event的父级为他本身event.target.getAttribute("aid") //这样便获取到了它的自定义属性aid}render() {return (<div><h2>{