立即使用SQLite3运行rails网站。
大约每500个请求大约一次,我得到一个
ActiveRecord :: StatementInvalid(SQLite3 :: BusyException:数据库被锁定:...
解决这个问题的方法是什么,这对我的代码来说是微创的?
我目前正在使用SQLLite,因为您可以将数据库存储在源代码管理中,这使备份变得自然,您可以非常快速地推出更改。但是,它显然没有真正设置为并发访问。我明天早上会迁移到MySQL。
答案 0 :(得分:53)
您提到这是一个Rails网站。 Rails允许您在database.yml配置文件中设置SQLite重试超时:
production:
adapter: sqlite3
database: db/mysite_prod.sqlite3
timeout: 10000
超时值以毫秒为单位指定。将其增加到10或15秒应该会减少您在日志中看到的BusyExceptions的数量。
但这只是一个临时解决方案。如果您的站点需要真正的并发性,那么您将不得不迁移到另一个数据库引擎。
答案 1 :(得分:9)
默认情况下,如果数据库忙且已锁定,则sqlite会立即返回一个阻塞的忙碌错误。你可以要求它等待,并在放弃之前继续尝试一段时间。这通常可以解决问题,除非你有1000个线程访问你的数据库,当我同意sqlite不合适。
// set SQLite to wait and retry for up to 100ms if database locked sqlite3_busy_timeout( db, 100 );
答案 2 :(得分:3)
所有这些都是正确的,但它没有回答这个问题,这很可能是:为什么我的Rails应用程序偶尔会在生产中引发SQLite3 :: BusyException?
<莎> @Shalmanese:生产托管环境是什么样的?它是在共享主机上吗?是否在NFS共享上包含sqlite数据库的目录? (可能,在共享主机上)。这个问题可能与文件锁定w / NFS共享和SQLite缺乏并发现象有关。
答案 3 :(得分:2)
仅供记录。在使用Rails 2.3.8的一个应用程序中,我们发现Rails忽略了Rifkin Habsburg建议的“超时”选项。
经过一些调查后,我们在Rails开发中发现了一个可能相关的错误:http://dev.rubyonrails.org/ticket/8811。经过一些调查后,我们发现the solution(使用Rails 2.3.8测试):
编辑此ActiveRecord文件:activerecord-2.3.8 / lib / active_record / connection_adapters / sqlite_adapter.rb
替换它:
def begin_db_transaction #:nodoc:
catch_schema_changes { @connection.transaction }
end
与
def begin_db_transaction #:nodoc:
catch_schema_changes { @connection.transaction(:immediate) }
end
就是这样!我们没有注意到性能下降,现在该应用程序支持更多请求而不会中断(它等待超时)。 Sqlite很不错!
答案 4 :(得分:2)
bundle exec rake db:reset
它对我有用,它会重置并显示待处理的迁移。
答案 5 :(得分:1)
来源:this link
- 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(...)
答案 6 :(得分:1)
Sqlite可以允许其他进程等到当前进程完成。
当我知道可能有多个进程尝试访问Sqlite DB时,我使用此行进行连接:
conn = sqlite3.connect('filename', isolation_level ='exclusive')
根据Python Sqlite文档:
您可以控制哪种BEGIN 语句pysqlite含蓄 通过执行(或根本不执行) isolation_level参数 connect()调用,或通过 isolation_level属性 连接。
答案 7 :(得分:1)
我在rake db:migrate方面遇到了类似的问题。问题是工作目录位于SMB共享上。 我通过将文件夹复制到我的本地机器来修复它。
答案 8 :(得分:1)
如果您遇到此问题,但增加超时不会改变任何内容,您可能会遇到另一个与事务并发的问题,这里是摘要:
解决此问题的一种方法是通过将active_record
选项填充到驱动程序来修补:immediate
sqlite适配器以在事务开始时直接获取 RESERVED 锁定。这会稍微降低性能,但至少所有交易都将遵循您的超时并且一个接一个地发生。以下是使用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
答案 9 :(得分:1)
大多数答案都是针对Rails而不是原始的红宝石,而且OPs对轨道问题是IS,这很好。 :)
因此,如果任何原始ruby用户遇到此问题,并且未使用yml配置,我只想将此解决方案留在此处。
实例化连接后,您可以这样设置:
db = SQLite3::Database.new "#{path_to_your_db}/your_file.db"
db.busy_timeout=(15000) # in ms, meaning it will retry for 15 seconds before it raises an exception.
#This can be any number you want. Default value is 0.
答案 10 :(得分:0)
遇到锁定时正在访问哪个表?
你有长期交易吗?
你可以找出遇到锁时仍在处理的请求吗?
答案 11 :(得分:0)
Argh--上周我的存在的祸根。当任何进程写入到数据库时,Sqlite3会锁定db文件。 IE任何UPDATE / INSERT类型查询(由于某种原因也选择count(*))。但是,它处理多次读取就好了。
所以,我终于感到沮丧,在数据库调用周围编写自己的线程锁定代码。通过确保应用程序在任何时候只能有一个线程写入数据库,我能够扩展到1000个线程。
是的,它的速度很慢。但它也足够快和正确,这是一个不错的属性。
答案 12 :(得分:0)
我在sqlite3 ruby扩展程序上发现了一个死锁,并在此处修复它:继续使用它,看看这是否解决了你的问题。
https://github.com/dxj19831029/sqlite3-ruby
我打开了一个拉取请求,没有他们的回复。
无论如何,如sqlite3本身所述,预计会有一些繁忙的异常。
请注意此条件:sqlite busy
The presence of a busy handler does not guarantee that it will be invoked when there is lock contention. If SQLite determines that invoking the busy handler could result in a deadlock, it will go ahead and return SQLITE_BUSY or SQLITE_IOERR_BLOCKED instead of invoking the busy handler. Consider a scenario where one process is holding a read lock that it is trying to promote to a reserved lock and a second process is holding a reserved lock that it is trying to promote to an exclusive lock. The first process cannot proceed because it is blocked by the second and the second process cannot proceed because it is blocked by the first. If both processes invoke the busy handlers, neither will make any progress. Therefore, SQLite returns SQLITE_BUSY for the first process, hoping that this will induce the first process to release its read lock and allow the second process to proceed.
如果您遇到此情况,超时将不再有效。要避免它,请不要将select放在begin / commit中。或者使用独占锁来开始/提交。
希望这会有所帮助。 :)
答案 13 :(得分:0)
这通常是访问同一数据库的多个进程的连续错误,即如果在RubyMine中未设置“仅允许一个实例”标志
答案 14 :(得分:0)
尝试运行以下内容,可能有所帮助:
ActiveRecord::Base.connection.execute("BEGIN TRANSACTION; END;")
来自:Ruby: SQLite3::BusyException: database is locked:
这可能会清除阻止系统的任何交易
答案 15 :(得分:-8)
我相信当交易超时时会发生这种情况。你真的应该使用“真正的”数据库。像Drizzle或MySQL这样的东西。您之所以更喜欢SQLite而不是之前的两个选项吗?