SQLite WAL 模式和具有副作用的读-修改-写操作

问题描述 投票:0回答:2

我是 SQLite 新手。

假设表中的一行有一列

status
正在保存状态机。此类行上的典型操作是读取当前状态,并根据该状态设置下一个状态,该代码有一些副作用。

在交易中:

val state = db.execute("SELECT state FROM ...")
if (state == "READY_TO_SEND") {
    .... do some side-effecting stuff, like sending an email ...
    db.execute("UPDATE ... SET state = "MAIL_SENT")
}

在 Postgres 中,在这种情况下,我可以使用“SELECT ... FOR UPDATE”悲观地锁定行以进行同步。

对于启用了 WAL 模式的 SQLite,如果我理解正确的话,当前状态的读取可能会由两个事务同时发生。两个线程都会产生副作用。然后两个事务都想升级为写入。其中一个会成功升级,另一个则不会,但现在同步副作用已经太晚了。

我对SQLite隔离机制的理解正确吗?

在这种情况下你会如何进行同步?

谢谢!

sqlite synchronization
2个回答
1
投票

我相信在你的例子中不会有问题。

如果两个读取都是并发的并且读取相同的行,则两者都希望更新并将状态从“READY_TO_SEND”设置为“MAIL_SENT”。即使更新同时运行,并发性也会受到干扰,因为一次只能运行 1 个编写器。因此,这两个更新将按顺序应用(即使只需要 1),然后最终结果将是相同的。

也许更大的问题/副作用可能是如果更新位置

UPDATE .... SET state = 'MAIL_SENT' WHERE state = 'READY_TO_SEND';
和更新结果(更新的行数)然后用于指示电子邮件是否已发送。第二个可能是 0,因为 WHERE 子句会抑制更新。因此,更新的结果可能会导致表明电子邮件在已发送时尚未发送。

  • 但是,这将是一个设计问题,因为显然它们是单独的电子邮件。

0
投票

来自 https://www.sqlite.org/lang_transaction.html

读事务仅用于读取。写事务允许读和写。读事务由 SELECT 语句启动,写事务由 CREATE、DELETE、DROP、INSERT 或 UPDATE 等语句(统称为“写语句”)启动。如果在读事务处于活动状态时发生写语句,则如果可能,读事务将升级为写事务。如果其他数据库连接已经修改了数据库或已经在修改数据库的过程中,则无法升级到写入事务,并且写入语句将失败并显示 SQLITE_BUSY。

我认为这意味着:

  • 读取两个交易是可以的。
  • 如果一个事务向数据库写入任何内容,而另一个事务仅读取,这也可以,尽管读者可能会读取过时的值。
  • 如果一个事务向数据库写入任何内容,然后另一个事务随后尝试成为写入者,则该事务将失败。
© www.soinside.com 2019 - 2024. All rights reserved.