我正在使用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+秒才能完成。
使用延迟加载,我需要手动加载每个关系,并有多个往返到服务器。
例如,操作,作为查询依次执行:
尽管如此,它都在不到200毫秒内完成。
我想知道是否确实需要使用延迟加载,但是并行执行关系加载查询。例如,使用concurrent
模块、asyncio
或通过使用gevent
。
例如步骤1(并行):
步骤2(并行):
步骤3(并行):
实际上,此时,使子查询类型load也可以工作,即在两个单独的查询中返回Organization和OrganizationID/Project/凭据:
例如步骤1(并行):
步骤2(并行):
发布于 2017-02-07 17:31:36
您要做的第一件事是检查在db上实际执行了哪些查询。除非您对SQLAlchemy非常熟悉,否则我不会认为它是在做您期望的事情。您可以在引擎配置中使用echo=True
,或者查看一些db日志(不确定如何使用mysql)。
您已经提到您使用了不同的加载策略,所以我想您已经阅读了关于它的文档( relationships.html)。对于您所做的工作,我可能会建议加载子查询,但这完全取决于所处理的行/列的数量。但以我的经验来看,这是一个很好的总体起点。
有一件事要注意,你可能需要这样的东西:
db.query(Thing).options(subqueryload('A').subqueryload('B')).filter(Thing.id==x).first()
使用filter.first
而不是get
,如果主对象已经在标识映射中,则不会根据加载策略重新执行查询。
最后,我不知道你的数据-但这些数字听起来相当糟糕,任何一个巨大的数据集。检查是否在所有表上指定了正确的索引。
您可能已经经历了所有这些,但根据您提供的信息,听起来您需要做更多的工作来缩小您的问题。是数据库模式,还是SQLA正在执行的查询?
无论哪种方式,我都会说,“不”在不同的连接上运行多个查询。任何试图这样做都可能导致不一致的数据返回到您的应用程序,如果您认为您现在有问题……:-)
发布于 2017-02-02 17:57:46
MySQL在单个连接中没有并行性。要使ORM这样做,需要多个连接到MySQL。一般来说,尝试这样做的开销是“不值得的”。
要获得一个user
,他的Organizations
、Schools
等都可以通过一个查询来完成(在mysql中):
SELECT user, organization, ...
FROM Users
JOIN Organizations ON ...
etc.
这比
SELECT user FROM ...;
SELECT organization ... WHERE user = ...;
etc.
(这不是“并行”。)
或者你的“步子”不完全是‘对’?
SELECT user, organization, project
FROM Users
JOIN Organizations ...
JOIN Projects ...
在一个步骤中,所有用户,以及他们的所有组织和项目。
但是,“用户”是否与“项目”相关联?如果没有,那么这是错误的做法。
如果ORM没有提供一种机制来生成这样的查询,那么它就是“碍手碍脚”。
https://stackoverflow.com/questions/41827516
复制