为啥在使用 Perl 的 DBD::SQLite 时,SQLite 会为事务中的第二个查询提供“数据库已锁定”?

Posted

技术标签:

【中文标题】为啥在使用 Perl 的 DBD::SQLite 时,SQLite 会为事务中的第二个查询提供“数据库已锁定”?【英文标题】:Why does SQLite give a "database is locked" for a second query in a transaction when using Perl's DBD::SQLite?为什么在使用 Perl 的 DBD::SQLite 时,SQLite 会为事务中的第二个查询提供“数据库已锁定”? 【发布时间】:2011-04-25 23:55:38 【问题描述】:

在使用 Perl DBD::SQLite 时,SQLite 是否存在一个已知问题,即在单个事务中的第二个查询出现“数据库已锁定”错误?场景:Linux,Perl DBI,AutoCommit => 0,具有两个代码块的子例程(使用这些块来本地化变量名)。在第一个代码块中,prepare() 在 select 语句上创建了一个查询句柄,它被 execute() 并关闭了块。第二个代码块另一个查询句柄是由准备更新语句创建的,并且经常(30% 的时间)SQLite/DBI 在此阶段给出数据库锁定错误。我认为错误发生在prepare()期间而不是execute()期间。

我的解决方法是在第一次查询之后提交。 (在第一个查询上调用完成没有帮助)。出于与优雅和性能相关的几个原因,我不想承诺。多年来,原始代码使用 Postgres 作为数据库运行良好。我试过 sqlite_use_immediate_transaction 没有效果。

在所有其他情况下,我发现 SQLite 的性能非常好,所以我怀疑这是 DBD 驱动程序的疏忽,而不是 SQLite 的问题。遗憾的是,我当前的代码是一大堆脚本和模块,所以我没有简短的单文件测试用例。

【问题讨论】:

你能给我们展示你的小测试用例来证明这个问题吗? 【参考方案1】:

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

AutoCommit 或 begin_work 的事务非常好用,但有时您可能会遇到烦人的“数据库已锁定”错误。这通常发生在某人开始事务并尝试写入数据库时​​,而其他人正在从数据库中读取(在另一个事务中)。您可能会感到惊讶,但是当您刚刚开始正常(延迟)事务以最大化并发性时,SQLite 不会锁定数据库。当您发出要写入的语句时,它会保留一个锁,但在您实际尝试使用提交语句进行写入之前,它允许其他人从数据库中读取。但是,从数据库读取也需要共享锁,这会阻止给您保留的独占锁,因此您会收到“数据库已锁定”错误,如果其他人稍后尝试写入,也会收到相同的错误,如你还有一个挂起的锁。在这种情况下,busy_timeout 没有帮助。

为避免这种情况,请明确设置事务类型。您可以为每个事务发出开始立即事务(或开始独占事务),或将 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

【讨论】:

以上是关于为啥在使用 Perl 的 DBD::SQLite 时,SQLite 会为事务中的第二个查询提供“数据库已锁定”?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 PowerShell(使用 Perl)在简单的打印语句中删除双引号?

为啥 Perl 5 的函数原型不好?

为啥 Perl 使用空字符串来表示 boolean false 值?

为啥 STDIN 会导致我的 Perl 程序冻结?

为啥 Perl 文件 glob() 不能在标量上下文中的循环之外工作?

Perl:为啥在循环中声明(我的)变量会更慢?