Rails Rails 中 MySQL IN () 关联子查询问题

sanster · March 16, 2017 · Last by quakewang replied at March 17, 2017 · 1661 hits

背景

称如下这种查询为关联子查询:

SELECT ... FROM t1 WHERE t1.a IN (SELECT b FROM t2);

MySQL5.5 的 关联子查询有很严重的性能问题,《高性能 MySQL》(第三版) 6.5.1 节里有提到,

MySQL 自己的 subquery-restrictions 文档 的第一条也提到了这点。

问题

我们自己部署的 gitlab 用了 MySQL5.5 作为数据库(gitlab 官方推荐使用 PG...), 代码里面有很多关联子查询,比如下面链接里的代码: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/finders/union_finder.rb#L6

class UnionFinder
  def find_union(segments, klass)
    if segments.length > 1
      union = Gitlab::SQL::Union.new(segments.map { |s| s.select(:id) })
      klass.where("#{klass.table_name}.id IN (#{union.to_sql})")  # 这里会成 IN() 子查询
    else
      segments.first
    end
  end
end

目前解决的方法是用 union 先把 id 查出来然后再给 where:

ids = klass.find_by_sql(union.to_sql).map(&:id)
klass.where("#{klass.table_name}.id" => ids) 

两个问题:

  • 没有一个工具像 Bullet 那样能找出这种关联子查询?
  • 除了一个一个人工找出来这种查询,以及迁移到 PG 的方案外,不知道有什么其他的方案?

虽然 MySQL 5.7 的 subquery-restrictions 文档里已经把 5.5 文档里第一条关于关联子查询的内容移除了,但是在 5.7 上测试以后发现还是很慢!是我的使用姿势不对么😂

用 inner join…

Reply to hooopo

恩,有些地方是这么改的。。。

工具嘛,跑一段时间,匹配日志里的 where in

数据库开 slow query log,5.7 还是慢可能和查询语句有关,把 log 到的语句 explain 一下看看

sanster in MySQL 的 order by and limit 查询语句 mention this topic. 21 Mar 19:53
You need to Sign in before reply, if you don't have an account, please Sign up first.