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


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()中处理的。
write.png
model.png

jdbc.png

saveTable.png

savePartition.png

insertStatement.png

  而在上面我们的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)

总结

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

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值