为什么我需要重新连接到数据库才能查看表数据的变化?

时间:2015-04-16 16:25:01

标签: python mysql sql cursor mysql-python

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

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

实际上,行数据每3秒更改一次,但光标始终返回相同的值。

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

我怀疑错误的代码是在评论之后开始的:

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])

1 个答案:

答案 0 :(得分:2)

这几乎肯定是事务隔离的结果。我会假设您已经使用默认存储引擎(InnoDB)和隔离级别(REPEATABLE READ)了,因为您还没有说明:

  

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