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

数据库间隔多日写入重复记录的原因排查求助

为什么你的Create动作会出现重复UID的记录?

兄弟,你遇到的这个重复记录问题我太熟了,结合你给出的代码片段,大概率是这几个原因在搞鬼,咱们一步步拆解:

最常见的元凶:并发请求的竞争窗口

你当前的逻辑是「先查UID是否存在 → 不存在就新建」,但这个操作不是原子性的!举个例子:

  • 请求A过来,查数据库发现UID不存在,正准备执行新建操作;
  • 就在请求A还没把新记录写入数据库的间隙,请求B也来了,同样查到UID不存在,也开始执行新建;
  • 最后两个请求都完成写入,就出现了同UID的重复记录。
    这个时间窗口可能很短(毫秒级),但只要并发量上来,或者网络/数据库有点延迟,就会触发,甚至间隔数天的情况也可能是因为重复的请求重试(比如前端重复提交、定时任务重复触发)。

数据库没加兜底的唯一约束

就算代码逻辑没问题,如果你的records表没给uid字段加唯一索引,那数据库本身是不会阻止重复插入的。哪怕你代码里做了检查,一旦出现并发或者代码疏漏,重复记录就会直接存进库。

代码逻辑的潜在漏洞

看你给出的代码片段写到「unle...」,应该是unless record吧?要确认这部分逻辑有没有写错:

  • 是不是新建记录时忘了把record_uid赋值给新记录的uid字段?比如写成了Record.create(params)而不是Record.create(uid: record_uid, **params);
  • 有没有可能params.delete(:uid)之后,后续逻辑又把uid参数加回去了?或者参数处理时出现了覆盖?

怎么解决?给你几个靠谱的方案

1. 先给数据库加唯一约束(最关键的兜底)

这是必须做的,不管代码怎么优化,数据库层面的约束是最后一道防线。在Rails里生成迁移文件:

# 生成迁移
rails generate migration AddUniqueIndexToRecordsUid
# 编辑迁移文件内容
add_index :records, :uid, unique: true
# 执行迁移
rails db:migrate

这样之后,哪怕代码出现并发插入,数据库会直接抛出RecordNotUnique错误,不会让重复记录落地。

2. 用原子性的UPSERT操作替代分步逻辑

Rails 6及以上版本支持upsert方法,它会直接调用数据库的原生UPSERT语句(原子操作,不存在竞争窗口):

def create
  record_uid = params.delete(:uid)
  if record_uid.present?
    # 原子性更新或创建:如果UID存在就更新,不存在就新建
    Record.upsert(
      { uid: record_uid }.merge(params),
      unique_by: :uid # 指定唯一约束的字段
    )
    # 之后可以获取这条记录
    record = Record.find_by(uid: record_uid)
  else
    # 处理没有UID的情况
    record = Record.create(params)
  end
  # 后续的响应逻辑
end

如果是Rails 6以下的版本,可以用「事务+捕获异常」的方式:

def create
  record_uid = params.delete(:uid)
  if record_uid.present?
    begin
      Record.transaction do
        # 先查,存在就更新
        record = Record.find_by!(uid: record_uid)
        record.update!(params)
      end
    rescue ActiveRecord::RecordNotFound
      # 没查到就新建
      record = Record.create!(uid: record_uid, **params)
    rescue ActiveRecord::RecordNotUnique
      # 捕获唯一约束错误,再去查并更新
      record = Record.find_by(uid: record_uid)
      record.update!(params) if record
    end
  else
    record = Record.create(params)
  end
end

3. 检查前端/请求来源的重复提交

如果重复记录的间隔是数秒、数分钟甚至更久,还要排查是不是前端重复提交(比如用户多次点击按钮)、定时任务重复触发、第三方回调重试导致的。可以在前端加防重复提交的逻辑(比如按钮置灰),或者在服务端对请求做幂等性校验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 06:24:24