美好的一天,
这是我在SQL Simultaneous transactions ignore each other's locks??? DEADLOCK [InnoDB, Python]的旧帖子的更新,后来我意识到这个问题与我认为问题无关。我试图为客户端创建基于T-SQL的脚本的MySQL等效。
我有两个相同的脚本, Alice 和 Barry 并发运行。他们的目标是
SELECT * FROM job WHERE status = 0 LIMIT 1 FOR UPDATE
UPDATE job SET status = 1
其中jobID匹配,做其他一些事情,然后...... COMMIT
更改,然后继续工作我遇到的问题是 Alice 进行锁定,读取作业,UPDATES
将其置于状态1,但一旦提交更改, Barry 占用锁并读取状态0,原始状态......甚至Alice有时会在她COMMIT
之后看到状态0
这是我的python脚本中的一小部分,但它应该足以理解该过程:
connection = MySQLdb.connect(host=..., user=..., [...])
cursor = connection.cursor(MySQLdb.cursors.DictCursor)
[...]
execute("START TRANSACTION")
execute("SELECT * FROM job WHERE status = %s LIMIT 1 FOR UPDATE", 0)
job_data = cursor.fetchone()
# debug("Made lock")
if not job_data:
connection.commit()
# debug("No rows")
else:
# debug("Locked row with status "+str(job_data['status']))
execute("SELECT status FROM job")
# debug("Double checked status is "+str(cursor.fetchone()))
execute("UPDATE job SET status = %s WHERE jobID = %s", 1, job_data['jobID'])
time.sleep(5)
execute("SELECT status FROM job")
# debug("Status before commit "+str(cursor.fetchone()))
connection.commit()
# debug('Committed')
execute("SELECT status FROM job")
# debug("Status after commit "+str(cursor.fetchone()))
原始脚本的这个臃肿版本充满了调试方法,试图了解正在发生的事情,time.sleep
能够跟上正在发生的事情。我已经在这个摘录中注释掉了调试功能,以便更容易阅读实际发生的事情
这些是来自 Alice 和 Barry 的输出:
爱丽丝
41,351161: Made lock
41,351161: Locked row with status 0
41,351161: Double checked status is {'status': 0}
46,352156: Status before commit {'status': 1}
46,370601: Committed
46,370601: Status after commit {'status': 1} (Sometimes Alice sees 0 here)
百里
46,352682: Made lock
46,353184: No rows
48,365044: Made lock
48,365044: Locked row with status 0
48,365044: Double checked status is {'status': 0}
53,365062: Status before commit {'status': 1}
53,386910: Committed
53,388846: Status after commit {'status': 1}
输出开头的数字是时间戳,为SECONDS,MICROSECONDS
。
Alice 持有锁定五秒钟,只要她COMMITS
, Barry 就会锁定。但是,Barry看到状态为0(测试期间只有一行)。最终他们都认为他们可以处理这项工作。
我不确定为什么Barry在提交后读取状态为0。它回滚了吗?从他打电话给他SELECT
时,他是否正在阅读旧价值的缓存?不同的隔离级别是否有帮助(似乎没有)?
当我在一个封闭的环境中执行此代码时,没有程序的其余部分,它就可以了! Barry 一旦获得锁定就会报告没有行。我不明白它会有什么不同?我猜这意味着脚本中的其他地方出了问题。但它可能是什么?这是一个相当孤立的交易。在此代码之前调用COMMIT
并没有改变任何内容,我想也许以前的交易没有正确关闭。
Alice 和 Barry 是测试时访问数据库的唯一进程(除了phpmyadmin)。
我正在运行MariaDB
的InnoDB引擎,MySQLdb
(mysqlclient)作为Python的连接器。据我所知,AUTOCOMMIT
已关闭。脚本很长,这就是为什么我只发了几行。我将在接下来的几天内尝试将其分开以尝试隔离问题,或者创建一个小的示例脚本。我似乎无法找到如何让MariaDB打印出更改和锁定日志,但如果我这样做,我将更新此帖子。
任何想法,建议或评论都会令人惊叹。
祝你有美好的一天!
答案 0 :(得分:0)
在遵循 Solarflare 关于逐位切割程序的建议之后,我最终发现问题导致代码的远程部分,其运行没有错误,但完全没有从T-SQL迁移到SQL后不同。
我起初认为SQL服务器的问题,但它只是一个讨厌的bug。我出于某种原因现在遇到了可怕的僵局,还有另一种乐趣。
感谢你的评论以及你花时间解释可能出错的时间,我很抱歉这只是一个个人愚蠢的问题
祝你有美好的一天!