You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Slick如何实现记录存在则递增列值、不存在则插入默认值

Slick实现「存在则递增字段、不存在则插入默认记录」逻辑

以下实现针对用户访问计数场景,优先保证并发场景下的数据一致性:


前置准备

无论使用哪种实现方案,必须先给user_id字段加唯一索引,这是判断记录是否存在、避免重复数据的基础,执行对应DDL:

ALTER TABLE user_visit ADD UNIQUE INDEX idx_uv_user_id (user_id);

方案1:数据库原生Upsert语法(推荐,原子性最优)

直接使用数据库自带的INSERT ... ON DUPLICATE KEY UPDATE(MySQL)或INSERT ... ON CONFLICT DO UPDATE(PostgreSQL)语法,单条SQL完成逻辑,天然原子性,高并发下不会出现计数错误,性能最好。
以MySQL为例的实现代码:

import slick.jdbc.MySQLProfile.api._

/**
 * 递增指定用户的访问次数,不存在则初始化记录
 * @param targetUserId 目标用户ID
 */
def upsertUserVisit(targetUserId: Long): DBIO[Int] = {
  sqlu"""
    INSERT INTO user_visit (user_id, visits)
    VALUES ($targetUserId, 0)
    ON DUPLICATE KEY UPDATE visits = visits + 1
  """
}

如果使用PostgreSQL,只需要替换SQL部分即可:

sqlu"""
  INSERT INTO user_visit (user_id, visits)
  VALUES ($targetUserId, 0)
  ON CONFLICT (user_id) DO UPDATE SET visits = user_visit.visits + 1
"""

方案2:纯Slick DSL实现(无原生SQL,需事务包裹)

如果不想编写原生SQL,可以通过「查询→判断存在→更新/插入」的流程实现,必须将整个操作包裹在事务中,否则并发场景下会出现重复插入、计数覆盖的问题。
实现代码:

import slick.jdbc.MySQLProfile.api._
import scala.concurrent.ExecutionContext

def incrementUserVisit(targetUserId: Long)(implicit ec: ExecutionContext): DBIO[Long] = {
  val userQuery = userVisitTable.filter(_.userId === targetUserId)
  for {
    existingRecord <- userQuery.result.headOption
    currentVisits <- existingRecord match {
      case Some(record) =>
        // 记录存在,直接在数据库层面执行visits+1,避免内存计算导致的覆盖问题
        userQuery.map(_.visits).update(record.visits + 1L)
          .map(_ => record.visits + 1L)
      case None =>
        // 记录不存在,插入初始值为0的新记录,返回插入后的visits值
        (userVisitTable returning userVisitTable.map(_.visits)) 
          += UserVisitFO(userId = targetUserId, visits = 0L)
    }
  } yield currentVisits
}.transactionally // 必须加事务,保证查询和写入的原子性

注意事项

  • 禁止在无事务的情况下先查询再写入:并发场景下多个请求同时查到记录不存在,会触发重复插入、唯一键冲突问题
  • 递增操作必须在SQL层面执行visits = visits + 1,不要把字段值查到内存里加1之后再update,否则高并发下会出现更新覆盖,导致计数不准
  • 生产环境优先选择方案1的原生Upsert实现,性能和一致性都优于DSL分步实现

内容的提问来源于stack exchange,提问作者Alireza

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.31 21:15:44