HDFS Truncate文件截断

article/2025/9/22 10:18:38

前言


在linux操作系统的使用中,有的时候我们可能想对某个现有的文件做尾部的截取(比如为了保留头部关键信息),但同时又不想重新写一个新的文件出来,这个时候我们其实可以采用系统提供的truncate命令。单词truncate的本意是“截断”,在这里由于操作的对象是文件,所以此命令的作用就是文件的截断。那么同样作为一套成熟的文件系统,HDFS是否也能支持这样的API方法呢?可能它与linux文件系统的一个巨大差别在于它是分布式的,但是这不会影响到HDFS Truncate功能的实现,正如标题所显示的,HDFS同样支持了truncate的操作。

HDFS Truncate功能概述


首先第一点要让大家有一个大概的认识,HDFS Truncate不会是一个很大,很复杂的改动,它实质上是对现有HDFS读写文件操作方法的一个补充和完善。简单地说,就是在HDFS的客户端和服务端增加一个truncate(…)方法。个人认为其中唯一一点比较难的是truncate截断操作如何落实到HDFS的block文件块的操作中,这一点也是大家所要着重去理解的。

Truncate文件截断在HDFS上的表现其实是block的截断。传入目标文件,与目标保留的长度(此长度应小于文件原大小),如下图所示:



图 1-1 HDFS文件截断原理

上图表示的意思是说,一个文件有拥有4个block块,然后需要截取的长度刚好处在第二block快上,当真正截断操作发生后,B2的后半部,B3和B4就会被移除。

与原生的linux上的truncate命令略有不同的是,HDFS的truncate命令不支持截断超过当前文件大小的情况,如果是这种情况会抛出异常。而在linux中,这种情况下会在超出实际长度的部分返回空字节数据

HDFS Truncate使用场景


这节我们聚焦的话题是HDFS Truncate到底有哪些适用场景呢?在其设计文档中,列举了如下几项:

  • 第一点,允许用户移除意外写入的数据。
  • 第二点,当写事务发生失败的时候,可以进行回滚,回到之前写入成功的事务状态。
  • 第三点,有能力移除一次失败的写操作而写入的不完整的数据。
  • 第四点,提升HDFS对于其它文件系统的支持度。

这里很有意思的一点是,truncate方法与append方法刚好是互逆的方法,在许多意外情况下我们append的出错数据可以通过truncate进行还原。

HDFS Truncate的实现原理


在本小节中,我们来看看HDFS的具体的实现原理,步骤不会太复杂,下面会同时给出具体的操作图帮助大家进行理解。

首先是一个初始的文件块,当前长度与目标截取的长度如下所示:



图 1-2 原始文件

要截取到newLength所指示的地方,我们需要做下面2个过程,

第一步,定位到新的截取长度所对应的块,然后把后面多余的块(这里就是B4)从此文件中进行移除,如下图所示:



图 1-3 定位截取长度所在的block

第二步,找到新长度所对应的block块之后,计算此块内部需要移除的偏移量,然后进行删除,如下图:



图 1-4 截取最终效果图

上图就是最后的截取效果图,当然了,这里有一种特殊情况如果截取的位置恰好在block的分界处,比如刚好在B3的末尾处,则上面第二步block内部偏移量的截取操作则可以不用执行

以上是对普通文件截取的操作,我们可以看到,它是直接在原文件上做的修改,我们可以称之为“in-place-truncate”,但是在有些情况下,我们为了保留原始的数据,需要重新拷贝一份出来做截取的动作,我们称这样的情况为“copy-on-truncate”,主要有以下2类情况:

  • 当我们操作的文件为快照中的文件,需要保留当时快照维护的数据。
  • 当前DataNode正处于升级过程。

上述2类情况是我们需要特殊对待的时候。

HDFS Truncate的实现


前面原理部分的内容大家如果理解了之后,将会非常有助于此小节的学习。本节我们将从源码层面去分析HDFS Truncate的实现。

首先我们从truncate方法的发起入口开始,就是DFSClient端的客户端方法,

  // 文件截断方法public boolean truncate(String src, long newLength) throws IOException {checkOpen();// 如果截断长度为负数,则抛出异常if (newLength < 0) {throw new HadoopIllegalArgumentException("Cannot truncate to a negative file size: " + newLength + ".");}try (TraceScope ignored = newPathTraceScope("truncate", src)) {// 调用服务端的对应处理方法return namenode.truncate(src, newLength, clientName);} catch (RemoteException re) {throw re.unwrapRemoteException(AccessControlException.class,UnresolvedPathException.class);}}

这里我们省略中间的方法,直接跳到最终的处理方法,最终进入到了类FSDirTruncateOp中,此类的作用就是专门处理文件截断的,
在进行真正文件截断之前,服务端会做一些判断,如果不符合要求,则程序会直接退出,

  // 截断一个文件到给定的大小static TruncateResult truncate(final FSNamesystem fsn, final String srcArg,final long newLength, final String clientName,final String clientMachine, final long mtime,final BlocksMapUpdateInfo toRemoveBlocks, final FSPermissionChecker pc)throws IOException, UnresolvedLinkException {...try {iip = fsd.resolvePathForWrite(pc, srcArg);src = iip.getPath();if (fsd.isPermissionEnabled()) {fsd.checkPathAccess(pc, iip, FsAction.WRITE);}// 获取输入路径对应的INodeFile对象INodeFile file = INodeFile.valueOf(iip.getLastINode(), src);// 不支持EC下的条带式的文件块if (file.isStriped()) {throw new UnsupportedOperationException("Cannot truncate file with striped block " + src);}final BlockStoragePolicy lpPolicy = fsd.getBlockManager().getStoragePolicy("LAZY_PERSIST");// 不支持内存方式存储的文件if (lpPolicy != null && lpPolicy.getId() == file.getStoragePolicyID()) {throw new UnsupportedOperationException("Cannot truncate lazy persist file " + src);}...

然后再判断一下当前文件是否已经被截断为目标长度,

      ...// 判断文件是否已经被截断为相同的目标长度final BlockInfo last = file.getLastBlock();if (last != null && last.getBlockUCState()== BlockUCState.UNDER_RECOVERY) {// 获取最后一个正在被操作的截断块final Block truncatedBlock = last.getUnderConstructionFeature().getTruncateBlock();if (truncatedBlock != null) {// 计算当前操作文件的总长度=不包含最后一个块的字节大小+最后一个截断块的字节大小final long truncateLength = file.computeFileSize(false, false)+ truncatedBlock.getNumBytes();// 如果当前的截断后的长度等于目标截断长度,则说明此文件已经被截断成功,返回截断结果if (newLength == truncateLength) {return new TruncateResult(false, fsd.getAuditFileInfo(iip));}}}...

如果当前没有截断操作在进行,则进行文件的长度检查,判断是否满足当前要求,

      ...// Opening an existing file for truncate. May need lease recovery.fsn.recoverLeaseInternal(RecoverLeaseOp.TRUNCATE_FILE, iip, src,clientName, clientMachine, false);// 再次判断当前文件文件大小与目标大小的关系long oldLength = file.computeFileSize();// 如果大小一致,则无须截断,返回结果if (oldLength == newLength) {return new TruncateResult(true, fsd.getAuditFileInfo(iip));}// 如果目标文件大小大于原始文件大小,则抛异常退出if (oldLength < newLength) {throw new HadoopIllegalArgumentException("Cannot truncate to a larger file size. Current size: " + oldLength+ ", truncate size: " + newLength + ".");}...

下面将要开始真正的截断操作了,

      ...// 下面准备开始真正的文件截断操作了,此过程对应上小节的图1-2到图1-3final QuotaCounts delta = new QuotaCounts.Builder().build();// 判断截断的位置是否刚好在block与block的分割处onBlockBoundary = unprotectedTruncate(fsn, iip, newLength,toRemoveBlocks, mtime, delta);...

这里进入unprotectedTruncate方法,

  private static boolean unprotectedTruncate(FSNamesystem fsn,INodesInPath iip, long newLength, BlocksMapUpdateInfo collectedBlocks,long mtime, QuotaCounts delta) throws IOException {assert fsn.hasWriteLock();...verifyQuotaForTruncate(fsn, iip, file, newLength, delta);// 获取此文件中需要保留的快照中的块Set<BlockInfo> toRetain = file.getSnapshotBlocksToRetain(latestSnapshot);// 累加block大小直到获取首次超出目标长度为止long remainingLength = file.collectBlocksBeyondMax(newLength,collectedBlocks, toRetain);file.setModificationTime(mtime);// 如果当前block累加的长度恰好等于目标截断长度,说明截断的位置刚好在block边界上return (remainingLength - newLength) == 0;}

继续进入INodeFile的collectBlocksBeyondMax方法,看看里面到底是如何执行的,

  public long collectBlocksBeyondMax(final long max,final BlocksMapUpdateInfo collectedBlocks, Set<BlockInfo> toRetain) {final BlockInfo[] oldBlocks = getBlocks();if (oldBlocks == null) {return 0;}int n = 0;long size = 0;// max为目标截断长度,进行size的累加直到累加大小第一次超出截断长度for(; n < oldBlocks.length && max > size; n++) {size += oldBlocks[n].getNumBytes();}if (n >= oldBlocks.length) {return size;}// 重新设置当前文件的inode,对原块做一个新的拷贝,原来的块要么后面被删除,要么被保留truncateBlocksTo(n);// 将超出截断位置后的block进行回收和删除if (collectedBlocks != null) {for(; n < oldBlocks.length; n++) {final BlockInfo del = oldBlocks[n];// 如果这些块不是快照中的块,则回收入待移除列表中if (toRetain == null || !toRetain.contains(del)) {collectedBlocks.addDeleteBlock(del);}}}return size;}

最后是进行最后块的偏移截取,也就是上一节中图1-3到图1-4的过程,

      ...// 如果不是在边界上,则对于最后一个块还需要进行偏移量的设置if (!onBlockBoundary) {// 获取最后一块还需要截去的偏移量long lastBlockDelta = file.computeFileSize() - newLength;assert lastBlockDelta > 0 : "delta is 0 only if on block bounday";// 进行最后一个块的截取truncateBlock = prepareFileForTruncate(fsn, iip, clientName,clientMachine, lastBlockDelta, null);}...

这里进入prepareFileForTruncate方法,

  static Block prepareFileForTruncate(FSNamesystem fsn, INodesInPath iip,String leaseHolder, String clientMachine, long lastBlockDelta,Block newBlock) throws IOException {assert fsn.hasWriteLock();...// 获取当前文件的最后一个块BlockInfo oldBlock = file.getLastBlock();// 此处判断是否要重新拷贝一个block进行截断操作boolean shouldCopyOnTruncate = shouldCopyOnTruncate(fsn, file, oldBlock);if (newBlock == null) {newBlock = (shouldCopyOnTruncate) ? fsn.createNewBlock(false): new Block(oldBlock.getBlockId(), oldBlock.getNumBytes(),fsn.nextGenerationStamp(fsn.getBlockManager().isLegacyBlock(oldBlock)));}...

这里copyOnTruncate的条件就是上节提到的2大场景,

  private static boolean shouldCopyOnTruncate(FSNamesystem fsn, INodeFile file,BlockInfo blk) {// 是否处于升级相关操作if (!fsn.isUpgradeFinalized()) {return true;}if (fsn.isRollingUpgrade()) {return true;}// 当前block是否为在当前最近一次的快照中return file.isBlockInLatestSnapshot(blk);}

上述逻辑判断完毕,则进行对应条件下的截断操作,

    ...final BlockInfo truncatedBlockUC;BlockManager blockManager = fsn.getFSDirectory().getBlockManager();if (shouldCopyOnTruncate) {// 如果是copy-on-truncate的方式,则将会分配新的块进行截断truncatedBlockUC = new BlockInfoContiguous(newBlock,file.getPreferredBlockReplication());truncatedBlockUC.convertToBlockUnderConstruction(BlockUCState.UNDER_CONSTRUCTION, blockManager.getStorages(oldBlock));// 设置最后一个块的长度,oldBlock.getNumBytes() - lastBlockDelta得到的值对应的就是目标截断的长度truncatedBlockUC.setNumBytes(oldBlock.getNumBytes() - lastBlockDelta);truncatedBlockUC.getUnderConstructionFeature().setTruncateBlock(oldBlock);file.setLastBlock(truncatedBlockUC);blockManager.addBlockCollection(truncatedBlockUC, file);...} else {// 直接在原块上做修改blockManager.convertLastBlockToUnderConstruction(file, lastBlockDelta);oldBlock = file.getLastBlock();assert !oldBlock.isComplete() : "oldBlock should be under construction";BlockUnderConstructionFeature uc = oldBlock.getUnderConstructionFeature();uc.setTruncateBlock(new Block(oldBlock));// 设置最后一个块的长度,oldBlock.getNumBytes() - lastBlockDelta得到的值对应的就是目标截断的长度uc.getTruncateBlock().setNumBytes(oldBlock.getNumBytes() - lastBlockDelta);uc.getTruncateBlock().setGenerationStamp(newBlock.getGenerationStamp());truncatedBlockUC = oldBlock;...}

OK,至此truncate操作正式结束。大家可以细致地与上节中所述的原理过程进行对照。

小结


HDFS Truncate截断功能目前发布在Hadoop 2.7.0以及以上的版本中,社区相关JIRA HDFS-3107(HDFS truncate),想更加详细了解此特性的同学可以阅读此JIRA.

参考资料


[1].https://issues.apache.org/jira/browse/HDFS-3107
[2].https://issues.apache.org/jira/secure/attachment/12697141/HDFS_truncate.pdf


http://chatgpt.dhexx.cn/article/p6hRLaMg.shtml

相关文章

oracle 数据库truncate,Oracle中的truncate用法

语法&#xff1a;TRUNCATE TABLE table; 在使用truncate语句(DDL语言)可以删除表中的所有记录。使用truncate语句删除数据时&#xff0c;通常要比使用delete语句快得多&#xff0c;这是因为使用truncate语句删除数据时&#xff0c;不会产生任何回退信息&#xff0c;因此执行t…

mysql truncate 条件_MySQL的TRUNCATE关键字

在MysqL中&#xff0c;还有一种方式可以删除表中的所有记录&#xff0c;需要使用TRUNCATE关键字。 TRUNCATE [TABLE] 表名 参数说明 表名&#xff0c;指定要执行删除操作的数据表 在数据库chapter03中创建一张表tab_truncate 图片描述" title""> 向数据表tab…

truncate命令简介

一、truncate命令简介 truncate命令通常用于将文件缩小或扩展到指定的大小。如果文件大于指定的大小&#xff0c;则会丢失额外的数据。如果文件较短&#xff0c;则会对其进行扩展&#xff0c;并且扩展部分的读数为零字节。 二、truncate命令安装 我们使用turncate命令之前需要…

Linux 命令(147) —— truncate 命令

文章目录 1.命令简介2.命令格式3.选项说明4.常用示例参考文献 1.命令简介 truncate 将文件的大小缩小或扩展到指定的大小。 如果指定的文件不存在将被创建。 如果文件大于指定的大小&#xff0c;则会丢失额外的数据。如果较短&#xff0c;它将被扩展&#xff0c;扩展的稀疏部…

Public Key Infrastructure——公钥基础设施

今天做一篇关于PKI的笔记&#xff0c;PKI是目前来说信息安全领域很流行的技术&#xff0c;它的应用已经很广泛了。 PKI是指公钥基础设施&#xff0c;它是通过公钥加密技术和数字签名服务保证传输数据的安全。它最重要的几个部分是公钥加密算法&#xff0c;数字证书&#xff0c;…

检查安装包(grid infrastructure和Oracle database所需补丁)

检查安装包 根据文档说明&#xff0c;安装 Grid Infrastructure 和 Oracle Database 所需的补丁包有&#xff1a; binutils-2.15.92.0.2 compat-libcap1-1.10 compat-libstdc-33-3.2.3 elfutils-libelf-0.97 elfutils-libelf-devel-0.97 expat-1.95.7 gcc-3.4.6 gcc-c…

DAIR-V2X: A Large-Scale Dataset for Vehicle-Infrastructure Cooperative 3D Object Detection

VICAD系统开发挑战 &#xff1a;缺乏来自真实场景的VICAD数据集。 3 DAIR-V2X 数据集 DAIR-V2X 采集来自真实场景的大规模、多模态、多视图数据集&#xff0c;带有 3D 标签注释&#xff0c;用于车辆-路边设施协同感知。 针对车辆和路边设施传感器之间的时间异步问题&#xff…

ins40401 oracle,安装orace grid infrastructure 提示[INS-40404]问题

grid infrastructure 提示“[INS-40404] The installer has detected a configured instance of oracle grid infrastructure software on the server! 今天是2013-12-03,描述一下出现这个错误的过程,我没按照正常的方式删掉 grid infrastructure,而是直接删掉了安装目录,但…

A/B-Test (Overlapping Experiment Infrastructure: More, Better, Faster Experimenta)

本文针对上周的工作进行了总结&#xff0c;上上周的工作将在接下来通过文章进行总结&#xff0c;主要内容为DEIN 模型。 为了解决推荐算法基于web实验的模型验证&#xff0c;我参考了Overlapping Experiment Infrastructure: More, Better, Faster Experimentation 2010 的goo…

Infrastructure-Based Object Detection and Tracking for Cooperative Driving Automation: A Survey

由于基础设施的&#xff08;infrastructure-based&#xff09;传感器安装位置和姿势的灵活性&#xff0c;基于基础设施的目标检测和跟踪系统可以增强联网车辆的感知能力。 一、基于基础设施的感知系统 基于基础设施的目标感知系统包含四个典型阶段&#xff1a;1&#xff09;信…

公钥基础设施 (Public-key infrastructure PKI)

公钥基础设施 PKI 1.功能2.设计3.认证方法3.1证书机构&#xff08;CA&#xff09;3.2信任网络&#xff08;Wot&#xff09;3.3简单的公共关键基础设施&#xff08;SKPI&#xff09;3.4分散的PKI3.5基于区块链的 PKI 4.使用 密码学上&#xff0c;公开密钥基础建设借着数字证书认…

shell infrastructure host占用cpu_网速、CPU、内存监控软件TrafficMonitor

Traffic Monitor 是一款 Windows 平台的网速、CPU等监控悬浮窗软件,可以显示当前网速、CPU及内存占用率,小窗口可以拖动到窗口的任意位置,并且支持嵌入到任务栏显示,历史流量统计等功能。 Traffic Monitor 是免安装的,打开即用。下载地址:https://github.com/zhongyan…

Oracle 12cR1 Grid Infrastructure 安装指南之一(官方直译)

1 Oracle Grid Infrastructure安装清单 以下清单提供了所需的 Preinstallation 步骤的列表. 使用此清单可协调任务, 以帮助确保在启动Oracle Grid Infrastructure以进行群集安装之前完成所有系统和存储准备和配置任务. 1.1系统硬件, 软件和配置清单 本节包含以下服务器配置…

HLA RTI(Run-time Infrastructure)

RTI&#xff08;Run-time Infrastructure&#xff09; 概述 主要作用 具体实现了HLA接口规范。为了实现联邦内部各个联邦成员之间进行高效的信息交换&#xff0c;HLA接口规范用文字定义了各种标准服务和接口&#xff0c;而RTI则用程序设计语言将这些标准的服务和接口转成了标…

DDS之DCPS Infrastructure模块

DCPS Infrastructure Infrastructure ModuleEntityEntity IdentifierQoS policyListenerStatusStatusConditionEnabling Entities QosPolicyStatusStatus 定义StatusMask 定义Listener callback定义Listener callback的实现 Condition and WaitSet Infrastructure Module 从Inf…

Oracle 19.3 Grid Infrastructure 软件安装详细教程

更多文章&#xff0c;欢迎关注作者公众号&#xff0c;欢迎一起交流。 1 安装环境 CentOS 7.9Oracle Database 19.3 - Enterprise Edition 2 安装配置 2.1 内存要求 1&#xff09;数据库安装&#xff1a;至少1GB&#xff0c;推荐2G以上&#xff1b; 2&#xff09;Grid安装&…

Oracle 19c Grid Infrastructure安装

概述 本文描述在单个主机上&#xff08;不是RAC&#xff09;GI 19c的安装。 Oracle数据库软件19c已安装&#xff0c;但未创建任何数据库。参见这篇文章。 主机为Oracle Linux 7&#xff0c;主机上已安装先决条件包(oracle-database-preinstall-19c)&#xff0c;数据库软件用户…

infra-structure Ad Hoc

“infrastructure”模式&#xff1a; 所谓infrastructure是在一种整合有线与无线局域网架构的应用模式&#xff0c;与ad- hoc不同的是配备无线网卡的设备必须通过ap来进行无线通讯&#xff0c;设置后&#xff0c;无线网络设备就必须有AP&#xff08;Access Pointer&#xff09;…

DDD(八)【基础设施层】

最近被&#xff24;&#xff24;&#xff24;吸引了阿&#xff0c;在这里感谢一下小佟&#xff0c;呵呵&#xff0c;领域驱动设计是个不错的东西&#xff0c;帮助我们把问题清晰化&#xff0c;这候对于复杂业务逻辑是很重要的&#xff0c;今天这一讲主要说一下&#xff24;&…