我正在学习如何使用索引进行正确的查询优化。假设我有一个巨大的产品表,其中包含每种产品的各种详细信息,例如:价格,类别,购买数量,评论平均值等。当有多个“where”条件时,我了解到最好将多列索引放在“where”条件的任何位置,按它们出现的顺序排列。
但是,如果针对不同目的有如此多的查询,并且如果用户选择如何过滤产品表,那么我很难弄清楚如何扩展它。例如,用户可以浏览产品WHERE rating > 4 AND purchases > 100
,也可以是WHERE category = 'x' AND price < 100 AND price > 20
。如果选择要过滤的列是随机的,那么正确的多列索引如何工作?
答案 0 :(得分:1)
我了解到最好将多列索引放在你的&#34;其中&#34;条件是它们出现的顺序。
你学到了......不太正确。
WHERE
子句中的出现顺序没有意义,因为优化器可以以任何逻辑上有效的方式自由地评估条件,当然还要包括括号和逻辑运算符(AND
,{表达式中的{1}}等。
多列索引中列的顺序很重要,因为从左到右,只要在where子句中未提及的索引中遇到列,就不会再向该索引的右侧添加列了使用。
如果索引3列(a,b,c),并且查询为OR
,那么优化器将只能使用最左边的&#34; a&#34;该索引中的列值,而不是&#34; c&#34;。
在这种情况下,它可能仍然会选择使用索引查找a = 1的行,然后仅扫描那些c = 6的行。
您可以将多列索引可视化为多维数组。如果没有您需要匹配第一列(a)的已知值或范围,则第二列(b)的值是无意义的,无序的混乱数据,因为它们在&#34;组中排序&#39; a&#39;&#34; ...你必须遍历每一个&#34; a&#34;找到匹配的&#34; b&#34;值,并遍历每个&#34; a,b&#34;找到匹配的&#34; c&#34;值。因为,在上面的例子中,&#34; b&#34;价值是&#34;任何&#34;因为它没有被指定,所以&#34; c&#34;的排序。对于优化查询,值无意义且无法访问(尽管WHERE a = 1 AND c = 6
列表中的每个列在单个索引中都可用,优化程序可以扫描索引而不是扫描整个表,将其视为&#34;覆盖索引,&#34;这通常比全表扫描更好,但仍然不是最理想的。)
如果您的SELECT
子句包含两个列,这两列都是单独索引的,优化器将检查索引统计信息并尝试使用最有可能产生最少匹配的列...如果&#34 ;一个&#34;和&#34; c&#34;每个都有一个单独的索引,索引统计表明有很多值为&#34; c&#34; (高基数)但只有几个值为&#34; a&#34; (低基数)优化器通常会使用&#34; c&#34;找到匹配的行,然后扫描所有这些行以获取&#34; a&#34;的请求值。
或者,它可能会尝试使用两个索引的并集来精确识别哪些行满足两个条件。
这两种策略都不是最优的,但仍远远优于全表扫描,所以它建议你至少应该将每个可独立搜索的列作为索引中最左边的列。也就是说,任何可以单独查询的列,WHERE
子句中没有其他列,并返回一个合理大小的结果集。如果结果集的大小不合理,您可能希望限制用户在应用程序中搜索其他属性。
在WHERE
的情况下,更好的索引是(类别,价格)而不是(价格,类别),但这不是因为WHERE category = 'x' AND price < 100 AND price > 20
子句中表达式的排序。这是因为类别是一个相等测试,但价格是一个范围。 WHERE
是等价的,(category,price)仍然是合适的索引 - 因为索引按第一列排序,然后在第一列的每个值中,它们按第二列的值排序,然后在每个(第一,第二)对中,它们按第三列中的值排序, ad infinitum ...所以使用(category,price)服务器直接转到所有类别的行=&#39; x&#39;并且在索引中的分组中,引用的行已经按价格排序,因此它只需要选择类别中的价格范围&#39; x&#39;的索引。最佳。 (价格,类别)索引要求检查范围内的所有价格,然后为所有这些价格划分类别值。仍然可以使用索引,但根据条件,优化器仍然可以选择扫描整个表。
如果向未编入索引的WHERE price < 100 AND price > 20 AND category ='x'
子句添加第三个条件,则将遵循相同的路径,但服务器将扫描标识的行以查找具有非必需值的匹配项索引列。同样,不是最理想的,但通常是可接受的,这取决于您的业务需求 - 这在确定这个问题的正确答案中起作用。
每个索引都需要空间和资源,因为每次插入,更新和删除都要求服务器对当前对表的更改影响的每个索引进行必要的更改。
另请注意,如果您在(a,b)或(a,b,c)等上有索引,则(a)上的单独索引通常被视为浪费空间,因为索引上( a,......任何其他......)也将作为(a)的索引。
尝试使用EXPLAIN SELECT
(从MySQL 5.6开始也支持WHERE
/ INSERT
/ UPDATE
)并且真正understanding its output是了解如何使用optimizer tracing
不可或缺的工具索引工作。 MySQL 5.6还支持{{3}},它为您提供了优化器如何理解您的查询,它考虑的各种计划,每个计划的估计成本以及它如何决定如何执行特定的详细输出查询。