当使用Perl的DBD :: SQLite时,为什么SQLite为事务中的第二个查询提供"数据库被锁定"?

Tom*_*Tom 6 sqlite perl prepared-statement dbi dbd

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

我的工作是在第一次查询后提交.(在第一个查询上调用完成没有帮助).我不喜欢有几个与优雅和表现有关的理由.Postgres作为数据库,原始代码已经运行了很多年.我试过sqlite_use_immediate_transaction没有效果.

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

dra*_*tun 7

无论如何都与此无关:来自perldoc的事务和数据库锁定DBD::SQLite

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

为避免这种情况,请明确设置事务类型.您可以为每个事务发出begin immediate事务(或者开始独占事务),或者将sqlite_use_immediate_transaction数据库句柄属性设置为true(从1.30_02开始)以始终使用立即事务(即使您只是使用begin_work或关闭AutoCommit.) .

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

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