如何解锁SQLite数据库?
sqlite> DELETE FROM mails WHERE (`id` = 71); SQL error: database is locked
我该如何解锁数据库才能正常工作?
解决方案
SQLite数据库文件只是文件,因此第一步是确保它不是只读的。要做的另一件事是确保在打开数据库时没有某种GUI SQLite DB查看器。我们可以在另一个外壳中打开数据库,或者代码可以打开数据库。通常,如果其他线程或者应用程序(例如SQLite数据库浏览器)打开了可写的数据库,就会看到此消息。
SQLite Wiki DatabaseIsLocked页面提供了关于此错误消息的很好的解释。它部分说明争用的源是内部的(对于发出错误的过程而言)。
该页面未解释的是SQLite如何确定进程中的某些内容已锁定,以及哪些条件可能导致误报。
我只是发生了类似的事情,我的Web应用程序能够从数据库中读取,但是无法执行任何插入或者更新。重新启动Apache至少可以暂时解决该问题。
但是,能够找到根本原因会很不错。
我发现有关SQLite锁定的各种状态的文档非常有帮助。迈克尔,如果我们可以执行读取操作但不能执行对数据库的写入操作,则意味着进程已在数据库上获得了RESERVED锁,但尚未执行写入操作。如果我们使用的是SQLite3,则有一个名为PENDING的新锁,该锁不允许再有其他进程进行连接,但是现有的连接会阻止执行读取操作,因此,如果这是问题,我们应该考虑一下。
从先前的评论中,我们说到存在-journal文件。
这可能意味着我们已经打开和(独占?)事务并且尚未提交数据。程序或者其他进程是否将-journal留下了?
重新启动sqlite进程将查看日志文件并清理所有未提交的操作并删除-journal文件。
如果某个进程在SQLite数据库上具有锁定并崩溃,则该数据库将永久保持锁定状态。那就是问题所在。并非其他进程具有锁定功能。
我在从终端会话运行Python脚本的Mac OS X 10.5.7上遇到了同样的问题。即使我已经停止了脚本并且终端窗口位于命令提示符下,它在下次运行时也会出现此错误。解决方法是关闭终端窗口,然后再次将其打开。对我来说没有意义,但确实有效。
删除-journal文件听起来是个糟糕的主意。它可以使sqlite在崩溃后将数据库回滚到一致的状态。如果我们在数据库处于不一致状态时删除它,那么我们将拥有损坏的数据库。引用来自sqlite网站的页面:
If a crash or power loss does occur and a hot journal is left on the disk, it is essential that the original database file and the hot journal remain on disk with their original names until the database file is opened by another SQLite process and rolled back. [...] We suspect that a common failure mode for SQLite recovery happens like this: A power failure occurs. After power is restored, a well-meaning user or system administrator begins looking around on the disk for damage. They see their database file named "important.data". This file is perhaps familiar to them. But after the crash, there is also a hot journal named "important.data-journal". The user then deletes the hot journal, thinking that they are helping to cleanup the system. We know of no way to prevent this other than user education.
回滚应该在下次打开数据库时自动发生,但是如果该进程无法锁定数据库,则回滚将失败。正如其他人所说的,一个可能的原因是当前有另一个进程处于打开状态。如果数据库位于NFS卷上,则另一种可能性是过时的NFS锁定。在这种情况下,一种解决方法是用未锁定在NFS服务器上的新副本替换数据库文件(mv database.db original.db; cp original.db database.db)。请注意,由于对NFS文件锁定的错误实现,sqlite常见问题解答建议我们谨慎使用NFS卷上的数据库。
我无法解释为什么删除-journal文件会使我们锁定以前无法访问的数据库。那是可复制的吗?
顺便说一句,-journal文件的存在并不一定意味着发生了崩溃或者有待回滚的更改。 Sqlite有几种不同的日志模式,在PERSIST或者TRUNCATE模式下,它始终将-journal文件保留在原地,并更改内容以指示是否存在要回滚的部分事务。
我的锁定是由系统崩溃而不是由挂起进程引起的。为了解决这个问题,我只是简单地重命名了文件,然后将其复制回其原始名称和位置。
使用Linux外壳将是...
mv mydata.db temp.db cp temp.db mydata.db
正如Seun Osewa所说,有时僵尸进程会在终端中坐上锁,即使我们认为不可能,也需要锁。脚本运行,崩溃,然后返回到提示,但库调用在某个地方生成了一个僵尸进程,并且该进程具有锁定。
关闭我们所在的终端(在OSX上)可能会起作用。重新启动将起作用。我们可能会寻找没有做任何事情的" python"进程(例如)并将其杀死。
在关闭重新启动选项之前,值得一看的是,我们是否可以找到sqlite数据库的用户。
在Linux上,可以为此目的使用fuser
:
$ fuser database.db $ fuser database.db-journal
就我而言,我得到以下回应:
philip 3556 4700 0 10:24 pts/3 00:00:01 /usr/bin/python manage.py shell
这表明我有另一个使用数据库的pid 3556(manage.py)的Python程序。
我们可以尝试以下方法:.timeout 100
来设置timeout。
我不知道在命令行中会发生什么,但是在C.Net中,当我这样做时:" UPDATE table-name SET column-name = value;" -name = value"
就可以了。
看起来,当我们添加;时,sqlite's将寻找进一步的命令。
在Windows中,我们可以尝试使用该程序http://www.nirsoft.net/utils/opened_files_view.html来了解正在处理db文件的过程。尝试关闭该程序以解锁数据库
在Linux和macOS中,我们可以执行类似的操作,例如,如果锁定的文件是development.db:
$ fuser development.db
此命令将显示什么进程正在锁定文件:
> development.db: 5430
只是扼杀这个过程...
kill -9 5430
...并且数据库将被解锁。