SQLite3,SQLSTATE[HY000]:一般错误:5 数据库已被锁定

5

我有一个小测试脚本:

session_start();
session_write_close();
error_reporting(-1);
register_shutdown_function(function() {
    //echo 'shutdown';
});

$MAX = 120;
set_time_limit($MAX);
echo date('Y-m-d H:i:s').'<br>';
$m = microtime(true);
$file_db = new PDO('sqlite:'.dirname(__FILE__).'/test.sqlite3');
$file_db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$file_db->exec("CREATE TABLE IF NOT EXISTS messages (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, message TEXT, time INTEGER)");

$d = date('U');
do
{
    $file_db->exec ('INSERT INTO messages VALUES (null, "titleee'.rand(1,9).'", "MESSAGEEEE'.rand(1,99).'", "'.rand(1,999).'")');
    if (date('U') - $d > $MAX/2)
    {
        break;
    }
} while (true);
$file_db = null;
echo 'ok: '.(microtime(true)-$m);

如果在浏览器中多次运行此程序,迟早会出现“SQLSTATE [HY000]:General error: 5 database is locked”异常。如何避免?


1
$file_db->exec 后面添加 sleep(2)。有太多进程试图快速插入数据库,导致表被锁定。 - Justin E
嗯,它不能设置为只是“等一下”,直到它不再锁定吗? - John Smith
1
尽管我没有使用过这个函数,但您可以尝试在实例化$file_db后立即调用$file_db->query("SET LOCK MODE TO WAIT 120")。这样可以让脚本等待最多两分钟以解除表锁定状态... - Justin E
1
我建议您检查是否有未保存的更改,答案就在下面。如果这解决了问题,并且足够精确,请将其选为此问题的答案。 - Luis Milanese
1
对我来说,将未保存的更改直接保存到数据库文件中就解决了这个问题。 - nightrain
3个回答

10

我遇到这个错误是因为在SQLiteBrowser上有一个未保存的记录,所以SQLite不能对其进行写入。一旦我保存了该记录,我的脚本就恢复正常工作了。因此,每当出现此错误时,请检查您的SQLiteBrowser(或任何其他编辑器)是否打开,并且是否有任何未保存的更改。


2
$file_db->exec后添加sleep(2)。过多的进程试图快速插入数据库中,导致锁定表格。在实例化$file_db之后,您可以尝试立即执行$file_db->query("SET LOCK MODE TO WAIT 120")。这样可以让脚本等待最多两分钟,以便表格解锁...

我添加了 "$file_db->setAttribute(PDO::ATTR_TIMEOUT, 0);" 并捕获了异常。如果发生该异常,我只需再次运行该查询 + 进行 "usleep" - 理论上这应该是有效的,不是吗? - John Smith
1
是的,在理论上那会很好,虽然你可能想要先从 usleep 开始,以便稍微延迟一下重试... - Justin E
1
抱歉,这是我所能做到的最好的... - Justin E
1
切换到mysql将允许您使用延迟关键字,例如:INSERT DELAYED INTO ...,这提供了并发数据库插入的功能。 - Justin E
1
使用完毕后关闭游标:$stmt->closeCursor(); - edercortes

1
我发现一个锁定问题与虚拟机网络文件共享有关:https://www.sqlite.org/lockingv3.html#how_to_corrupt 需要注意的是,许多NFS实现(包括最新版本的Mac OS X)已知存在POSIX咨询锁定错误或未实现,并且有关Windows下网络文件系统的锁定问题的报告。您最好不要在网络文件系统上使用SQLite以确保安全。
我通过将SQLite数据库文件移动到没有与主机OS(Windows)挂载/共享的Linux客户机OS(Linux)目录中来解决了这个问题。
在我的情况下,我将SQLite数据库文件移动到Linux客户机OS上的/tmp目录中。

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