我了解到,无论您的“where”条件如何,最好是按照它们出现的顺序放置多列索引。
你学到了......不太正确。
外观WHERE
子句中的次序是没有意义的,因为优化是自由以任何逻辑上有效的方法来评估的条件,受试者过程中括号和逻辑运算符的(AND
,OR
等)中的表达。
多列索引中列的顺序很重要,因为从左到右,一旦在where子句中未提及的索引中遇到列,没有更多指向该索引的右侧可以使用。
如果索引3列(a,b,c),并且查询为WHERE a = 1 AND c = 6
,那么优化器将只能使用该索引中最左边的“a”列值,而不是“c”。
在这种情况下,它可能会仍然选择使用该索引来查找行其中,a = 1,然后扫描所有这些确定的行只有那些有C = 6
你可以想像一个多列索引作为多维数组。如果没有一个已知的值或范围需要匹配第一列(a),那么第二列(b)的值是无意义的无序数据混乱,因为它们按照“a”组排序。 ..你必须迭代每个“a”来找到匹配的“b”值,并遍历每个“a,b”以找到匹配的“c”值。由于在上面的示例中,由于未指定“b”值,所以“c”值的排序对于优化查询而言是无意义且无法访问的(尽管列表SELECT
中的每列都可用在单个索引中,优化器可以扫描索引而不是扫描整个表格,将其视为“覆盖索引”,通常比全表扫描更好,但仍不理想)。
如果您的WHERE
子句包含两个列,每个列都单独编制索引,优化器将检查索引统计信息并尝试使用最有可能产生最少匹配的那个列表...如果“a”和“c “每个都有一个单独的索引,并且索引统计表明”c“(高基数)有许多值,但对于”a“(低基数)只有少数值,优化器通常会使用”c“找到匹配的行,然后扫描所有这些行以获得所需的“a”值。
或者,它可能会尝试使用两个索引的联合,以精确识别哪些行满足这两个条件。
这两种策略都不是最佳选择,但仍然远胜于全表扫描,因此建议您至少应将每个可独立搜索的列作为索引中最左边的列。也就是说,可以自行查询的任何列,WHERE
子句中不包含其他列,并返回合理大小的结果集。如果结果集的大小不合理,您可能希望限制用户在应用程序中搜索其他属性。
在WHERE category = 'x' AND price < 100 AND price > 20
的情况下,较好的指数是(类别,价格)而不是(价格,类别),但这不是因为WHERE
条款中的表达式的排序。这是因为类别是平等测试,但价格是一个范围。 WHERE price < 100 AND price > 20 AND category ='x'
是等价的,并且(类别,价格)仍然是适当的指数 - 因为指数按第一列排序,然后在第一列的每个值内按第二列的值排序,然后在每个第一,第二)对,它们按第三列中的值排序,ad infinitum ...因此(类别,价格)服务器直接转到category ='x'的所有行,并在该分组内索引中,引用的行已经按价格排序,因此只需在索引的类别“x”内选择价格范围。最佳。 (价格,类别)索引需要检查范围内的所有价格,然后欺骗所有这些类别的价值。该索引仍然可以使用,但根据条件,优化器仍然可以选择扫描整个表格。
如果向WHERE
子句添加第三条标准,但未遵循索引,则将遵循相同的路径,但服务器将扫描已标识的行以找到具有非索引列所需值的匹配项。再次,取决于您的业务需求,这不是最理想的,但通常是可以接受的 - 这对确定这个问题的正确答案起着作用。
每个索引需要的空间和资源,因为每次插入,更新和删除,要求服务器进行必要的更改 - 权然后 - 为每个被修改表中受影响的索引。请注意,如果您有关于(a,b)或(a,b,c)等的索引,则(a)上的单独索引通常被认为是浪费空间,因为索引(a,...任何其他...)也将作为(a)的索引。
与EXPLAIN SELECT
进行实验(其也支持INSERT
/UPDATE
/DELETE
如MySQL的5.6的)和真正understanding its output是用于理解索引是如何工作的不可或缺的工具。 MySQL的5.6还支持optimizer tracing
,它给你的优化器如何理解您的查询,它考虑的各种计划,但估计每一个计划的成本详细的输出,以及它是如何到达的如何执行特定查询的决定。
非常感谢您的解释。虽然我明白其中的一些,但我必须承认,其中一些仍然凌驾于我的头上。当我有空时,我将不得不阅读EXPLAIN输出页面,然后查看MySQL书籍中的优化章节。再次感谢。 –
谢谢。请考虑接受答案或让我知道是否有我可以澄清的要点。 –