在使用DBeaver操作SQLite时,提交事务没有效果
我在使用 SQLite 与 DBeaver。最近我接到一个大学作业,要求展示数据库的 锁定机制。于是我决定用DBeaver来做。
我可以用 MySQL 解决得很好,但一旦使用 SQLite,问题就会非常严重。
因为我要展示 锁定机制,我使用了两种不同的 SQL scripts/windows 来更好地理解它。然后我在 Window/SQL Script1: 里运行了以下代码
START TRANSACTION;
SELECT * FROM Account WHERE account_id = 1 FOR UPDATE;
UPDATE Account SET balance = balance - 2000 WHERE account_id = 1;
COMMIT;
然后我在 Windows/SQL Script2: 里运行了以下代码
UPDATE Account SET balance = balance + 1000 WHERE account_id = 1;
如何运行这段代码:
- 运行START部分
- 运行FOR UPDATE部分
- 运行UPDATE部分
- 运行第二个窗口的UPDATE部分(它给了我
Executing query.........) - 从窗口1 提交
用MySQL这样做时,效果正常,得到的结果也正确。
但每次在使用 SQLite,搭配下列代码
BEGIN IMMEDIATE;
UPDATE Accounts
SET balance = balance - 2000
WHERE account_id = 1;
COMMIT;
时,它会给出各种类型的错误,
SQL Error [1]: [SQLITE_ERROR] SQL error or missing database (cannot start a transaction within a transaction)
有时会出现如下这类错误:
SQL Error [1]: [SQLITE_ERROR] SQL error or missing database (cannot start a transaction within a transaction)
有谁能帮我找出在 SQLITE 中如何解决吗?
解决方案
我在Alma Linux 9.8上使用SQLite 3.34。
我无法解释DBeaver的行为,但我建议先用命令行测试场景,以确保实际使用了哪些语句。
如果我尝试用两个BEGIN IMMEDIATE语句来运行该场景:
在会话1 得到
sqlite> begin immediate;
sqlite> update account set balance = balance + 1000 where account_id = 1;
sqlite>
在会话2 得到
sqlite> begin immediate;
Error: database is locked
sqlite>
如果我尝试用BEGIN语句来运行场景:
在会话1 得到
sqlite> begin;
sqlite> update account set balance = balance + 1000 where account_id = 1;
sqlite>
在会话2 得到
sqlite> begin;
sqlite> update account set balance = balance + 1000 where account_id = 1;
Error: database is locked
sqlite>
这是预期之中的,因为SQLite的锁定是在数据库级别进行的,而不是按行级别进行的,具体请参见
https://sqlite.org/lockingv3.html
我不建议用SQLite来测试数据库锁定,因为 https://sqlite.org/whentouse.html 的说法是
If many threads and/or processes need to write the database at the same instant (and they cannot queue up and take turns) then it is best to select a database engine that supports that capability, which always means a client/server database engine.
SQLite only supports one writer at a time per database file. But in most cases, a write transaction only takes milliseconds and so multiple writers can simply take turns. SQLite will handle more write concurrency than many people suspect. Nevertheless, client/server database systems, because they have a long-running server process at hand to coordinate access, can usually handle far more write concurrency than SQLite ever will.
MySQL或 PostgreSQL被设计为多用户数据库,像所有客户端-服务器数据库引擎一样:在测试数据库锁定时,应使用客户端-服务器数据库。