HDFS中的回收站

    在Linux操作系统下面,如果用户删除了某一个文件或者是某一个文件目录,操作系统并不会把这文件从文件系统中真正删除,而是先把它放入回收站中,这样在用户误操作的情况下还能找回原文件,以防止给用户造成中大损失。实际上,HDFS也为用户提供了类似这样的一个功能,但是这样的功能只限于用户在客户端的脚本操作,也就是HDFS的shell命令,而当用户写程序调用HDFS的API时,NameNode并不会把删除的文件或目录放入回收站Trash中,所以这一点请用户们要注意了。下面就来具体的谈谈HDFS是如何来实现这个回收站的功能的吧。

      首先,HDFS在客户端为用户提供了对文件系统的命令行操作,这个命令行操作是通过FsShell来实现的。当用户通过命令-rm/-rmr删除文件系统中的一个文件或者是目录的时候,HDFS并没有真正的删除这个文件或目录,而是把这个文件/目录移动到HDFS的系统回收站目录下。和Linux系统的回收站设计一样,HDFS会为每一个用户创建一个回收站目录:/user/用户名/.Trash/。这里就有一个问题了,经过长时间之后,删除的文件/目录占用了该文件系统大量的存储空间,而这些删除文件/目录确实已经真的对用户来说没有任何用处,那么HDFS是如何处理这个问题的呢?

      对于上面的问题,HDFS给出的一个解决方案是:每一个被用户通过Shell删除的文件/目录,在系统回收站中都一个周期,也就是当系统回收站中的文件/目录在一段时间之后没有被用户回复的话,HDFS就会自动的把这个文件/目录彻底删除,之后,用户就永远也找不回这个文件/目录了。在HDFS内部的具体实现就是在NameNode中开启了一个后台线程Emptier,这个线程专门管理和监控系统回收站下面的所有文件/目录,对于已经超过生命周期的文件/目录,这个线程就会自动的删除它们,不过这个管理的粒度很大。另外,用户也可以手动清空回收站,清空回收站的操作和删除普通的文件目录是一样的,只不过HDFS会自动检测这个文件目录是不是回收站,如果是,HDFS当然不会再把它放入用户的回收站中了。

   在HDFS中,用户的回收站用类org.apache.hadoop.fs.Trash来表示,这个类主要包含四个属性:

fs:当前用户使用的文件系统;

trash:用户的回收站目录(/user/用户名/.Trash);

current:存放被用户删除的文件/目录的路径(/user/用户名/.Trash/current);

interval:被用户删除的文件/目录在回收站中的生命期;

    在默认的情况下,interval的被Hadoop设置为0,即关闭了用户的回收站,所以我强烈的建议用户在读完本文之后赶紧在配置文件中设置fs.trash.interval的值(单位是minute),同时这个设置只与客户端和NameNode节点都相关,但是在客户端设置这个值,只能说明是开启了用户的回收站功能,而不能决定删除的文件/目录在回收站中的生命期,在NameNode节点设置这个值只能说是开启了自动清空所有用户回收站的功能。所以当某一个用户删除一个文件/目录时,HDFS就把这个待删除的文件/目录移动到该用户对应的回收站目录/user/用户名/.Trash/current中。另外,如果用户的回收站中已经存在了用户当前删除的文件/目录,则HDFS会将这个当前被删除的文件/目录重命名,命名规则很简单就是在这个被删除的文件/目录名后面紧跟一个编号(从1开始知道没有重名为止)。

    还有,NameNode是通过后台线程(org.apache.hadoop.fs.Trash.Emptier)来定时清空所有用户回收站中的文件/目录的,它每隔interval分钟就清空一次用户回收站。具体的操作步骤是,先检查用户回收站目录/user/用户名/.Trash下的所有yyMMddHHmm形式的目录,然后删除寿命超过interval的目录,最后将当前存放删除的文件/目录回收站目录/user/用户名/.Trash/current重命名为一个/user/用户名/.Trash/yyMMddHHmm,相关的源代码是:

/** Create a trash checkpoint. */
  public void checkpoint() throws IOException {
    if (!fs.exists(current))                      // no trash, no checkpoint
      return;

    Path checkpoint;
    synchronized (CHECKPOINT) {
      checkpoint = new Path(trash, CHECKPOINT.format(new Date()));
    }

    if (fs.rename(current, checkpoint)) {
      LOG.info("Created trash checkpoint: "+checkpoint.toUri().getPath());
    } else {
      throw new IOException("Failed to checkpoint trash: "+checkpoint);
    }
  }

  /** Delete old checkpoints. */
  public void expunge() throws IOException {
    FileStatus[] dirs = fs.listStatus(trash);            // scan trash sub-directories
    if( dirs == null) return;
    long now = System.currentTimeMillis();
    for (int i = 0; i < dirs.length; i++) {
      Path path = dirs[i].getPath();
      String dir = path.toUri().getPath();
      String name = path.getName();
      if (name.equals(CURRENT.getName()))  continue;      // skip current        

      long time;
      try {
        synchronized (CHECKPOINT) {
          time = CHECKPOINT.parse(name).getTime();
        }
      } catch (ParseException e) {
        LOG.warn("Unexpected item in trash: "+dir+". Ignoring.");
        continue;
      }

      if ((now - interval) > time) {
        if (fs.delete(path, true)) LOG.info("Deleted trash checkpoint: "+dir);
        else LOG.warn("Couldn't delete checkpoint: "+dir+" Ignoring.");
      }
    }
  }


private static final PathHOMES = new Path("/user/");
public void run() {
      if (interval == 0)  return;    // trash disabled
      
      long now = System.currentTimeMillis();
      long end;
      while (true) {
        end = ceiling(now, interval);
        try {                                     // sleep for interval
          Thread.sleep(end - now);
        } catch (InterruptedException e) {
          return;                                 // exit on interrupt
        }    
        try {
          now = System.currentTimeMillis();
          if (now >= end) {
            FileStatus[] homes = null;
            try {
              homes = fs.listStatus(HOMES);         // list all home dirs
            } catch (IOException e) {
              LOG.warn("Trash can't list homes: "+e+" Sleeping.");
              continue;
            }
            if (homes == null) continue;
            for (FileStatus home : homes) {         // dump each trash
              if (!home.isDir()) continue;
              try {
                Trash trash = new Trash(home.getPath(), conf);
                trash.expunge();
                trash.checkpoint();
              } catch (IOException e) {
                LOG.warn("Trash caught: "+e+". Skipping "+home.getPath()+".");
              }
            }
          }
        } catch (Exception e) {
          LOG.warn("RuntimeException during Trash.Emptier.run() " + StringUtils.stringifyException(e));
        }
      }
    }
      从这个回收线程(Emptier )的实现可以看出,被用户用命令删除的文件最多可在其回收站中保存2*interval分钟,最少可保存 interval分钟,过了这个有效期,用户删除的文件就永远也不可能恢复了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值