MySql查询不使用索引集

时间:2018-05-13 18:47:28

标签: mysql optimization indexing mysql-5.7 percona-xtradb-cluster

最近我们将数据库从MariaDB 10.2切换到Percona Server(Mysql 5.7),我们有一个大约需要15秒的时间(在大约0.5之前),因为查询优化器没有在主表上使用任何索引。因为app逻辑,我们不能改变查询格式,我们需要让DB使用索引。

查询结构很简单:

EXPLAIN SELECT `clients`.`id` AS `t0_c0`,
       `client`.`name1` AS `t0_c1`,
       `client`.`name2` AS `t0_c2`,
       `users`.`id` AS `t1_c0`,
       `users`.`suffix` AS `t1_c1`,
       `users`.`position` AS `t1_c2`,
       `users`.`first_name` AS `t1_c3`,
       `users`.`last_name` AS `t1_c4`,
       `privateData`.`id` AS `t2_c0`,
       `privateData`.`first_name` AS `t2_c1`,
       `privateData`.`last_name` AS `t2_c2`,
       `tariff`.`id` AS `t3_c0`,
       `tariff`.`provider_id` AS `t3_c1`,
       `tariff`.`tariff_type` AS `t3_c2`,
       `tariff`.`name` AS `t3_c3`,
       `providers`.`id` AS `t4_c0`,
       `providers`.`name1` AS `t4_c1`,
       `providers`.`name2` AS `t4_c2`,
       `addresses`.`id` AS `t5_c0`,
       `addresses`.`zipcode` AS `t5_c1`,
       `addresses`.`country` AS `t5_c2`,
       `addresses`.`city` AS `t5_c3`,
       `private`.`id` AS `t6_c0`,
       `private`.`first_name` AS `t6_c1`,
       `private`.`last_name` AS `t6_c2`,
       `commercial`.`id` AS `t7_c0`,
       `commercial`.`name1` AS `t7_c1`,
       `commercial`.`name2` AS `t7_c2`,
       `commercial`.`name_on_invoice` AS `t7_c3`,
       `commercial`.`organization_type` AS `t7_c4`,
       `organizations`.`id` AS `t8_c0`,
       `organizations`.`person_id` AS `t8_c1`,
       `organizations`.`address_id` AS `t8_c2`,
       `organizations`.`status` AS `t8_c3`,
       `shaddresses`.`id` AS `t9_c0`,
       `shaddresses`.`zipcode` AS `t9_c1`,
       `shaddresses`.`country` AS `t9_c2`,
       `shaddresses`.`city` AS `t9_c3`,
       `shprivate`.`id` AS `t10_c0`,
       `shprivate`.`first_name` AS `t10_c1`,
       `shprivate`.`last_name` AS `t10_c2`,
       `coraddresses`.`id` AS `t11_c0`,
       `coraddresses`.`zipcode` AS `t11_c1`,
       `coraddresses`.`country` AS `t11_c2`,
       `corprivate`.`id` AS `t12_c0`,
       `corprivate`.`first_name` AS `t12_c1`,
       `corprivate`.`last_name` AS `t12_c2`,
FROM `client` `client`
LEFT OUTER JOIN `users` `users` ON (`client`.`user_id`=`users`.`id`)
AND (users.status!=5)
LEFT OUTER JOIN `private` `privateData` ON (`users`.`person_id`=`privateData`.`id`)
LEFT OUTER JOIN `tariff` `tariff` ON (`client`.`rate_id`=`tariff`.`id`)
LEFT OUTER JOIN `providers` `providers` ON (`client`.`provider_id`=`providers`.`id`)
LEFT OUTER JOIN `addresses` `addresses` ON (`client`.`main_address_id`=`addresses`.`id`)
LEFT OUTER JOIN `private` `private` ON (`client`.`main_person_id`=`private`.`id`)
LEFT OUTER JOIN `commercial` `commercial` ON (`client`.`main_organization_id`=`commercial`.`id`)
LEFT OUTER JOIN `organizations` `organizations` ON (`client`.`id_organization`=`organizations`.`id`)
AND (organizations.status!=5)
LEFT OUTER JOIN `addresses` `shaddresses` ON (`client`.`shipping_address_id`=`shaddresses`.`id`)
LEFT OUTER JOIN `private` `shprivate` ON (`client`.`shipping_person_id`=`shprivate`.`id`)
LEFT OUTER JOIN `addresses` `coraddresses` ON (`client`.`correspondense_address_id`=`coraddresses`.`id`)
LEFT OUTER JOIN `private` `corprivate` ON (`client`.`correspondense_person_id`=`corprivate`.`id`)
WHERE (client.status!=5)
ORDER BY client.id DESC
LIMIT 10
OFFSET 10

我可以更改任何INDEX,但是,我无法更改查询。 在旧主机上它运行0.2秒,但是,优化器它使用来自clients表的索引。使用Percona Server(mysql 5.7)需要15秒。 Optimiser没有使用来自clients表的任何索引。使用来自客户端的FORCE INCEX()表不到1秒(复合索引大约需要0.2秒)。 表'提供者'它只有1行。 我在'clients'表上设置了索引,但是,在解释中,它们不是显示为有效键。

我尝试将MySql变量'max_seeks_for_key'设置为1,但仍然没有使用索引。

我认为我遗漏了一些基本的东西,但我无法弄清楚是什么。

此查询的EXPLAIN是:

enter image description here

ORDER BY正在生成TEMPORARY TABLE并且正在使用所有资源(没有order by在一秒钟内运行,即使没有INDEX)。

任何ideea都是apreciated。

4 个答案:

答案 0 :(得分:1)

这应该比使用FORCE

更有效
SELECT  ...
    FROM ( SELECT  id
              FROM  client
              WHERE  status != 5
              ORDER BY  id DESC
              LIMIT  10 OFFSET 10 
          ) AS ids
    JOIN  client USING(id)
    LEFT OUTER JOIN  `users` `users`  ON (`client`.`user_id`=`users`.`id`)
      AND  (users.status!=5)
    LEFT OUTER JOIN  `private` `privateData`  ON (`users`.`person_id`=`privateData`.`id`)
    LEFT OUTER JOIN  `tariff` `tariff`  ON (`client`.`rate_id`=`tariff`.`id`)
    LEFT OUTER JOIN  `providers` `providers`  ON (`client`.`provider_id`=`providers`.`id`)
    LEFT OUTER JOIN  `addresses` `addresses`  ON (`client`.`main_address_id`=`addresses`.`id`)
    LEFT OUTER JOIN  `private` `private`  ON (`client`.`main_person_id`=`private`.`id`)
    LEFT OUTER JOIN  `commercial` `commercial`  ON (`client`.`main_organization_id`=`commercial`.`id`)
    LEFT OUTER JOIN  `organizations` `organizations`  ON (`client`.`id_organization`=`organizations`.`id`)
      AND  (organizations.status!=5)
    LEFT OUTER JOIN  `addresses` `shaddresses`  ON (`client`.`shipping_address_id`=`shaddresses`.`id`)
    LEFT OUTER JOIN  `private` `shprivate`  ON (`client`.`shipping_person_id`=`shprivate`.`id`)
    LEFT OUTER JOIN  `addresses` `coraddresses`  ON (`client`.`correspondense_address_id`=`coraddresses`.`id`)
    LEFT OUTER JOIN  `private` `corprivate`  ON (`client`.`correspondense_person_id`=`corprivate`.`id`)
    ORDER BY  client.id DESC -- Yes, repeated here 

答案 1 :(得分:0)

我没有找到任何解决方案,我只是使用了... FORCE INDEX(状态)。

答案 2 :(得分:0)

MySQL应该能够使用ORDER BY子句上的索引来快速跟踪client表所需的记录。从那里开始,将几行连接到其他表以获取更多信息应该很快。

尝试添加此索引:

ALTER TABLE `client` ADD INDEX `client_idx_id` (`id`);

如果MySQL选择不使用此索引(有时会做出错误的决定),请尝试强制它并比较此选项的执行持续时间。

答案 3 :(得分:0)

使用ORDER BY ... LIMIT查询的索引是否有益取决于表上条件的选择性。如果大多数行不满足条件,则使用表扫描可能比扫描索引的大部分更快。

因此,如果关闭选择性的估计,则查询优化器可以选择非最优计划。对于此查询,如果状态列上没有索引,则估计值将不准确,因为优化程序将不会对此列的分布进行任何统计。要获得更准确的估算值,您可以尝试在此列上创建索引。尝试的另一个选择是关闭MySQL 5.7中引入的过滤估计的使用。为此,请执行:

SET optimizer_switch='condition_fanout_filter=off'

但是,即使条件选择性的估计是正确的,如果列是相关的,优化器也可能无法选择最佳计划。在您的情况下,我怀疑状态和ID之间可能存在高度相关性。也许只有极少数行的状态!= 5整体,但在具有高ID的行中非常频繁。如果是这种情况,那么我担心查询优化器将无法检测到,并且获得最佳计划的唯一方法是使用索引提示。

如果您可以为此查询提供优化程序跟踪,我将能够了解更多正在发生的情况。有关如何获取优化程序跟踪的建议,请参阅https://oysteing.blogspot.no/2016/01/how-to-get-optimizer-trace-for-query.html