如何在sqlalchemy中强制执行sqliteselect以更新事务行为

xghobddn  于 2023-11-21  发布在  SQLite
关注(0)|答案(3)|浏览(220)

昨天我在处理一些sqlalchemy的东西,需要一个“select ... for update”的概念来避免竞争条件。在InnoDB和Postgres上,将.with_lockmode('update')添加到查询中是一种享受,但对于sqlite,我最终不得不偷偷地添加一个

if session.bind.name == 'sqlite':
    session.execute('begin immediate transaction')

字符串
在选择之前。
这似乎是目前的工作,但感觉像作弊。有没有更好的方法来做到这一点?

20jt8wwn

20jt8wwn1#

不支持SELECT... FOR UPDATE OF...。这是可以理解的,因为SQLite的行锁定机制是多余的,因为在更新任何数据库时都会锁定整个数据库。但是,如果SQLite的未来版本出于SQL可互换性的原因支持它,那就太好了。唯一需要的功能是确保“保留”如果数据库中还没有锁,则会对数据库加锁。
来源:https://www2.sqlite.org/cvstrac/wiki?p=UnsupportedSql
[编辑]也看到https://sqlite.org/isolation.html感谢@michauwilliam。
我认为你必须同步访问整个数据库。正常的同步机制也应该适用于这里文件锁,进程同步等

2lpgd968

2lpgd9682#

我认为SELECT FOR UPDATE与SQLite相关。在我开始写之前没有办法锁定数据库。到那时已经太晚了。下面是场景:
我有两台服务器和一个数据库队列表。每台服务器都在寻找工作,当它拾取一个作业时,它会用“I got it”更新队列表,这样另一台服务器就不会拾取同样的工作。我需要在队列中保留记录,以防恢复。
服务器1读取第一个无人认领的项目,并将其保存在内存中。服务器2读取相同的记录,现在也将其保存在内存中。然后服务器1更新记录,锁定数据库,更新,然后解锁。然后服务器2锁定数据库,更新,然后解锁。结果是两个服务器现在都在处理同一个作业。该表显示服务器2保存该项目,而服务器1的更新丢失。
我通过创建一个锁数据库表解决了这个问题。服务器1开始一个事务,写入锁表,锁表锁定数据库进行写入。服务器2现在试图开始一个事务并写入锁表,但被阻止。服务器1现在读取第一个队列记录,然后用“我得到了”代码更新它。然后删除它刚刚写入锁表的记录,提交并释放锁。现在服务器2能够开始它的事务,写入锁表,读取第二个队列记录,用它的“我得到了它”代码更新它,删除它的锁记录,提交,数据库可供下一个服务器查找工作。

h7wcgrx3

h7wcgrx33#

您可以使用条件更新来解决这个问题,因为更新调用会返回受影响的行数。

row := "SELECT * from queue WHERE status = 'unclaimed' LIMIT 1"
  res := "UPDATE queue SET status = 'claimed' where id = ? AND status = 'unclaimed'"
  if (res.affectedRowCount == 0) // try again in a loop...

字符串
附加条件status = 'unclaimed'确保只有第一个服务器会成功地将该行标记为已声明。第二次更新将报告0个受影响的行,因为该行不再是未声明的。

相关问题