Rails:加入记录的性能问题

use*_*950 13 mysql activerecord ruby-on-rails database-performance ruby-on-rails-4

我使用ActiveRecord和MySQL进行以下设置:

用户通过成员身份拥有多个组通过成员身份,
组拥有许多用户

schema.rb中还描述了group_id和user_id的索引:

add_index "memberships", ["group_id", "user_id"], name: "uugj_index", using: :btree
Run Code Online (Sandbox Code Playgroud)

3个不同的查询:

User.where(id: Membership.uniq.pluck(:user_id))`
Run Code Online (Sandbox Code Playgroud)

(3.8ms)SELECT DISTINCT memberships.user_idFROM memberships User Load(11.0ms)SELECT users.*FROM usersWHERE users.idIN(1,2 ......)

User.where(id: Membership.uniq.select(:user_id))
Run Code Online (Sandbox Code Playgroud)

用户加载(15.2ms)SELECT users.*FROM usersWHERE users.idIN(SELECT DISTINCT memberships.user_idFROM memberships)

User.uniq.joins(:memberships)
Run Code Online (Sandbox Code Playgroud)

用户负载(135.1ms)SELECT DISTINCT users.*FROM usersINNER JOIN membershipsON memberships.user_id= users.id

这样做的最佳方法是什么?为什么带连接的查询要慢得多?

Nat*_*han 7

第一个查询是错误的,因为它将所有用户id吸收到Ruby数组中,然后将它们发送回数据库.如果你有很多用户,这是一个巨大的阵列和巨大的带宽,加上2次往返数据库而不是一次.此外,数据库无法有效处理这个庞大的数组.

第二种和第三种方法都是有效的数据库驱动的解决方案(一种是子查询,一种是连接),但您需要具有适当的索引.你需要memberships桌子上一个索引user_id.

add_index :memberships, :user_id

您已经拥有的索引仅在您想要查找属于特定组的所有用户时才有用.

更新:

如果您在有大量列和数据users表中,DISTINCT users.*在第三查询将是相当缓慢的,因为MySQL有一个大量的数据,以确保其唯一性比较.

要明确:这不是内在的缓慢JOIN,而是缓慢的DISTINCT.例如:这是一种避免使用DISTINCT和仍然使用的方法JOIN:

SELECT users.* FROM users
INNER JOIN (SELECT DISTINCT memberships.user_id FROM memberships) AS user_ids
ON user_ids.user_id = users.id;
Run Code Online (Sandbox Code Playgroud)

鉴于所有这些,在这种情况下,我相信第二个查询将是最适合您的方法.如果添加上述索引,第二个查询应该比原始结果中报告的更快.如果您在添加索引后还没有这样做,请重试第二种方法.

虽然第一个查询本身有一些缓慢的问题,但是从你的评论中可以看出它仍然比第三个查询更快(至少对于你的特定数据集).这些方法的权衡取决于您的特定数据集,关于您拥有多少用户以及您拥有多少成员资格.一般来说,我认为第一种方法仍然是最差的,即使它最终更快.

另请注意,我推荐的索引特别针对您在问题中列出的三个查询而设计.如果您对这些表有其他类型的查询,则可以通过其答案中提到的@tata为其他索引或可能的多列索引提供更好的服务.