【发布时间】:2018-08-04 17:56:20
【问题描述】:
美好的一天!
我遇到了麻烦。我的客户要求我重新利用 python 程序来使用 MySQL 而不是 Microsoft 的 SQL Server。我在 SQL 中找不到等效的解决方案。
我似乎无法在一行上创建正确的更新锁。当两个相同的事务同时执行时,尽管在序列化隔离级别打开事务,并且使用 SELECT ... FOR UPDATE,但它们都会读取该行。
也许我的代码会更好地解释它:
execute("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE")
execute("START TRANSACTION")
execute("SELECT * FROM job WHERE status = %s LIMIT 1 FOR UPDATE", jobStatus.imported)
job_data = cursor.fetchone()
if not job_data:
connection.rollback()
else:
execute("UPDATE job SET status = %s WHERE jobID = %s", jobStatus.ingesting, job_data['jobID']) # Update the job data
if job_data['jobUUID'] == None:
job_data['jobUUID'] = new_unused_uuid().bytes
execute("UPDATE job SET jobUUID = %s WHERE jobID = %s LIMIT 1", job_data['jobUUID'], job_data['jobID'])
if job_data['dateAdded'] == None:
job_data['dateAdded'] = datetime.datetime.now()
execute("UPDATE job SET dateAdded = %s WHERE jobID = %s LIMIT 1", job_data['dateAdded'], job_data['jobID'])
execute("INSERT INTO ingestJob (fk_jobUUID, fk_nodeUUID, status) VALUES (%s, %s, %s) ON DUPLICATE KEY UPDATE fk_nodeUUID = %s, status = %s", job_data['jobUUID'], unique_id.bytes, smallJobStatus.running, unique_id.bytes, smallJobStatus.running)
connection.commit()
流程如下:
- 使用 FOR UPDATE 选择一项可能的工作
- 如果没有作业,则回滚(释放锁),或者...
- ...更新行使其无法重新选择,进行一些不相关的更改
- 提交
他们都做自己的事,忽略彼此的锁和事务
让我害怕的是它是随机的。它大约每隔一次运行就会发生一次。在隔离环境中尝试相同的查询时,有足够的延迟,我得到了我想要的确切结果。
只要 Alice 调用了 SELECT ... FOR UPDATE,Barry 就无法读取该行,并挂起直到 Alice 提交或回滚。我的现象需要在同一程序的两个实例之间精确同时执行。
我尝试在第 4 行打印获取的行,它们返回 exact 同一行...我在 Ubuntu Server 上使用带有 InnoDB 引擎的 MariaDB 10.1.30,以及 Python 和 MySQLdb (mysqlclient) 通信模块。是玛丽亚吗?我认为与 MySQL 相比,它可能是更好的选择。
事务和锁正在生成
为了显示一个 FOR UPDATE 锁和一个正确的事务,我做了以下测试。我同时运行了这个小 poke 脚本,同时在主脚本提交之前添加了 time.sleep(10),以保持锁定至少 10 秒。
while True:
cursor.execute("SELECT * from job FOR UPDATE")
print('Selected')
time.sleep(1)
connection.rollback()
print('Released')
time.sleep(1)
一旦主脚本获得锁,小戳脚本就会挂起,无法选择行。十秒后,poke 脚本获得了锁,但是两个节点都执行了,再次!!!。如您所见,顶部的那个抱怨死锁,因为底部的已经在事务的其他地方插入了一行。
我愿意接受其他更正确的 SQL 解决方案。也许我做错了。在 T-SQL 中,可以使用 OUTPUT 子句更新一行并返回修改后的行,就像在 UPDATE 之后运行了一条 SELECT 语句一样。我唯一的解决方案是使用 FOR UPDATE 选择一行,然后运行 UPDATE。我还没有真正考虑过使用过程,将其从 Python 中取出并在 MariaDB 上原生运行会更好吗?
我非常感谢任何提示或建议。我对 SQL 没有那么丰富的经验,但离开 SQL Server 的过程尤其令人痛苦。由于我的客户希望使用 docker,我担心这可能不仅是一种不太可能的情况,而且是一种可能性,因为在产生极端负载时可能会同时创建 docker。
谢谢,祝你有美好的一天!
【问题讨论】:
标签: python sql transactions mariadb database-deadlocks