内容纲要
概要描述
在数据持续写入的过程中,ORC 格式的普通表(非事务表)可能会产生大量小文件。小文件过多会导致以下问题:
- 查询性能下降:读取时需要打开大量文件,增加 I/O 开销和调度成本
- NameNode 压力增大:HDFS 元数据占用过多内存,影响集群稳定性
- 资源利用率低:Map 任务启动和销毁频繁,计算资源浪费严重
本文档提供一种通过 创建过渡表并重新导入数据 的方式来合并小文件的操作方法。
详细说明
步骤一:创建过渡表
根据原表结构创建一张结构相同的过渡表,用于接收合并后的数据。
-- 请将 your_db.target_tb 替换为实际的数据库和表名
CREATE TABLE your_db.stage_tb LIKE your_db.target_tb;
说明:
LIKE语法会完整复制原表的列定义、分区结构和存储格式,不会复制数据。
步骤二:设置合并参数并导入数据
在当前会话中设置小文件合并参数,然后将原表数据重新写入过渡表。
-- 以下参数仅在当前会话生效,不会影响其他会话或任务
SET ngmr.partition.automerge = true; -- 开启自动合并,默认false 关闭状态
SET ngmr.partition.mergesize.mb = 200; -- 合并以后每个task最多处理的数据量大小,默认8M
SET mapred.reduce.tasks = 10; -- 设置 Reduce 任务数,控制输出文件数量,默认-1,由上下文决定reduce数量。
--reduce数目需要考虑表是否分区:
--非分区表:例如,表总数据量约为20GB,则可设置为100~400,这样数据文件大小能控制在50M~200M;
--分区表:例如,表总数据量约为20GB,但细分到每个分区只有2G,则可设置为10~40,这样数据文件大小能控制在50M~200M;
INSERT INTO your_db.stage_tb
SELECT * FROM your_db.target_tb
DISTRIBUTE BY {col1};
关于 DISTRIBUTE BY 列的选择:
建议选择源表中分布较为离散的列(如唯一键或近似唯一的列),以确保最终写入的数据文件大小均匀。若表中无合适的离散列,可使用多列拼接的方式,例如:
DISTRIBUTE BY id || email || homeaddress
执行完成后,请重点检查目标表在 HDFS 上的文件大小是否均匀(检查方法见步骤五)。
步骤三:备份原表
将原表重命名为备份表,确保数据可随时回退。
ALTER TABLE your_db.target_tb RENAME TO your_db.target_tb_bak;
注意:重命名操作会立即生效,请确认下游任务(如定时调度、ETL 作业等)是否依赖该表名。如有依赖,请选择合适的执行时间窗口。
步骤四:替换目标表
将过渡表重命名为目标表,完成表替换。
ALTER TABLE your_db.stage_tb RENAME TO your_db.target_tb;
步骤五:验证与清理
-
通过hdfs命令检查小文件数量、大小是否均匀
hdfs dfs -count /path/to/table/dir -
执行
SELECT COUNT(*)对比原表与合并后表的行数是否一致。 -
抽样检查数据内容是否正确。
确认数据无误后,删除备份表。建议观察至少一个月后再做清理,以确保下游所有任务正常运行:
DROP TABLE IF EXISTS your_db.target_tb_bak;
根本解决方案
以上方法通过重建表来重新组织数据,能有效合并小文件,但无法从源头避免小文件的持续产生。要从根本上解决小文件问题,建议将表重建为 ORC 事务表(TORC),该表类型支持后台自动合并小文件,无需人工干预。
如需了解事务表的创建方式,请联系技术支持团队获取详细指导。