如何在Sqlalchemy ORM会话中正确禁用缓存?
我有一个在后台运行的线程,它会不断循环并执行以下查询:
try:
newsletter = self.session.query(models.Newsletter).\
filter(models.Newsletter.status == 'PROCESSING').\
limit(1).one()
except sa.orm.exc.NoResultFound:
self.logger.debug('No PROCESSING newsletters found. Sleeping...')
self.sleep()
return
# (...) more code to do with found newsletter
在这里,sleep方法只是让这个线程暂停一段时间,然后return语句会让它回到主循环。不过我发现,如果在后台运行的时候把任何一个新闻通讯的状态改成'PROCESSING',什么也不会发生,也就是说查询仍然会返回没有结果的错误。但是如果我重启后台程序,它就能找到这个新闻通讯。所以我觉得这个查询的结果一定是被缓存了。我该怎么做才能清除这个缓存呢?session.expire_all()似乎没用。我也可以在每次循环时创建一个新的Session()对象,但我不知道这样做是否会对系统资源造成影响。
4 个回答
嗯,我已经找到答案了。你需要明确地使用 session.commit() 来让它更新,或者你可以在创建会话时设置 autocommit=True,像是通过 sessionmaker 来设置。
sessionmaker(bind=self.engine, autocommit=True)
不过我还没有测试过 session.commit() 这种方法。
所以这似乎不是缓存的问题,而是事务工作的方式就是这样。
你代码中的问题是因为数据库默认使用了 可重复读隔离级别。这意味着,除非你调用 commit()
或 rollback()
(或者像Xeross建议的那样使用 autocommit=True
),否则查询结果会一直保持不变,像是被锁住了一样,或者你手动改变隔离级别。
没错,SQLAlchemy确实会缓存映射的对象(而不是查询结果!),因为ORM模式需要为每个身份提供一个唯一的对象。默认情况下,SQLAlchemy使用弱身份映射作为缓存,所以当没有任何引用指向这个对象时,它会自动从会话中删除。需要注意的是,后续的查询会用新数据更新缓存对象的状态,所以你不需要担心这个缓存的问题。
SQLAlchemy 本身是不会自动缓存的。除非你自己特别设置了缓存,像这样。
你可以在创建 sessionmaker
时加上 echo=True
,这样就可以查看 logging
输出的信息了。