如何解锁 SQLite 数据库?

sqlite> DELETE FROM mails WHERE (`id` = 71);
SQL error: database is locked

我如何解锁数据库,以便这将工作?

534089 次浏览

SQLite db文件只是文件,所以第一步是确保它不是只读的。另一件要做的事情是确保在DB打开时没有某种GUI SQLite DB查看器。可以在另一个shell中打开DB,也可以在代码中打开DB。通常情况下,如果不同的线程或应用程序(如SQLite Database Browser)打开了数据库以便写入,则会看到这种情况。

SQLite wiki DatabaseIsLocked页面提供了此错误消息的解释。在某种程度上,它指出争用的来源是内部的(对发出错误的进程而言)。本页没有解释的是SQLite如何决定进程中的某些东西持有锁,以及哪些条件会导致误报。

当您试图从同一个数据库连接同时对数据库执行两项不兼容的操作时,就会出现此错误代码。


v3中引入的与文件锁定相关的更改可能对未来的读者有用,可以在这里找到:SQLite版本3中的文件锁定和并发

我刚刚遇到了类似的情况——我的web应用程序能够从数据库中读取数据,但不能执行任何插入或更新。重启Apache至少暂时解决了这个问题。

不过,如果能找到根本原因就好了。

我发现SQLite中锁的各种状态的文档非常有用。Michael,如果您可以执行读操作,但不能对数据库执行写操作,这意味着某个进程已经获得了数据库上的保留锁,但还没有执行写操作。如果你正在使用SQLite3,有一个叫做PENDING的新锁,不允许更多的进程连接,但现有的连接仍然可以执行读取,所以如果这是问题所在,你应该看看这个锁。

从您之前的评论中,您说存在一个-journal文件。

这可能意味着您已经打开和(EXCLUSIVE?)事务,还没有提交数据。是你的程序或者其他进程留下了-journal吗?

重新启动sqlite进程将查看日志文件,清除任何未提交的操作并删除-journal文件。

如果一个进程在SQLite DB上有一个锁,并且崩溃了,那么这个DB将永远处于锁定状态。这就是问题所在。并不是其他进程有锁。

我在Mac OS X 10.5.7上从终端会话运行Python脚本时遇到了同样的问题。尽管我已经停止了脚本,并且终端窗口位于命令提示符处,但它在下次运行时仍然会给出这个错误。解决方案是关闭终端窗口,然后再次打开它。我觉得没道理,但奏效了。

删除-journal文件听起来是个糟糕的主意。它允许sqlite在崩溃后将数据库回滚到一致的状态。如果在数据库处于不一致状态时删除它,则会留下一个损坏的数据库。引用sqlite网站中的一页:

如果确实发生了崩溃或断电,并且磁盘上保留了热日志,则原始数据库文件和热日志必须保留在磁盘上,保留它们的原始名称,直到数据库文件被另一个SQLite进程打开并回滚。[…]

我们怀疑SQLite恢复的常见故障模式是这样的:发生电源故障。恢复电源后,好心的用户或系统管理员开始查看磁盘是否损坏。他们看到他们的数据库文件名为“important.data”。他们可能对这个文件很熟悉。但在股灾之后,还有一种热门的日志叫做“important.data-journal”。然后,用户删除热日志,认为他们正在帮助清理系统。除了用户教育,我们没有办法防止这种情况发生。

回滚应该在下次打开数据库时自动进行,但是如果进程不能锁定数据库,那么回滚将失败。正如其他人所说,一个可能的原因是另一个进程当前打开了它。另一种可能是过期的NFS锁,如果数据库位于NFS卷上。在这种情况下,一个解决方案是用一个没有锁定在NFS服务器上的新副本替换数据库文件(mv database.db original.db;Cp original.db database.db)。请注意,sqlite常见问题解答建议在并发访问NFS卷上的数据库时要谨慎,因为NFS文件锁定的实现存在bug。

我无法解释为什么删除一个-journal文件会让你锁定一个数据库,而你以前不能。这是可复制的吗?

顺便说一下,-journal文件的存在并不一定意味着发生了崩溃或有要回滚的更改。Sqlite有几种不同的日志模式,在PERSIST或TRUNCATE模式下,它始终保留-journal文件,并更改内容以指示是否有要回滚的部分事务。

我的锁是由系统崩溃引起的,而不是由挂起进程引起的。为了解决这个问题,我简单地重命名了文件,然后将其复制回原来的名称和位置。

使用Linux shell将是:

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来设置超时。 我不知道在命令行中发生了什么,但在c# . net中,当我这样做时:"UPDATE table-name SET column-name = value;"我得到数据库被锁定,但这个"UPDATE table-name SET column-name = value"它很好。< / p >

看起来,当您添加;,sqlite将寻找进一步的命令。

在windows中,你可以尝试这个程序http://www.nirsoft.net/utils/opened_files_view.html来找出进程正在处理db文件。尝试关闭该程序解锁数据库

在Linux和macOS中,你可以做类似的事情,例如,如果你锁定的文件是development.db:

$ fuser development.db

这个命令将显示哪个进程正在锁定该文件:

> development.db: 5430

只需终止进程……

杀死-9 5430

...您的数据库将被解锁。

应该是数据库的内部问题 对我来说,这是在尝试用“SQLite管理器”浏览数据库后表现出来的 所以,如果你找不到另一个连接到数据库的进程,你也无法修复它, 试试这个激进的解决方案:

  1. 提供导出您的表(您可以在Firefox上使用“SQLite管理器”)
  2. 如果迁移改变了数据库方案,请删除上次失败的迁移
  3. 重命名“数据库”。sqlite”文件
  4. 执行“rake db:migrate”创建一个新的工作数据库
  5. 提供给正确的权限数据库表的导入
  6. 导入备份的表
  7. 编写新的迁移
  8. 用"rake db:migrate"执行它

我刚才在远程服务器上使用存储在NFS挂载上的SQLite数据库时遇到了这个问题。当我使用的远程shell会话在数据库打开时崩溃后,SQLite无法获得锁。

上面建议的恢复方法对我不起作用(包括先移动然后再复制数据库的想法)。但是在将其复制到非nfs系统后,数据库变得可用,数据似乎没有丢失。

lsof命令在我的Linux环境中帮助我找出一个进程挂起保持文件打开

.终止进程,问题解决

我导致我的sqlite数据库在写入过程中崩溃一个应用程序而被锁定。以下是我如何修复它:

echo ".dump" | sqlite old.db | sqlite new.db

取自:http://random.kakaopor.hu/how-to-repair-an-sqlite-database

我刚刚有同样的错误。 5分钟后,我发现我没有关闭一个shell巫婆正在使用db。 请关闭它,然后重试;)

我也有同样的问题。显然,回滚函数似乎用与db文件相同但没有最近更改的日志覆盖了db文件。我已经在下面的代码中实现了这一点,从那时起它一直工作得很好,而之前我的代码会因为数据库保持锁定而陷入循环。

希望这能有所帮助

我的python代码

##############
#### Defs ####
##############
def conn_exec( connection , cursor , cmd_str ):
done        = False
try_count   = 0.0
while not done:
try:
cursor.execute( cmd_str )
done = True
except sqlite.IntegrityError:
# Ignore this error because it means the item already exists in the database
done = True
except Exception, error:
if try_count%60.0 == 0.0:       # print error every minute
print "\t" , "Error executing command" , cmd_str
print "Message:" , error


if try_count%120.0 == 0.0:      # if waited for 2 miutes, roll back
print "Forcing Unlock"
connection.rollback()


time.sleep(0.05)
try_count += 0.05




def conn_comit( connection ):
done        = False
try_count   = 0.0
while not done:
try:
connection.commit()
done = True
except sqlite.IntegrityError:
# Ignore this error because it means the item already exists in the database
done = True
except Exception, error:
if try_count%60.0 == 0.0:       # print error every minute
print "\t" , "Error executing command" , cmd_str
print "Message:" , error


if try_count%120.0 == 0.0:      # if waited for 2 miutes, roll back
print "Forcing Unlock"
connection.rollback()


time.sleep(0.05)
try_count += 0.05








##################
#### Run Code ####
##################
connection = sqlite.connect( db_path )
cursor = connection.cursor()
# Create tables if database does not exist
conn_exec( connection , cursor , '''CREATE TABLE IF NOT EXISTS fix (path TEXT PRIMARY KEY);''')
conn_exec( connection , cursor , '''CREATE TABLE IF NOT EXISTS tx (path TEXT PRIMARY KEY);''')
conn_exec( connection , cursor , '''CREATE TABLE IF NOT EXISTS completed (fix DATE, tx DATE);''')
conn_comit( connection )

我在应用程序中有这样的问题,从2个连接访问SQLite -一个是只读的,第二个用于写入和读取。看起来这个只读连接阻止了第二个连接的写入。最后,需要在使用后立即完成或至少重置准备好的语句。直到准备语句被打开,导致数据库写入被阻塞。

别忘了打电话:

sqlite_reset(xxx);

sqlite_finalize(xxx);

如果文件位于远程文件夹(如共享文件夹)中,则可能引发此错误。我将数据库更改为本地目录,它工作得很好。

获得此异常的一个常见原因是,当您试图执行写操作时,仍然为读操作保留资源。例如,如果你从一个表中选择,然后尝试更新你所选择的东西,而不是先关闭你的ResultSet。

如果你想删除一个“database is locked”错误,请按照以下步骤执行:

  1. 将数据库文件复制到其他位置。
  2. 用复制的数据库替换数据库。这将解除对访问数据库文件的所有进程的引用。

这个环节解决了问题。: 当Sqlite给出:数据库锁定错误 它解决了我的问题也许对你有用。

并且可以使用开始事务和结束事务来避免将来数据库被锁定。

我得到这个错误时,使用Delphi与LiteDAC组件。 事实证明,它只发生在运行我的应用程序从Delphi IDE,如果连接属性设置为True的SQLite连接组件(在这种情况下TLiteConnection)。< / p >

由于某种原因,数据库被锁定了。以下是我的解决方法。

  1. 我将sqlite文件下载到我的系统(FTP)
  2. 删除在线sqlite文件
  3. 将文件上传到主机提供商

现在可以正常工作了。

有些函数,比如INDEX'ing,可能会花费很长时间——而且在运行时它会锁定整个数据库。在这种情况下,它甚至可能不使用日志文件!

所以最好/唯一的方法来检查你的数据库是否被锁定,因为一个进程正在积极地写它(因此你应该让它独自呆着,直到它完成它的操作)是md5(或md5sum在某些系统)文件两次。 如果你得到一个不同的校验和,数据库正在被写入,你真的真的真的不想杀死-9这个进程,因为如果你这样做,你很容易得到一个损坏的表/数据库

我要重申一下,因为这很重要——解决方案不是找到锁程序并杀死它——而是找出数据库是否有一个很好的写锁,然后从那里开始。有时候正确的解决方法就是喝杯咖啡休息一下。

创建这种锁定但不被写入的情况的唯一方法是,如果你的程序运行BEGIN EXCLUSIVE,因为它想要做一些表更改或其他事情,然后不管出于什么原因,永远不会在后面发送END这个过程永远不会结束。在任何正确编写的代码中,都不太可能满足这三个条件,因此,当有人想要终止锁定过程时,100次中有99次锁定过程实际上是出于一个很好的原因锁定了您的数据库。除非真的需要,程序员通常不会添加BEGIN EXCLUSIVE条件,因为它会阻止并发并增加用户抱怨。SQLite本身只在真正需要时才添加它(比如索引时)。

最后,“锁定”状态在文件内部不存在,正如几个答案所述-它驻留在操作系统的内核中。运行BEGIN EXCLUSIVE的进程向操作系统请求对该文件加锁。即使你的专用进程崩溃了,你的操作系统也能判断出它是否应该维护文件锁!!不可能出现数据库被锁定,但没有进程主动锁定它的情况!! 当要查看哪个进程正在锁定文件时,通常使用lsof而不是fuser更好(这很好地说明了原因:https://unix.stackexchange.com/questions/94316/fuser-vs-lsof-to-check-files-in-use)。或者,如果你有DTrace (OSX),你可以在文件上使用iosnoop

我在多线程应用程序中也有“数据库被锁定”错误,这似乎是SQLITE_BUSY结果代码,我解决了这个问题,将sqlite3_busy_timeout设置为合适的长度,如30000。

(顺便说一句,对于一个7年前的问题,居然没有人发现这一点,这真奇怪!SQLite真的是一个奇特而神奇的项目…)

我添加了“Pooling=true”到连接字符串,它工作了。

一个老问题,有很多答案,这里是我最近遵循的步骤,阅读上面的答案,但在我的情况下,问题是由于cifs资源共享。这个病例以前没有报道过,希望能帮助到一些人。

  • 检查java代码中没有任何连接是打开的。
  • 使用lsof检查是否有其他进程正在使用您的SQLite db文件。
  • 检查正在运行的jvm进程的用户所有者对该文件具有r/w权限。
  • 尝试在连接打开时强制锁定模式

    final SQLiteConfig config = new SQLiteConfig();
    
    
    config.setReadOnly(false);
    
    
    config.setLockingMode(LockingMode.NORMAL);
    
    
    connection = DriverManager.getConnection(url, config.toProperties());
    

If your using your SQLite db file over a NFS shared folder, check this point of the SQLite faq, and review your mounting configuration options to make sure your avoiding locks, as described here:

//myserver /mymount cifs username=*****,password=*****,iocharset=utf8,sec=ntlm,file,nolock,file_mode=0700,dir_mode=0700,uid=0500,gid=0500 0 0

在我的例子中,我也得到了这个错误。

我已经检查了其他进程,可能是锁定数据库的原因,如(SQLite管理器,连接到我的数据库的其他程序)。但是没有其他程序连接到它,它只是同一个应用程序中另一个活动的SQLConnection,保持连接

在建立新SQLConnection新命令之前,尝试检查之前可能仍然连接的活跃SQLConnection(首先断开它)。

这是因为该数据库上正在运行其他一些查询。SQLite是一个同步执行查询的数据库。如果其他人正在使用该数据库,那么如果你执行查询或事务,它就会给出这个错误。

因此,停止正在使用特定数据库的进程,然后执行查询。

我在一个场景中得到了这个错误,与这里描述的略有不同。

SQLite数据库位于由3个服务器共享的NFS文件系统上。在其中2个服务器上,我能够成功地对数据库运行查询,在第三个服务器上,我认为我得到了“数据库已锁定”消息。

第三台机器的问题是/var上没有剩余空间了。每次我试图在位于这个文件系统的任何SQLite数据库中运行查询时,我都得到了“数据库被锁定”的消息,并且在日志上出现了这个错误:

Aug 8 10:33:38 server01 kernel: lockd: cannot monitor 172.22.84.87

还有这个:

Aug 8 10:33:38 server01 rpc。statd[7430]: Failed to insert: writing /var/lib/nfs/statd/sm/other.server.name.com: No space left on device .日志含义 Aug 8 10:33:38 server01 rpc。statd[7430]: STAT_FAIL to server01 for SM_MON of 172.22.84.87

太空的情况处理好之后,一切都恢复正常了。

如果你试图解锁Chrome数据库用SQLite查看,那么只需关闭Chrome。

窗户

%userprofile%\Local Settings\Application Data\Google\Chrome\User Data\Default\Web Data


or


%userprofile%\Local Settings\Application Data\Google\Chrome\User Data\Default\Chrome Web Data

Mac

~/Library/Application Support/Google/Chrome/Default/Web Data

在我编写的一个c# . net 4.6.1应用程序中,当它试图写入数据时,我也收到了sqlite锁,但在我的开发机器上的Visual Studio中运行该应用程序时却没有。相反,只有在远程Windows 10机器上安装并运行该应用程序时,才会出现这种情况。

最初我认为是文件系统权限,但事实证明是我使用Nuget在项目中安装的system . data . sqlite包驱动程序(v1.0.109.2)导致了这个问题。我删除了NuGet包,并在项目中手动引用了旧版本的驱动程序,一旦应用程序重新安装在远程机器上,锁定问题就神奇地消失了。只能认为是最新的驱动程序或Nuget包有bug。

我有工具“DB Browser for sqlite”;一边跑步一边工作。显然,这个工具也会锁上东西。 点击“Write Changes"或“Revert Changes"”时,锁已经消失,而另一个进程(一个React-Native脚本)不再给出这个错误

DB Browser for SQLite - Write Changes

根据我的经验,此错误是由以下原因引起的:您打开了多个连接。

例如:

  1. 1个或多个sqlitebrowser (GUI)
  2. 一个或多个电子线
  3. rails线程

我不确定SQLITE3如何处理多线程/请求的细节,但当我关闭sqlitebrowser和电子线程时,rails运行良好,不会再阻塞了。

我在谷歌Chrome浏览器中查看存储的密码时遇到了这个错误。

# ~/.config/google-chrome/Default
$ sqlite3 Login\ Data
SQLite version 3.35.5 2021-04-19 18:32:05
sqlite> .tables
Error: database is locked

如果你不是特别关心父进程,或者你不想停止当前正在使用数据库的chrome进程,只需将文件复制到其他地方。

$ cp Login\ Data ~/tmp/ld.sql
$ sqlite3 ~/tmp/ld.sql .tables
field_info              meta   sync_model_metadata
insecure_credentials    stats
logins                  sync_entities_metadata

这样做将允许您读取数据库的内容,而不打扰或停止主chrome进程。