我目前正在尝试查询包含多个(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 INDEX
)
1,"SIMPLE","table","ALL","correct_index",NULL,NULL,NULL,6977553,"Using where"
其中正确的索引是上面解释的索引(index(a, b, c, d, date)
)
答案 0 :(得分:4)
您不能期望通过范围谓词的索引搜索多个列,例如IN
。
即使你有一个多列索引(a,b,c,d,日期),最左边的列应该是等式谓词(=
),最多一个列可以用于范围谓词。索引中的任何后续列都没有帮助。
示例:
WHERE a = 2884 AND b = 'b' AND c IN (1, 2, 3) AND d = 'abcd'
所以a
和b
是等式谓词,c
是范围谓词,d
是另一个等式谓词。
对查询运行EXPLAIN,并注意len
和ref
列表示您仅使用索引的前两列。 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子句中引用的列有不同的选择,以及我们要搜索的具体值。因此,我们可能需要另一个具有不同列集的索引,甚至是不同顺序的相同列。需要多个索引并不罕见,因为正如我在演示文稿中提到的,您需要创建的索引取决于您要优化的查询。