【发布时间】:2017-06-09 05:05:39
【问题描述】:
我正在使用 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 步(并行):
- 对于每个项目,获取其凭据
- 为每门课程获取其证书
其实此时,做一个子查询类型加载也是可以的,即在两个单独的查询中分别返回Organization和OrganizationID/Project/Credentials:
例如 第 1 步(并行):
- 获取用户
- 获取用户的组织
- 获取用户的学校
- 获取用户凭据
第 2 步(并行):
- 获取组织
- 获取学校
- 获取组织的项目,加入凭证
- 获取学校课程,加入证书
【问题讨论】:
-
让我们看看你的 15 秒查询的 SQL;也许我们可以从那里开始工作。
-
您可以查看范围会话以了解并发方法:sqlalchemy: scoped session
-
范围会话不会给你并发性 - 除非我遗漏了什么?
标签: python mysql orm parallel-processing sqlalchemy