外观
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、同并发:
- 复制一日分区到 ORC 与 Parquet
- 跑 3 类查询:窄列聚合、宽列
SELECT *限制、高选择率过滤 - 记录:输入字节、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. 迁移步骤(已有大表)
- 双写或影子表按分区回填
- 校验行数 + 关键指标 + 抽样字段
- 切换视图/下游依赖
- 保留旧路径只读一个窗口再归档
禁止:无校验直接改原路径格式名「以为覆盖」。
8. 文档化「默认标准」
text
团队默认(示例,按你们引擎改):
- 仓内 Hive 重批事实:ORC + ZSTD
- 跨引擎共享 / 湖表数据文件:Parquet + Snappy
- 例外:需书面原因 + A/B 数据常见坑(至少牢记这些)
只比压缩比不比查询画像
体积小但 CPU 解压更贵,SLA 任务可能更慢。Spark 写完 Hive 读崩
版本、小数、时间戳精度不一致。先小表互通测试。忽略 stripe / row group 大小
过小→小文件效应;过大→谓词跳过变差、内存尖刺。ACID / 事务表盲目改 Parquet
能力因版本而异,先查官方支持矩阵。全站一刀切迁移
无影子表与 checksum,回滚成本极高。把「列存」当成「不用分区」
分区裁剪永远是第一道过滤。
如何验收(可量化)
- [ ] 选型理由写清写引擎、读引擎、查询类型
- [ ] 同分区体积、扫描字节、p95 耗时有对比表
- [ ] 行数与关键指标 checksum / 对账通过
- [ ] EXPLAIN 显示分区与列裁剪生效
- [ ] 默认标准进团队规范,例外有记录
sql
SELECT
COUNT(*) AS cnt,
COUNT(DISTINCT event_id) AS uniq_event
FROM dwd_event_parquet
WHERE dt = '${biz_date}';
-- 与 ORC 表差值应为 0bash
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 原创数仓/面试文,已改写成可执行清单;未包含未公开公司数据与内部表名。