upsert在mysql中的实现(附spark应用)

文章目录

    • upsert概述以及在mysql中的实现
    • spark写入mysql使用upsert
    • 总结


upsert概述以及在mysql中的实现

  upsert是update和insert的合体,这里暂时不对其具体的语义进行探讨,简单对其做一个定义,基本功能为:存在时更新,不存在时插入,简单的解释就是,当某种条件成立时使用update,条件不成立时使用insert。

  在很多场景下,我们少不了使用upsert功能,比如数据增量处理,商品状态更新等,而在mysql中并没有这样的一种语句,如果我们先查询是否存在,再选择使用update或者insert,这样不仅比较复杂而且还容易造成事务上的冲突,不过后来发现有方式实现这种需求,那就是ON DUPLICATE KEY UPDATE

举个栗子:

INSERT INTO table (a,b,c) VALUES (1,2,3) ON DUPLICATE KEY UPDATE c=c+1;  

ON DUPLICATE KEY UPDATE需要配合UNIQUE索引PRIMARY KEY使用。

  这句sql的基本解释为,当UNIQUE索引PRIMARY KEY的字段在表中不存在时,行作为新记录被插入,如果字段存在,则执行ON DUPLICATE KEY UPDATE后面的逻辑,这里就是c=c+1。

  这里我们可以将字段a设置为唯一索引或者主键,还可以将a,b设置为联合唯一索引,就能实现upsert的需求,具体如何应用还是要看具体的场景和需求。

spark写入mysql使用upsert

  一般存入mysql中的数据都是结构化的数据,这里我推荐先将RDD转为dataframe,再进行数据库方面的操作,因为同为结构化的数据转换起来比较简单,而且只需要很小的改动就能得实现我们的需求。

而一般的dataframe写入mysql中一般是使用的

dataFrame.write().mode("append").jdbc(url, "table", mysqlProperties)

  下面过一遍源码,首先调用的dataframe的write()生成DataFrameWriter,然后再配置mode,我们这里选用的是append,接下来来到了jdbc()中创建createConnectionFactory()以及检查表是否存在,如果不存在则从dataframe中取出schema来创建表,然后调用的是saveTable(),最后使用的是savePartition()来进行数据类型映射以及数据的commit,这里执行语句是在insertStatement()中处理的。
upsert在mysql中的实现(附spark应用)_第1张图片
upsert在mysql中的实现(附spark应用)_第2张图片

upsert在mysql中的实现(附spark应用)_第3张图片

upsert在mysql中的实现(附spark应用)_第4张图片

upsert在mysql中的实现(附spark应用)_第5张图片

upsert在mysql中的实现(附spark应用)_第6张图片

  而在上面我们的mysql upsert实现中,主要还是通过对执行语句的处理来实现,也就是说我们只需要在调用链的函数中加上一个参数,并且改动一下insertStatement(),就能实现我们想要的需求,在这里我是这样操作的:

  def insertStatement(conn: Connection, table: String, rddSchema: StructType, accumulateColumns: Array[String]): PreparedStatement = {
    val columns = rddSchema.fields.map(_.name).mkString(",")
    val placeholders = rddSchema.fields.map(_ => "?").mkString(",")
    var sql = s"INSERT INTO $table ($columns) VALUES ($placeholders)"
    if (!accumulateColumns.equals(null)) {
      val finalMap = accumulateColumns.map(x=>x+ "=" + "values(" + x + ")+" + x).mkString(",")
      sql = s"INSERT INTO $table ($columns) VALUES ($placeholders) ON DUPLICATE KEY UPDATE $finalMap"
    }
    conn.prepareStatement(sql)
  }

  累加的列为accumulateColumns,如果我们要累加的列为c,最后的字符串会变成

INSERT INTO table (a,b,c) VALUES (?,?,?) ON DUPLICATE KEY UPDATE c=VALUES(c)+c

  最后我们编译完之后打包,最后调用写入mysql的代码为:

dataFrame.write().mode("append").jdbc(url, "table", mysqlProperties,accumulateColumns)

总结

  我们这里的业务逻辑比较简单,只是简单增量的处理,更加复杂的逻辑还是需要个性化的定制,在这里提供一个简单的思路,希望对大家有所帮助。

你可能感兴趣的:(spark,mysql)