在我看来,在使用 sqlalchemy 时,经历创建表达式树然后再次从中创建查询的整个过程是浪费时间。除了偶尔的动态查询之外,在应用程序的整个生命周期中,几乎所有内容都完全相同(当然除了参数之外)。
有什么方法可以在创建查询后保存查询并在以后使用不同的参数重用它?或者也许有一些内部机制已经做了类似的事情?
在我看来,在使用 sqlalchemy 时,经历创建表达式树然后再次从中创建查询的整个过程是浪费时间。除了偶尔的动态查询之外,在应用程序的整个生命周期中,几乎所有内容都完全相同(当然除了参数之外)。
有什么方法可以在创建查询后保存查询并在以后使用不同的参数重用它?或者也许有一些内部机制已经做了类似的事情?
在我看来,在使用 sqlalchemy 时,经历创建表达式树然后再次从中创建查询的整个过程是浪费时间。
与应用程序的其余部分相比,您对浪费了多少时间有任何估计吗?在使您的程序更复杂之前,此处的分析非常重要。正如我经常注意到的,Reddit 每天提供超过 10 亿的页面浏览量,他们使用 SQLAlchemy Core 来查询他们的数据库,而我上次查看他们的代码时,他们并没有尝试优化这个过程——他们在上面构建了表达式树每次飞行并编译。然而,我们有一些用户已经确定他们的特定系统实际上受益于这些领域的优化。
我在这里写了一些关于分析的背景知识:如何分析 SQLAlchemy 驱动的应用程序?
有什么方法可以在创建查询后保存查询并在以后使用不同的参数重用它?或者也许有一些内部机制已经做了类似的事情?
有几种方法,具体取决于您使用的 API 以及您想要优化的区域。
呈现 SQL 有两个主要部分 - 可以说是表达式树的构造,然后是表达式树中字符串的编译。
树本身可以被重用,如果使用 Core,它可以是一个 select() 构造,或者如果使用 ORM,它可以是一个 Query()。一个 select() 尤其没有任何关联的东西,这会阻止它像你喜欢的那样经常被重用(对于 insert()、delete()、update() 等也是如此)。
在 ORM 中,Query 也可以使用with_session()方法与不同的会话一起使用。这里的胜利并不多,因为每次调用它时Query()
仍然会产生一个整体。select()
然而,正如我们将在下面看到的,有一个可以允许缓存的方法。
下一级优化涉及缓存呈现的实际 SQL 文本。这是一个需要多加注意的领域,因为我们生成的 SQL 是特定于目标后端的;还有一些极端情况,各种参数化会改变 SQL 本身(例如在 SQL Server 中使用“TOP N ROWS”,我们不能在那里使用绑定参数)。使用 的execution_options()方法提供 SQL 字符串的缓存,该方法Connection
在其他一些地方也可用,compiled_cache
通过向其发送字典或其他类似 dict 的对象来设置功能,该对象将缓存语句的字符串格式,键为方言、构造的身份和发送的参数。这个特性通常被 ORM 用于插入/更新/删除语句。
我发布了一个食谱,该食谱将该compiled_cache
功能与BakedQueryQuery
的,集成在一起。通过使用 any并说,您现在可以使用 any 运行该查询,只要您不再对其调用任何链式方法,它就应该每次都使用 SQL 字符串的缓存形式:Query
query.bake()
Session
q = s.query(Foo).filter(Foo.data==bindparam('foo')).bake()
for i in range(10):
result = q.from_session(s).params(foo='data 12').all()
它是实验性的,并不经常使用,但这正是您在这里所要求的。我建议您根据自己的需要定制它,在使用它时注意内存使用情况,并确保遵循它的工作原理。
SQLAlchemy 1.0 引入了baked
扩展,这是一个专门为缓存Query
对象设计的缓存。它缓存对象的构造和字符串编译步骤,以最大限度地减少 Python 解释器在重复查询上的开销。
官方文档在这里:http ://docs.sqlalchemy.org/en/latest/orm/extensions/baked.html
请注意,它不会缓存数据库返回的结果集。为此,您需要查看dogpile.cache
:
http://docs.sqlalchemy.org/en/latest/orm/examples.html#module-examples.dogpile_caching