☰
为什么tmp不能替代Parquet?一文讲透主流数据格式的本质区别
2026/9/29 16:38:00 网站建设 项目流程

"为什么 tmp 不能替代 Parquet 作为主流数据格式?"——这个问题这两天在技术群里又冒出来了。问的人大概率是被 .tmp 这个后缀迷惑了,觉得它也是一种"数据格式",那凭什么 Parquet 能当主流,它就不行。说实话这个问题问得特别好,因为它逼着我们把"主流数据格式"的底层逻辑拆开看:一个格式能成为主流,靠的不是名字好不好听,而是规范是否明确、性能是否可预期、生态是否足够大。

我用大白话先给结论:Parquet 是一套有严格规范、专门为分析场景设计的列式存储格式,而 tmp 压根不算"格式",它只是操作系统和应用程序用来标记"临时文件"的通用后缀,里面装什么完全取决于谁创建了它。所以"tmp 替代 Parquet"这个命题本身就不成立,就像问"塑料袋能不能替代集装箱"一样。这篇文章我会从概念、原理、性能、实操几个角度把这个事彻底讲透,最后附上我在真实项目里跟这两种"东西"打交道的踩坑记录,新手看完至少不会再被 .tmp 后缀误导。

1. 先把概念捋清楚:tmp 到底是什么?

1.1 tmp 不是格式,是一种"临时状态"标记

很多人的误区是把文件后缀当成了格式本身。看到 .tmp 就以为它是一种数据格式,就像看到 .doc 认为是 Word 格式一样。但 .tmp 和 .doc 有本质区别:.doc 背后有 Microsoft Office 的二进制格式规范,而 .tmp 背后什么规范都没有。

.tmp(也叫 .temp)只是"temporary file"的缩写,表示"这是一个临时文件"。它是程序在运行过程中产生的中间产物,用途五花八门:

  • 程序崩溃时的自动保存内容(比如 Word、Excel 的恢复文件);
  • 下载工具下载到一半的缓存块;
  • 编辑器给正在编辑的文件创建的副本锁;
  • 数据库写日志前的临时排序文件;
  • 安装程序解压出来的安装包部件。

你甚至可以自己写一个程序,往 /tmp 目录里随便丢一个命名为 x.tmp 的文件,内容写什么都不违反任何"规范"。因为 tmp 根本没有规范可违反。操作系统层面的临时目录(Linux 的 /tmp、Windows 的 %TEMP%)也是一样,它只是一个约定俗成的"放临时东西的地方",不是一种数据格式。

注意:这里要区分两件事——/tmp是一个目录,xxx.tmp是一个文件后缀。它们都带 "tmp",但一个是路径,一个是文件扩展名。后面会讲到,MySQL 报错里的/tmp和 Oracle 安装报错里的/tmp都是目录问题,和数据格式无关,但很多人把它们混在一起。

1.2 为什么 tmp 看起来像是个"格式"

既然 tmp 不是格式,为什么总有人觉得它有资格跟 Parquet 比?我分析下来有三个原因。

第一,它有后缀。任何带点后缀的文件都会给人"这是一种类型"的错觉,比如 .log、.bak、.tmp。第二,它确实被某些系统当作输出。比如一些 ETL 工具把中间结果默认落成带 .tmp 后缀的文件,看起来好像真是一种"业界格式"。第三,搜索引擎"tmp 文件用什么打开"这类问题很多,问题多容易让人误以为它是个常见格式。

但只要你较真去查,就会发现没有任何一家机构给 .tmp 出过白皮书,没有任何一个库叫 "tmp format library",也没有任何一个开源项目声称"我用 tmp 格式存储数据"。反观 Parquet,有 Apache 基金会的规范文档、有官方实现的 Java/C++ 库、有专门的测试套件。这就是"看起来像格式"和"真的是格式"的差别。

为了加深理解,我建议新手做一个实验:随便找一台 Linux 机器,用echo "hello" > /tmp/test.tmp生成一个临时文件,然后用file /tmp/test.tmp看一下,系统会告诉你这是 ASCII text,而不是 "tmp format data"。file命令不认识 .tmp,因为它根本不是一种可识别的类型。这是最直观的验证方式。

2. Parquet 凭什么能成为主流数据格式?

2.1 Parquet 是有标准的列式存储格式

Parquet 是 Apache 软件基金会旗下的开源列式存储格式,最初由 Twitter 和 Cloudera 合作开发,设计灵感来自 Google 的 Dremel 论文里那套嵌套数据模型。它解决的痛点是:在大数据场景下,传统行式存储(比如 CSV、JSON Lines)在分析查询时效率太低,而列式存储可以大幅减少 IO、提升压缩率。

它的文件结构分三层:

  • 行组(Row Group):文件按行切成多个行组,每个行组包含若干行,行组是并行读取的基本单位;
  • 列块(Column Chunk):每个行组内部按列存储数据,每一列单独存放,这就是"列式"的由来;
  • 页(Page):列块进一步切成页,页是编码和压缩的最小单位。

每个文件末尾还有元数据区,记录 schema 信息、各列块的统计信息(min/max、null count 等)。这些统计信息是后续查询优化的重要基础,后面讲谓词下推时还会提到。

另外,Parquet 天生支持嵌套数据结构,不需要把复杂对象拍平。它通过 repetition level 和 definition level 两个概念来还原嵌套关系,存出来的文件依然是扁平的列,但读出来能还原成原始的嵌套 JSON/对象结构。这一点对现在大量 JSON 数据的分析特别重要。

2.2 列式存储赢在压缩率和查询 IO

为什么列式存储对分析场景这么重要?我拿一个生活化例子说明。假设你有一张全校学生的成绩表,一万行、二十列。传统行式存储像一个打印好的成绩册,每一页是一行完整的学生信息。你只想统计所有人的数学成绩,也得翻完一整本册子,把每一页都扫一遍。列式存储则像把册子拆成二十本小册子,一本只装一列,查数学成绩就只拿"数学"这本。

对应的工程收益有两个:

  • 列裁剪(Column Pruning):查询只读取需要的列。比如一张 100 列的大表,分析 SQL 只用其中 3 列,列式存储可以跳过其他 97 列的数据块,IO 直接减少 90% 以上;
  • 高压缩率:同一列的数据类型一致、取值范围相近,字典编码、RLE 编码在这种数据上压缩效果极好。我实测过一份 100GB 的 CSV 日志,转成 Parquet 加 Snappy 压缩后大概 12GB,压缩比接近 8:1;如果换 ZSTD 还能再小一些。

还有一个细节很多人容易忽略:Parquet 的页级统计信息支持谓词下推。比如查询条件带WHERE dt = '2024-01-01',引擎会先看每个行组的 dt 列 min/max 统计,如果发现某行组的日期范围不包含 2024-01-01,整个行组直接跳过。这相当于给查询加了"快速预览",连数据都不用读。

实操心得:如果你给用户导出一份 50GB 的 CSV,他们用 Excel 打开之前得先等半天,还可能直接崩。同样是这份数据转成 Parquet,配合 DuckDB 或 Trino 查询,秒级出结果。这就是列式存储带来的体验差异。

2.3 生态才是"主流"的真正门槛

一个格式光性能好还不够,要成为主流必须有生态。Parquet 在这点上享有几乎全宇宙的适配:

  • 计算引擎:Spark、Hive、Flink、Presto/Trino、Doris、ClickHouse、DuckDB 都原生支持;
  • 数据集成:DataX、Flink CDC、Airbyte、NiFi 都能直接读写;
  • 存储服务:AWS Athena、阿里云 MaxCompute、Snowflake 等云服务默认推荐 Parquet 作为交换格式;
  • 编程语言:Java/C++/Python(pyarrow、pandas)/Go/Rust 都有官方或社区实现。

这带来的结果是:一份 Parquet 文件在任何一个环节都不会被卡住。你在 HDFS 上写的 Parquet,拿到本地用 Python 能读,传到云上 Athena 能查,塞进 Doris 能导入。这种"一次写成、处处可读"的体验,是 CSV 之外几乎没有其他格式能做到的。

另外我还想提一句"iq15数据格式"这个热词。SAP IQ(Sybase IQ)很早就用列式存储做分析型数据库,后来被并入 SAP 产品线。这说明列式存储的理念早在 90 年代就在商业数据库里验证过了,Parquet 只是把这个理念变成了开放标准,让整个生态都能用。所以当你看到"iq15 数据格式"时,要意识到它和 Parquet 是一派的思路,而不是说 iq15 是一种文件后缀。

3. 正面回答:为什么 tmp 替代不了 Parquet?

3.1 从格式定义看,tmp 没有参赛资格

这个问题最粗暴的回答是:你得先定义清楚 tmp 的格式是什么,它才有资格跟 Parquet 比。但正如前面说的,tmp 没有规范。它没有文件头标识、没有字段类型系统、没有编码规则、没有压缩算法定义、没有任何版本兼容的约定。

而 Parquet 的规范里,连"一段数据应该用什么编码写在哪个位置,读取器该怎么解析"都规定得明明白白。规范意味着可预期:任何两个不相关的系统,只要都遵循 Parquet 规范,就能无损交换数据。你见过哪个系统宣称"支持 tmp 格式交换数据"吗?没有,因为它根本无从支持。

这就像你问"为什么便利贴不能替代合同"一样——便利贴随手写几个字很方便,但合同有法律要件、有签署流程、有格式规范。数据格式的本质就是"机器之间的合同",没有规范的 tmp 无法承担这个角色。

3.2 从存储与查询性能看,tmp 毫无胜算

就算我们退一万步,假设 tmp 是一种"格式",那它顶多算一种无结构的普通二进制/文本存储。拿它跟 Parquet 比性能,等于拿手推车跟高铁比赛。

  • 无压缩机制:tmp 文件通常就是原始字节,磁盘占用是 Parquet 的 5~8 倍;
  • 无列式布局:查询任何字段都得全文件扫描,列裁剪、谓词下推这种优化根本无从谈起;
  • 无统计信息:引擎无法知道哪个文件包含哪些数据范围,只能老老实实读完整内容再过滤;
  • 无并行切分能力:合理设计的 Parquet 文件支持按行组拆分并行读,而 tmp 文件往往是随机写入的字节流,切不了片。

我在生产环境做过对比:同一份 30GB 的明细数据,存成临时文件被 Spark 读取做聚合,跑完要 40 分钟;转成 Parquet 后,同样的 SQL 只要 6 分钟。这个差距不是优化手段能弥补的,是存储布局决定的。

3.3 从数据治理与生态看,tmp 一无所有

现代数据平台讲究的是可治理、可追踪、可演进。Parquet 在这方面有完整能力:

  • Schema 携带:文件里本身就带着字段名、类型、嵌套结构,读的人不需要额外一份建表语句;
  • Schema 演进:新增列、删除列不会破坏旧文件,老文件依然能读;
  • 元数据丰富:文件级、行组级都有统计数据,方便做数据分片、优化和审计;
  • 生态联通:从 Kafka 到数仓、从湖到仓,Parquet 都能无缝流动。

tmp 文件呢?没有 schema、没有元数据、没有版本概念。一个离职同事留下的 .tmp 文件,三个月后你根本不知道它是什么程序写的、里面装的是什么结构的数据。这种情况我见得太多了:任务跑失败后留下一堆 .tmp 中间文件,想排查问题都不知道从哪下手。临时文件之所以叫临时文件,就是因为它不该被当作资产保存,而主流数据格式的本质是"数据资产的标准载体",两者的定位天然冲突。

3.4 真正该对比的是 Parquet vs ORC vs Avro

把 tmp 和 Parquet 放在一起,本身就是舆论上的错位。业界真正纠结的取舍是 Parquet、ORC、Avro 之间的选择:

  • Parquet:列式、压缩高、生态最广,适合分析型负载和湖上通用存储;
  • ORC:也是列式,Hive 生态里表现好,事务支持和索引更激进,但跨生态支持不如 Parquet 普及;
  • Avro:行式、schema 以 JSON 定义、序列化快,适合写密集的流式场景和消息队列持久化。

我的选型经验是:只要场景是以分析查询为主,优先 Parquet;如果是流式写入、逐条 append,Avro 更顺手;如果深度绑定 Hive 并且吃 ORC 的索引红利,那 ORC 也不差。但无论选哪个,都不会有人把 tmp 放进这个选项池里,因为它不是一个选项。

4. 实操场景对比:同一份数据,走 tmp 和走 Parquet 的差距

4.1 场景一:离线数仓 ETL 中间结果

数仓 ETL 里最常见的操作是"从源表抽数 → 清洗转换 → 落结果 → 下游读取"。很多新手会图省事,把中间结果写成临时文件放到 /tmp,或者干脆用自定义的 .tmp 后缀文件。这样做的代价很快会暴露:

第一,下游读取不稳定。你写的是自定义文本格式,下游 Spark 脚本得写自定义解析器,字段顺序一变全挂。第二,没有容错点。如果任务在第三步失败,回看中间文件时因为格式不明,几乎无法判断数据是否正确。第三,性能差。下游要重新全量解析文件,没有列裁剪、没有压缩,IO 白花花的烧。

我当时接手的一个老项目就是这种状态。任务链上有五个步骤,中间文件全是一堆 .tmp 文本,跑一次全量要三小时,而且经常因为某一步的解析器跟写入格式对不上而失败。后来我把所有中间落盘统一换成 Parquet,加上 ZSTD 压缩,任务时间从三小时压到四十分钟,并且任何一步失败都能从上一层的 Parquet 文件直接断点续跑。这就是把"临时文件"升级成"可检查点数据资产"的收益。

4.2 场景二:数据集成与 DataX 对 Parquet 的支持

"datax hdfsreader支持parquet"这个热搜词我很熟悉,因为 DataX 是很多公司做离线同步的标配工具。DataX 的 HDFS Reader/Writer 在较新版本里对 Parquet 的支持已经相当成熟,配置里通过fileType: "parquet"和parquetSchema字段来声明。

一个典型的 DataX 配置片段(hdfsreader 读 Parquet 到 hdfswriter 写另一个 Parquet)长这样:

{ "job": { "setting": { "speed": { "channel": 8 } }, "content": [ { "reader": { "name": "hdfsreader", "parameter": { "path": "/user/hive/warehouse/ods/detail", "defaultFS": "hdfs://namenode:8020", "fileType": "parquet", "column": [ { "index": 0, "type": "string" }, { "index": 1, "type": "long" }, { "index": 2, "type": "date" } ] } }, "writer": { "name": "hdfswriter", "parameter": { "path": "/user/hive/warehouse/dwd/detail", "defaultFS": "hdfs://namenode:8020", "fileType": "parquet", "writeMode": "append", "compress": "zstd", "parquetSchema": "id string, amount long, dt date" } } } ] } }

注意几个实操细节:

  • fileType必须写parquet,否则 DataX 默认按文本处理,读出来全是乱码;
  • parquetSchema要跟源文件字段顺序一一对应,类型不一致时 DataX 会尽力做隐式转换,但 date 和 string 之间的转换经常翻车,建议提前用preSql处理好字段类型;
  • 压缩参数compress建议用zstd或snappy,不要图省事不压缩,否则 HDFS 磁盘会很快报警。

为什么 DataX 要支持 Parquet?因为在湖仓一体的架构里,HDFS 上的事实表几乎统一用 Parquet 存储,同步工具不支持 Parquet 就意味着每次同步都要做一次全量文本解析,代价巨大。Parquet 在这里扮演的是"统一中间格式"的角色,而这恰恰是 tmp 最想当又当不了的岗位。

4.3 场景三:Java 里的 tmp 文件与"统一返回数据格式"

"tmp在java中的意思"也是一个高频搜索词。在 Java 里,File.createTempFile(prefix, suffix)会在系统临时目录生成文件,默认后缀通常是 .tmp。很多人第一次见这个 API 时以为它在定义"tmp 数据格式",其实它只是创建了一个"用完即走"的磁盘临时空间。

Path tmpFile = Files.createTempFile("myapp", ".tmp"); Files.write(tmpFile, "一些临时内容".getBytes(StandardCharsets.UTF_8)); // 用完删除 Files.deleteIfExists(tmpFile);

这个文件的格式是什么?你写什么就是什么——可以是纯文本、序列化对象、JSON、甚至二进制快照。「tmp」在这里只是命名约定,不参与任何格式定义。真正对 Java 程序有意义的是"统一返回数据格式"这个概念。

我在后端项目里经常听到有人把"统一返回数据格式"挂在嘴边,比如 Controller 层统一用Result<T> { code, message, data }返回 JSON。这里的数据格式是 JSON,不是 tmp。为什么没人会用 tmp 做统一返回格式?道理跟前面一样:返回格式要被人读、被前端解析、被网关校验,它需要稳定契约,而 tmp 恰恰最没有契约。

顺带提一句,Java 程序如果频繁往临时目录写大文件,要注意两点:一是用完必须删除,createTempFile不会自动清理;二是临时目录空间会被撑爆,Linux 的 /tmp 满了之后,很多依赖临时文件的功能会莫名失败。我们线上的报错里,"No space left on device" 出现在 /tmp 下的次数,比出现在数据盘下的还多。

4.4 场景四:本地分析与小规模报表

最后聊一个很多人忽略的场景:本地分析。过去分析师习惯把数据导成 CSV 发给别人,但 CSV 既没有类型信息(日期全成字符串),也没有压缩,大一点就卡。现在越来越多的个人分析师开始用 Parquet 做本地数据交换。

我自己的习惯是:从数仓导出数据时,如果下游要的是明细数据,我直接导出 Parquet;如果对方非要 Excel,再在本地用 DuckDB 或 pandas 转一下。DuckDB 读 Parquet 快到什么程度?一个 2GB 的 Parquet 文件,SELECT count(*) FROM read_parquet('data.parquet')基本一两秒出结果,同等数据量的 CSV 要多好几倍时间。

# 用 DuckDB 直接查 Parquet duckdb -c "SELECT dt, sum(amount) FROM read_parquet('orders.parquet') GROUP BY dt ORDER BY dt;" # 用 parquet-tools 看文件元数据 parquet-tools inspect orders.parquet

这个场景再次说明:只要是"数据要被人反复读、被工具链处理",Parquet 就是更稳的选择。tmp 在这类场景里只会制造困惑——别人收到一个 .tmp 文件,第一反应就是"这玩意儿用什么打开",然后浪费半小时。

5. 常见问题速查与避坑实录

5.1 "tmp 文件用什么打开"

这是搜索热度最高的衍生问题,答案很简单:要看是谁创建的这个 tmp 文件,它不存在通用打开方式。

三个排查技巧:

  • 先用file xxx.tmp看文件真实类型。如果是 "ASCII text",直接用文本编辑器开;如果是 "gzip compressed data" 或 "Parquet" 之类,说明创建方其实写的是压缩流或特定格式,只是套了 .tmp 后缀;
  • 用十六进制查看器看文件头部。比如前四个字节如果是PAR1,这其实是个 Parquet 文件被改名成了 .tmp,直接用 Parquet 工具打开即可;
  • 回忆/查证它的来源。如果是 Office 自动恢复文件,改名成 .docx/.xlsx 再打开;如果是下载工具残留,多半是死文件,删了就行。

在 Java 项目里排查生产事故时,我习惯对落盘文件统一加真实格式后缀,比如xxx.csv.tmp、xxx.parquet.tmp。这样既保留了"写入中"的状态标识,又不会丢失真实格式信息。永远不要只写 .tmp 三个字母,那等于把信息丢进黑洞。

5.2 "Parquet 文件怎么打开"

这个问题其实非常简单,工具很多,我按使用频率排个序:

工具用途上手难度
DuckDBSQL 查询 Parquet,最推荐低
parquet-tools查看 schema、元数据、转 CSV低
pyarrow + pandasPython 生态读写中
Spark / Trino大规模集群查询中高
IDE 插件(如 Big Data Tools)可视化预览低

最快速的上手路径是装一个 DuckDB:

pip install duckdb duckdb -c "DESCRIBE SELECT * FROM read_parquet('data.parquet');"

如果你只想临时看几行数据,用 parquet-tools:

parquet-tools head -n 5 data.parquet

踩过的坑提醒一句:不要用 Excel 直接开 Parquet,Excel 不认识它,会报二进制格式错误。正确的流程是用工具转成 CSV 再进 Excel。

5.3 MySQL 报错里的 /tmp 是怎么回事

热搜词里有一条error 2002 (hy000): can't connect to local mysql server through socket '/tmp/mysql.sock'。很多人看到/tmp又开始联想了,觉得是不是数据格式问题。不是,这是 socket 路径问题。

MySQL 客户端连接本机时默认走 Unix socket,路径通常在/tmp/mysql.sock。报这个错大概率是:

  • MySQL 服务没启动;
  • socket 文件路径配置不一致(服务端socket=/tmp/mysql.sock,客户端却指定了别的路径);
  • socket 文件被系统清理或权限不对,检查ls -l /tmp/mysql.sock。

排查顺序建议:先systemctl status mysql看服务状态,再mysqladmin -uroot ping测连通,最后看 my.cnf 里的 socket 配置。这里的 tmp 只是一个目录名,跟数据格式半毛钱关系都没有,但它和"tmp文件用什么打开"这类搜索词混在一起,恰恰说明大家对 tmp 的认知确实模糊。

5.4 Oracle Grid 安装时的 /tmp 目录报错

再提一条热搜:prvf-7546 : the work directory "/tmp/gridsetupactions2026-09-24_04-14-35pm/..."。这是 Oracle Grid Infrastructure 安装时被 /tmp 目录卡住。原因通常是:

  • /tmp 空间不足,Oracle 解压安装包要大量临时空间;
  • /tmp 挂载选项不允许执行文件(比如noexec),导致安装程序无法运行;
  • 目录权限不对。

解决方向:清理 /tmp 释放空间;或者把临时目录改到别的位置,设置TMP=/u01/tmp、TMPDIR=/u01/tmp再重跑安装脚本。我自己装 RAC 时遇到过一次 /tmp 只有 2GB 的情况,后来统一把安装临时目录指向单独的大分区,再也没被 PRVF-7546 折磨过。

这个案例进一步说明:tmp 是"运行空间"概念,不是"数据格式"概念。把运行空间和数据格式混为一谈,才会产生"tmp 能不能替代 Parquet"这种问题。

5.5 格式选型决策参考

最后给一张实用决策表,是我这几年做数据架构时反复用的判断逻辑:

你的场景推荐格式原因
长期存储的分析明细数据Parquet压缩高、查询快、生态全
流式写入、消息队列持久化Avroschema 演进友好、序列化快
Hive 深度绑定、重索引场景ORC事务和索引能力强
单机小数据、给人看的交付物CSV / Excel工具兼容、人类可读
进程内临时中转、用完即删直接内存或用系统临时文件不追求格式规范
任何需要长期依赖的数据资产绝不选 tmp没有规范就没有未来

判断标准就一句话:这份数据要不要被其他程序、其他团队、其他时间点的人重复使用?要的话,就必须选择有规范、有生态的格式。临时文件可以随手扔进 /tmp,但如果它有价值,请尽快让它转正成 Parquet。

我个人在实际项目里被 .tmp 文件坑过太多次,最惨的一次是花了整整一天去猜一个离职同事留下的一堆 .tmp 文件到底是什么结构,最后用十六进制工具一点一点还原出来,才发现是带表头的自定义文本。自那以后我给自己定了一条铁律:所有落盘文件,命名必须带真实格式后缀;所有超过 24 小时还需要保留的文件,一律转成 Parquet 存到正式存储。希望你看完这篇,别再对被 .tmp 后缀"伪装"的文件抱有不切实际的幻想——它当不了主流数据格式,但这恰恰是好事,因为真正的数据资产值得用更靠谱的格式来承载。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询