如何使用 pony orm 以多对多关系加载数据?
How do I load data in many-to-many relation using pony orm?
这是我的实体:
class Article(db.Entity):
id = PrimaryKey(int, auto=True)
creation_time = Required(datetime)
last_modification_time = Optional(datetime, default=datetime.now)
title = Required(str)
contents = Required(str)
authors = Set('Author')
class Author(db.Entity):
id = PrimaryKey(int, auto=True)
first_name = Required(str)
last_name = Required(str)
articles = Set(Article)
这是我用来获取一些数据的代码:
return left_join((article, author) for article in entities.Article
for author in article.authors).prefetch(entities.Author)[:]
无论我是否使用预取方法,生成的 sql 看起来总是一样的:
SELECT DISTINCT "article"."id", "t-1"."author"
FROM "article" "article"
LEFT JOIN "article_author" "t-1"
ON "article"."id" = "t-1"."article"
然后当我迭代结果时,pony 发出了另一个查询(查询):
SELECT "id", "creation_time", "last_modification_time", "title", "contents"
FROM "article"
WHERE "id" = %(p1)s
SELECT "id", "first_name", "last_name"
FROM "author"
WHERE "id" IN (%(p1)s, %(p2)s)
我希望的行为是 orm 只发出一个查询来加载所有需要的数据。那么我该如何实现呢?
这应该有效
python
from pony.orm import select
select((article, author) for article in Article if Article.authors == Authors.id)
PonyORM作者来了。我们不想只使用一个查询来加载所有这些对象,因为这样效率很低。
使用单个查询加载多对多关系的唯一好处是减少往返数据库的次数。但是,如果我们将三个查询替换为一个查询,这并不是一个重大改进。当您的数据库服务器位于您的应用程序服务器附近时,与 Python.
中处理结果数据相比,这些往返实际上非常快
另一方面,当使用同一个查询加载多对多关系的两边时,不可避免地会在多行中一遍又一遍地重复同一个对象的数据。这有很多缺点:
与没有传输重复信息的情况相比,从数据库传输的数据的大小变得更大。在您的示例中,如果您有十篇文章,每篇文章由三位作者撰写,则单个查询将 return 三十行,其中像 article.contents
这样的大字段会重复多次。单独的查询将传输尽可能少的数据量,根据特定的多对多关系,大小差异可能很容易达到一个数量级。
数据库服务器一般都是用C等编译语言编写的,运行速度非常快。网络层也是如此。但是 Python 代码被解释,Python 代码消耗的时间(与某些观点相反)通常比在数据库中花费的时间多得多。您可以看到 SQLAlchemy 作者 Mike Bayer 执行的 profiling tests,之后他得出结论:
A great misconception I seem to encounter often is the notion that communication with the database takes up a majority of the time spent in a database-centric Python application. This perhaps is a common wisdom in compiled languages such as C or maybe even Java, but generally not in Python. Python is very slow, compared to such systems (...) Whether a database driver (DBAPI) is written in pure Python or in C will incur significant additional Python-level overhead. For just the DBAPI alone, this can be as much as an order of magnitude slower.
当使用同一个查询加载多对多关系的所有数据并且在多行中重复相同的数据时,需要解析所有这些重复的数据[=68] =] 只是为了扔掉其中的大部分 。由于 Python 是过程中最慢的部分,因此 "optimization" 可能会导致性能下降。
作为对我的话的支持,我可以指出 Django ORM。该 ORM 有两种方法可用于查询优化。第一个,叫做select_related loads all related objects in a single query, while the more recently added method called prefetch_related loads objects in a way Pony does by default. According to Django users the second method works much faster:
In some scenarios, we have found up to a 30% speed improvement.
需要数据库执行连接,这会消耗数据库服务器的宝贵资源。
虽然 Python 代码是处理单个请求时最慢的部分,但数据库服务器 CPU 时间是所有并行请求使用的共享资源。您可以通过在不同的服务器上启动多个 Python 进程来轻松扩展 Python 代码,但是扩展数据库要困难得多。正因为如此,在高负载应用中,最好将有用的工作从数据库服务器卸载到应用服务器,这样这项工作可以由多个应用服务器并行完成。
当数据库执行连接时,它需要花费额外的时间来完成它。但是对于 Pony 来说,数据库是否进行连接是无关紧要的,因为在任何情况下,一个对象都会在 ORM identity map 中被插入link。 所以数据库在执行连接时所做的工作只是数据库时间的无用消耗。另一方面,使用标识映射模式 Pony 可以 link 对象同样快速,无论它们是否在同一数据库行中提供。
回到往返次数,Pony有专门的机制来消除"N+1 query"问题。当 ORM 发送数百个非常相似的查询时,"N+1 query" 反模式就会出现,每个查询都会从数据库中加载单独的对象。许多 ORM 都存在这个问题。但是 Pony 可以检测到它并用一次加载所有必要对象的单个查询替换重复的 N 个查询。这种机制非常高效,可以大大减少往返次数。但是当我们谈到加载多对多关系时,这里没有 N 个查询,只有三个查询,单独执行时效率更高,因此尝试执行单个查询没有任何好处。
总而言之,我需要说的是,ORM 性能对我们 Pony ORM 开发人员来说非常重要。 因为,我们不想在单个查询中实现加载多对多关系,因为它肯定会更慢 比我们目前的解决方案。
因此,要回答您的问题,您不能在单个查询中同时加载多对多关系的两端。我认为这是一件好事。
这是我的实体:
class Article(db.Entity):
id = PrimaryKey(int, auto=True)
creation_time = Required(datetime)
last_modification_time = Optional(datetime, default=datetime.now)
title = Required(str)
contents = Required(str)
authors = Set('Author')
class Author(db.Entity):
id = PrimaryKey(int, auto=True)
first_name = Required(str)
last_name = Required(str)
articles = Set(Article)
这是我用来获取一些数据的代码:
return left_join((article, author) for article in entities.Article
for author in article.authors).prefetch(entities.Author)[:]
无论我是否使用预取方法,生成的 sql 看起来总是一样的:
SELECT DISTINCT "article"."id", "t-1"."author"
FROM "article" "article"
LEFT JOIN "article_author" "t-1"
ON "article"."id" = "t-1"."article"
然后当我迭代结果时,pony 发出了另一个查询(查询):
SELECT "id", "creation_time", "last_modification_time", "title", "contents"
FROM "article"
WHERE "id" = %(p1)s
SELECT "id", "first_name", "last_name"
FROM "author"
WHERE "id" IN (%(p1)s, %(p2)s)
我希望的行为是 orm 只发出一个查询来加载所有需要的数据。那么我该如何实现呢?
这应该有效
python
from pony.orm import select
select((article, author) for article in Article if Article.authors == Authors.id)
PonyORM作者来了。我们不想只使用一个查询来加载所有这些对象,因为这样效率很低。
使用单个查询加载多对多关系的唯一好处是减少往返数据库的次数。但是,如果我们将三个查询替换为一个查询,这并不是一个重大改进。当您的数据库服务器位于您的应用程序服务器附近时,与 Python.
中处理结果数据相比,这些往返实际上非常快另一方面,当使用同一个查询加载多对多关系的两边时,不可避免地会在多行中一遍又一遍地重复同一个对象的数据。这有很多缺点:
与没有传输重复信息的情况相比,从数据库传输的数据的大小变得更大。在您的示例中,如果您有十篇文章,每篇文章由三位作者撰写,则单个查询将 return 三十行,其中像
article.contents
这样的大字段会重复多次。单独的查询将传输尽可能少的数据量,根据特定的多对多关系,大小差异可能很容易达到一个数量级。数据库服务器一般都是用C等编译语言编写的,运行速度非常快。网络层也是如此。但是 Python 代码被解释,Python 代码消耗的时间(与某些观点相反)通常比在数据库中花费的时间多得多。您可以看到 SQLAlchemy 作者 Mike Bayer 执行的 profiling tests,之后他得出结论:
A great misconception I seem to encounter often is the notion that communication with the database takes up a majority of the time spent in a database-centric Python application. This perhaps is a common wisdom in compiled languages such as C or maybe even Java, but generally not in Python. Python is very slow, compared to such systems (...) Whether a database driver (DBAPI) is written in pure Python or in C will incur significant additional Python-level overhead. For just the DBAPI alone, this can be as much as an order of magnitude slower.
当使用同一个查询加载多对多关系的所有数据并且在多行中重复相同的数据时,需要解析所有这些重复的数据[=68] =] 只是为了扔掉其中的大部分 。由于 Python 是过程中最慢的部分,因此 "optimization" 可能会导致性能下降。
作为对我的话的支持,我可以指出 Django ORM。该 ORM 有两种方法可用于查询优化。第一个,叫做select_related loads all related objects in a single query, while the more recently added method called prefetch_related loads objects in a way Pony does by default. According to Django users the second method works much faster:
In some scenarios, we have found up to a 30% speed improvement.
需要数据库执行连接,这会消耗数据库服务器的宝贵资源。
虽然 Python 代码是处理单个请求时最慢的部分,但数据库服务器 CPU 时间是所有并行请求使用的共享资源。您可以通过在不同的服务器上启动多个 Python 进程来轻松扩展 Python 代码,但是扩展数据库要困难得多。正因为如此,在高负载应用中,最好将有用的工作从数据库服务器卸载到应用服务器,这样这项工作可以由多个应用服务器并行完成。
当数据库执行连接时,它需要花费额外的时间来完成它。但是对于 Pony 来说,数据库是否进行连接是无关紧要的,因为在任何情况下,一个对象都会在 ORM identity map 中被插入link。 所以数据库在执行连接时所做的工作只是数据库时间的无用消耗。另一方面,使用标识映射模式 Pony 可以 link 对象同样快速,无论它们是否在同一数据库行中提供。
回到往返次数,Pony有专门的机制来消除"N+1 query"问题。当 ORM 发送数百个非常相似的查询时,"N+1 query" 反模式就会出现,每个查询都会从数据库中加载单独的对象。许多 ORM 都存在这个问题。但是 Pony 可以检测到它并用一次加载所有必要对象的单个查询替换重复的 N 个查询。这种机制非常高效,可以大大减少往返次数。但是当我们谈到加载多对多关系时,这里没有 N 个查询,只有三个查询,单独执行时效率更高,因此尝试执行单个查询没有任何好处。
总而言之,我需要说的是,ORM 性能对我们 Pony ORM 开发人员来说非常重要。 因为,我们不想在单个查询中实现加载多对多关系,因为它肯定会更慢 比我们目前的解决方案。
因此,要回答您的问题,您不能在单个查询中同时加载多对多关系的两端。我认为这是一件好事。