Flink的物理分区策略

  • Flink的分区策略
  • Random Partitioning
  • Roundrobin Partitioning
  • Rescaling Partitioning
  • Broacasting
  • 自定义分区



Flink的物理分区操作可将数据重新分配到集群的不同节点的Task完成支线。DataStream进行数据处理过程依赖于算子本身对数据的分区控制,简单的情形这就足够了,但是复杂的应用场景中,我们难免会遇到如数据倾斜等问题,我们希望自己控制分区完成对数据的再平衡的处理,就需要定义物理分区策略。


Flink已经为我们定义了随机分区(Random Partitioning)、Roundobin分区和Roundrobin分区等8中分区策略。

Flink的分区策略

Random Partitioning

Random Partitioning即随机分区,其完成DataStream->DataStream的数据转换。Random分区通过随机的方式将数据分配到下游算子的每个分区中,分区相对均衡,但是较易丢失原有数据的分区结构。
DataStream数据执行Random分区非常简单,只需要调用shuffle方法即可。

  • Scal实现:
//通过ataStream的API实现数据的随机分区
val randomPartDs= dataStream.shuffle
  • Java实现:
DataStream<Integer> randomPartDs= dataStream.shuffle();

Roundrobin Partitioning

Roundrobin分区是通过循环的方式对数据集中的数据进行重分区,所以又称为循环分区方式,Roundrobin分区数据转换情况是DataStream->DataStream。Roundrobin分区能够尽可能的保证每个分区的数据平衡,尤其数据集倾斜的时候,非常时候使用该策略。DataStream的rebalance方法实现了Roundrobin分区策。

  • Scala实现
val rbStream = dataStream.rebalance()
  • Java实现
DataStream<Integer> rbStream = dataStream.rebalance();

Rescaling Partitioning

Rescaling分区与Roundrobin分区一样,也是一种通过循环的方式进行数据平衡的分区策略。不同的是Roundrobin分区时,数据会全局性地通过网络介质传输到其他的节点完成数据的再平衡。而Rescaling分区仅仅会对上下游继承的算子数据进行再平衡,具体的分区主要根据上下游算子的并行度决定。比如上游算子的并发度为3,下游算子的并发度为6,就会发生上游算子中一个分区的数据按照同等比例将数据路由在下游的固定的2个分区中,同样,另外2个分区也会分别路由到下游两个分区中。DataStream的rescale方法可实现该分区策略。

  • Scala实现
val stream = dataStream.rescale();
  • Java实现
DataStream<Integer> stream = dataStream.rescale();

Broacasting

广播分区测试将输入的数据集复制到下游算子的并行的Tasks中,下游算子中的Tasks可以直接从本地内存中获取广播数据集,不再依赖于网络传输。广播分区策略适合于小数据集,如大数据集关联小数据集时,可以通过广播的方式将小数据集分发到算子的每个分区中。DataStream的broadcas可实现该策略。

  • Scala实现
val stream=dataStream.broadcast();
  • Java实现
DataStream<Integer> stream = dataStream.broadcast();

自定义分区

若已有的几种分区器不满足需求,用户可以自定义分区器,然后调用DataStream的partitionCustom()方法将创建的分区器应用到数据集上。
自定义分区器需要实现Partitioner接口,接口定义如下

@Public
@FunctionalInterface
public interface Partitioner<K> extends java.io.Serializable, Function {

   /**
    * Computes the partition for the given key.
    *
    * @param key The key.
    * @param numPartitions The number of partitions to partition into.
    * @return The partition index.
    */
   int partition(K key, int numPartitions);
}

实现Partitioner接口需要实现int partition(K key, int numPartitions)方法,完成分区。定义好分区策略后,就可通过DataStream的partitionCustom()API应用分区策略。partitionCustom()可public调用的有3种定义,如下

DataStream<T> partitionCustom(Partitioner<K> partitioner, int field);

主要用于T为tuple类型的DataStream的再分区。field指定用户自定义分区的字段位置索引,其为int型。该方法只能指定单个字段用于分区

DataStream<T> partitionCustom(Partitioner<K> partitioner, String field);

该方法主要用户T为POJO类型的DataStream的再分区。filed指定用于分区的字段。该方法同样只能指定单个字段用于分区

DataStream<T> partitionCustom(Partitioner<K> partitioner, KeySelector<T, K> keySelector);

该方法通过KeySelector指定由于分区的字段。由于方法只能指定单个字段用于分区,所以keySelector只能返回一个字段。