如何在加载关系时指示SQLAlchemy ORM并行执行多个查询?

时间:2017-01-24 11:50:11

标签: python mysql orm parallel-processing sqlalchemy

我正在使用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步(并行):

  • 获取组织
  • 获取学校
  • 获取组织的项目,加入凭据
  • 获取学校课程,加入证书

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,他的OrganizationsSchools等等都可以通过单个查询完成(在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;。