Spark中便捷生成全局唯一自增ID

总体思路

利用spark RDD API所提供的的zipWithIndex() 和 zipWithUniqueId()生成ID,两者的区别如下。
 

zipWithIndex()

首先基于分区索引排序,然后是每个分区中的项的排序。所以第一个分区中的第一项得到索引0,第二个分区的起始值是第一个分区的最大值。从0开始。分区内id连续。会触发spark job。

zipWithUniqueId()

每个分区是一个等差数列,等差为分区数n,每个分区的第一个值为分区id(id从0开始)。第k个分区:num*n+k。分区内id不连续。从0开始。不会触发spark job。

 

工具类

import org.apache.spark.SparkException
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.catalyst.expressions.GenericRowWithSchema
import org.apache.spark.sql.types.LongType
import org.apache.spark.sql.{DataFrame, Row, SparkSession}

object SparkCommon {
  /**
    * 不改变分区数,不产生shuffle
    *
    * @param offset                 自增id的起始值,默认0
    * @param isPartitionConsecutive 分区内ID是否连续(不连续时是等差数列),连续时会触发spark job
    */
  def addUniqueIdColumn(sparkSession: SparkSession, df: DataFrame, uidKey: String, offset: Long = 0, isPartitionConsecutive: Boolean = false): DataFrame = {
    val newSchema = df.head().schema.add(uidKey, LongType)
    val rdd = if (isPartitionConsecutive) df.rdd.zipWithIndex() else df.rdd.zipWithUniqueId()
    val result: RDD[Row] = rdd.map(e => {Row.merge(e._1,Row(e._2+offset))})
    sparkSession.createDataFrame(result, newSchema)
  }
}

测试

数据

{"fid":1,"name":"A","score":100,"createtime":"2017-01-12"}
{"fid":2,"name":"B","score":"88","createtime":"2017-06-27"}
{"fid":3,"name":"C","score":"89","createtime":"2017-06-30"}
{"fid":4,"name":"D","score":"88","createtime":"2017-06-30"}
{"fid":5,"name":"A","score":"96","createtime":"2018-06-30"}
{"fid":6,"name":"B","score":"92","createtime":"2018-05-14"}
{"fid":7,"name":"C","score":"95","createtime":"2018-09-12"}
{"fid":8,"name":"D","score":"98","createtime":"2018-10-12"}
{"fid":9,"name":"A","score":"100","createtime":"2019-11-12"}
{"fid":10,"name":"B","score":"98","createtime":"2019-11-12"}
{"fid":11,"name":"C","score":"89","createtime":"2019-11-12"}
{"fid":12,"name":"D","score":"96","createtime":"2019-11-12"}

本地测试

import org.junit.{After, Before, Test}
import org.apache.spark.sql.{DataFrame, SparkSession}

class SparkCommonTest{

  @Test def testUid(): Unit = {
    val sparkSession=SparkSession.builder().master("local[2]").getOrCreate()
    var df: DataFrame =sparkSession.read.json("E:\\Data\\input\\JSON\\json4x12.txt")
    df=df.repartitionByRange(4,df("name"))
    SparkCommon.addUniqueIdColumn(sparkSession,df,"uid").sort("name").show()
    SparkCommon.addUniqueIdColumn(sparkSession,df,"pid",12,true).sort("name").show()
    sparkSession.stop()
  }
}

测试结果

                                 

全局唯一自增ID

如果需要多次运行程序并保证id始终自增,可以在redis中维护偏移量,在调用addUniqueIdColumn时传入对应的offset即可。

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spark,RDD的执行流程遵循有向无环图(DAG)的方式进行。下面是关于DAG执行流程图生成的步骤: 1. 转换操作:首先,通过RDD的转换操作(例如map、filter、reduce等)定义数据处理的逻辑。这些操作将构建一个RDD转换图。 2. 逻辑优化:Spark会对RDD转换图进行逻辑优化,以提高执行效率。例如,它可能会合并相邻的转换操作或推测执行等。 3. 任务划分:Spark会将RDD转换图划分为一系列的阶段(Stage)。每个阶段由一组具有相同父RDD依赖关系的转换操作组成。 4. 任务划分优化:对于每个阶段,Spark会将其划分为一组任务(Task),以便并行执行。任务划分也可以根据数据本地性进行优化,尽量将任务分配给与数据最接近的节点。 5. 任务调度:一旦任务划分完成,Spark会将这些任务提交给集群管理器进行调度,以在集群的多个节点上并行执行。 6. 依赖关系跟踪:在执行期间,Spark会跟踪RDD之间的依赖关系,并确保所有依赖的转换操作被正确执行。 7. 数据传输和处理:在任务执行期间,Spark将数据从一个节点传输到另一个节点,并对其进行相应的转换操作。 8. 结果返回:一旦所有任务都完成,Spark将结果返回给驱动程序,供进一步处理或输出。 总之,Spark的RDD执行流程图生成步骤涉及转换操作定义、逻辑优化、任务划分、任务划分优化、任务调度、依赖关系跟踪、数据传输和处理以及结果返回。这些步骤确保了高效的分布式数据处理和计算。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值