8

我目前正在尝试查询具有多个 (3) 子句的表,例如:

 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 将允许我强制它使用索引,但否则会选择另一个非覆盖索引。无论将哪个INs 更改为 ,情况都是如此=

我的问题:

可用于索引查询的 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)

4

1 回答 1

8

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

即使您有一个多列索引(a、b、c、d、日期),最左边的列也应该用于相等谓词( =),并且最多一列可以用于范围谓词。索引中的任何后续列都无济于事。

例子:

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

所以aandb是相等谓词,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

我在我的演示文稿如何设计索引中更多地讨论了这一点,真的。


回复您的评论:

有什么方法可以解决这个问题而不必重写代码?

你已经明白了,你只能有一个从索引中受益的范围谓词。您仍然可以在 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 子句中引用的列以及我们正在搜索的特定值有不同的选择。因此,我们可能需要另一个具有不同列集的索引,甚至是不同顺序的相同列。需要多个索引并不少见,因为正如我在演示文稿中提到的,您需要创建的索引取决于您要优化的查询。

于 2013-10-30T22:00:42.453 回答