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

SQLite3数据库锁致ActiveRecord插入异常问题求助

解决SQLite数据库锁导致的消息重复插入问题

我明白这种“首次点击没反应、二次点击插两条”的问题有多闹心——尤其是试过各种现有方案还没解决的情况下。咱们从SQLite的锁特性入手,一步步拆解修复思路:

1. 先搞懂为什么会出现SQLite::BusyException

SQLite是文件级别的数据库锁,同一时间只能允许一个写操作执行。如果有未释放的数据库连接、长时间运行的读查询,或者多线程/进程同时写入,都会导致写操作被阻塞,抛出这个锁异常。

先排查基础配置

  • 检查config/database.yml里的连接池配置:如果用了Puma这类多线程服务器,pool的数值要和Puma的threads配置匹配(比如Puma设了threads 1:5,pool至少要设为5),避免因为连接不够用导致等待锁的情况。
  • 关闭所有可能占用数据库的额外进程:比如开发环境的Spring、rails console,或者后台运行的Sidekiq任务,这些进程可能握着连接不释放,导致主服务的写操作被锁。

2. 代码层面加重试+幂等性,解决重复插入

既然首次点击因为锁失败,二次点击又会重复插入,我们需要从两个维度解决:

  • 给写操作加有限次数的重试,应对临时锁;
  • 加幂等性约束,确保即使重复调用也不会生成重复数据。

方案一:捕获锁异常并有限重试

把创建notification的代码包裹在重试块里,针对SQLite的BusyException做最多3次重试(次数可以调整):

def create_notification(content, user_id)
  retry_count = 0
  begin
    Notification.create!(content: content, user_id: user_id)
  rescue SQLite3::BusyException => e
    retry_count += 1
    if retry_count < 3
      sleep 0.1 # 等待一小会儿再重试
      retry
    else
      raise e # 超过重试次数再抛出异常,方便排查
    end
  end
end

方案二:给表加唯一索引实现幂等性

如果消息有唯一标识(比如前端传的消息UUID,或者内容+用户ID的组合),给notifications表加唯一索引,这样即使重复调用create,数据库也会拒绝插入重复数据:

  1. 生成迁移文件:
rails generate migration AddUniqueIndexToNotifications user_id:integer message_uuid:string
  1. 在迁移文件里添加唯一约束:
class AddUniqueIndexToNotifications < ActiveRecord::Migration[7.0]
  def change
    add_index :notifications, [:user_id, :message_uuid], unique: true
  end
end
  1. 创建消息时传入唯一的message_uuid:
Notification.create!(content: content, user_id: user_id, message_uuid: SecureRandom.uuid)

这样重复调用时会触发ActiveRecord::RecordNotUnique异常,不会生成两条相同消息。

3. 排查并发写入场景

如果你的应用里有后台任务(比如Sidekiq)或者其他线程同时操作notifications表,需要确保这些操作不会互相抢锁:

  • 用乐观锁:给notifications表加lock_version字段(通过迁移add_column :notifications, :lock_version, :integer, default: 0),这样并发写入时会触发ActiveRecord::StaleObjectError,你可以在代码里捕获这个异常,提示用户“操作太频繁,请稍后重试”。
  • 避免长时间占用连接:比如不要在写入notification之前执行耗时的查询(比如加载大量用户数据),尽量把读操作和写操作分开,减少连接占用时间。

4. 生产环境建议

如果这是生产环境的问题,SQLite本身并不适合高并发写入场景——它的文件锁机制在多进程/多线程写入时很容易出现锁等待。建议换成PostgreSQL或者MySQL,它们支持更细粒度的行级锁,能更好地处理并发写入。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:38:45