我目前正在使用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会立即返回一个被阻止的繁忙错误。您可以要求它在放弃之前等待并尝试一段时间。这通常可以解决问题,除非您有成千上万个线程访问数据库,那么我同意sqlite可能不合适。
// 将SQLite设置为等待并重试,最多100毫秒,如果数据库被锁定 sqlite3_busy_timeout( db, 100 );
仅供记录。我们在一个使用Rails 2.3.8的应用程序中发现,Rails忽略了Rifkin Habsburg建议的“timeout”选项。
经过更多的调查,我们发现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很好用!
所有这些事情都是真的,但它没有回答问题,也就是:为什么我的Rails应用程序在生产中偶尔会引发一个SQLite3::BusyException异常?
@Shalmanese: 生产托管环境是怎样的?它是在共享主机上吗?包含sqlite数据库的目录是否在NFS共享上?(很可能在共享主机上)。
这个问题很可能与使用NFS共享和SQLite缺乏并发性的文件锁定现象有关。
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
点击此处了解更多信息。该链接涉及IT技术,讨论了在某些情况下设置sqlite3_busy_timeout后仍会立即引发sqlite3busyexceptions的问题。
bundle exec rake db:reset
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数据库时,我使用这行代码进行连接:
conn = sqlite3.connect('filename', isolation_level = 'exclusive')
根据Python Sqlite文档的说明:
您可以通过connect()调用的isolation_level参数或连接的isolation_level属性来控制pysqlite隐式执行哪种类型的BEGIN语句(或根本不执行)。
尝试运行以下代码,可能会有所帮助:
ActiveRecord::Base.connection.execute("BEGIN TRANSACTION; END;")
来自: Ruby: SQLite3::BusyException: 数据库已被锁定:
这可能会清除任何阻塞系统的事务