在Python中使用MySQLdb时结果长期过时

25 投票
4 回答
6293 浏览
提问于 2025-04-16 17:19

我的Python程序会查询MySQL数据库中的一组表,查询完后会睡30秒,然后再查询一次,如此反复。这些表是由第三方不断更新的,我当然希望每30秒能看到新的结果。

假设我的查询是这样的:

"select * from A where A.key > %d" % maxValueOfKeyFromLastQuery

但我发现,经过一两次查询后,我的程序就不再找到新的结果,尽管表中确实有新数据。我知道表中有新数据,因为我在使用交互式mysql时可以看到这些新行(也就是说,不是通过Python查询的)。

我发现,如果我在每次查询后断开与数据库的连接,然后为下一个查询重新建立连接,问题就解决了。

我想这可能是服务器端缓存的问题,正如这里讨论的那样:在程序的某些部分显式禁用MySQL查询缓存

但是:

  1. 当我检查交互式mysql命令行时,它显示缓存是开启的。(如果这是缓存问题,为什么交互式命令行没有受到影响呢?)

  2. 如果我在Python程序中明确执行 SET SESSION query_cache_type = OFF,问题依然存在。

目前,我只能通过为每个查询创建一个新的数据库连接来解决这个问题。

我该如何让我的Python查询看到那些我知道存在的新结果呢?

4 个回答

10

你可以在MySQLdb中自动开启自动提交功能!试试下面的代码:

conn = MySQLdb.Connect("host", "user", "password")
conn.autocommit(True)

这样做会让你在使用交互式命令行时,体验到一样的操作方式。

17

你可能需要检查一下你数据库的事务隔离级别。你描述的行为,如果设置为REPEATABLE-READ,是很正常的。你可以考虑把它改成READ-COMMITTED。

因为提问的人说他只是查询数据库,所以不可能是忘记提交了。虽然插入一个提交看起来是个解决办法,因为这样会开始一个新的事务,并且可能需要建立一个新的快照。但在每次选择之前都要插入一个提交,这听起来并不是一个好的编程习惯。

这里没有Python代码可以展示,因为解决方案在于正确配置数据库。

一定要查看MySQL的文档,地址是 http://dev.mysql.com/doc/refman/5.5/en/set-transaction.html

REPEATABLE READ
这是InnoDB的默认隔离级别。对于一致性读取,它与READ COMMITTED隔离级别有一个重要区别:同一个事务中的所有一致性读取都读取第一次读取时建立的快照。这意味着如果你在同一个事务中发出多个普通(非锁定)SELECT语句,这些SELECT语句之间也是一致的。请参见第14.3.9.2节,“一致性非锁定读取”。

READ COMMITTED
这是一个有点像Oracle的隔离级别,针对一致性(非锁定)读取:每次一致性读取,即使在同一个事务中,都会设置并读取自己的新快照。请参见第14.3.9.2节,“一致性非锁定读取”。

检查配置的隔离级别:

>mysql > SELECT @@GLOBAL.tx_isolation, @@tx_isolation;
+-----------------------+-----------------+
| @@GLOBAL.tx_isolation | @@tx_isolation  |
+-----------------------+-----------------+
| REPEATABLE-READ       | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set (0.01 sec)

将事务隔离级别设置为READ-COMMITTED

mysql> SET GLOBAL tx_isolation='READ-COMMITTED';
Query OK, 0 rows affected (0.00 sec)

mysql> SET SESSION tx_isolation='READ-COMMITTED';
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @@GLOBAL.tx_isolation, @@tx_isolation;
+-----------------------+----------------+
| @@GLOBAL.tx_isolation | @@tx_isolation |
+-----------------------+----------------+
| READ-COMMITTED        | READ-COMMITTED |
+-----------------------+----------------+
1 row in set (0.01 sec)

mysql>

然后再次运行应用程序……

26

这个网站这个网站上都有关于同一个问题的信息。为了让你的表格保持最新状态,你必须提交你的事务。可以用 db.commit() 来完成这一步。

正如下面的帖子提到的,你可以通过开启自动提交来省去这一步。你只需要运行 db.autocommit(True) 就可以了。

另外,在交互式命令行中,自动提交是默认开启的,所以这也解释了为什么你在那里的时候没有遇到这个问题。

撰写回答