为什么 SQLite 给出“数据库已锁定”信息使用 Perl 的 DBD::SQLite 时事务中的第二个查询?

发布于 2024-09-27 19:11:30 字数 529 浏览 10 评论 0原文

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

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

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

Is there a known problem with SQLite giving a "database is locked" error for a second query in a single transaction when using Perl DBD::SQLite? Scenario: Linux, Perl DBI, AutoCommit => 0, a subroutine with two code blocks (using the blocks to localize variable names). In the first code block a query handle is created by prepare() on a select statement, it is executed() and the block closed. The second code block another query handle is created by prepare for an update statement, and frequently (30% of the time) SQLite/DBI gives a database locked error at this stage. I think the error happens during prepare() and not during the execute().

My work around is to commit after the first query. ( Calling finish on the first query did not help). I prefer not to commit for several reasons relating to elegance and performance. The original code has worked fine for many years with Postgres as the database. I tried sqlite_use_immediate_transaction with no effect.

In all other situations, I've found SQLite to perform very well, so I suspect this is an oversight in the DBD driver, rather than an issue with SQLite. Sadly, my current code is a big pile of scripts and modules, so I don't have a short, single file test case.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

甜心小果奶 2024-10-04 19:11:30

无论如何与此无关的是: 事务和数据库从 DBD::SQLite > 佩尔多克?

通过 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 了解锁定详细信息。

Not related to this in anyway is it: Transaction and Database Locking from the DBD::SQLite perldoc?

Transaction by AutoCommit or begin_work is nice and handy, but sometimes you may get an annoying "database is locked" error. This typically happens when someone begins a transaction, and tries to write to a database while other person is reading from the database (in another transaction). You might be surprised but SQLite doesn't lock a database when you just begin a normal (deferred) transaction to maximize concurrency. It reserves a lock when you issue a statement to write, but until you actually try to write with a commit statement, it allows other people to read from the database. However, reading from the database also requires shared lock, and that prevents to give you the exclusive lock you reserved, thus you get the "database is locked" error, and other people will get the same error if they try to write afterwards, as you still have a pending lock. busy_timeout doesn't help in this case.

To avoid this, set a transaction type explicitly. You can issue a begin immediate transaction (or begin exclusive transaction) for each transaction, or set sqlite_use_immediate_transaction database handle attribute to true (since 1.30_02) to always use an immediate transaction (even when you simply use begin_work or turn off the AutoCommit.).

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

Note that this works only when all of the connections use the same (non-deferred) transaction. See http://sqlite.org/lockingv3.html for locking details.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文