为什么使用Perl的DBD::SQLite在事务中执行第二个查询时,SQLite会返回“数据库已锁定”的错误信息?

7
在使用Perl DBD::SQLite时,在单个事务中进行第二个查询时,SQLite是否会出现“数据库已锁定”错误的已知问题?场景:Linux,Perl DBI,AutoCommit => 0,一个包含两个代码块的子例程(使用这些代码块来本地化变量名)。在第一个代码块中,通过prepare()创建了一个查询句柄用于选择语句,它被执行()并关闭该块。在第二个代码块中,通过prepare()为更新语句创建了另一个查询句柄,并且经常(30%的时间)SQLite/DBI在此阶段给出数据库锁定错误。我认为错误发生在prepare()而不是execute()期间。
我的解决方法是在第一个查询后提交。(对第一个查询调用finish没有帮助)。出于优雅和性能等多种原因,我不想提交。原始代码在Postgres作为数据库上运行多年没有问题。我尝试过sqlite_use_immediate_transaction但没有效果。
在所有其他情况下,我发现SQLite表现非常好,因此我认为这是DBD驱动程序中的疏忽,而不是SQLite的问题。可悲的是,我的当前代码是一堆脚本和模块,因此我没有一个简短的、单文件的测试用例。

你能展示一下你的小测试案例来说明问题吗? - brian d foy
1个回答

9

与此无关的是:Transaction and Database Locking,来自DBD::SQLite的perldoc?

通过AutoCommit或begin_work事务很方便,但有时可能会遇到烦人的“数据库被锁定”错误。这通常发生在某人开始事务并尝试写入数据库时,另一个人正在从数据库中读取(在另一个事务中)。你可能会感到惊讶,但SQLite在仅开始普通(延迟)事务时不会锁定数据库以最大化并发性。当您发出写入语句时,它会保留锁定,但直到您实际尝试使用提交语句进行写入之前,它允许其他人从数据库中读取。然而,从数据库中读取也需要共享锁定,这会阻止为您保留的排他锁定,因此您会收到“数据库被锁定”错误,并且如果其他人之后尝试写入,则他们也会收到相同的错误,因为您仍然有待处理的锁定。在这种情况下,busy_timeout没有帮助。

为避免这种情况,显式设置事务类型。您可以为每个事务发出begin immediate事务(或begin exclusive事务),或将sqlite_use_immediate_transaction数据库句柄属性设置为true(自1.30_02以来)以始终使用立即事务(即使您仅使用begin_work或关闭AutoCommit)。

my $dbh = DBI->connect("dbi:SQLite::memory:", "", "", {
  sqlite_use_immediate_transaction => 1,
});

请注意,仅当所有连接使用相同的(非延迟)事务时才起作用。有关锁定详细信息,请参见http://sqlite.org/lockingv3.html

这里有一个稍微更好的解释,也是为什么在这种特定情况下sqlite会忽略busy_timeout的原因:https://code.djangoproject.com/ticket/29280 - Davor Josipovic

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接