为啥需要重新连接数据库才能看到表数据的变化?

Posted

技术标签:

【中文标题】为啥需要重新连接数据库才能看到表数据的变化?【英文标题】:Why do I need to reconnect to the database to see changes in table data?为什么需要重新连接数据库才能看到表数据的变化? 【发布时间】:2015-06-23 05:04:56 【问题描述】:

我定期查询 mysql 表并检查同一行中的数据。

我使用 MySQLdb 来完成这项工作,每 15 秒查询同一个表和行。

实际上,行数据每3秒改变一次,但游标总是返回相同的值。

奇怪的是:我关闭MySQL连接并重新连接后,使用新游标执行相同的选择命令,返回新值。

我怀疑错误的代码是在注释之后开始的:

config = SafeConfigParser()
config.read("../test/settings_test.conf")

settings = 
settings["mysql_host"] = config.get("mysql","mysql_host")
settings["mysql_port"] = int(config.get("mysql","mysql_port"))
settings["mysql_user"] = config.get("mysql","mysql_user")
settings["mysql_password"] = config.get("mysql","mysql_password")
settings["mysql_charset"] = config.get("mysql","mysql_charset")

#suspected wrong code
conn = mysql_from_settings(settings)
cur = conn.cursor()
cur.execute('use database_a;')
cur.execute('select pages from database_a_monitor where id=1;')
result = cur.fetchone()[0]
print result
#during 15 second, I manually update the row and commit from mysql workbench
time.sleep(15)    

cur.execute('select pages from database_a_monitor where id=1;')
result = cur.fetchone()
print result
conn.close()

输出是:

94
94

如果我更改代码以关闭连接并重新连接,它会返回最新值而不是重复相同的值:

conn = mysql_from_settings(settings)
cur = conn.cursor()
cur.execute('use database_a;')
cur.execute('select pages from database_a_monitor where id=1;')
result = cur.fetchone()[0]
print result
conn.close()

time.sleep(15)
#during that period, I manually update the row and commit from mysql workbench

conn = mysql_from_settings(settings)
cur = conn.cursor()
cur.execute('use database_a;')
cur.execute('select pages from database_a_monitor where id=1;')
result = cur.fetchone()[0]
print result
conn.close() 

输出是:

94
104

为什么会有这种行为差异?

这里是mysql_from_settings的定义:

def mysql_from_settings(settings):
    try:
        host = settings.get('mysql_host')
        port = settings.get('mysql_port')
        user = settings.get('mysql_user')
        password = settings.get('mysql_password')
        charset = settings.get('mysql_charset')
        conn=MySQLdb.connect(host=host,user=user,passwd=password,port=port,\
               charset=charset)

        return conn
    except MySQLdb.Error,e:
        print "Mysql Error %d: %s" % (e.args[0], e.args[1])

【问题讨论】:

相关,可能重复:***.com/q/384228/2359271 【参考方案1】:

这几乎可以肯定是事务隔离的结果。由于您没有另外说明,我将假设您使用的是默认存储引擎 (InnoDB) 和隔离级别 (REPEATABLE READ):

可重复读取

InnoDB 的默认隔离级别。它可以防止任何被查询的行被其他人更改 事务,从而阻塞不可重复读取,但不会 幻影 读取。它使用适度严格的锁定策略,以便事务中的所有查询都能看到来自 相同的快照,即事务时的数据 开始了。

有关详细信息,请参阅 MySQL 文档中的 Consistent Nonlocking Reads。

简单来说,这意味着当您在事务中从表中SELECT 时,您从表中读取的值在事务期间不会改变;您将继续看到事务打开时表的状态,以及在同一事务中所做的任何更改

在您的情况下,每 3 秒更改一次是在其他会话和事务中进行的。为了“看到”这些更改,您需要离开在您发出第一个 SELECT 时开始的事务并开始一个新事务,然后它将“看到”表的新快照。

您可以在 SQL 中使用START TRANSACTION, COMMIT and ROLLBACK 或调用Connection.commit() and Connection.rollback() 显式管理事务。这里更好的方法可能是利用context managers;例如:

conn = mysql_from_settings(settings)
with conn as cur:
    cur.execute('use database_a;')
    cur.execute('select pages from database_a_monitor where id=1;')
    result = cur.fetchone()[0]
print result
#during 15 second, I manually update the row and commit from mysql workbench
time.sleep(15)    

cur.execute('select pages from database_a_monitor where id=1;')
result = cur.fetchone()
print result
conn.close()

with 语句与 MySQLdb 的 Connection 对象一起使用时,会返回一个游标。当你离开 with 块时,Connection.__exit__ 被调用:

def __exit__(self, exc, value, tb):
    if exc:
        self.rollback()
    else:
        self.commit()

由于您所做的只是读取数据,因此无需回滚或提交;写入数据时,请记住,通过异常离开块将导致您的更改回滚,而正常离开将导致您的更改被提交。

请注意,这并没有关闭游标,它只管理事务上下文。我在对When to close cursors using MySQLdb 的回答中详细介绍了这个主题,但简短的故事是,您通常不必担心在使用 MySQLdb 时关闭游标。

您还可以通过passing the database as a parameter to MySQLdb.connect 而不是发出USE 声明让您的生活更轻松。

This answer to a very similar question 提供了另外两种方法——您可以将隔离级别更改为READ COMMITTED,或打开autocommit。

【讨论】:

谢谢。它工作正常。引擎是 InnoDB。你拯救了我的一天。

以上是关于为啥需要重新连接数据库才能看到表数据的变化?的主要内容,如果未能解决你的问题,请参考以下文章

mysql只能取出一条记录为啥不显示

为啥数据库连接池比单个连接更好?

为啥前端不能直接连接数据库,一定要通过后台

MySQL中使用连接查询

为啥要用Spring管理连接池,它有啥好处?

为啥我的电脑无线网用一会后自动断线,需要重启才能连上