2008-09-17 44 views
35

现在使用SQLite3运行rails站点。SQLite3 :: BusyException

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

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

什么是解决这一问题,这将是最小的方式侵入到我的代码?

我使用SQLLite的时刻,因为你可以存储在数据库中的源代码控制这使得备份自然,你可以非常迅速地推动改变的。但是,这显然不是真正建立起来并发访问,明天早上我将迁移到MySQL。

+0

什么源码的版本? – 2008-09-17 01:12:03

+0

我敢打赌,您的生产环境主机使用NFS作为应用用户的家目录,不是吗? – ybakos 2010-11-19 15:41:35

回答

7

默认情况下,如果数据库处于忙碌状态并且被锁定,sqlite会立即返回阻塞的繁忙错误。你可以要求它在放弃之前等待并继续尝试一段时间。这通常解决了这个问题,除非你有1000线程访问你的数据库,当我同意sqlite会不合适。

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

你把sqlite3_busy_timeout放在哪里? – Sam 2010-03-26 01:28:34

+0

安置并不重要。在打开数据库之后并在执行被阻止的请求之前。为了方便起见,我在打开数据库后立即放置它。 – ravenspoint 2010-03-26 11:17:41

-8

我相信这发生在交易超时时。你真的应该使用“真正的”数据库。像Drizzle或MySQL。为什么比以前的两个选项更喜欢SQLite?

+0

该工作的正确工具。示例一:SQLite非常适合测试(它可以在内存中运行,因此速度很快)。例二:一个CMS /博客网站,它们通常数量不多,缓存数据库几乎没有受到任何影响。 – Kris 2011-11-09 15:45:26

+0

你认为这个人问这个问题不明白SQLite与MySQL的区别 – c2h2 2012-10-19 03:53:50

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(...) 
0

什么表中遇到锁时,被访问?

您是否有长期运行的交易?

你可以找出哪些请求在遇到锁定时仍在处理中?

0

阿格 - 我上周存在的祸根。当任何进程写入数据库时​​,Sqlite3锁定数据库文件。 IE的任何UPDATE/INSERT类型的查询(由于某种原因也选择count(*))。但是,它处理多个读取就好了。

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

而且,它慢得要死。但它也足够快,并且正确,这是一个很好的属性。

53

您提到这是一个Rails站点。 Rails允许你设置SQLite的重试超时在你的database.yml配置文件:

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

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

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

1

Sqlite可以允许其他进程等待,直到当前的一个完成。

我用这条线时,我知道我可以有多个进程试图去访问SQLite数据库进行连接:

康恩= sqlite3.connect(“文件名”,ISOLATION_LEVEL =“独家”

根据Python的SQLite的文档:

您可以控制哪些类型的BEGIN 语句通过pysqlite隐含 执行(或者根本没有)isolation_level参数调用 connect()或通过 isolation_level属性的 连接。

3

所有这些都是真实的,但它不回答这个问题,这可能是:为什么我的Rails应用程序偶尔会在生产中引发SQLite3 :: BusyException?

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

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

1

我有一个与rake db:migrate类似的问题。问题在于工作目录位于SMB共享上。 我通过将文件夹复制到我的本地机器来修复它。

2

只是为了记录。在Rails 2.3.8的一个应用程序中,我们发现Rails忽略了Rifkin Habsburg提出的“超时”选项。

经过一番调查,我们在Rails dev中发现了一个可能相关的错误: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很好!

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. 

如果符合这个条件,超时不再有效。为了避免它,不要把选择放在开始/提交。或者使用独占锁来开始/提交。

希望这会有所帮助。 :)

0

这往往是多个进程访问同一个数据库,即连续错,如果“允许只有一个实例”标志,没有的RubyMine

1

设置如果你有这个问题,但增加超时不不能改变什么,则可能是另一个事务的并发性问题,这里是它总结:

  1. 开始事务(aquires一个SHARED锁)
  2. 读取一些数据DB(我们仍在使用锁)
  3. 同时,另一个进程启动一个事务并写入数据(获取锁定)。
  4. 然后尝试写,你现在正试图要求RESERVED
  5. 的SQLite引发SQLITE_BUSY例外立即(indenpendently您超时),因为你以前的读取可能不再受时间精确它可以获得锁定保留。解决这个问题

的一种方法是通过填充的:immediate选项司机修补active_record sqlite的适配器可以直接在交易开始时AQUIRE一个RESERVED锁。这会降低性能,但至少您的所有交易都会遵守您的超时时间并一个接一个地发生。这里是如何做到这一点使用prepend(红宝石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

2
bundle exec rake db:reset 

它的工作对我来说将重置并显示挂起的迁移。

1

大多数的答案是为Rails而不是原始红宝石,和OP的问题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.