现在使用SQLite3运行rails站点。
大概每500个请求左右,我得到一个
ActiveRecord :: StatementInvalid(SQLite3 :: BusyException:数据库被锁定:...
解决这个问题的方法是什么,这对我的代码来说是微创的?
我目前正在使用SQLLite,因为您可以将数据库存储在源代码控制中,这使得备份变得自然,您可以非常快速地推出更改。 但是,它显然没有真正设置为并发访问。 我明天早上会迁移到MySQL。
现在使用SQLite3运行rails站点。
大概每500个请求左右,我得到一个
ActiveRecord :: StatementInvalid(SQLite3 :: BusyException:数据库被锁定:...
解决这个问题的方法是什么,这对我的代码来说是微创的?
我目前正在使用SQLLite,因为您可以将数据库存储在源代码控制中,这使得备份变得自然,您可以非常快速地推出更改。 但是,它显然没有真正设置为并发访问。 我明天早上会迁移到MySQL。
您提到这是一个Rails站点。 Rails允许您在database.yml配置文件中设置SQLite重试超时:
production:
adapter: sqlite3
database: db/mysite_prod.sqlite3
timeout: 10000
超时值以毫秒为单位指定。 将其增加到10或15秒应该会减少您在日志中看到的BusyExceptions的数量。
不过,这只是一个临时解决方案。 如果您的站点需要真正的并发性,那么您将不得不迁移到另一个数据库引擎。
默认情况下,如果数据库忙且已锁定,则sqlite会立即返回阻塞的忙碌错误。 你可以要求它等待,并在放弃之前继续尝试一段时间。 这通常可以解决问题,除非你有1000个线程访问你的数据库,当我同意sqlite是不合适的。
// set SQLite to wait and retry for up to 100ms if database locked sqlite3_busy_timeout( db, 100 );
所有这些都是正确的,但它没有回答这个问题,这很可能是:为什么我的Rails应用程序偶尔会在生产中引发SQLite3 :: BusyException?
@Shalmanese:生产托管环境是什么样的? 它是在共享主机上吗? 是否在NFS共享上包含sqlite数据库的目录? (可能在共享主机上)。
这个问题可能与文件锁定w / NFS共享和SQLite缺乏并发现象有关。
bundle exec rake db:reset
它对我有用,它将重置并显示待处理的迁移。
仅供记录。 在使用Rails 2.3.8的一个应用程序中,我们发现Rails忽略了Rifkin Habsburg建议的“超时”选项。
经过一些调查后,我们在Rails dev中发现了一个可能相关的错误: http : //dev.rubyonrails.org/ticket/8811 。 经过一些调查后,我们找到了解决方案 (使用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很好!
如果您遇到此问题但增加超时不会改变任何内容 ,那么事务可能会出现另一个并发问题,这里是摘要:
修复此问题的一种方法是修补active_record
sqlite适配器,以便在事务开始时直接通过将:immediate
选项填充到驱动程序来获取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
在这里阅读更多内容: https : //rails.lighthouseapp.com/projects/8994/tickets/5941-sqlite3busyexceptions-are-raised-immediately-in-some-cases-despite-setting-sqlite3_busy_timeout
大多数答案都是针对Rails而不是原始ruby,OPs问题是轨道问题,这很好。 :)
所以我只想在这里留下这个解决方案,如果任何原始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.
我在rake db:migrate中遇到了类似的问题。 问题是工作目录位于SMB共享上。 我通过将文件夹复制到我的本地机器来修复它。
Sqlite可以允许其他进程等到当前进程完成。
当我知道我可能有多个进程尝试访问Sqlite DB时,我使用此行进行连接:
conn = sqlite3.connect('filename', isolation_level ='exclusive' )
根据Python Sqlite文档:
您可以通过isolation_level参数到connect()调用或通过connections的isolation_level属性来控制pysqlite隐式执行哪种BEGIN语句(或根本不执行)。
资料来源: 此链接
- 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 ruby扩展程序上发现了一个死锁,并在此处修复它:继续使用它,看看这是否能修复你的问题。
https://github.com/dxj19831029/sqlite3-ruby
我打开了一个拉取请求,没有他们的回复。
无论如何,如sqlite3本身所述,预计会有一些繁忙的异常。
请注意这个条件: sqlite很忙
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中。 或者使用独占锁来开始/提交。
希望这可以帮助。 :)
这通常是访问同一数据库的多个进程的连续错误,即如果在RubyMine中未设置“仅允许一个实例”标志
尝试运行以下内容,它可能有所帮助:
ActiveRecord::Base.connection.execute("BEGIN TRANSACTION; END;")
来自: Ruby:SQLite3 :: BusyException:数据库被锁定:
这可以清除阻止系统的任何事务
遇到锁时访问了什么表?
你有长期交易吗?
在遇到锁定时,您能确定哪些请求仍在处理中吗?
唉 - 这是我上周存在的祸根。 当任何进程写入数据库时,Sqlite3会锁定db文件。 IE任何UPDATE / INSERT类型查询(由于某种原因也选择count(*))。 但是,它处理多个读取就好了。
所以,我终于感到沮丧,在数据库调用周围编写自己的线程锁定代码。 通过确保应用程序在任何时候只能有一个线程写入数据库,我能够扩展到1000个线程。
是的,它的速度很慢。 但它也足够快和正确 ,这是一个不错的财产。
我相信当交易超时时会发生这种情况。 你真的应该使用“真正的”数据库。 像Drizzle或MySQL这样的东西。 您之所以更喜欢SQLite而不是之前的两个选项吗?