python - 插入重复键错误后 SELECT 失败

标签 python mysql transactions sqlalchemy

我正在其默认隔离级别 (REPEATABLE-READ) 上运行 MySQL。

我的 python 代码使用 sqlalchemy 来管理事务,该事务可以(并且经常)在两个并发进程中同时运行。

c.begin();
# this SELECT establishes the transaction snapshot from which all other SELECTs will read data
c.execute("SELECT something FROM sometable;")
try:
  c.execute("INSERT INTO othertable (unique_key) VALUES (1)")
except sqlalchemy.exc.IntegrityError as e:
  code, msg = e.orig
  if code != 1062:
    raise
  # duplicate key: another transaction commited the above INSERT so I can't rely on LAST_INSERT_ID
  rows = c.execute("SELECT * FROM table WHERE unique_key=1")
  inserted_id = None
  for id, in rows:
    inserted_id = id
    break
  assert inserted_id is not None
else:
  inserted_id = c.last_insert_id()
c.commit()

确切的代码显然要复杂一些,有更多的查询,但问题的关键是该代码经常命中异常处理程序中的断言。

原因是这个事务经常在不同的进程中同时运行:重复键异常触发的 SELECT 失败,因为第一个 SELECT 建立的数据库快照不包含新插入的行(它在第一个 SELECT 之后和 INSERT 之前由另一个事务插入)。

现在,我知道我可以更改我的 SELECT 以使用 FOR UPDATE,这会在快照中打一个洞以读取插入的实际值。

但是,我不愿意这样做,因为这会迫使我把它撒在很多地方,而且我认为这是相对脆弱的。

是否有其他更强大、更标准、更简洁的方法来处理这些并发插入(它们是我必须处理的现实)。

最佳答案

添加人工更新似乎足以强制 mysql 更新其事务快照:

c.begin();
# this SELECT establishes the transaction snapshot from which all other SELECTs will read data
c.execute("SELECT something FROM sometable;")
try:
  c.execute("INSERT INTO othertable (unique_key) VALUES (1)")
except sqlalchemy.exc.IntegrityError as e:
  code, msg = e.orig
  if code != 1062:
    raise
  # duplicate key: another transaction commited the above INSERT so I can't rely on LAST_INSERT_ID
  # ARTIFICAL UPDATE TO FORCE MYSQL TO UPDATE ITS SNAPSHOT
  c.execute("UPDATE table SET value = something WHERE unique_key=1")
  rows = c.execute("SELECT * FROM table WHERE unique_key=1")
  inserted_id = None
  for id, in rows:
    inserted_id = id
    break
  assert inserted_id is not None
else:
  inserted_id = c.last_insert_id()
c.commit()

注意:

  1. MariaDB 10.2.9 似乎对此行上的任何 UPDATE 语句(包括不更改行值的值更新)都满意,以触发快照更新
  2. MySQL 5.6.28 会忽略快照更新,除非 UPDATE 以某种方式更改了行值

关于python - 插入重复键错误后 SELECT 失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47810893/

相关文章:

php - 无法连接到数据库。连接器返回编号 : Database sqlsrv_connect failed

mysql - 使用全文搜索无法返回精确匹配?

mysql - 如何同步表更新

sql-server - "Set Transaction Level"是否需要开始交易?

java - 使用 Realm 持久化对象(错误 : Changing Realm data can only be done from inside a transaction)

javascript - 返回 HttpResponse 时如何执行 JavaScript 函数?

python - 使用 anaconda 更新到 python 3.7

python - 是否可以将交互式 python 窗口链接到正在运行的 jupyter notebook 内核?

python - 从(网页)表格中有选择地选取文本

sql-server - 出现错误时退出并回滚脚本中的所有内容