gpt4 book ai didi

python - 更好地理解 SQLalchemy 的 `yield_per()` 问题

转载 作者:IT老高 更新时间:2023-10-28 20:42:15 27 4
gpt4 key购买 nike

引用 SQLalchemy documentation :

The Query.yield_per() method is not compatible with most eager loading schemes, including subqueryload and joinedload with collections.

Warning

Use this method with caution; if the same instance is present in more than one batch of rows, end-user changes to attributes will be overwritten.

In particular, it’s usually impossible to use this setting with eagerly loaded collections (i.e. any lazy=’joined’ or ‘subquery’) since those collections will be cleared for a new load when encountered in a subsequent result batch. In the case of ‘subquery’ loading, the full result for all rows is fetched which generally defeats the purpose of yield_per().

Also note that while yield_per() will set the stream_results execution option to True, currently this is only understood by psycopg2 dialect which will stream results using server side cursors instead of pre-buffer all rows for this query. Other DBAPIs pre-buffer all rows before making them available. The memory use of raw database rows is much less than that of an ORM-mapped object, but should still be taken into consideration when benchmarking.

我真的很难理解 yield_per() 是如何工作的,以及使用这种方法到底有什么问题。还有什么是解决这些问题并继续使用此函数迭代大量行的正确方法。

我对您拥有的所有建设性信息感兴趣,但这里有一些提示性问题:

  • 同一行怎么会有多个实例?仅通过关系(如果迭代表的两行对另一个表中的同一行有 FK)?如果您不知道会发生这种情况,或者您只读取关系上的属性,是否存在问题?
  • lazy='joined' 或 'subquery' 是不可能的,但究竟为什么呢?它们都只是您调用 yield_per() 的查询的一部分。
    • 如果它们在后续结果批处理中被清除,则只需重新加载即可。那么问题出在哪里?或者,如果进行了更改,您是否会失去关系的更改?
    • 在“子查询”加载的情况下,为什么要获取所有行? SQL Server 可能需要保存一个大表,但为什么不简单地为整个查询一个接一个地分批返回结果呢?
    • yield_per() doc 中的一个示例中q = sess.query(Object).yield_per(100).options(lazyload('*'),joinedload(Object.some_related)) 他们使用 lazyload('*') 停用 eagerload 但保持单个连接负载。有没有办法仍然使用 yield_per() 和 eagerload?有什么条件?
  • 他们说 psycopg2 是唯一支持流结果的 DBAPI。那么这是唯一可以与 yield_per() 一起使用的 DBAPI 吗?据我了解 yield_per 使用 DBAPI 的 cursor.fetchmany() ( example ) 函数,它支持其中的许多函数。据我了解 cursor.fetchmany() 支持仅获取部分结果而不获取所有内容(如果它会获取所有内容,为什么该函数存在?)
  • 我觉得 yield_per() 如果您只进行读取访问(例如用于统计信息)是完全安全的(即使使用 eagerload)。对吗?

最佳答案

如果您尝试将这两种有问题的加载策略与 yield_per 一起使用,它们都会引发异常,因此您不必太担心。

相信 subqueryload 的唯一问题是第二个查询的批量加载(目前)尚未实现。语义上不会出错,但是如果您使用 yield_per,您可能有一个很好的理由不想一次加载所有结果。所以 SQLAlchemy 礼貌地拒绝违背你的意愿。

joinedload 更微妙一些。仅在集合的情况下才被禁止,其中主行可能有多个关联的行。假设您的查询产生了这样的原始结果,其中 A 和 B 是来自不同表的主键:

 A | B 
---+---
1 | 1
1 | 2
1 | 3
1 | 4
2 | 5
2 | 6

现在您使用 yield_per(3) 获取这些。问题是 SQLAlchemy 只能通过 rows 限制它获取的数量,但它必须返回 objects。在这里,SQLAlchemy 只看到前三行,因此它创建了一个 A 对象,其中键为 1 和 三个 B 子对象:1、2 和3.

当它加载下一批时,它想创建一个新的 A 对象,其键为 1...啊,但它已经有了其中一个,所以不需要再次创建它。额外的 B 4 丢失了。 (所以,不,即使使用 yield_per 读取连接的集合也是不安全的——你的数据 block 可能会丢失。)

你可能会说“好吧,继续阅读行,直到你有一个完整的对象”——但是如果那个 A 有一百个 child 呢?还是一百万? SQLAlchemy 无法合理地保证它可以按照您的要求进行操作产生正确的结果,因此它拒绝尝试。


请记住,DBAPI 的设计使得任何 数据库都可以与相同的 API 一起使用,即使该数据库不支持所有 DBAPI 功能。考虑到 DBAPI 是围绕游标设计的,但 MySQL 实际上并没有 游标!相反,MySQL 的 DBAPI 适配器必须伪造它们。

所以虽然 cursor.fetchmany(100)工作,你可以从 the MySQLdb source code 看到它不会懒惰地从服务器获取;它将所有内容提取到一个大列表中,然后在您调用 fetchmany 时返回一个切片。

psycopg2 支持的是真正的流式传输,结果在服务器上永久被记住,而您的 Python 进程一次只能看到其中的几个。

您仍然可以将 yield_perMySQLdb 或任何其他 DBAPI 一起使用;这就是 DBAPI 设计的重点。您必须为隐藏在 DBAPI 中的所有原始行支付内存成本(它们是元组,相当便宜),但您不必支付所有 ORM 对象的费用同时。

关于python - 更好地理解 SQLalchemy 的 `yield_per()` 问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31387980/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com