我有一个很大的表(数百万行),运行在MariaDB(InnoDB,5.5.48-MariaDB-1〜precise-wsrep)上,假设我的表结构如下
[
ID,
Field A,
Field B,
Field C,
Field D
]
我在此表上有3个索引:
- PRIMARY[ID]
- INDEX 1 -> [A,B,C]
- INDEX 2 -> [A, D]
我要优化的查询如下
SELECT * FROM table
WHERE (a = val1) AND (B NOT IN ([val2, val3])) AND (C BETWEEN val4 AND val5)
ORDER BY ID ASC LIMIT 50 OFFSET 100
此查询应该自然适合我的INDEX 1,对吗?但是,玛丽亚更喜欢使用PRIMARY INDEX,这基本上意味着对表进行全表扫描(导致40多个查询...)。
当我从该查询中删除ORDER或LIMIT(或两者)时,Maria DB能够选择INDEX 2,这显然比PRIMARY更好。
问题1->当ORDER BY和LIMIT结合使用时,为什么Maria会退回到PRIMARY INDEX吗?
我决定通过禁止使用PRIMARY来对查询进行一些调整。
SELECT * FROM table IGNORE INDEX(`PRIMARY`)
WHERE (a = val1) AND (B NOT IN ([val2, val3])) AND (C BETWEEN val4 AND val5)
ORDER BY ID ASC LIMIT 50 OFFSET 100
结果->对我的第一个优化感到非常满意,这个40秒的查询现在花费1秒,但仍然...
问题2->为什么MariaDB选择INDEX 2?
当我强迫Maria使用INDEX 1时,查询下降到100ms延迟(快10倍),因此,我还不完全满意...
感谢您的帮助:)
答案 0 :(得分:1)
是B NOT IN (val2, vl3)
部分无法使用索引,或者没有您认为的那么有效。我建议您创建此索引:
(A, C, B)
答案 1 :(得分:0)
在不了解Maria的情况下,我猜想是您将ID限制为前50个的原因。
如果使用索引1,系统将无法知道哪个 50个ID最低,因此必须读取查询的 all 个匹配项(< em> you 知道这样做会更快,但是优化器不知道),然后读取其所有ID,然后对前50个进行排序并取整,然后删除其余的。
这里的“其余”可能是5亿条记录-优化器无法事先知道;因此它决定按ID进行,并累积匹配项,直到获得您的前50个。
我假设如果您将限制值删除为50(或按ID指定ORDER),那么它会很高兴使用索引1。
另一种选择是将ID包含在索引1中,甚至在末尾也可以;这样一来,系统就可以对索引的结果进行排序和过滤,因此看起来非常适合。