SQL 并发事务忽略彼此的锁???死锁 [InnoDB,Python]
SQL Simultaneous transactions ignore each other's locks??? DEADLOCK [InnoDB, Python]
美好的一天!
我已经 运行 头晕目眩了。我的客户要求我重新调整 python 程序的用途以使用 MySQL 而不是 Microsoft 的 SQL 服务器。我在 SQL.
中找不到等效的解决方案
我似乎无法在某行上创建正确的更新锁。当两个相同的 t运行saction 同时执行时,尽管在序列化隔离级别打开了 t运行saction,但它们都读取该行,并且 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()
过程如下:
- Select 一份可能的工作,FOR UPDATE
- 如果没有作业,则回滚(释放锁),或者...
- ...更新该行使其无法重新select编辑,进行一些不相关的更改
- 提交
他们都各干各的,无视对方的锁和t运行saction
让我害怕的是它是 运行dom。它大致每隔 运行 发生一次。在隔离环境中尝试相同的查询时,有足够的延迟,我得到了我想要的确切结果。
一旦 SELECT ... FOR UPDATE 被 Alice 调用,Barry 无法读取行 ,并挂起直到 Alice 提交或回滚。我的现象需要在同一程序的两个实例之间精确地同时执行。
我尝试在第 4 行打印获取的行,它们 return 完全 同一行...我正在使用 MariaDB 10.1.30 和 InnoDB 引擎在 Ubuntu 服务器上,使用 Python 和 MySQLdb (mysqlclient) 模块进行通信。是玛丽亚吗?我认为与 MySQL.
相比,它可能是更好的选择
一个引发异常,因为它正在与另一个争夺资源(菜鸟太慢了!)
运行交易和锁定正在制作中
为了显示一个 FOR UPDATE 锁和一个正确的 t运行saction,我做了以下测试。我同时 运行 这个小 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 脚本就会挂起,无法 select 该行。十秒后,poke 脚本获取了锁,但是 两个节点再次执行!!!。如您所见,顶部的那个抱怨死锁,因为底部的那个已经在 t运行saction.
的其他地方插入了一行
我愿意接受其他更正确的 SQL 解决方案。也许我做错了。在 T-SQL 中,可以使用 OUTPUT 子句更新行和 return 修改后的行,就好像 SELECT 语句在 UPDATE 之后是 运行 一样。我唯一的解决办法是 SELECT 一行 FOR UPDATE,然后 运行 UPDATE。我还没有真正考虑过使用程序,将它从 MariaDB 上的 Python 和 运行 中拿走会更好吗?
如果有任何提示或建议,我将不胜感激。我对 SQL 没有那么多经验,但是离开 SQL 服务器的过程特别痛苦。由于我的客户希望使用 docker,我担心这可能不仅是一种不太可能的情况,而且是一种可能性,因为当产生极端负载时可能会同时创建 docker。
谢谢,祝你有美好的一天!
SELECT... FOR UPDATE
根据您的配置隔离不同级别的事务。您可以在此处找到更多信息 https://dev.mysql.com/doc/refman/5.5/en/innodb-transaction-isolation-levels.html#isolevel_repeatable-read。
但是您的代码中最重要的是您必须为不同的事务使用不同的会话。
如此处所述https://dev.mysql.com/doc/refman/5.7/en/lock-tables-and-transactions.html。如果您 运行 在事务完成之前同一会话中的另一个事务,它将被隐式提交,这就是导致代码中出现随机结果的原因。
您现在在代码中所做的与 运行在一个终端中进行两笔交易是一样的,这与 运行在 non-transaction 中进行所有操作并没有太大区别。
为了模拟不同的会话,您需要通过池化等方法在不同的连接中创建事务。
在交易的不同部分添加了一些time.sleep
语句后,我意识到问题与Alice和Barry无关同时执行或忽略彼此的锁。
没有sleep语句,速度太快了,看不清发生了什么。真正的问题是 Barry 在他的 SELECT... FOR UPDATE
中读取旧数据,即使在 Alice 的 COMMIT
更新了工作状态之后,让他继续使用相同的数据Alice 释放锁后立即工作。
由于这是一个完全不同的问题,我在此处重新发布了带有不同解释和更多相关代码示例的问题:
很抱歉这无法帮助您。我自己还没找到问题。
(这可能无法解决您的问题,但这是一个不适合评论的建议。)
一次完成所有更新:
UPDATE job SET
status = %s,
jobUUID = IFNULL(jobUUID, UUID()),
dateAdded = IFNULL(dateAdded, NOW())
WHERE jobID = %s
LIMIT 1
您可以使用 LAST_INSERT_ID(jobID)
获得 jobID
,从而避免 SELECT
.
美好的一天!
我已经 运行 头晕目眩了。我的客户要求我重新调整 python 程序的用途以使用 MySQL 而不是 Microsoft 的 SQL 服务器。我在 SQL.
中找不到等效的解决方案我似乎无法在某行上创建正确的更新锁。当两个相同的 t运行saction 同时执行时,尽管在序列化隔离级别打开了 t运行saction,但它们都读取该行,并且 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()
过程如下:
- Select 一份可能的工作,FOR UPDATE
- 如果没有作业,则回滚(释放锁),或者...
- ...更新该行使其无法重新select编辑,进行一些不相关的更改
- 提交
他们都各干各的,无视对方的锁和t运行saction
让我害怕的是它是 运行dom。它大致每隔 运行 发生一次。在隔离环境中尝试相同的查询时,有足够的延迟,我得到了我想要的确切结果。
一旦 SELECT ... FOR UPDATE 被 Alice 调用,Barry 无法读取行 ,并挂起直到 Alice 提交或回滚。我的现象需要在同一程序的两个实例之间精确地同时执行。
我尝试在第 4 行打印获取的行,它们 return 完全 同一行...我正在使用 MariaDB 10.1.30 和 InnoDB 引擎在 Ubuntu 服务器上,使用 Python 和 MySQLdb (mysqlclient) 模块进行通信。是玛丽亚吗?我认为与 MySQL.
相比,它可能是更好的选择运行交易和锁定正在制作中
为了显示一个 FOR UPDATE 锁和一个正确的 t运行saction,我做了以下测试。我同时 运行 这个小 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 脚本就会挂起,无法 select 该行。十秒后,poke 脚本获取了锁,但是 两个节点再次执行!!!。如您所见,顶部的那个抱怨死锁,因为底部的那个已经在 t运行saction.
的其他地方插入了一行我愿意接受其他更正确的 SQL 解决方案。也许我做错了。在 T-SQL 中,可以使用 OUTPUT 子句更新行和 return 修改后的行,就好像 SELECT 语句在 UPDATE 之后是 运行 一样。我唯一的解决办法是 SELECT 一行 FOR UPDATE,然后 运行 UPDATE。我还没有真正考虑过使用程序,将它从 MariaDB 上的 Python 和 运行 中拿走会更好吗?
如果有任何提示或建议,我将不胜感激。我对 SQL 没有那么多经验,但是离开 SQL 服务器的过程特别痛苦。由于我的客户希望使用 docker,我担心这可能不仅是一种不太可能的情况,而且是一种可能性,因为当产生极端负载时可能会同时创建 docker。
谢谢,祝你有美好的一天!
SELECT... FOR UPDATE
根据您的配置隔离不同级别的事务。您可以在此处找到更多信息 https://dev.mysql.com/doc/refman/5.5/en/innodb-transaction-isolation-levels.html#isolevel_repeatable-read。
但是您的代码中最重要的是您必须为不同的事务使用不同的会话。
如此处所述https://dev.mysql.com/doc/refman/5.7/en/lock-tables-and-transactions.html。如果您 运行 在事务完成之前同一会话中的另一个事务,它将被隐式提交,这就是导致代码中出现随机结果的原因。
您现在在代码中所做的与 运行在一个终端中进行两笔交易是一样的,这与 运行在 non-transaction 中进行所有操作并没有太大区别。
为了模拟不同的会话,您需要通过池化等方法在不同的连接中创建事务。
在交易的不同部分添加了一些time.sleep
语句后,我意识到问题与Alice和Barry无关同时执行或忽略彼此的锁。
没有sleep语句,速度太快了,看不清发生了什么。真正的问题是 Barry 在他的 SELECT... FOR UPDATE
中读取旧数据,即使在 Alice 的 COMMIT
更新了工作状态之后,让他继续使用相同的数据Alice 释放锁后立即工作。
由于这是一个完全不同的问题,我在此处重新发布了带有不同解释和更多相关代码示例的问题:
很抱歉这无法帮助您。我自己还没找到问题。
(这可能无法解决您的问题,但这是一个不适合评论的建议。)
一次完成所有更新:
UPDATE job SET
status = %s,
jobUUID = IFNULL(jobUUID, UUID()),
dateAdded = IFNULL(dateAdded, NOW())
WHERE jobID = %s
LIMIT 1
您可以使用 LAST_INSERT_ID(jobID)
获得 jobID
,从而避免 SELECT
.