我正在使用SQLAlchemy的ORM。我有一个具有多对多关系的模型:
User
User <--MxN--> Organization
User <--MxN--> School
User <--MxN--> Credentials
我正在使用association tables实现这些,因此还有我不直接使用的User_to_Organization,User_to_School和User_to_Credentials表。
现在,当我尝试使用加入的热切加载加载单个用户(使用其PK标识符)及其关系(和相关模型)时,我会得到可怕的性能(15秒以上)。我认为这是由于this issue:
当多个深度级别与连接或子查询加载一起使用时,在集合内加载集合将乘以以笛卡尔方式提取的总行数。两种形式的急切加载始终从原始父类加入。
如果我在层次结构中引入另一个或两个级别:
Organization <--1xN--> Project
School <--1xN--> Course
Project <--MxN--> Credentials
Course <--MxN--> Credentials
查询需要50秒以上才能完成,即使每个表中的记录总量相当小。
使用延迟加载,我需要手动加载每个关系,并且有多次到服务器的往返。
e.g。 操作,作为查询串行执行:
尽管如此,它还是在不到200毫秒的时间内完成。
我想知道是否确实使用延迟加载,但是并行执行加载查询的关系。例如,使用concurrent
模块,asyncio
或使用gevent
。
e.g。 第1步(并行):
第2步(并行):
步骤3(并行):
实际上,此时,进行子查询类型加载也可以工作,即在两个单独的查询中返回Organization和OrganizationID / Project / Credentials:
e.g。 第1步(并行):
第2步(并行):
答案 0 :(得分:2)
您要做的第一件事是检查数据库上实际执行的查询。我不认为SQLAlchemy正在做你期望的事情,除非你非常熟悉它。您可以在引擎配置上使用echo=True
或查看一些db日志(不知道如何使用mysql执行此操作)。
你已经提到过你正在使用不同的加载策略,所以我猜你已经阅读了那些文档( http://docs.sqlalchemy.org/en/latest/orm/loading_relationships.html)。对于你正在做的事情,我可能会建议子查询加载,但这完全取决于你正在处理的行数/列数。根据我的经验,这是一个很好的总体起点。
有一点需要注意,您可能需要:
db.query(Thing).options(subqueryload('A').subqueryload('B')).filter(Thing.id==x).first()
使用filter.first
而不是get
,因为如果主要对象已经在身份映射中,后一种情况将不会根据您的加载策略重新执行查询。
最后,我不知道你的数据 - 但是对于任何一个庞大的数据集,这些数字听起来都非常糟糕。检查您是否在所有表上都指定了正确的索引。
您可能已经完成了所有这些工作,但根据您提供的信息,听起来您需要做更多工作来缩小您的问题范围。它是db模式,还是SQLA正在执行的查询?
无论哪种方式,我都会说“不”在不同的连接上运行多个查询。任何尝试这样做都可能导致不一致的数据返回到您的应用程序,如果您认为现在有问题.....: - )
答案 1 :(得分:0)
MySQL在单个连接中没有并行性。要使ORM这样做,需要多次连接MySQL。一般来说,尝试这样做的开销是“不值得的”#34;。
要获得user
,他的Organizations
,Schools
等等都可以通过单个查询完成(在mysql中):
SELECT user, organization, ...
FROM Users
JOIN Organizations ON ...
etc.
这比
效率更高SELECT user FROM ...;
SELECT organization ... WHERE user = ...;
etc.
(这不是&#34;并行性&#34;。)
或许你的&#34;步骤&#34;不太正确&#39;?...
SELECT user, organization, project
FROM Users
JOIN Organizations ...
JOIN Projects ...
只需一步即可完成所有用户及其所有组织和项目。
但是&#34;用户&#34;与&#34;项目相关联&#34;?如果没有,那么这是错误的方法。
如果ORM没有提供生成类似查询的机制,那么它就会阻碍&#34;阻碍&#34;。