如何在 sqlalchemy 中强制执行 sqlite select 来更新事务行为

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

昨天我正在处理一些 sqlalchemy 的东西,这些东西需要一个“选择...更新”的概念来避免竞争条件。在查询中添加

.with_lockmode('update')
对 InnoDB 和 Postgres 来说是一种享受,但对于 sqlite 我最终不得不偷偷地添加一个

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

在进行选择之前。

目前看来这可行,但感觉像是作弊。有更好的方法吗?

python sqlite sqlalchemy
3个回答
12
投票

不支持选择...更新...。这是可以理解的 考虑到 SQLite 的机制,行锁定是多余的 因为更新任何部分时整个数据库都会被锁定。然而, 如果未来版本的 SQLite 支持 SQL 就好了 如果没有其他原因的话,就是可互换性。唯一的功能 需要确保在以下情况下在数据库上放置“RESERVED”锁: 还没有。

摘自 https://www2.sqlite.org/cvstrac/wiki?p=UnsupportedSql

[编辑]另请参阅https://sqlite.org/isolation.html谢谢@michauwilliam。

我认为你必须同步对整个数据库的访问。正常的同步机制也应该在这里应用文件锁、进程同步等


5
投票

我认为 SELECT FOR UPDATE 与 SQLite 相关。在我开始写入之前无法锁定数据库。到那时就太晚了。这是场景:

我有两台服务器和一张数据库队列表。每个服务器都在寻找工作,当它找到一个工作时,它会用“我找到了”来更新队列表,这样其他服务器就不会再找到相同的工作。我需要将记录保留在队列中如果康复的话。

服务器 1 读取第一个无人认领的项目并将其保存在内存中。服务器 2 读取相同的记录并且现在也将其存储在内存中。然后,服务器 1 更新记录,锁定数据库,更新,然后解锁。然后,服务器 2 锁定数据库、更新并解锁。结果是两台服务器现在都在执行相同的工作。该表显示服务器 2 有它,而服务器 1 更新丢失。

我通过创建锁定数据库表解决了这个问题。服务器 1 开始一个事务,写入锁定数据库以进行写入的锁定表。服务器 2 现在尝试开始事务并写入锁定表,但被阻止。服务器 1 现在读取第一个队列记录,然后使用“我明白了”代码更新它。然后删除刚刚写入锁表的记录,提交并释放锁。现在服务器 2 能够开始其事务,写入锁定表,读取第二条队列记录,用其“我明白了”代码更新它,删除其锁定记录,提交,并且数据库可供下一个寻找的服务器使用工作。


0
投票

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

  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 个受影响的行,因为该行不再无人认领。

© www.soinside.com 2019 - 2024. All rights reserved.