尽管受互斥锁保护,SQLite 更新失败并显示 'database is locked'
SQLite updates failing with 'database is locked' despite being mutex-protected
问题:我有多个进程访问同一个 SQLite 数据库,所有进程都试图更新同一行、同一 table 中的相同 3 列。 UPDATE
受 sqlite3_mutex
保护(UPDATE
被 sqlite3_mutex_enter
和 sqlite3_mutex_leave
括起来)。这是行不通的,其中一个更新失败并显示 'database is locked'.
是很常见的
更详细地说,每个进程都是由 Apache 创建的,它响应来自客户端的多个(大约十几个)异步 Ajax 请求。每个请求都从记录当前时间开始,用于会话管理,因此我在“相同”时间执行 UPDATE
时得到 12 个进程全部 运行 (日志都显示它们 运行 在同一秒,具有唯一的 PID,但我没有检查更精确的时间)。
数据库由 sqlite3_open
使用默认选项打开,因此被序列化。
那么,显而易见的问题 - 为什么这行不通?我不认为互斥量的使用是错误的,那么 'busy' 期间是否可以超出互斥量保持时间?我可以做的一件事是用 sqlite3_open_v2
打开数据库,然后改为多线程而不是序列化(因为每个进程都有一个唯一的数据库连接)——这会有帮助吗?
我添加了以下代码的简化版本。正在执行的语句是 UPDATE users SET Limit1=%lx, Limit2=%lx, Limit3=%lx WHERE UserName='%s'
,其中 %
字段在运行时以类似 C 的方式填充。
谢谢。
// sqlite3 version 3.26.0, Linux
sqlite3_mutex *mutex = sqlite3_db_mutex(con);
assert(mutex);
sqlite3_mutex_enter(mutex);
rc = sqlite3_exec(con, statement, 0, 0, 0);
if(rc != SQLITE_OK)
... save error message from sqlite3_errmsg
else
... save the changed row count from sqlite3_changes
sqlite3_mutex_leave(mutex);
294
在 windows 中你可以尝试这个程序 http://www.nirsoft.net/utils/opened_files_view.html 来找出正在处理 db 文件的进程。尝试关闭解锁数据库的程序
在 Linux 和 macOS 中你可以做类似的事情,例如,如果你的锁定文件是 development.db:
$ fuser development.db
此命令将显示锁定文件的进程:
> development.db: 5430
杀死进程...
kill -9 5430
...您的数据库将被解锁。
您提到使用多个进程,并且正在使用专为“线程同步”设计的SQLite mutexes。
重要的是,在 Windows 上,它们是通过临界区实现的,而在 Unix 平台上,它们是通过未设置 PTHREAD_PROCESS_SHARED 标志的 pthread 互斥体实现的。在这两种情况下,它们只在一个进程的范围内工作,每个进程都有自己的一组互斥体供 SQLite 使用。
如果你想阻止多个进程访问同一个数据库,你需要使用你自己命名的互斥锁或其他进程间锁定机制。
问题:我有多个进程访问同一个 SQLite 数据库,所有进程都试图更新同一行、同一 table 中的相同 3 列。 UPDATE
受 sqlite3_mutex
保护(UPDATE
被 sqlite3_mutex_enter
和 sqlite3_mutex_leave
括起来)。这是行不通的,其中一个更新失败并显示 'database is locked'.
更详细地说,每个进程都是由 Apache 创建的,它响应来自客户端的多个(大约十几个)异步 Ajax 请求。每个请求都从记录当前时间开始,用于会话管理,因此我在“相同”时间执行 UPDATE
时得到 12 个进程全部 运行 (日志都显示它们 运行 在同一秒,具有唯一的 PID,但我没有检查更精确的时间)。
数据库由 sqlite3_open
使用默认选项打开,因此被序列化。
那么,显而易见的问题 - 为什么这行不通?我不认为互斥量的使用是错误的,那么 'busy' 期间是否可以超出互斥量保持时间?我可以做的一件事是用 sqlite3_open_v2
打开数据库,然后改为多线程而不是序列化(因为每个进程都有一个唯一的数据库连接)——这会有帮助吗?
我添加了以下代码的简化版本。正在执行的语句是 UPDATE users SET Limit1=%lx, Limit2=%lx, Limit3=%lx WHERE UserName='%s'
,其中 %
字段在运行时以类似 C 的方式填充。
谢谢。
// sqlite3 version 3.26.0, Linux
sqlite3_mutex *mutex = sqlite3_db_mutex(con);
assert(mutex);
sqlite3_mutex_enter(mutex);
rc = sqlite3_exec(con, statement, 0, 0, 0);
if(rc != SQLITE_OK)
... save error message from sqlite3_errmsg
else
... save the changed row count from sqlite3_changes
sqlite3_mutex_leave(mutex);
294
在 windows 中你可以尝试这个程序 http://www.nirsoft.net/utils/opened_files_view.html 来找出正在处理 db 文件的进程。尝试关闭解锁数据库的程序
在 Linux 和 macOS 中你可以做类似的事情,例如,如果你的锁定文件是 development.db:
$ fuser development.db
此命令将显示锁定文件的进程:
> development.db: 5430
杀死进程...
kill -9 5430
...您的数据库将被解锁。
您提到使用多个进程,并且正在使用专为“线程同步”设计的SQLite mutexes。
重要的是,在 Windows 上,它们是通过临界区实现的,而在 Unix 平台上,它们是通过未设置 PTHREAD_PROCESS_SHARED 标志的 pthread 互斥体实现的。在这两种情况下,它们只在一个进程的范围内工作,每个进程都有自己的一组互斥体供 SQLite 使用。
如果你想阻止多个进程访问同一个数据库,你需要使用你自己命名的互斥锁或其他进程间锁定机制。