Perl DBD::SQLite:数据库已锁定:如何重试?

Perl DBD::SQLite:数据库已锁定:如何重试?,perl,sqlite,dbi,Perl,Sqlite,Dbi,我并行使用SQLite数据库。主要用于阅读——这意味着一切都很顺利。还可以写和删除表格。然后突然,我在随机的时间里得到了这个结果(这表明了一种竞争条件——预期会并行运行): 现在我知道在10毫秒内数据库不会被锁定,所以我想等待10毫秒然后再试一次,但我找不到捕获该错误的方法 如何捕获该错误?更新 请注意以上内容 听起来您的子进程共享同一个数据库连接,并且彼此锁定 如果您想要真正的并行性,那么您需要与数据库建立多个连接。SQLite将对来自不同连接的请求进行排队,并为您解决冲突,除非禁用该行为

我并行使用SQLite数据库。主要用于阅读——这意味着一切都很顺利。还可以写和删除表格。然后突然,我在随机的时间里得到了这个结果(这表明了一种竞争条件——预期会并行运行):

现在我知道在10毫秒内数据库不会被锁定,所以我想等待10毫秒然后再试一次,但我找不到捕获该错误的方法

如何捕获该错误?

更新 请注意以上内容

听起来您的子进程共享同一个数据库连接,并且彼此锁定

如果您想要真正的并行性,那么您需要与数据库建立多个连接。SQLite将对来自不同连接的请求进行排队,并为您解决冲突,除非禁用该行为



您需要设计DBI应用程序的错误处理。在
connect
调用中可以指定三个选项

  • PrintError
    -默认情况下打开-如果出现错误,将发出警告

  • RaiseError
    -默认情况下关闭-如果出现错误,这将导致进程死亡

  • HandleError
    -默认情况下取消设置-此选项必须设置为子例程引用,如果出现错误,将调用该子例程

如果您认为没有数据库错误,那么最好使用

my $dbh = DBI->connect( ..., { PrintError => 0, RaiseError => 1 } )
然后,您可以对代码中可能出现错误的部分启用错误处理,并尝试修复它

政府是这样说的

如果要暂时关闭RaiseError(例如,在可能失败的库函数中),建议的方法如下:

{
  local $h->{RaiseError};  # localize and turn off for this block
  ...
}
这样,
RaiseError
选项将在右大括号处隐式打开,并且在块内,您可以检查
execute
返回的值,该值表示操作是否成功,以及
errstr
返回的值,该值提供了持续错误类型的详细信息。然后,您可以用Perl编写重试代码来做任何您想做的事情


标准的
sleep
调用将挂起进程,但粒度为1秒。如果您的程序无法在重试之间等待那么长的时间,那么请查看模块中的
usleep
函数,它需要一微秒的倍数

如果在您使用的SQLite包装器中有一种方法可以做到这一点,那么您可能应该设置一个“忙超时”(请参阅),SQLite应该处理这个问题。建议的值似乎在5-10s左右(即5000-10000)。问题似乎是它没有被视为繁忙,而是被锁定,并且它(错误地)假设锁定无法消失。正如这里可以看到的:SQLITE_锁定错误与SQLITE_繁忙(5)不同。SQLITE_BUSY表示另一个数据库连接(可能在另一个进程中)正在以阻止您使用该数据库的方式使用该数据库。SQLITE_锁定表示争用源是内部的,并且来自接收SQLITE_锁定错误的同一数据库连接。所以等待和重试可能没有帮助。我不知道这是否是你遇到的,但我想我会把它扔出去,就像你提到的“平行”。我最近遇到了这个问题,发现问题在于我在
fork()
下同时使用了两个单独的db句柄。当我重构代码来传递一个句柄,并删除了第二个句柄时,问题就消失了。这被记录在@GeorgMavridis中:这很重要;非常感谢。我假设一个锁定的错误就像一个文件系统锁,任何进程都可以独立地应用它。听起来OP的问题是所有的子进程都使用相同的数据库连接,因此相互锁定。
{
  local $h->{RaiseError};  # localize and turn off for this block
  ...
}