为啥需要重新连接数据库才能看到表数据的变化?
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。你拯救了我的一天。以上是关于为啥需要重新连接数据库才能看到表数据的变化?的主要内容,如果未能解决你的问题,请参考以下文章