sqlite3的:: BusyException
现在使用SQLite3运行一个rails站点。
大约每500个请求一次,我得到一个
ActiveRecord :: StatementInvalid(SQLite3 :: BusyException:数据库被locking:…
有什么办法来解决这将是微创入侵我的代码?
目前我正在使用SQLLite,因为您可以将数据库存储在源代码pipe理中,这使得备份变得非常自然,而且您可以快速地将更改推送出去。 但是,显然不是真正为并发访问设置的。 明天早上我将迁移到MySQL。
默认情况下,如果数据库处于忙碌和locking状态,sqlite会立即返回一个阻塞的忙碌错误。 你可以要求它等待,并继续尝试一段时间,然后放弃。 这通常解决了这个问题,除非你有1000线程访问你的数据库,当我同意SQLite将是不合适的。
如果数据库被locking,//设置SQLite等待并重试长达100ms sqlite3_busy_timeout(db,100);
你提到这是一个Rails站点。 Rails允许你在你的database.ymlconfiguration文件中设置SQLite重试超时:
production: adapter: sqlite3 database: db/mysite_prod.sqlite3 timeout: 10000
超时值以毫秒为单位指定。 将其增加到10或15秒将会减less在日志中看到的BusyException的数量。
但这只是一个临时的解决scheme。 如果您的站点需要真正的并发性,那么您将不得不迁移到另一个数据库引擎。
所有这些事情都是真实的,但它不回答这个问题,这可能是:为什么我的Rails应用程序偶尔会在生产中引发SQLite3 :: BusyException?
@Shalmanese:什么是生产托pipe环境? 它在共享主机上吗? 是在NFS共享上包含sqlite数据库的目录? (可能在共享主机上)。
这个问题可能与NFS共享和SQLite缺乏并发性的文件locking有关。
只是为了logging。 在Rails 2.3.8的一个应用程序中,我们发现Rails忽略了Rifkin Habsburg提出的“超时”选项。
经过一番调查,我们在Rails dev中发现了一个可能相关的错误: http : //dev.rubyonrails.org/ticket/8811 。 经过一些更多的调查,我们find了解决scheme (使用Rails 2.3.8进行testing):
编辑这个ActiveRecord文件:activerecord-2.3.8 / lib / active_record / connection_adapters / sqlite_adapter.rb
replace这个:
def begin_db_transaction #:nodoc: catch_schema_changes { @connection.transaction } end
同
def begin_db_transaction #:nodoc: catch_schema_changes { @connection.transaction(:immediate) } end
就这样! 我们没有注意到性能下降,现在应用程序支持更多的请求没有中断(它等待超时)。 Sqlite很好!
bundle exec rake db:reset
它为我工作,它会重置并显示待处理的迁移。
来源: 这个链接
- 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(...)
Sqlite可以让其他进程等待,直到当前的一个完成。
我使用这条线来连接,当我知道我可能有多个进程试图访问Sqlite数据库:
conn = sqlite3.connect('filename', isolation_level ='exclusive' )
根据Python Sqlite文档:
您可以通过isolation_level参数调用connect()或者通过连接的isolation_level属性来控制pysqlite隐式执行哪种types的BEGIN语句(或根本不执行)。
我有一个与rake db:migrate类似的问题。 问题在于工作目录在SMB共享上。 我通过将文件夹复制到我的本地计算机来修复它。
如果您遇到此问题,但增加超时并不会改变任何内容 ,那么您可能会遇到另一个与事务有关的并发问题,下面是总结:
- 开始交易(获得共享锁)
- 从数据库中读取一些数据(我们仍然使用SHARED锁)
- 同时,另一个进程启动事务并写入数据(获取RESERVED锁)。
- 然后你尝试写,你现在试图请求保留锁
- SQLite 立即引发SQLITE_BUSYexception(独立于您的超时),因为之前的读取在获得RESERVEDlocking时可能不再准确。
解决这个问题的一种方法是修补active_record
sqlite适配器,直接在事务开始时通过将:immediate
选项填充到驱动程序来获得一个RESERVED锁。 这会降低性能,但是至less你所有的交易都会尊重你的超时,并且一个接一个地发生。 这里是如何使用prepend
(Ruby 2.0+)把它放在初始化程序中:
module SqliteTransactionFix def begin_db_transaction log('begin immediate transaction', nil) { @connection.transaction(:immediate) } end end module ActiveRecord module ConnectionAdapters class SQLiteAdapter < AbstractAdapter prepend SqliteTransactionFix end end end
阅读更多: https : //rails.lighthouseapp.com/projects/8994/tickets/5941-sqlite3busyexceptions-are-raised-immediately-in-some-cases-despite-setting-sqlite3_busy_timeout
遇到锁时,哪个表正被访问?
你有长期交易吗?
你可以找出哪些请求仍然在处理锁时遇到?
阿格 – 这是我上个星期存在的祸根。 当任何进程写入数据库时,Sqlite3locking数据库文件。 IE的任何UPDATE / INSERTtypes的查询(由于某种原因也select计数(*))。 但是,它处理多个读取就好了。
所以,我终于感到沮丧,写数据库调用我自己的线程locking代码。 通过确保应用程序在任何时候只能有一个线程写入数据库,我可以扩展到1000个线程。
而且,它是缓慢的地狱。 但它也足够快, 正确 ,这是一个很好的财产。
我在sqlite3 ruby扩展中发现了一个死锁,并在这里修复它:一起去看看这是否解决了你的问题。
https://github.com/dxj19831029/sqlite3-ruby
我打开了一个拉请求,没有他们的回应。
无论如何,预计一些繁忙的exception,如sqlite3本身所述。
注意这个情况: sqlite繁忙
繁忙处理程序的存在并不保证它会在有时被调用 锁争用。 如果SQLite确定调用忙处理程序可能会导致一个 死锁,它会继续并返回SQLITE_BUSY或SQLITE_IOERR_BLOCKED而不是 调用繁忙的处理程序。 考虑一个进程持有读锁的场景 它试图提升到一个保留的锁,第二个进程保留一个保留 它正尝试将其提升为独占锁。 第一个过程不能继续 因为它被第二个阻塞,第二个过程因为它而不能继续 被第一个阻挡。 如果两个进程都调用繁忙的处理程序,则不会生成任何进程 进展。 因此,SQLite为第一个进程返回SQLITE_BUSY,希望这一点 将导致第一个进程释放它的读锁,并允许第二个进程 继续。
如果你遇到这种情况,超时是无效的。 为了避免这种情况,不要把select放在begin / commit里面。 或者使用独占锁来开始/提交。
希望这可以帮助。 🙂
这通常是多个进程访问相同数据库的连续错误,即如果RubyMine中没有设置“仅允许一个实例”标志
尝试运行以下,它可能有所帮助:
ActiveRecord::Base.connection.execute("BEGIN TRANSACTION; END;")
来自: Ruby:SQLite3 :: BusyException:数据库被locking:
这可能会清除任何阻碍系统的交易
我相信这发生在一个交易超时。 你真的应该使用“真实”的数据库。 像Drizzle或MySQL。 什么原因为什么你喜欢SQLite超过前两个选项?