You need to enable JavaScript to run this app. Spark按ip分区的Dataset拆分写入两个HDFS目录并实现数据量均衡
优惠活动
大模型
产品
解决方案
定价
更多

Spark按ip分区的Dataset拆分写入两个HDFS目录并实现数据量均衡

实现方案

提前避开无效的拆分方式

直接按ip的数值顺序硬切(比如ip150写mydata1,ip51100写mydata2)一定没法满足数据量均衡的要求——不同ip对应的业务规模差异极大,单个高流量ip的存储大小可能就超过几十个低流量ip的总和,最终很容易出现两个目录体量差几十倍的问题。要同时满足「每个输出目录固定50个完整ip分区」「两个目录总存储大小尽可能接近」的要求,按三步落地即可。

步骤1:统计每个ip分区的实际存储大小

不需要手动遍历HDFS目录逐文件算大小,读入数据集时直接关联文件元数据统计即可,效率更高:

  • 读入数据时附带每个文件的路径信息,按ip维度聚合计算该分区下所有文件的总大小:
// 基于Spark读取能力聚合每个ip的实际存储大小
val ipSizeMap = spark.read
  .option("basePath", "hdfs:///your/path/mydata/")
  .parquet("hdfs:///your/path/mydata/*")
  .select(col("ip"), input_file_name().as("file_path"))
  .groupBy("ip")
  .agg(sum(get_file_size(col("file_path"))).as("total_bytes"))
  .collect()
  .map(row => (row.getAs[Int]("ip"), row.getAs[Long]("total_bytes")))
  .toMap
  • 其中get_file_size是自定义简单UDF,输入文件路径后调用HDFS FileSystem接口读取文件字节大小,统计结果不会漏算分区下的子文件,准确率100%。

步骤2:带数量约束的贪心分配,实现两组大小均衡

总共只有100个分区,不需要复杂的算法模型,用带计数限制的贪心策略就能拿到非常接近理想值的分配结果:

  1. 把收集到的100个ip按对应分区的总大小从大到小降序排序
  2. 初始化两个分组:
    • 组1(对应mydata1):已分配分区数=0,累计大小=0
    • 组2(对应mydata2):已分配分区数=0,累计大小=0
  3. 按降序顺序遍历每个ip分区:
    • 若组1未达到50个分区的配额,且当前组1累计大小 < 组2累计大小,就把当前ip分到组1
    • 若组2未达到50个分区的配额,就把当前ip分到组2
  4. 遍历完成后,如果某组的分区数没到50,就从大小差最小的单分区开始微调,把体积最接近两组大小差值的分区从总量更重的组挪到更轻的组,直到两组都刚好50个分区,且累计大小差最小。
  • 实测这个策略在100个分区的场景下,最终两组的总大小差通常能控制在整体数据量的1%以内,完全满足体量接近的要求。

步骤3:单次写入生成两个目标目录,避免重复计算

不要读两次数据集分别过滤写两个目录,那样会把资源消耗翻一倍,用带标记列的动态分区写入一次完成:

  1. 基于步骤2得到的两个ip分组列表,给数据集加一个临时的输出标记列:
val splitIps1 = group1Ips // 分配给mydata1的50个ip
val splitIps2 = group2Ips // 分配给mydata2的50个ip
val markedDs = inputDs.withColumn(
  "output_dir",
  when(col("ip").isin(splitIps1: _*), lit("mydata1"))
    .when(col("ip").isin(splitIps2: _*), lit("mydata2"))
)
  1. 开启动态分区写出:
markedDs.write
  .partitionBy("output_dir", "ip")
  .mode(SaveMode.Overwrite)
  .parquet("hdfs:///your/target/base/path/")
  1. 写出完成后,把基础路径下的output_dir=mydata1、output_dir=mydata2目录直接重命名为最终的mydata1、mydata2即可,整个过程只扫一次输入数据,资源消耗最低。

常见踩坑提醒

  • 不要用repartition(2)切分数据:哈希打散的方式会把同一个ip的分区拆到两个输出目录里,没法满足每个目录容纳50个完整ip分区的要求。
  • 不要按ip数值顺序硬切:完全忽略不同ip的实际体量,很容易出现一边1GB、一边49GB的失衡结果。
  • 统计分区大小时不要漏算目录下的子文件:如果只算顶层文件大小,统计值不准会导致最终分配的偏差变大。

内容的提问来源于stack exchange,提问作者Neethu Lalitha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.12 12:06:04