<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>sanster (cwq)</title>
    <link>https://ruby-china.org/sanster</link>
    <description/>
    <language>en-us</language>
    <item>
      <title>MySQL 的 order by and limit 查询语句</title>
      <description>&lt;p&gt;继这个帖子（&lt;a href="https://ruby-china.org/topics/32552" rel="nofollow" target="_blank"&gt;https://ruby-china.org/topics/32552&lt;/a&gt;）中提到的坑之后，今天又遇到了一个经典的 MySQL 坑 &lt;a href="http://stackoverflow.com/questions/14368211/mysql-very-slow-order-by" rel="nofollow" target="_blank" title=""&gt;mysql slow order by with limit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;代码里的原始查询，这个查询查出来 &lt;strong&gt;6&lt;/strong&gt; 个结果，耗时 1000ms。&lt;/p&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;  &lt;span class="n"&gt;merge_requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;merge_requests&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;
  &lt;span class="n"&gt;merge_requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;state&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'opened'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'reopened'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt;
  &lt;span class="n"&gt;merge_requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;target_project_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;32100&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
  &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="k"&gt;OFFSET&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;按照 &lt;a href="https://explainextended.com/2011/02/11/late-row-lookups-innodb/" rel="nofollow" target="_blank"&gt;https://explainextended.com/2011/02/11/late-row-lookups-innodb/&lt;/a&gt; 的介绍改写，耗时 500ms，还是好久。。&lt;/p&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;  &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;merge_requests&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt;
      &lt;span class="k"&gt;state&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'opened'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'reopened'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt;
      &lt;span class="n"&gt;target_project_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;32100&lt;/span&gt;
    &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
    &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;
   &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;merge_requests&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果把 &lt;code&gt;LIMIT&lt;/code&gt; 改成 6 或者 6 以下，耗时 10ms！；如果把 where 中的条件去掉一个，耗时也是 10ms！...还没找到比较好的写法。。&lt;/p&gt;

&lt;p&gt;gitlab 的各种分页很容易有这种查询，自己部署 gitlab 慎用 MySQL...不过大概搜了一下，好像 PG 也有这种问题，还是要优化查询语句啊。。。&lt;/p&gt;</description>
      <author>sanster</author>
      <pubDate>Tue, 21 Mar 2017 19:53:57 +0800</pubDate>
      <link>https://ruby-china.org/topics/32599</link>
      <guid>https://ruby-china.org/topics/32599</guid>
    </item>
    <item>
      <title>Rails 中 MySQL IN () 关联子查询问题</title>
      <description>&lt;h2 id="背景"&gt;背景&lt;/h2&gt;
&lt;p&gt;称如下这种查询为关联子查询：&lt;/p&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;MySQL5.5 的 关联子查询有很严重的性能问题，《高性能 MySQL》(第三版) 6.5.1 节里有提到，&lt;img src="https://l.ruby-china.com/photo/2017/2328bb4051e71fde7208921140f83c3d.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;MySQL 自己的 &lt;a href="https://dev.mysql.com/doc/refman/5.5/en/subquery-restrictions.html" rel="nofollow" target="_blank" title=""&gt;subquery-restrictions 文档&lt;/a&gt; 的第一条也提到了这点。&lt;/p&gt;
&lt;h2 id="问题"&gt;问题&lt;/h2&gt;
&lt;p&gt;我们自己部署的 gitlab 用了 MySQL5.5 作为数据库（gitlab 官方&lt;a href="https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#6-database" rel="nofollow" target="_blank" title=""&gt;推荐使用 PG&lt;/a&gt;...），
代码里面有很多关联子查询，比如下面链接里的代码：
&lt;a href="https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/finders/union_finder.rb#L6" rel="nofollow" target="_blank"&gt;https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/finders/union_finder.rb#L6&lt;/a&gt;&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UnionFinder&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;find_union&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;segments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;segments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="n"&gt;union&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Gitlab&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SQL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Union&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;segments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;table_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.id IN (&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;union&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_sql&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# 这里会成 IN() 子查询&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;segments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;目前解决的方法是用 union 先把 id 查出来然后再给 where：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by_sql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;union&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_sql&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;table_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.id"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;两个问题：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;没有一个工具像 Bullet 那样能找出这种关联子查询？&lt;/li&gt;
&lt;li&gt;除了一个一个人工找出来这种查询，以及迁移到 PG 的方案外，不知道有什么其他的方案？&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;虽然 MySQL 5.7 的 &lt;a href="https://dev.mysql.com/doc/refman/5.7/en/subquery-restrictions.html" rel="nofollow" target="_blank" title=""&gt;subquery-restrictions 文档&lt;/a&gt;里已经把 5.5 文档里第一条关于关联子查询的内容移除了，但是在 5.7 上测试以后发现还是很慢！是我的使用姿势不对么&lt;img title=":joy:" alt="😂" src="https://twemoji.ruby-china.com/2/svg/1f602.svg" class="twemoji"&gt; &lt;/p&gt;</description>
      <author>sanster</author>
      <pubDate>Thu, 16 Mar 2017 20:13:57 +0800</pubDate>
      <link>https://ruby-china.org/topics/32552</link>
      <guid>https://ruby-china.org/topics/32552</guid>
    </item>
  </channel>
</rss>
