代码之家  ›  专栏  ›  技术社区  ›  Marcus Cemes

选择。。。用于更新在提交后选择旧数据

  •  0
  • Marcus Cemes  · 技术社区  · 6 年前

    很好的一天,

    这是对我的旧帖子的更新 SQL Simultaneous transactions ignore each other's locks??? DEADLOCK [InnoDB, Python] ,因为我后来意识到这个问题与我认为的问题无关。我正在尝试为客户端创建与基于T-SQL的脚本等效的MySQL。

    我有两个完全相同的脚本, 爱丽丝 巴里 同时运行。他们的目标是

    1. SELECT * FROM job WHERE status = 0 LIMIT 1 FOR UPDATE
    2. UPDATE job SET status = 1 在jobIDs匹配的地方,做一些其他的事情,然后。。。
    3. COMMIT 更改,然后继续作业

    我的问题是 爱丽丝 锁定,读取作业, UPDATES 状态1,但一旦她做出改变, 巴里 获取锁并读取状态0,即原始状态。。。甚至Alice有时也会在她之后看到状态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 能够跟上正在发生的事情。我已经注释掉了本文摘录中的调试函数,以便于阅读实际发生的情况。
    这些是 爱丽丝 巴里 :

    爱丽丝

    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 .

    爱丽丝 锁上5秒钟,一旦她 COMMITS , 巴里 拿走锁。然而,Barry看到状态为0(测试期间只有一行)。最终,双方都认为自己可以处理好这份工作。

    我不知道为什么Barry在提交后读取到状态为0。是否已回滚?他是否正在读取旧值的缓存,从他调用 SELECT ? 不同的隔离级别会有帮助吗(似乎没有)?

    当我在封闭环境中执行此代码时, 没有剩下的程序 ,很有效!!! 巴里 当他最终拿到锁时,报告没有行。我不明白这有什么不同?我猜这意味着脚本的其他地方出了问题。但那会是什么呢?这是一个相当孤立的事务。使命感 提交 就在这段代码没有改变任何东西之前,我想可能之前的一个事务没有正确关闭。
    爱丽丝 巴里 是测试时访问数据库的唯一进程(除了phpmyadmin)。

    我在跑步 MariaDB 的InnoDB引擎 MySQLdb (mysqlclient)作为Python的连接器。 AUTOCOMMIT 据我所知,他已经离开了。剧本很长,所以我只发了几行。在接下来的几天里,我将尝试将其分割开来,以尝试隔离问题,或者创建一个小的示例脚本。我似乎不知道如何让MariaDB打印出更改和锁定的日志,但如果我找到了,我会更新这篇文章。

    任何想法、建议或评论都会令人惊叹。

    祝你有美好的一天!

    1 回复  |  直到 6 年前
        1
  •  0
  •   Marcus Cemes    6 年前

    跟踪之后 日光灯 的建议,我最终发现了导致代码远程部分的问题,该部分功能正常,但在从T-SQL迁移到SQL之后完全不同。

    起初,我认为SQL server的问题在于此,但这只是一个令人讨厌的bug。出于某种原因,我现在陷入了可怕的僵局,这是另一种值得关注的乐趣。

    感谢您的评论和花时间解释可能的错误,很抱歉这只是一个个人愚蠢的问题

    祝你有美好的一天!