资讯详情

资讯详情

tmp能否替代Parquet?从临时文件到主流列式存储格式的全面解析

很多人问过我一个问题tmp 能不能替代 Parquet成为主流的数据格式说实话第一次听到这个说法的时候我愣了一下因为这两个名字根本不是同一个维度的东西。tmp 只是一个扩展名、一个文件生命周期的标记Parquet 是一套有完整设计的开源列式存储格式是整个大数据生态里事实上的主流数据文件格式。之所以会有这种疑问大概率是工作中见多了带.tmp的中间文件也见过 Spark 写 Parquet 时先落盘成.parquet.tmp再改名的过程于是有人误以为它们是可以互相对标的两种格式。这个问题拆开讲其实特别有意思。它背后包含的是临时文件到底算什么数据文件为什么需要“格式”所谓主流格式靠什么赢得生态地位把这些想清楚了你自然能理解为什么 tmp 替代不了 Parquet同时也会明白 tmp 在什么场景下才是合理的选择。这篇文章适合所有做数据开发、数仓、数据平台、数据同步的人也适合刚开始接触大数据、正在为“文件到底存成什么格式”而纠结的新手。我会把 tmp 和 Parquet 从原理到实操拆开讲透再顺手把日常开发里围绕/tmp和.parquet的那些坑一并梳理出来。1. 先搞清楚一个前提tmp 从来不是一种数据格式1.1 tmp 的三种常见身份tmp 在日常开发里至少有三种完全不同的身份很多人把它们混在一起讨论。第一是/tmp目录操作系统的临时目录。几乎所有程序都会往这里写运行时产生的中间文件Linux 和 macOS 下尤其常见。比如 Java 的java.io.tmpdir默认就指向/tmpJava 程序创建的临时文件默认都在这个目录里。你随便打开一台 Linux 服务器/tmp底下大概率躺着一堆乱七八糟的文件有些是安装程序的缓存有些是软件的 PID 文件有些是数据库的 socket 文件。第二是.tmp后缀。程序运行过程中需要“暂时存在”的数据很多开发人员会随手命名成xxx.tmp。这类文件的语义很明确它不是最终产物任务跑完就可以删除。Java 里的File.createTempFile(prefix, .tmp)就是这种模式的典型代表生成的临时文件通常带一串随机数字用完即弃。第三是“内容不确定”。这个东西最重要——.tmp文件的内容可以是任何东西。它可能是纯文本、CSV、JSON、二进制序列化对象、加密数据、或者干脆只是一个空的锁标记文件。你拿到一个.tmp文件如果不了解它是哪个程序、在什么阶段、以什么编码方式生成的就完全没法解析它。tmp 描述的是文件的生命周期而不是文件的内容结构。所以结论已经很清楚了tmp 不是一种数据格式它只是一个约定俗成的命名标签。你完全可以把一个 Parquet 文件命名为data.tmp文件内容在解析器眼里依然是 Parquet你也可以把一个 CSV 命名为data.parquet但它依然不是 Parquet。后缀不决定格式内容才决定格式。1.2 Java 程序里的 tmp 到底是干什么的热搜词“tmp在java中的意思”一直有人搜我顺便把这个点讲透。Java 里最常用的临时文件 API 是File.createTempFile调用一次会在系统临时目录下生成一个带随机数的新文件避免文件名冲突。它解决了什么问题多进程/多线程并发写临时文件时的命名冲突问题。Java 程序里 tmp 文件最常见的用途有几个。第一大对象溢写比如 JVM 在做外部排序、计算引擎在做 shuffle 时内存不够就把中间数据 spill 到磁盘临时文件里。第二文件上传和下载的中转比如 Web 应用接收到一个 2GB 的上传文件先把完整内容落到.tmp文件校验通过后再 rename 成正式文件名这样即使用户上传中断也不会产生半截的正式文件。第三解压和压缩的中间过程比如解压一个 ZIP 包时先把当前条目释放到临时文件再移动到目标目录。这些 tmp 文件有一个共同特点只在单个进程的短生命周期内有价值。进程退出、任务完成、连接断开之后它们就变成了纯粹的垃圾文件。它们没有 schema没有格式规范没有跨进程可识别的结构甚至连命名都是随机生成的。理解了这一点再看 Parquet就能感受到“真正的数据格式”和“临时文件标签”之间的差距有多大。1.3 Parquet 是真正的“数据格式”Parquet 是 Apache 基金会的顶级项目由 Twitter 和 Cloudera 合作开发目标是提供一种面向分析场景的、跨语言、自描述的列式存储格式。它跟 tmp 完全是两个物种。Parquet 的“格式能力”体现在几个地方。文件内部自带 schema 描述列名、列类型、编码方式、压缩算法全部写在文件尾部的 footer 里。读取任何 Parquet 文件不需要额外的元数据服务不需要建表语句解析器从文件里就能还原出完整的结构信息。数据按列组织同一列的数据连续存放配合压缩编码既能减小体积又能让查询只读取需要的列。文件内部还划分为多个 row group每个 row group 都带着自己的统计信息为分布式并行读取和谓词下推提供了基础。这些设计不是拍脑袋定的而是针对大数据场景下“文件体积大、扫描次数多、单次查询只关心少数列”的特点做的。你拿一个.tmp文件跟它对比会发现 tmp 连“格式”的门都没有入——它只是告诉别人“这是个临时文件”仅此而已。2. 四个关键维度Parquet 凭什么成为主流格式我把 tmp 和 Parquet 的核心差异整理成一个表后文再逐个展开。对比维度tmp 文件Parquet 文件本质临时文件命名约定列式存储格式规范schema 描述无内嵌在文件 footer自描述数据布局与内容无关按生产顺序写入列式存储 row group 分区压缩能力取决于内含格式通常无优化内置多种编码天然适合压缩读取优化无列裁剪、谓词下推、统计信息生态兼容无固定规则Hadoop、Spark、Hive、DataX 等广泛支持生命周期短用完即弃可长期存储、归档、跨系统交换2.1 schema 自描述Parquet 把“约定”写进了文件数据文件最容易出问题的地方不是读写性能而是“两个系统之间怎么理解同一份数据”。后端开发都很熟悉“统一返回数据格式”这套东西——接口层把响应统一成{code, message, data}这样的结构调用方才能只写一套解析逻辑。数据文件其实也需要这种“统一格式约定”。CSV 和 JSON 为什么在大数据场景里让人头疼因为它们的 schema 是外置的。两个人传 CSV如果没约定好字段顺序和类型接收方就只能靠猜。今天写任务的人把order_id放第一列明天维护的人把userId放第一列下游解析全部错位。JSON 稍微好一点字段名内嵌在文件里但每个文件都要扫描全部内容才能知道有哪些字段而且类型信息很弱数字可能是字符串字符串也可能被解析成数字。Parquet 把 schema 直接写进文件读取端拿到的是一份完整的、强类型的结构描述。列名、类型、嵌套结构、空值标记全部自包含。这意味着什么意味着任何人拿到一个 Parquet 文件即使没有任何外部文档也能通过标准工具看到它的字段结构。你可以把 Parquet 理解成“自带说明书”的数据文件而.tmp文件连说明书都没有。2.2 列式存储带来的压缩红利同样数据体积差几倍压缩率是 Parquet 最直观的优势也是最容易打动人的地方。这要从数据布局说起。行式存储CSV、JSON Line写入时一行接一行一行的所有字段连续存放。压缩算法虽然能压掉一些重复内容但不同类型的数据混在一起压缩效率天然受限。比如一个订单文件里金额是小数、时间是字符串、状态是枚举值它们在一个压缩块里混杂出现通用压缩算法很难找到足够长的重复模式。列式存储则把同一列的数据连续放在一起。时间列全是时间戳状态列全是那几个枚举值枚举字段用字典编码后原来几万个重复的PAID、PENDING字符串只需要存一次字典表数据区全部换成短编号。RLERun-Length Encoding还能把连续出现的相同值压成“值重复次数”两个字段。数字列用 Delta Encoding 存相邻值的差值差值通常很小再配合压缩效果非常好。我做个具体例子。一批电商订单日志JSON Line 格式大概 20GB转成 Parquet 通常是 5GB 左右视数据重复度而定压缩 4 倍很正常有些高重复度数据能做到 10 倍以上。这不仅是省钱还意味着扫描相同数据量的磁盘 IO 能减少一个数量级。你拿一个.tmp文件去比如果它内部装的是 CSV它享受不到任何列式布局带来的压缩红利体积就是实打实的原始大小。2.3 读取性能列裁剪与谓词下推文件存储格式的第二个硬性指标是查询效率。这在分析场景里往往比压缩更重要因为磁盘扫描才是最大的瓶颈。列裁剪Column Pruning很好理解。Parquet 按列存储查询只需要读涉及的那几列。一张订单宽表有 100 列业务查询只关心金额和状态Parquet 可以只读取这两列对应的数据块其余 98 列完全不碰。行式存储做不到这件事你必须把每一行的完整内容读出来再丢掉不需要的字段100 列就得读 100 列的量。谓词下推Predicate Pushdown更进一步。每个 Parquet 文件由多个 row group 组成每个 row group 在文件 footer 里记录了统计信息比如某列的最小值、最大值、空值数量。Spark 读 Parquet 时如果过滤条件和一个 row group 的统计信息完全不匹配比如status CLOSED而某个 row group 的状态列最大值是PENDING那这个 row group 会被直接跳过连数据块都不用打开。在执行计划里你能看到PushedFilters这一项看到它就意味着过滤条件下推到文件读取层了。这两种优化叠加起来的效果非常夸张。同样是几十 GB 的表行式格式全表扫描可能要跑几分钟Parquet 配合分区裁剪和谓词下推可能几十秒就出结果。tmp 文件没有这种结构它就是一块“死数据”查询引擎面对它只能老老实实扫描。2.4 可拆分性文件结构决定分布式并行能力大数据场景还有一个隐性的硬要求——文件必须能被切分成多个分片交给多台机器并行处理。这决定了任务的扩展性。Parquet 文件内部天然被划分为多个 row group每个 row group 都是一个独立的读取单元。HDFS 上一个大文件按 block 切分默认 128MBSpark、Hive 可以把不同 block 分给不同 Executor 并行处理。即使某个 row group 跨越了 block 边界解析器也能正确处理因为 row group 在文件里是有明确边界标记的。.tmp文件呢如果里面是未压缩的文本切开很容易把一行记录从中间劈开如果是自定义二进制切分点在哪都找不到。这也是为什么 MapReduce 时代的文本格式要依赖 InputFormat 傻傻地找换行符而自定义二进制格式基本没法在分布式框架里安全拆分。一个不能安全切分的文件在分布式计算里就没有并行度可言。这就是 tmp 不可能成为主流格式的底层原因之一。3. 大数据链路里tmp 只是临时落脚点Parquet 才是正式格式3.1 写入过程中那个.tmp是什么来头很多人在数据目录里看到xxx.parquet.tmp或_temporary文件夹就误以为 tmp 和 Parquet 是某种“竞争关系”。实际上恰恰相反这些.tmp是引擎在写入 Parquet 过程中使用的原子性保护机制。Spark 写 Parquet 时的大致流程是所有 task 先把数据写到临时目录或带.tmp标记的文件里当所有 task 都成功完成后driver 统一做一次 rename 操作把临时文件改成正式文件名对数据消费者“一次性”暴露结果。这样做的目的很简单在写入过程中如果某个 task 失败实际数据和外部能看到的数据保持隔离不会出现下游读到半截文件的情况消费者永远只能看到完整提交成功的版本。Hive、Flink、DataX 写 HDFS 也有类似的机制。这个设计本身其实是 tmp 的“合理用法”——tmp 就是用来做中间态的。它是写入流程的保护罩不是数据的最终归宿。你单独挑出一个.parquet.tmp文件看它的格式核心还是 Parquet只是当前处于“未提交”状态。这跟“用 tmp 替代 Parquet 格式”完全是两码事。3.2 DataX、Spark、Hive 为什么都认 Parquet文件格式的“主流”地位最终体现在生态兼容性上。DataX 是阿里开源的数据同步工具它的 HDFS Reader 原生支持读取 Parquet 文件。配置里指定fileType为parquetDataX 就会用 Parquet 解析逻辑去读取数据{ reader: { name: hdfsreader, parameter: { defaultFS: hdfs://nameservice1, path: /user/hive/warehouse/ods.db/order_daily, fileType: parquet, column: [ { index: 0, type: string }, { index: 1, type: long } ] } } }如果你把fileType配成textDataX 就会按文本行解析即使文件真实格式是 Parquet读出来的也是一堆乱码。所以这不仅是“支持不支持”的问题而是每个主流工具都针对 Parquet 实现了专门的解析和优化路径。Spark 更夸张Parquet 是它默认的高性能数据源之一spark.read.parquet、df.write.parquet基本可以无缝对接。Hive 建表时写STORED AS PARQUET就能让查询引擎自动识别列式布局享受列裁剪和谓词下推。为什么大家都围绕 Parquet 做适配因为它开放、标准、自描述而且生态已经滚雪球滚起来了。你今天选一个只有 A 公司能读的私有二进制格式明天你的下游同事就会因为打不开文件而骂人。3.3 tmp 文件走出单机就“没人认识”我见过太多这样的场景开发机上的 Python 脚本生成了一批中间结果文件名是result.tmp放在/tmp下。当天任务跑完没问题三天后要复用这份数据写脚本的同事已经忘了文件里是什么序列化格式最后只能重跑任务。这不是个别现象而是 tmp 作为“非格式”的必然结果。/tmp下的文件是单机生命周期内的产物跟具体进程、具体文件系统、具体机器强绑定。换一台机器、换一个进程文件里的字节流就变成天书。Parquet 不会这样它是跨平台、跨语言的标准。Python 用pandas能读Java 用ParquetReader能读Go、Rust 都有自己的库DuckDB 一条 SQL 就能查。所有工具都依赖文件内嵌的 schema 工作不需要额外传一份元数据。“tmp 文件用什么打开”这个问题本身就证明了 tmp 不适合做主流格式。因为答案是“看情况”——看你当初是用什么程序、什么序列化方式、什么编码规则写的。而“parquet 文件怎么打开”的答案是确定性的任何支持 Parquet 的工具输入路径直接读。4. 工作里那些和 tmp、Parquet 纠缠的常见问题实录4.1 MySQL 报 error 2002/tmp被清理后的经典连锁反应热搜词里有一个高频报错ERROR 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock。这个问题十有八九跟/tmp目录被清理或权限被改有关。MySQL 在本地连接时默认走 Unix socket路径通常是/tmp/mysql.sock。有些运维脚本为了腾磁盘空间会对整个/tmp做无差别清理把 socket 文件一起删掉。结果就是 MySQL 服务进程还活着但客户端找不到连接入口了于是报 error 2002。更隐蔽的情况是/tmp目录权限被改成 000或者挂载选项里带了noexec即使 socket 文件还在客户端也无法正常访问。遇到这个报错先确认两件事第一MySQL 进程是否存活用ps -ef | grep mysqld看第二my.cnf 里配置的 socket 路径和报错路径是否一致。如果服务在但 socket 没了重启 mysqld 会重新生成 socket 文件如果频繁被误删最好把 socket 路径配置到受控目录比如/var/run/mysqld/mysqld.sock并让清理脚本避开这个目录。注意别把清理脚本设置成对整个/tmp一刀切。真正稳妥的做法是清理超过一定时间的文件并维护一个排除名单把 socket、锁文件、运行中进程占用的临时文件都排除在外。4.2 Oracle Grid 安装时 PRVF-7546临时目录也能卡住部署另一个跟/tmp相关的经典报错是 Oracle Grid Infrastructure 安装时的PRVF-7546完整信息类似The work directory /tmp/gridSetupActions2026-09-24_04-14-35PM不可用。Oracle 安装程序会在/tmp下创建gridSetupActions开头的临时工作目录用来存放安装过程中的操作记录、校验脚本和日志。报错原因基本就三类。第一/tmp所在文件系统空间不足安装程序写不了临时文件。第二/tmp目录权限不对安装用户没有写权限。第三/tmp被挂载成noexec安装程序无法执行拷过去的脚本。排查方式也很简单df -h /tmp看空间ls -ld /tmp看权限mount | grep /tmp看挂载选项。该清理的清理该改权限的改权限该换临时目录的就在安装参数里指定别的路径。这种问题不复杂但很烦人因为安装过程卡在 1% 然后报个目录不可用排查半天才发现是/tmp的空间被日志撑满了。这类经历其实就是在提醒我们依赖无规范临时目录的流程天然有脆弱性。数据格式选型也是同样的逻辑你把正式数据也塞进一个没有规范、没有结构、随时可能被清理的载体里那整个数据链路的稳定性都得陪葬。4.3 Parquet 文件怎么打开四种标准姿势跟 tmp 的“无从打开”相反Parquet 的打开方式非常确定。我给新手整理四种最常用的姿势。第一种DuckDB一行 SQL 就能查SELECT * FROM read_parquet(order_daily.parquet) LIMIT 20;第二种Python 环境pandas 配合 pyarrowimport pandas as pd df pd.read_parquet(order_daily.parquet, enginepyarrow) print(df.head())第三种命令行工具 parquet-toolsparquet-tools show order_daily.parquet --limit 20 parquet-tools schema order_daily.parquet第四种Spark SQLSELECT * FROM parquet./path/to/order_daily.parquet LIMIT 20;这些工具的共同点完全依赖 Parquet 文件内嵌的 schema输入路径直接读不需要任何外部元数据。这才是“主流格式”应该有的底气。4.4 特定领域的专用格式在自己的生态里很强出了门就没人认顺便说一下热搜词里的“iq15 数据格式”。在 DSP、音频、通信这类硬件领域Q15 或 IQ15 这种定点数编码格式是很常见的一个符号位加 15 个小数位纯粹为了定点 DSP 的计算效率。它在自己的芯片和编译器生态里是“标准格式”性能极好但拿到大数据平台、数据仓库里几乎没有任何通用工具能直接解析它必须自己写解码逻辑。这个例子恰好能说明“主流格式”的本质一种格式能成为主流不是因为它在某个特定场景下最优而是因为它获得了足够广泛的生态共识。Parquet 胜出的地方就在于它虽然是列式存储但在数据仓库、数据湖、ETL、分析引擎、云存储这些大数据的主要场景里它就是那个“通用语言”。tmp 没有共识没有规范没有工具链自然不可能成为主流。5. 格式选型的实操建议与个人心得5.1 什么场景用 tmp 完全没问题tmp 不是一无是处它有非常合理的生存空间。凡是在单个进程内、短生命周期、不跨系统交换的中间态数据用 tmp 都没有问题甚至是最合理的选择。举几个例子排序算法的磁盘溢写、大文件上传的暂存、解压过程的中间文件、进程间通信的临时锁文件。这些场景的共同点是“自己写自己读”生命周期以分钟甚至秒为单位不需要 schema不需要跨工具解析不需要长期归档。这时候给文件加.tmp后缀反而是一种好习惯它明确告诉所有人“这不是正式产物别对它负责”。关键是要有边界感。tmp 可以用在临时态但不能充当正式数据的容器。一旦数据需要跨进程、跨机器、跨团队传递或者需要长期保留再扔进.tmp里就是给自己埋雷。5.2 什么场景应该果断用 Parquet反过来以下场景我建议你直接上 Parquet。数仓分层表。不管 ODS、DWD 还是 ADS只要是落表到 HDFS 或对象存储上的数据用 Parquet 基本是数仓的最佳实践。数仓的核心操作是列式分析Parquet 的列裁剪、谓词下推、压缩能力就是为这个场景量身定做的。多引擎共享的数据湖。Spark 批处理、Flink 流写、Presto/Trino 即席查询、DataX 离线同步如果大家读写的是同一个 Parquet 文件各自的解析逻辑都能正常工作。换成私有格式每接入一个引擎就要做一次特殊适配。需要长期归档的数据。Parquet 自描述 开放标准意味着十年后你不需要一个快失传的 SDK 才能打开当年的数据。这一点对归档场景尤其重要。实操上有一个配置组合值得参考Parquet Zstd 压缩 合理的分区策略。row group 大小也不用刻意调保持 Spark 默认128MB 左右就比较稳。字段变更靠 schema 演进解决新增列直接追加老文件不重写也能被新逻辑读取。5.3 我的格式选型心得与踩坑记录最后分享两个我自己踩过的坑。第一个是临时结果被“临时”了三个星期。当时为了图省事把中间结果写成一个自定义二进制.tmp文件想着“第二天跑关联任务时读一下就行”。结果第二天任务延期三天后我又要处理别的事等想起这份数据时已经不知道那串字节流是什么结构了。最后只能重跑整个链路浪费时间也浪费算力。那之后我给自己定了一条规矩中间结果如果有可能被二次使用直接写 Parquet哪怕多花几行代码。第二个是 CSV 的 schema 错位问题。早期团队用 CSV 做接口间的数据交换字段顺序一旦调整下游组那边解析全乱。查了半天才发现是生产链路里加了一个列但下游的表结构没有同步。换成 Parquet 之后schema 内嵌在每个文件里新增列是老文件不存在的字段读取端按自己的 schema 解释配合演进规则再也没出现过这种“列错位”的故障。我个人的原则很简单文件的生命周期决定格式选择。如果它只能活几十秒tmp 完全够用如果它要活过任务重跑、要跨越引擎和团队边界就老老实实写成 Parquet。格式不是玄学它本质上是在回答“谁会在什么时候、通过什么工具、读这份数据”的问题。想清楚这个问题选型就不会跑偏。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →