最近我们将数据库从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是:
ORDER BY正在生成TEMPORARY TABLE并且正在使用所有资源(没有order by在一秒钟内运行,即使没有INDEX)。
任何ideea都是apreciated。
答案 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。