【问题标题】:Get updated MySQL table entries in python without closing connection在不关闭连接的情况下在 python 中获取更新的 MySQL 表条目
【发布时间】:2023-03-26 05:14:01
【问题描述】:

我有 2 个 python 程序正在运行。 Script1 定期将条目写入表,而 Script2 从同一个 MySQL 表中读取。两者同时运行。 Script2 必须获取表的最后一个(最新添加的)条目。

现在,问题是 Script1 正在向表中完美添加条目,但 Script2 每次都无法读取最新条目。只有当我阅读后关闭连接时它才会读取最新条目,并在我想再次阅读时重新打开它。

这是唯一可以采取的方法吗?有没有一种方法可以在不需要每次都关闭和打开连接的情况下获取更新的值?访问不断更新的数据库时,程序员遵循的最佳做法是什么?

更详细的:

下面的代码工作正常,但无法显示更新的值。它第一次成功显示最后一个条目,但接下来的几次 readComm() 被调用,同样的条目再次显示,尽管表格已经更新。

import MySQLdb
import time

db = MySQLdb.connect("localhost", "root", "abc", "abc")
cursor=db.cursor()

def readComm():
    sql = "SELECT * FROM my_table ORDER BY id DESC LIMIT 1;"
    try:
        cursor.execute(sql)
        # Fetch all the rows in a list of lists.
        results = cursor.fetchall()
        print '~~~~', results
    except:
        print "Error! Unable to fetch data" 
    return
for i in range(5):
    readComm()
    time.sleep(10)

如果我修改代码,则代码会显示更新的值,以便在我每次进入和退出 readComm() 时分别打开和关闭 DB。

【问题讨论】:

    标签: python mysql sql


    【解决方案1】:

    正如@DanielRoseman 指出的那样,您正在事务中写入数据,这旨在让您在发生一组更改之前出现问题时回滚一组更改。因此,在事务期间所做的更改在它们发生的会话之外是不可见的,直到事务由the COMMIT statement 永久化和最终化。即使您只是在 SELECT 语句中读取数据,您也会开始一个事务 - 因此您的“读取”脚本每次都会查看第一个 SELECT 时的数据库状态。

    最明显的解决方案是使用与Connection 对象关联的显式commit() 方法。然而,一个更优雅的解决方案利用了从PEP 343 采用的 MySQLdb Connection 对象的implementation of the context manager protocol

    def __enter__(self):
       if self.get_autocommit():
           self.query("BEGIN")
       return self.cursor()
    
    def __exit__(self, exc, value, tb):
       if exc:
           self.rollback()
       else:
           self.commit()
    

    这告诉您Connection 对象如何与with 语句结合使用。所以,如果你以这种方式使用你的连接对象db

    with db as x:
        # indented code block here
    

    然后发生以下情况:

    • x(或您选择的名称)绑定到db.__enter__() 返回的Cursor 对象*
    • 如果在缩进块中抛出异常,db 将调用自己的rollback() 方法
    • 否则,db 将在离开缩进块时调用自己的 commit() 方法

    换句话说,该模块的设计使您可以通过将应该是单个事务事务的每组语句放入with 块中来轻松实现事务。由于您在此处显示的代码仅从您的表中读取数据,因此 other 脚本 - 对数据库进行更改的脚本 - 最重要的是修改,无论您决定使用 with或显式调用commit()

    最简单的事情...

    ...对于“阅读”脚本将是通过在打开连接后调用db.autocommit(True) 来启用自动提交模式。 The Python database API 指定“如果数据库支持自动提交功能,则必须首先关闭它”,但如果您不担心并发问题,则没有理由不能打开它,这应该是你的“阅读”脚本。

    事实上,如果这两个脚本是服务器上唯一的事情,并且您不需要其他脚本中的事务,最简单的做法是在两个脚本中打开自动提交并忘记它.但是,如果您忘记了在这里所做的事情并去编写其他需要执行并发事务的脚本,那么这可能会在以后咬您一口。

    *注意db.__enter__() 为您创建的游标不是db.__exit__() 关闭的。在这种情况下,MySQLdb.Cursor 实际上只是a Python object that emulates a cursor;它不会占用任何额外的服务器资源,而且您通常不必担心关闭它。事实上,在with 块退出后,只要它的父对象Connection 保持打开状态,您就可以继续在with 语句中以任何名称引用Cursor 对象。 (当然,除非您明确地close() 光标或将其名称绑定到另一个对象。)

    【讨论】:

      【解决方案2】:

      这仅仅是因为事务内部的读隔离。在每个循环之后执行db.commit()

      【讨论】:

      • 如果 OP 的其他脚本没有定期提交其事务,则在此脚本中提交不一定有帮助。更糟糕的是,如果 OP 的其他脚本以某种 OP 没有意识到/理解的方式提交,给人的印象是 db.commit() 跨会话执行...
      • @AirThomas 但问题不在于其他脚本没有提交 - 我们可以从删除和重新打开连接 确实 显示新数据的事实中看出.正如我所说,问题在于 OP 在一个事务中 read 数据,而 InnoDB 的默认隔离级别是“REPEATABLE READ”,所以他只会看到事务时存在的数据已经开始了。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-12-13
      • 1970-01-01
      • 1970-01-01
      • 2018-03-08
      • 1970-01-01
      • 2019-02-16
      • 2012-08-19
      相关资源
      最近更新 更多