我定期查询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])
答案 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。