MySQL中使用索引的最大IN子句数

时间:2013-10-30 21:44:24

标签: mysql sql

我目前正在尝试查询包含多个(3)in子句的表:

 SELECT *
 FROM table
 WHERE
     a IN (2884,5320)
   AND 
     b IN ('a', 'b', 'c')
   AND 
     c IN (1, 2, 3)
   AND d='abcd'
   AND date BETWEEN 0 AND 1383177599

该表的索引类似于index(a, b, c, d, date)

然而,当我对查询运行解释时,解释器显示没有合适的索引可供使用。即使我FORCE INDEX,情况仍然如此。

如果我将上述IN之一更改为=,例如

SELECT *
FROM table
WHERE
    a = 2884
  AND 
    b IN ('a', 'b', 'c')
  AND 
    c IN (1, 2, 3)
  AND d = 'abcd'
  AND date BETWEEN 0 AND 1383177599

MySQL允许我强制它使用索引,但是会选择另一个非覆盖索引。无论IN中的哪一个更改为=,都会出现这种情况。

我的问题:

您可以为索引查询使用的in子句数量是否有限制?有什么明显的东西我在这里不见了吗?

关于桌子的一些事情:
9 GB,~8,000,000行。它包含一个可能非常大的文本列(JSON字段),但此列不是上面列出的任何列。上面显示的in子句可能会大得多(200-300项)

谢谢!

编辑:
这是查询解释的输出(FORCE INDEX1,"SIMPLE","table","ALL","correct_index",NULL,NULL,NULL,6977553,"Using where" 其中正确的索引是上面解释的索引(index(a, b, c, d, date)

1 个答案:

答案 0 :(得分:4)

您不能期望通过范围谓词的索引搜索多个列,例如IN

即使你有一个多列索引(a,b,c,d,日期),最左边的列应该是等式谓词=),最多一个列可以用于范围谓词。索引中的任何后续列都没有帮助。

示例:

WHERE a = 2884 AND b = 'b' AND c IN (1, 2, 3) AND d = 'abcd'

所以ab是等式谓词,c是范围谓词,d是另一个等式谓词。

对查询运行EXPLAIN,并注意lenref列表示您仅使用索引的前两列。 d的条件是通过搜索前三列索引找到的所有行来完成的。

           id: 1
  select_type: SIMPLE
        table: t
         type: ref
possible_keys: a
          key: a
      key_len: 7              <--- two columns' length
          ref: const,const    <--- only two values for index columns `a` and `b`
         rows: 4
        Extra: Using where; Using index

c更改为等式谓词允许所有四列用于索引查找:

WHERE a = 2884 AND b = 'b' AND c = 2 AND d = 'abcd'

           id: 1
  select_type: SIMPLE
        table: t
         type: ref
possible_keys: a
          key: a
      key_len: 25                      <--- four columns' length
          ref: const,const,const,const <--- four values
         rows: 2
        Extra: Using where; Using index

我在演讲How to Design Indexes, Really中更多地谈到这一点。


重新评论:

  

有没有办法解决这个问题而不必重写代码?

您已经明白您只能拥有一个从索引中获益的范围谓词。您仍然可以在WHERE子句中使用其他范围谓词,但它们不会从索引中获得任何好处。

但这不是一个交易破坏者,因为如果您索引的一个范围表达式可以帮助缩小搜索范围99%,那就是胜利。然后将其他表达式应用于匹配的行是我们可以忍受的成本。

如果可以,优化器将尝试选择最有效的索引,这很大程度上取决于索引的选择性。然后查询使用索引来缩小搜索范围,并且只针对其他条件测试通过该搜索的行的子集。

再看看你的问题:

... WHERE
 a IN (2884,5320)
AND 
 b IN ('a', 'b', 'c')
AND 
 c IN (1, 2, 3)
AND d='abcd'
AND date BETWEEN 0 AND 1383177599

假设我们知道只有1%的行符合c IN (1,2,3),但其他字词的匹配程度更接近20-40%。

我们可以为等式谓词编制索引,这没关系。然后我们为索引选择另一列,因为所有其他术语都是范围谓词。我们选择最具选择性的列:c。因此,最佳索引位于(d,c),并且必须按此顺序

您的应用中可能有其他查询对WHERE子句中引用的列有不同的选择,以及我们要搜索的具体值。因此,我们可能需要另一个具有不同列集的索引,甚至是不同顺序的相同列。需要多个索引并不罕见,因为正如我在演示文稿中提到的,您需要创建的索引取决于您要优化的查询。