【问题标题】:How to disable SQLAlchemy caching?如何禁用 SQLAlchemy 缓存?
【发布时间】:2012-04-29 21:43:36
【问题描述】:

我在使用sqlalchemy 时遇到了缓存问题。

我使用sqlalchemy 将数据插入 MySQL 数据库。然后,我让另一个应用程序处理这些数据,并直接更新它。

但是sqlalchemy 总是返回旧数据而不是更新数据。我认为sqlalchemy 缓存了我的请求……所以……我应该如何禁用它?

【问题讨论】:

标签: python mysql sqlalchemy innodb


【解决方案1】:

除了事务本地的通常 SQLAlchemy 身份映射之外,人们认为存在“缓存”的通常原因是他们正在观察事务隔离的影响。 SQLAlchemy 的会话默认在事务模式下工作,这意味着它会等到调用session.commit() 以便将数据持久化到数据库中。在此期间,其他地方正在进行的其他事务将看不到此数据。

但是,由于交易的孤立性质,还有一个额外的转折点。那些正在进行的其他事务不仅在提交之前不会看到您的事务的数据,在某些情况下,他们也无法看到它,直到 它们被提交或回滚(这与您的close() 在这里)。具有平均隔离度的事务将保持它到目前为止已加载的状态,并继续为您提供与事务本地相同的状态,即使实际数据已更改 - 这称为 可重复读取。

http://en.wikipedia.org/wiki/Isolation_%28database_systems%29

【讨论】:

  • "SQLAlchemy 的会话默认在事务模式下工作" --- 你能告诉我们一种停止默认的方法吗?我不想解释只是想要 1 行代码来完全禁用事务。特别是对于愚蠢的 SELECT 调用。
  • 实际上 SQLAlchemy 中有缓存(至少现在是 2021 年)。我使用session.execute 命令遇到了这个问题。您可以在此处找到有关缓存的信息(在页面上搜索“cached since”字符串)github.com/sqlalchemy/sqlalchemy/blob/master/doc/build/core/…
  • @AnarSalimkhanov 请注意,您所指的缓存只是一个语句编译缓存。从您的链接文档中:它 "正在缓存 仅传递给数据库的 SQL 字符串,而不是 查询返回的数据。它不在一种数据缓存方式,不会影响为特定 SQL 语句返回的结果,也不会暗示与获取结果行相关的任何内存使用。”
  • @amain 嗯...有趣。因为我真的遇到了缓存问题。虽然数据库已更新,但我曾经获取旧的 RESPONSE 数据,直到我禁用它。现在我无法测试它,因为它在我的一个旧项目中,我不记得它在哪里)
【解决方案2】:

这个问题让我非常沮丧,但我终于想通了。

我有一个 Flask/SQLAlchemy 应用程序与一个较旧的 PHP 站点一起运行。 PHP 站点将写入数据库,而 SQLAlchemy 不会意识到任何更改。

我尝试 sessionmaker 设置 autoflush=True 失败 我在查询之前尝试了 db_session.flush()、db_session.expire_all() 和 db_session.commit(),但都没有工作。仍然显示陈旧的数据。

最后我看到了 SQLAlchemy 文档的这一部分:http://docs.sqlalchemy.org/en/latest/dialects/postgresql.html#transaction-isolation-level

设置isolation_level 效果很好。现在我的 Flask 应用程序正在与 PHP 应用程序“对话”。代码如下:

engine = create_engine(
    "postgresql+pg8000://scott:tiger@localhost/test",
    isolation_level="READ UNCOMMITTED"
)

当 SQLAlchemy 引擎以“READ UNCOMMITED”隔离级别启动时,它将执行“脏读”,这意味着它将直接从数据库中读取未提交的更改。

希望对你有帮助


这是 cmets 中 AaronD 提供的一个可能的解决方案

from flask.ext.sqlalchemy import SQLAlchemy

class UnlockedAlchemy(SQLAlchemy):
    def apply_driver_hacks(self, app, info, options):
        if "isolation_level" not in options:
            options["isolation_level"] = "READ COMMITTED"
    return super(UnlockedAlchemy, self).apply_driver_hacks(app, info, options)

【讨论】:

  • 如果你使用 Flask-SQLAlchemy,你可以继承 flask.ext.sqlalchemy.SQLAlchemy 并覆盖 apply_driver_hacks 函数来设置隔离级别,同时仍然保持所有的 Flask 集成。此外,隔离级别READ COMMITTED 可能就足够了,前提是两个应用程序都在它们完成写入之后提交它们的写入并且没有等待很长时间。这样您就不必担心脏读 - 它只会在您每次读取时为您提供一个新的数据库快照。
  • @AaronD 你能把你的代码发布到你提到的flask.ext.sqlalchemy.SQLAlchemy的子类吗?
  • 我的代码中有这个:class UnlockedAlchemy(SQLAlchemy): def apply_driver_hacks(self, app, info, options): if not "isolation_level" in options: options["isolation_level"] = "READ COMMITTED" return super(UnlockedAlchemy, self).apply_driver_hacks(app, info, options)
  • 救命稻草!我正在使用engine_from_config 从文件中读取 sqlalchemy 配置,我只是将:sqlalchemy.isolation_level = READ UNCOMMITTED 添加到我的配置文件中,外部更改现在已正确反映在我的应用程序中 :-)
  • 这没有意义。如果更新数据库的事务已正确提交(由 php 站点),为什么需要将隔离级别设置为“READ UNCOMMITTED”?这更像是您的 PHP 站点如何更新数据库的问题。
【解决方案3】:

除了 zzzeek 出色的答案,

我遇到了类似的问题。我通过使用短暂的生命周期解决了这个问题。

with closing(new_session()) as sess:
    # do your stuff

我为每个任务、任务组或请求使用了一个新会话(如果是 Web 应用程序)。这解决了我的“缓存”问题。

这个材料对我非常很有用:

When do I construct a Session, when do I commit it, and when do I close it

【讨论】:

【解决方案4】:

这发生在我的 Flask 应用程序中,我的解决方案是在每次请求后使会话中的所有对象过期。

from flask.signals import request_finished
def expire_session(sender, response, **extra):
    app.db.session.expire_all()
request_finished.connect(expire_session, flask_app)

工作就像一个魅力。

【讨论】:

    【解决方案5】:

    首先,SQLAlchemy 没有缓存。 根据你从数据库中获取数据的方法,你应该在别人更新数据库后做一些测试,看看你是否能得到新的数据。

    (1) use connection:
    connection = engine.connect()
    result = connection.execute("select username from users")
    for row in result:
        print "username:", row['username']
    connection.close()
    (2) use Engine ...
    (3) use MegaData...
    

    请按照以下步骤操作:http://docs.sqlalchemy.org/en/latest/core/connections.html

    另一个可能的原因是您的 MySQL 数据库没有永久更新。重启 MySQL 服务并检查一下。

    【讨论】:

    • 感谢您的回复。我已经解决了。当我使用scoped_session 时,我只是忘记了session.close ...
    【解决方案6】:

    我知道 SQLAlchemy does not store caches,所以您需要查看 logging 输出。

    【讨论】:

    • 我是这么认为的。我打开了echo = True,但没有任何用处。
    • 我在不使用 sqlalchemy 的情况下更新数据.. 使用 MySQLdb.. 我确保 MySQL 中的数据已更新..
    • 尝试在 sessionmaker 中将自动提交设置为 True (bind=self.engine, autocommit=True)
    • 感谢您的回复。我已经解决了。当我使用scoped_session 时,我只是忘记了session.close。晕倒..
    猜你喜欢
    • 2022-06-24
    • 2011-05-16
    • 2011-02-20
    • 2013-09-14
    • 2012-04-15
    • 2011-04-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多