查询性能不佳。我需要在哪里创建索引?

时间:2019-01-10 17:10:20

标签: mysql query-optimization

我有一个相对简单的查询,但是它的性能确实很差。

我目前的速度约为0.800秒。每个查询。

有什么我需要更改以使其更快?

我已经尝试过索引索引列,这些列用于where语句和联接,但是没有用。

这是我正在使用的查询:

SELECT c.bloqueada, c.nomeF, c.confirmadoData
FROM encomendas_c_linhas el1
LEFT JOIN encomendas_c c ON c.lojaEncomenda=el1.lojaEncomenda
WHERE (el1.artigoE IN (342197) OR el1.artigoD IN (342197))
AND el1.desmembrado = 1 

以下是说明: enter image description here

按照Bill Karwin的要求,下面是用于创建表格的查询:

表“ encomendas_c_linhas” https://pastebin.com/73WcsDnE

表“ encomendas_c”

https://pastebin.com/yCUx3wh0

2 个答案:

答案 0 :(得分:2)

在您的解释中,我们看到它正在使用el1访问type: ALL表,这意味着它正在扫描整个表。 rows字段显示已扫描的644,236行的估计值(大约)。

el1表的表定义中,您具有以下索引:

  KEY `order_item_id_desmembrado` (`order_item_id`,`desmembrado`),

即使desmembrado出现在索引中,该索引也无法帮助查询。您的查询搜索desmembrado = 1,没有条件搜索order_item_id

想一想电话簿:我可以搜索姓氏为“ Smith”的人,而电话簿的顺序有助于我快速找到他们。但是,如果我搜索名字叫“ Sarah”的人,这本书就没有帮助。只有在索引的最左列上进行搜索时,它才有帮助。

因此,您需要使用desmembrado作为最左列的索引。然后搜索desmembrado = 1可能会使用索引来选择那些匹配的行。

ALTER TABLE encomendas_c_linhas ADD INDEX (desmembrado);

请注意,如果表中足够大的部分匹配,MySQL仍将跳过该索引。如果索引会匹配大部分行,则使用索引没有优势。根据我的经验,MySQL的优化器的判断是,如果条件匹配表行的20%以上,它将避免使用索引。

其他条件处于析取状态(OR表达式的术语)。没有办法用单个索引来优化它们。同样,电话簿示例:用last name 'Smith' OR first name 'Sarah'搜索人。姓氏查找已优化,但姓氏查找未优化。没问题,我们可以创建另一个索引,该索引首先列出名字,因此将优化名字查找。但通常,MySQL每个查询的每个表引用仅使用一个索引。因此,优化名字查找会破坏姓氏查找。

这是一种解决方法:将OR条件重写为两个查询的UNION,每个查询中包含一个词:

SELECT c.bloqueada, c.nomeF, c.confirmadoData
FROM encomendas_c_linhas el1
LEFT JOIN encomendas_c c ON c.lojaEncomenda=el1.lojaEncomenda
WHERE el1.artigoD IN ('342197')
AND el1.desmembrado = 1 
UNION
SELECT c.bloqueada, c.nomeF, c.confirmadoData
FROM encomendas_c_linhas el1
LEFT JOIN encomendas_c c ON c.lojaEncomenda=el1.lojaEncomenda
WHERE el1.artigoE IN ('342197')
AND el1.desmembrado = 1;

确保每种情况都有一个索引。

ALTER TABLE encomendas_c_linhas 
  ADD INDEX des_artigoD (desmembrado, artigoD),
  ADD INDEX des_artigoE (desmembrado, artigoE);

这些复合索引中的每一个都可以在各自的子查询中使用,因此它将在每种情况下优化对两列的查找。

还要注意,我将值放在引号中,例如IN ('342197'),因为这些列是varchar,并且需要与varchar进行比较才能使用索引。将varchar列与整数值进行比较将成功进行匹配,但不会使用索引。

这是我为上一个查询测试的解释,它显示了使用了两个新索引,并且显示了ref: const,const,这意味着索引的两列都用于查找。

+----+--------------+------------+------+-------------------------+---------------+---------+------------------------+------+-----------------------+
| id | select_type  | table      | type | possible_keys           | key           | key_len | ref                    | rows | Extra                 |
+----+--------------+------------+------+-------------------------+---------------+---------+------------------------+------+-----------------------+
|  1 | PRIMARY      | el1        | ref  | des_artigoD,des_artigoE | des_artigoD   | 41      | const,const            |    1 | Using index condition |
|  1 | PRIMARY      | c          | ref  | lojaEncomenda           | lojaEncomenda | 61      | test.el1.lojaEncomenda |    2 | NULL                  |
|  2 | UNION        | el1        | ref  | des_artigoD,des_artigoE | des_artigoE   | 41      | const,const            |    1 | Using index condition |
|  2 | UNION        | c          | ref  | lojaEncomenda           | lojaEncomenda | 61      | test.el1.lojaEncomenda |    2 | NULL                  |
| NULL | UNION RESULT | <union1,2> | ALL  | NULL                    | NULL          | NULL    | NULL                   | NULL | Using temporary       |
+----+--------------+------------+------+-------------------------+---------------+---------+------------------------+------+-----------------------+

但是我们可以做得更好。有时向索引中添加其他列会有所帮助,因为如果查询所需的所有列都包含在索引中,则它根本不必读取表行。这称为覆盖索引,如果您在EXPLAIN Extra字段中看到“正在使用索引”,则会显示该信息。

所以这是新的索引定义:

ALTER TABLE encomendas_c_linhas 
  ADD INDEX des_artigoD (desmembrado, artigoD, lojaEncomenda),
  ADD INDEX des_artigoE (desmembrado, artigoE, lojaEncomenda);

第三列不用于查找,但是在连接到另一个表c时使用。

您还可以通过创建@TheImpaler答案中建议的第二个索引来获得相同的覆盖索引效果:

create index ix2 on encomendas_c (lojaEncomenda, bloqueada, nomeF, confirmadoData);

我们看到EXPLAIN现在显示所有表引用的“使用索引”注释:

+----+--------------+------------+------+-------------------------+-------------+---------+------------------------+------+--------------------------+
| id | select_type  | table      | type | possible_keys           | key         | key_len | ref                    | rows | Extra                    |
+----+--------------+------------+------+-------------------------+-------------+---------+------------------------+------+--------------------------+
|  1 | PRIMARY      | el1        | ref  | des_artigoD,des_artigoE | des_artigoD | 41      | const,const            |    1 | Using where; Using index |
|  1 | PRIMARY      | c          | ref  | lojaEncomenda,ix2       | ix2         | 61      | test.el1.lojaEncomenda |    1 | Using index              |
|  2 | UNION        | el1        | ref  | des_artigoD,des_artigoE | des_artigoE | 41      | const,const            |    1 | Using where; Using index |
|  2 | UNION        | c          | ref  | lojaEncomenda,ix2       | ix2         | 61      | test.el1.lojaEncomenda |    1 | Using index              |
| NULL | UNION RESULT | <union1,2> | ALL  | NULL                    | NULL        | NULL    | NULL                   | NULL | Using temporary          |
+----+--------------+------------+------+-------------------------+-------------+---------+------------------------+------+--------------------------+

答案 1 :(得分:1)

我将创建以下索引:

create index ix1 on encomendas_c_linhas (artigoE, artigoD, desmembrado);

create index ix2 on encomendas_c (lojaEncomenda, bloqueada, nomeF, confirmadoData);

第一个是关键的。第二个将进一步提高性能。