SQLite3 :: BusyException

时间:2020-03-05 18:57:36  来源:igfitidea点击:

现在使用SQLite3运行Rails站点。

大约每500个请求一次,我得到一个

ActiveRecord :: StatementInvalid(SQLite3 :: BusyException:数据库已锁定:...

有什么办法可以解决这种对我的代码造成最小影响的问题?

目前,我正在使用SQLLite,因为我们可以将数据库存储在源代码管理中,这使备份自然进行,并且可以非常迅速地推出更改。但是,显然并没有为并发访问设置它。我明天早上将迁移到MySQL。

解决方案

回答

我相信这会在交易超时时发生。我们确实应该使用"真实"数据库。像Drizzle或者MySQL。为什么我们比之前的两个选项更喜欢SQLite?

回答

来源:此链接

- Open the database
db = sqlite3.open("filename")

-- Ten attempts are made to proceed, if the database is locked
function my_busy_handler(attempts_made)
  if attempts_made < 10 then
    return true
  else
    return false
  end
end

-- Set the new busy handler
db:set_busy_handler(my_busy_handler)

-- Use the database
db:exec(...)

回答

遇到锁定时正在访问哪个表?

我们是否有长期交易?

我们能找出遇到锁定时仍在处理哪些请求吗?

回答

上个礼拜,我的生存祸根啊。当任何进程写入数据库时​​,Sqlite3都会锁定db文件。 IE浏览器的任何UPDATE / INSERT类型查询(出于某种原因也请选择count(*))。但是,它可以处理多次读取。

因此,我终于感到沮丧,无法在数据库调用周围编写自己的线程锁定代码。通过确保应用程序在任何时候只能有一个线程写入数据库,我能够扩展到1000个线程。

是的,它慢得要命。但是它也足够快且正确,这是一个不错的属性。

回答

默认情况下,如果数据库处于忙碌状态且已锁定,则sqlite立即返回一个阻塞的,忙碌的错误。我们可以要求它等待一段时间,然后再尝试放弃。通常,这可以解决问题,除非我们确实有数千个线程在访问数据库,否则我认为sqlite是不合适的。

// set SQLite to wait and retry for up to 100ms if database locked
    sqlite3_busy_timeout( db, 100 );

回答

我们提到这是一个Rails网站。 Rails允许我们在database.yml配置文件中设置SQLite重试超时:

production:
  adapter: sqlite3
  database: db/mysite_prod.sqlite3
  timeout: 10000

超时值以毫秒为单位指定。将其增加到10或者15秒应该会减少我们在日志中看到的BusyExceptions的数量。

不过,这只是一个临时解决方案。如果站点需要真正的并发,那么我们将不得不迁移到另一个数据库引擎。

回答

Sqlite可以允许其他进程等到当前进程完成。

当我知道可能有多个进程试图访问Sqlite DB时,我使用这条线进行连接:

conn = sqlite3.connect('文件名',isolation_level ='独占')

根据Python Sqlite文档:

You can control which kind of BEGIN
  statements pysqlite implicitly
  executes (or none at all) via the
  isolation_level parameter to the
  connect() call, or via the
  isolation_level property of
  connections.

回答

所有这些事情都是正确的,但它并不能回答问题,这很可能是:为什么我的Rails应用偶尔会在生产中引发SQLite3 :: BusyException?

@Shalmanese:生产托管环境是什么样的?它在共享主机上吗? NFS共享上是否包含sqlite数据库的目录? (就像在共享主机上一样)。

此问题可能与NFS共享的文件锁定现象以及SQLite缺乏并发现象有关。