无论如何,是否可以通过此请求对我的数据库产生较小的影响?
Is there anyway to make a lesser impact on my database with this request?
为了分析我的网站,我需要提取用户的 4 种状态。
@members = list.members.where(enterprise_registration_id: registration.id)
# This pulls roughly 10,0000 records.. Which is evidently a huge data pull for Rails
# Member Load (155.5ms)
@invited = @members.where("user_id is null")
# Member Load (21.6ms)
@not_started = @members.where("enterprise_members.id not in (select enterprise_member_id from quizzes where quizzes.section_id IN (?)) AND enterprise_members.user_id in (select id from users)", @sections.map(&:id) )
# Member Load (82.9ms)
@in_progress = @members.joins(:quizzes).where('quizzes.section_id IN (?) and (quizzes.completed is null or quizzes.completed = ?)', @sections.map(&:id), false).group("enterprise_members.id HAVING count(quizzes.id) > 0")
# Member Load (28.5ms)
@completes = Quiz.where(enterprise_member_id: registration.members, section_id: @sections.map(&:id)).completed
# Quiz Load (138.9ms)
操作 returns 503 表示我的应用放弃请求。有什么想法可以将此代码重构为 运行 更快吗?也许通过更好的连接语法?我很好奇具有较大数据集的站点如何完成看似微不足道的数据库调用。
第三个查询:
不幸的是,我不知道 ruby 关于 rails,但是从 postgresql 的角度来看,将您的 "not in" 更改为左外连接应该会使它更快一些:
您的代码:
enterprise_members.id not in (select enterprise_member_id from quizzes where quizzes.section_id IN (?)) AND enterprise_members.user_id in (select id from users)", @sections.map(&:id) )
更好的版本(SQL):
select blah
from enterprise_members em
left outer join quizzes q on q.enterprise_member_id = em.id
join users u on u.id = q.enterprise_member_id
where quizzes.section_id in (?)
and q.enterprise_member_id is null
根据我的理解,这将允许 postgres 对 enterprise_members table 和测验进行排序并进行散列连接。这比现在要好。现在它在 quizzes 子查询中找到所有内容,将其存入内存,然后 然后 尝试将其与 enterprise_members 匹配。
第一个查询:
您还可以在 user_id 上为您的第一个查询创建部分索引。如果在较大的 table 中有相对较少的 user_id 为 null,这将特别好。部分索引创建:
CREATE INDEX user_id_null_ix ON enterprise_members (user_id)
WHERE (user_id is null);
任何时候您使用与索引的 where 子句匹配的内容查询 enterprise_members 时,都可以使用部分索引并快速限制返回的行。有关详细信息,请参阅 http://www.postgresql.org/docs/9.4/static/indexes-partial.html。
答案是您的索引。检查您的 rails 日志(或在开发模式下检查控制台)并将查询复制到您的数据库工具。在查询前面打一个 "Explain",它会给你一个细分。从这里您可以看到您需要哪些索引来优化查询。
为了快速通过,您至少应该在您的架构中包含这些,
- enterprise_members:需要 enterprise_member_id
上的索引
- 成员:user_id
- 测验:section_id
正如其他人发布的那样,如果需要,肯定会考虑添加索引。一些如何重构取决于你究竟想用所有这些记录做什么。对于@members 查询,您使用@members 记录做什么?您真的需要检索每个成员记录的所有属性吗?如果您没有使用每个属性,我建议只获取您实际用于某事的属性,.pluck 的使用是有保证的。第三和第四个查询,看起来很可疑。我假设您已经 运行 控制台中的查询?再次不确定查询的用途是什么,但我会折腾一下,首先编写原始 sql 并首先在数据库上查询通常很有用。然后,您可以应用您的发现来重写 activerecord 查询。
最后的.completed 标记是什么?它应该在那里吗?我在 rails api 中找到的唯一东西是 .completed?如果它是一种自定义方法,一定要研究它。您可能还有范围的用例。
感谢大家的想法。我基本上按照大家说的做了。我添加了索引,采用了我调用所有内容的方式,但主要区别在于使用 pluck
方法。这是我的新统计数据:
@alt_members = list.members.pluck :id # 23ms
if list.course.sections.tests.present? && @sections = list.course.sections.tests
@quiz_member_ids = Quiz.where(section_id: @sections.map(&:id)).pluck(:enterprise_member_id) # 8.5ms
@invited = list.members.count('user_id is null') # 12.5ms
@not_started = ( @alt_members - ( @alt_members & @quiz_member_ids ).count #0ms
@in_progress = ( @alt_members & @quiz_member_ids ).count # 0ms
@completes = ( @alt_members & Quiz.where(section_id: @sections.map(&:id), completed: true).pluck(:enterprise_member_id) ).count # 9.7ms
@question_count = Quiz.where(section_id: @sections.map(&:id), completed: true).limit(5).map{|quiz|quiz.answers.count}.max # 3.5ms
为了分析我的网站,我需要提取用户的 4 种状态。
@members = list.members.where(enterprise_registration_id: registration.id)
# This pulls roughly 10,0000 records.. Which is evidently a huge data pull for Rails
# Member Load (155.5ms)
@invited = @members.where("user_id is null")
# Member Load (21.6ms)
@not_started = @members.where("enterprise_members.id not in (select enterprise_member_id from quizzes where quizzes.section_id IN (?)) AND enterprise_members.user_id in (select id from users)", @sections.map(&:id) )
# Member Load (82.9ms)
@in_progress = @members.joins(:quizzes).where('quizzes.section_id IN (?) and (quizzes.completed is null or quizzes.completed = ?)', @sections.map(&:id), false).group("enterprise_members.id HAVING count(quizzes.id) > 0")
# Member Load (28.5ms)
@completes = Quiz.where(enterprise_member_id: registration.members, section_id: @sections.map(&:id)).completed
# Quiz Load (138.9ms)
操作 returns 503 表示我的应用放弃请求。有什么想法可以将此代码重构为 运行 更快吗?也许通过更好的连接语法?我很好奇具有较大数据集的站点如何完成看似微不足道的数据库调用。
第三个查询:
不幸的是,我不知道 ruby 关于 rails,但是从 postgresql 的角度来看,将您的 "not in" 更改为左外连接应该会使它更快一些:
您的代码:
enterprise_members.id not in (select enterprise_member_id from quizzes where quizzes.section_id IN (?)) AND enterprise_members.user_id in (select id from users)", @sections.map(&:id) )
更好的版本(SQL):
select blah
from enterprise_members em
left outer join quizzes q on q.enterprise_member_id = em.id
join users u on u.id = q.enterprise_member_id
where quizzes.section_id in (?)
and q.enterprise_member_id is null
根据我的理解,这将允许 postgres 对 enterprise_members table 和测验进行排序并进行散列连接。这比现在要好。现在它在 quizzes 子查询中找到所有内容,将其存入内存,然后 然后 尝试将其与 enterprise_members 匹配。
第一个查询:
您还可以在 user_id 上为您的第一个查询创建部分索引。如果在较大的 table 中有相对较少的 user_id 为 null,这将特别好。部分索引创建:
CREATE INDEX user_id_null_ix ON enterprise_members (user_id)
WHERE (user_id is null);
任何时候您使用与索引的 where 子句匹配的内容查询 enterprise_members 时,都可以使用部分索引并快速限制返回的行。有关详细信息,请参阅 http://www.postgresql.org/docs/9.4/static/indexes-partial.html。
答案是您的索引。检查您的 rails 日志(或在开发模式下检查控制台)并将查询复制到您的数据库工具。在查询前面打一个 "Explain",它会给你一个细分。从这里您可以看到您需要哪些索引来优化查询。
为了快速通过,您至少应该在您的架构中包含这些,
- enterprise_members:需要 enterprise_member_id 上的索引
- 成员:user_id
- 测验:section_id
正如其他人发布的那样,如果需要,肯定会考虑添加索引。一些如何重构取决于你究竟想用所有这些记录做什么。对于@members 查询,您使用@members 记录做什么?您真的需要检索每个成员记录的所有属性吗?如果您没有使用每个属性,我建议只获取您实际用于某事的属性,.pluck 的使用是有保证的。第三和第四个查询,看起来很可疑。我假设您已经 运行 控制台中的查询?再次不确定查询的用途是什么,但我会折腾一下,首先编写原始 sql 并首先在数据库上查询通常很有用。然后,您可以应用您的发现来重写 activerecord 查询。
最后的.completed 标记是什么?它应该在那里吗?我在 rails api 中找到的唯一东西是 .completed?如果它是一种自定义方法,一定要研究它。您可能还有范围的用例。
感谢大家的想法。我基本上按照大家说的做了。我添加了索引,采用了我调用所有内容的方式,但主要区别在于使用 pluck
方法。这是我的新统计数据:
@alt_members = list.members.pluck :id # 23ms
if list.course.sections.tests.present? && @sections = list.course.sections.tests
@quiz_member_ids = Quiz.where(section_id: @sections.map(&:id)).pluck(:enterprise_member_id) # 8.5ms
@invited = list.members.count('user_id is null') # 12.5ms
@not_started = ( @alt_members - ( @alt_members & @quiz_member_ids ).count #0ms
@in_progress = ( @alt_members & @quiz_member_ids ).count # 0ms
@completes = ( @alt_members & Quiz.where(section_id: @sections.map(&:id), completed: true).pluck(:enterprise_member_id) ).count # 9.7ms
@question_count = Quiz.where(section_id: @sections.map(&:id), completed: true).limit(5).map{|quiz|quiz.answers.count}.max # 3.5ms