【问题标题】:SQLAlchemy and explicit lockingSQLAlchemy 和显式锁定
【发布时间】:2013-01-09 07:55:42
【问题描述】:

我有多个进程可能会在数据库中插入重复的行。这些插入不会经常发生(每小时几次),因此它不是性能关键。

我在插入之前尝试了存在检查,如下所示:

#Assume we're inserting a camera object, that's a valid SQLAlchemy ORM object that inherits from declarative_base...
try:
  stmt = exists().where(Camera.id == camera_id)
  exists_result = session.query(Camera).with_lockmode("update").filter(stmt).first()

  if exists_result is None:
    session.add(Camera(...)) #Lots of parameters, just assume it works
    session.commit()
except IntegrityError as e:
  session.rollback()

我遇到的问题是exist() 检查没有锁定表,因此多个进程可能会尝试同时插入同一个对象。在这种情况下,一个进程插入成功,其他进程失败并出现 IntegrityError 异常。虽然这可行,但我觉得它并不“干净”。

我真的很想在进行exists() 检查之前锁定相机表。

【问题讨论】:

  • 为什么不简单地使用UNIQUE 约束,尝试插入新行并在失败时捕获IntegrityError?这种方法有什么“不干净”的地方?
  • 这就是我目前所做的,如发布的代码所示。它可以工作,但主键计数器每次都会递增。所以我最终得到了不连续的主键。如果可能的话,我想避免这种情况。

标签: python orm sqlalchemy


【解决方案1】:

也许你会感兴趣:

https://groups.google.com/forum/?fromgroups=#!topic/sqlalchemy/8WLhbsp2nls

您可以通过直接执行 SQL 来锁定表。我不确定在 Elixir 中是什么样子,但在普通 SA 中它会是这样的:

 conn = engine.connect()
 conn.execute("LOCK TABLES 指针写入")
 #用 conn 做事
 conn.execute("解锁表")

【讨论】:

  • 并没有真正的帮助,因为我已经在 exists() 查询中指定了 with_lockmode("update"),它应该已经锁定了表。
  • SELECT..FOR UPDATE 不会锁定整个表,只会锁定符合 SELECT 条件的行,这对于 INSERT 尚不存在的行是无用的。您需要使用“LOCK TABLE”之类的 SQL,但这取决于您使用的数据库。
  • 对不起,我的意思是这个答案:您可以通过直接执行 SQL 来锁定表。我不确定在 Elixir 中是什么样子,但在普通 SA 中它会是这样的: conn = engine.connect() conn.execute("LOCK TABLES Pointer WRITE") ... 用 conn conn 做一些事情。执行(“解锁表”)
  • @AlexVhr,OP 对您的链接的困惑是 SO 不鼓励仅链接答案的原因。当您引用链接时,请引用相关上下文,以便他们知道您在说什么。