在什么情况下,散列分区比Spark中的范围分区更受欢迎?

时间:2017-11-12 08:28:05

标签: performance apache-spark rdd partitioning

我已经阅读了有关散列分区的各种文章。但是我仍然无法在什么情况下获得它比范围分区更有利。使用sortByKey后跟范围分区允许数据在集群中均匀分布。但在散列分区中可能不是这种情况。请考虑以下示例:

考虑一对带有键[8,96,240,400,401,800]的RDD,所需的分区数为4。

在这种情况下,散列分区按如下方式分配密钥 分区:

partition 0: [8, 96, 240, 400, 800]
partition 1: [ 401 ]
partition 2: []
partition 3: [] 

(计算分区:p = key.hashCode()%numPartitions)

上述分区会导致性能下降,因为密钥并非均匀分布在所有节点上。由于范围分区可以在集群中平均分配密钥,那么在什么情况下散列分区被证明最适合范围分区?

1 个答案:

答案 0 :(得分:9)

虽然hashCode的弱点值得关注,尤其是在处理小整数时,通常可以通过根据域特定知识调整分区数来解决。也可以使用更合适的散列函数将默认HashPartitioner替换为自定义Partitioner。只要没有数据偏差,散列分区的平均规模就足够好了。

数据偏差是完全不同的问题。如果密钥分发明显偏离,则无论使用何种Partitioner,分区数据的分发都可能会出现偏差。例如,考虑以下RDD:

sc.range(0, 1000).map(i => if(i < 9000) 1 else i).map((_, None))

根本无法统一分区。

为什么不默认使用RangePartitioner

  • 不如HashPartioner。虽然HashPartitioner只需要## ==K RangePartitioner的正确实施,但Ordering[K]需要HashPartitioner
  • val rdd1 = sc.range(0, 1000).map((_, None)) val rdd2 = sc.range(1000, 2000).map((_, None)) val rangePartitioner = new RangePartitioner(11, rdd1) rdd1.partitionBy(rangePartitioner).glom.map(_.length).collect 不同,它必须近似数据分布,因此requires additional data scan
  • 因为拆分是根据特定分布计算的,所以在跨数据集重用时可能会不稳定。请考虑以下示例:

    Array[Int] = Array(88, 91, 99, 91, 87, 92, 83, 93, 91, 86, 99)
    
    rdd2.partitionBy(rangePartitioner).glom.map(_.length).collect
    
    Array[Int] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1000)
    
    joins

    您可以想象这对val hashPartitioner = new HashPartitioner(11) rdd1.partitionBy(hashPartitioner).glom.map(_.length).collect 等操作有严重影响。同时

    Array[Int] = Array(91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 90)
    
    rdd2.partitionBy(hashPartitioner).glom.map(_.length).collect
    
    Array[Int] = Array(91, 91, 91, 91, 91, 91, 91, 91, 91, 90, 91)
    
    const axios = require('axios');
    const querystring = require('querystring');
    const keys = require('../config/keys');
    
    const getAccessToken = async refreshToken => {
      try {
        const accessTokenObj = await axios.post(
          'https://www.googleapis.com/oauth2/v4/token',
          querystring.stringify({
            refresh_token: refreshToken,
            client_id: keys.googleClientID,
            client_secret: keys.googleClientSecret,
            grant_type: 'refresh_token'
          })
        );
        return accessTokenObj.data.access_token;
      } catch (err) {
        console.log(err);
      }
    };
    

这让我们回到你的问题:

  

在什么情况下它比范围分区更有利。

散列分区是许多系统中的默认方法,因为它相对不可知,通常表现得相当好,并且不需要有关数据分布的其他信息。由于缺乏关于数据的任何先验知识,这些属性使其成为可取的。