最近,我们将一个db从mariadb 10.2切换到percona服务器(MySQL5.7),我们有一个大约需要15秒的查询(在0.5之前),因为查询优化程序没有在主表上使用任何索引。因为应用程序逻辑,我们不能改变查询格式,我们需要使数据库使用索引。
查询结构很简单:
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
我可以更改任何索引,但是,我不能更改查询。在旧主机上,它的运行时间为0.2秒,但Optimizer使用的是clients表中的索引。使用percona服务器(MySQL5.7)需要15秒。Optimizer没有使用clients表中的任何索引。对于clients表中的force incex(),将在1秒以下运行(对于复合索引,将在0.2秒左右运行)。表'providers'只有一行。我在“clients”表上设置了索引,但在解释中,它们并没有显示为可能的键。
我试图将mysql变量“max\u seeks\u for \u key”设置为1,但是它仍然没有使用索引。
我想我遗漏了一些基本的东西,但我不知道是什么。
此查询的解释是:
orderby正在生成临时表,并且正在使用所有的资源(没有orderby的情况下,运行时间不到一秒钟,甚至没有索引)。
任何想法都是值得赞赏的。
4条答案
按热度按时间ohfgkhjo1#
mysql应该能够使用ORDERBY子句上的索引来快速跟踪数据库中所需的记录
client
table。从这里开始,将几行连接到其他表以获取更多信息应该很快。尝试添加此索引:
如果mysql选择不使用这个索引(有时它会做出错误的决定),请尝试强制使用它并比较这个选项的执行时间。
i34xakig2#
这应该比使用
FORCE
:6ss1mwsb3#
使用索引进行排序是否有益。。。限制查询取决于表上条件的选择性。如果大多数行不满足条件,那么使用表扫描可能比扫描索引的大部分要快。
因此,如果选择性的估计值为off,查询优化器可能会选择一个非最佳计划。对于此查询,如果status列上没有索引,则估计值将不准确,因为优化器将不具有有关此列分布的任何统计信息。要获得更精确的估计,可以尝试在此列上创建索引。另一个尝试的方法是关闭mysql 5.7中引入的过滤估计。为此,请执行:
然而,即使条件选择性的估计是正确的,如果列是相关的,优化器也可能无法选择最优方案。在您的情况下,我怀疑状态和id之间可能有很高的相关性。可能只有很少的行显示状态=总的来说是5,但是它在具有高id的行中非常频繁。如果是这样,那么我担心查询优化器将无法检测到这一点,而获得最佳计划的唯一方法就是使用索引提示。
如果您能为这个查询提供优化器跟踪,我将能够了解更多正在发生的事情。看到了吗https://oysteing.blogspot.no/2016/01/how-to-get-optimizer-trace-for-query.html 获取有关如何获取优化器跟踪的建议。
t0ybt7op4#
我没有找到任何解决办法,我只是用。。。力指数(状态)。