【问题标题】:Chronic stale results using MySQLdb in Python在 Python 中使用 MySQLdb 的长期陈旧结果
【发布时间】:2011-08-22 01:17:20
【问题描述】:

我的 Python 程序查询 MySQL DB 中的一组表,休眠 30 秒,然后再次查询它们,等等。有问题的表由第三方不断更新,并且(显然)我希望看到每 30 秒生成一次新结果。

假设我的查询如下所示:

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

我经常会看到我的程序在一两次迭代后停止寻找新结果,即使表中存在新行。我知道表中存在新行,因为当我从交互式 mysql(即不是来自 Python)发出相同的查询时,我可以看到它们。

我发现,如果我在每次查询后终止与数据库的连接,然后为下一个查询建立一个新连接,那么 Python 中的问题就会消失。

我认为这可能是服务器端缓存问题,如下所述:Explicit disable MySQL query cache in some parts of program

但是:

  1. 当我检查交互式 mysql shell 时,它说缓存已打开。 (所以如果这是一个缓存问题,为什么交互式 shell 不会受到它的影响?)

  2. 如果我在 Python 程序中显式执行 SET SESSION query_cache_type = OFF,问题仍然存在。

为每个查询创建一个新的数据库连接是我能够解决问题的唯一方法。

如何从 Python 获取查询以查看我知道的新结果?

【问题讨论】:

  • 您不应在查询中使用% 格式运算符。对所有占位符使用%s(无论它们的类型是什么),并传递带有值的元组/列表,以便正确清理它们。
  • 只是一个猜测:确保从数据库读取的查询不会与事务的其他更改隔离。 IE。在每次读取之前创建一个新事务。
  • @ThiefMaster:%d 在查询中其实还可以,因为它强制替换为整数,不能用于 SQL 注入攻击。

标签: python mysql caching


【解决方案1】:

This websitethis website 包含有关同一问题的信息。为了使您的表保持最新,您必须提交您的事务。使用db.commit() 来执行此操作。

正如我下面的帖子所述,您可以通过启用自动提交来消除对此的需求。这可以通过运行db.autocommit(True)来完成

此外,在交互式 shell 中启用了自动提交,因此这解释了为什么您在那里没有遇到问题。

【讨论】:

  • 感谢您的提示!这正是问题所在。我想我太习惯于 Oracle 了。 :)
  • 我也从中受益。但是你能解释一下为什么自动提交有助于 SELECT 语句吗?我的来源(在开发中)是 PHPMyAdmin,它总是返回更新的结果。但是我的 Python 程序没有,直到启用自动提交。
  • 我和@aitchnyu有同样的问题,我的python进程只做selects。我有一个完全其他的写作过程,我将commit() 放在execute() 之后和fetchall() 之前,它可以工作。这很酷,但你为什么要在读取语句上使用commit()
  • @aitchnyu @Landon 虽然看起来很奇怪,但InnoDB transaction model 甚至导致SELECT 语句与对数据库所做的更改隔离。因此,如果您不终止旧事务(使用显式commit 或通过启用autocommit),您的下一个查询将使用与您的第一个查询运行时相同的数据库视图。 See also ...
【解决方案2】:

您可能需要检查数据库的事务隔离级别。如果将其设置为 REPEATABLE-READ,您描述的行为就是您所期望的。您可能希望将其更改为 READ-COMMITTED。

由于问题的原始发布者提到他只是在查询数据库,因此它不可能是被遗忘的提交。插入提交似乎是一种解决方法,因为它会导致新事务开始;并且可能需要建立新的快照。尽管如此,在每次选择之前都必须插入一个提交对我来说听起来不是好的编程实践。

没有可显示的 python 代码,因为解决方案在于正确配置数据库。

查看http://dev.mysql.com/doc/refman/5.5/en/set-transaction.html 上的 MySQL 文档。

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

已提交阅读
关于一致(非锁定)读取的有点类似于 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>

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

【讨论】:

  • 查看 MySQL 手册以了解事务隔离级别的行为。
【解决方案3】:

可以在 MySQLdb 中自动启用自动提交!请尝试以下操作:

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

这为您提供了与您在交互式 shell 中习惯的行为相同的行为。

【讨论】:

  • 另外,还有一个关键字参数'autocommit'
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-10-03
  • 2021-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-26
  • 1970-01-01
相关资源
最近更新 更多