Skip to content

ORC vs Parquet 选型与验收 ​

难度与前置

难度:进阶
前置:知道列存、压缩、谓词下推;接触过 ORC/Parquet 表。
读完能做什么:按引擎生态与查询画像选型,用体积、扫描量、耗时做 A/B 验收,避免「听说谁快就全站切」。

适用场景 ​

  • 新建数仓表要定 STORED AS / 文件格式
  • Spark 与 Hive 混跑,格式争议
  • 存储成本或扫描耗时过高,评估换格式/压缩
  • 跨引擎共享(如 Spark + Presto/Trino + Hive)

不适用:

  • 行存 OLTP、点查主键(不是列存主场)
  • 已锁定湖格式(Iceberg/Hudi)且由上层统一——仍需理解底层文件多为 Parquet
  • 无查询画像的「信仰之战」

真实场景(一条) ​

宽表日增 2 TB,Hive 批用 ORC,Ad-hoc 用 Trino 读同一路径偶发兼容与统计信息问题。评估后:Hive 重批仓内表维持 ORC+ZSTD;跨引擎共享明细统一 Parquet+Snappy;用同 SQL 扫同分区对比字节与耗时。存储降约 15%,跨引擎扫描稳定性提升,批作业耗时持平。

怎么做(步骤清单) ​

1. 先画「谁写、谁读」 ​

角色更常见偏好(经验向)说明
Hive MR/Tez 为主ORC与 Hive 谓词、向量化、ACID 历史更深
Spark / Trino / 多引擎Parquet生态默认更齐
流式入湖 / 开放表格式Parquet(居多)跟 Iceberg/Hudi 默认一致
强依赖 Hive ACID 事务表ORC以集群版本文档为准

原则:写入引擎生态 + 主要查询引擎 优先于网上微基准截图。

2. 对比维度清单(选型表) ​

维度关注点
压缩比同数据同压缩等级下体积
读性能列裁剪、谓词下推、向量化
写性能小文件、stripe/row group 大小
类型与嵌套struct/array/map、decimal
统计信息min/max、bloom(若启用)
工具链是否全员能读(Spark/Presto/Flink)
text
决策示意:
- 仅 Hive 批仓 + 复杂类型多 → 优先 ORC
- Spark 为主 + Trino 即席 → 优先 Parquet
- 两边都重 → 分区/层级拆分,或统一 Parquet 并优化 Hive 读参

3. 建表示例(Hive) ​

sql
-- ORC
CREATE TABLE dwd_event_orc (
  event_id STRING,
  user_id STRING,
  event_type STRING,
  payload STRING
)
PARTITIONED BY (dt STRING)
STORED AS ORC
TBLPROPERTIES (
  'orc.compress'='ZSTD',
  'orc.create.index'='true'
);

-- Parquet
CREATE TABLE dwd_event_parquet (
  event_id STRING,
  user_id STRING,
  event_type STRING,
  payload STRING
)
PARTITIONED BY (dt STRING)
STORED AS PARQUET
TBLPROPERTIES (
  'parquet.compression'='SNAPPY'
);

压缩选型经验:吞吐优先 Snappy/LZ4;冷存体积优先 ZSTD/ZLIB(以集群支持为准)。存储与扫描成本见 成本治理。

4. Spark 读写注意 ​

scala
// 写 Parquet
df.write.mode("overwrite")
  .option("compression", "snappy")
  .partitionBy("dt")
  .parquet("/warehouse/dwd_event_parquet")

// 写 ORC
df.write.mode("overwrite")
  .option("compression", "zstd")
  .partitionBy("dt")
  .orc("/warehouse/dwd_event_orc")

核对:Spark 写的 ORC/Parquet 能否被 Hive Metastore 表正确读取(SerDe、schema 合并)。

5. A/B 验收实验设计 ​

同分区、同 SQL、同并发:

  1. 复制一日分区到 ORC 与 Parquet
  2. 跑 3 类查询:窄列聚合、宽列 SELECT * 限制、高选择率过滤
  3. 记录:输入字节、CPU 时间、墙钟、结果校验 checksum
sql
-- 结果一致性
SELECT md5(concat_ws(',', event_id, user_id, event_type))
FROM (
  SELECT event_id, user_id, event_type
  FROM dwd_event_orc WHERE dt = '${biz_date}'
  ORDER BY event_id
  LIMIT 100000
) t;
-- 对 parquet 表跑同样逻辑,checksum 应对齐(注意排序稳定)
bash
# 体积
hdfs dfs -du -s /warehouse/dwd_event_orc/dt=${biz_date}
hdfs dfs -du -s /warehouse/dwd_event_parquet/dt=${biz_date}

6. 与小文件、谓词下推一起看 ​

格式再好,小文件与无分区裁剪都会毁掉收益。参见 Hive 小文件治理、分区策略。

sql
-- 确认分区裁剪与列裁剪(引擎 EXPLAIN 为准)
EXPLAIN EXTENDED
SELECT event_type, COUNT(*)
FROM dwd_event_parquet
WHERE dt = '${biz_date}' AND event_type = 'pay';

7. 迁移步骤(已有大表) ​

  1. 双写或影子表按分区回填
  2. 校验行数 + 关键指标 + 抽样字段
  3. 切换视图/下游依赖
  4. 保留旧路径只读一个窗口再归档

禁止:无校验直接改原路径格式名「以为覆盖」。

8. 文档化「默认标准」 ​

text
团队默认(示例,按你们引擎改):
- 仓内 Hive 重批事实:ORC + ZSTD
- 跨引擎共享 / 湖表数据文件:Parquet + Snappy
- 例外:需书面原因 + A/B 数据

常见坑(至少牢记这些) ​

  1. 只比压缩比不比查询画像
    体积小但 CPU 解压更贵,SLA 任务可能更慢。

  2. Spark 写完 Hive 读崩
    版本、小数、时间戳精度不一致。先小表互通测试。

  3. 忽略 stripe / row group 大小
    过小→小文件效应;过大→谓词跳过变差、内存尖刺。

  4. ACID / 事务表盲目改 Parquet
    能力因版本而异,先查官方支持矩阵。

  5. 全站一刀切迁移
    无影子表与 checksum,回滚成本极高。

  6. 把「列存」当成「不用分区」
    分区裁剪永远是第一道过滤。

如何验收(可量化) ​

  • [ ] 选型理由写清写引擎、读引擎、查询类型
  • [ ] 同分区体积、扫描字节、p95 耗时有对比表
  • [ ] 行数与关键指标 checksum / 对账通过
  • [ ] EXPLAIN 显示分区与列裁剪生效
  • [ ] 默认标准进团队规范,例外有记录
sql
SELECT
  COUNT(*) AS cnt,
  COUNT(DISTINCT event_id) AS uniq_event
FROM dwd_event_parquet
WHERE dt = '${biz_date}';
-- 与 ORC 表差值应为 0
bash
echo "format,dt,size_gb,query_cpu_sec,query_wall_sec,rows"
# orc,2026-09-25,120,800,120,900000000
# parquet,2026-09-25,125,780,118,900000000

相关面试题 ​

占位:与题库小程序对齐后补链接(建议关联:ORC vs Parquet、列存、谓词下推、压缩与扫描量)。


改写说明:结构与判断标准参考 Wood 原创数仓/面试文,已改写成可执行清单;未包含未公开公司数据与内部表名。