深入了解查询速度

时间:2017-08-03 18:38:41

标签: mysql sql explain

我不明白这两个select ac.AccountNumber , p.LicPLateno , p.LicPlateState , p.LicPlateCountry , ISNULL(vm.VehicleMakeDesc,'Other') , p.VehicleModel , p.VehicleYear , v.VehShortDesc , ISNULL(p.VehicleMakeId,'-1') VechicleMakeId --(-1 means Other) --, p.VehicleMakeId from account ac inner join Plate p on ac.AccountId=p.AccountId inner join VehClass v on p.VehClassId=v.VehClassID left join VehicleMake vm on p.VehicleMakeId=vm.VehicleMakeId where ac.AccountNumber= '12345678' and p.PlateStatusId=1 --(For Active Plates only) and p.EndDate is null --(Plates are not expired) order by p.LicPlateNo ; 的区别(第2行)。也许有人暗示我为什么mysql在这些方面的行为如此不同,这严重影响了查询速度。

慢查询持续12秒(等于查询具有该查询的所有行)并在整数列上使用连接,而连接表只有3条记录:

EXPLAIN

快速查询只消耗几分之一秒,在varchar(32)列上使用连接,连接表有1352条记录:

SELECT `inv_assets`.`id` AS `id`, `site`.`description` AS `sitename`, 
  (SELECT COALESCE(DATE_FORMAT(CONVERT_TZ(MIN(inspdate),'UTC','Europe/Vienna'),'%Y-%m-%d'),'') 
   FROM `mobuto_inv_inspections` AS `nextinsp` 
   WHERE ((`nextinsp`.`objectlink` = `inv_assets`.`id` 
            AND `nextinsp`.`inspdate` >= NOW()))
   ) AS `nextinsp` 
FROM `mobuto_inv_assets` AS `inv_assets` 
LEFT JOIN `mobuto_inv_sites` AS `site` 
  ON (`site`.`siteid` = `inv_assets`.`site` 
  AND `site`.`_state` IN (2,0)) 
ORDER BY `inv_assets`.`type` ASC LIMIT 0, 20;

+----+--------------------+------------+--------+----------------+---------+---------+------------------------------+-------+----------------------------------------------------+
| id | select_type        | table      | type   | possible_keys  | key     | key_len | ref                          | rows  | Extra                                              |
+----+--------------------+------------+--------+----------------+---------+---------+------------------------------+-------+----------------------------------------------------+
|  1 | PRIMARY            | inv_assets | ALL    | NULL           | NULL    | NULL    | NULL                         | 24857 | Using temporary; Using filesort                    |
|  1 | PRIMARY            | site       | ALL    | PRIMARY,_state | NULL    | NULL    | NULL                         |     3 | Using where; Using join buffer (Block Nested Loop) |
|  2 | DEPENDENT SUBQUERY | nextinsp   | ALL    | inspdate       | NULL    | NULL    | NULL                         |   915 | Using where                                        |
+----+--------------------+------------+--------+----------------+---------+---------+------------------------------+-------+----------------------------------------------------+

对我来说奇怪的是,当我删除'column-select-part'中连接表的列(SELECT `inv_assets`.`id` AS `id`, `guarantor`.`lastname` AS `guarantoruname`, (SELECT COALESCE(DATE_FORMAT(CONVERT_TZ(MIN(inspdate),'UTC','Europe/Vienna'),'%Y-%m-%d'),'') FROM `mobuto_inv_inspections` AS `nextinsp` LEFT JOIN `users` AS `saveuser` ON (`saveuser`.`uid` = `nextinsp`.`saveuser` AND `saveuser`.`_state` = '0') WHERE ((`nextinsp`.`objectlink` = `inv_assets`.`id` AND `nextinsp`.`inspdate` >= NOW())) ) AS `nextinsp` FROM `mobuto_inv_assets` AS `inv_assets` LEFT JOIN `users` AS `guarantor` ON (`guarantor`.`uid` = `inv_assets`.`guarantor` AND `guarantor`.`_state` = '0') ORDER BY `inv_assets`.`type` ASC LIMIT 0, 20; +----+--------------------+------------+--------+----------------+---------+---------+---------------------------------+-------+----------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+--------------------+------------+--------+----------------+---------+---------+---------------------------------+-------+----------------+ | 1 | PRIMARY | inv_assets | ALL | NULL | NULL | NULL | NULL | 24857 | Using filesort | | 1 | PRIMARY | guarantor | eq_ref | PRIMARY,_state | PRIMARY | 98 | mobuto_dev.inv_assets.guarantor | 1 | Using where | | 2 | DEPENDENT SUBQUERY | nextinsp | ALL | inspdate | NULL | NULL | NULL | 915 | Using where | | 2 | DEPENDENT SUBQUERY | saveuser | eq_ref | PRIMARY,_state | PRIMARY | 98 | mobuto_dev.nextinsp.saveuser | 1 | Using where | +----+--------------------+------------+--------+----------------+---------+---------+---------------------------------+-------+----------------+ )时(连接仍然存在且IMHO mysql不会优化它时)没用过),速度又回来了(因为mysql不再使用临时表,解释看起来和快速表一样,有description)。

但是为什么只有在没有选择列时才能为第一个样本工作,而我可以在第二个样本中选择一个!?

type=eq_ref

根据@Wilson Hauck的要求进行更改:

  • CREATE TABLE `mobuto_inv_assets` ( `id` int(11) NOT NULL AUTO_INCREMENT, `invnum` varchar(10) NOT NULL, `oebglcat` varchar(4) NOT NULL, `mark` varchar(100) NOT NULL, `type` varchar(100) NOT NULL, `serialnum` varchar(100) NOT NULL, `desc` varchar(100) NOT NULL, `site` int(11) NOT NULL DEFAULT '0', `licnum` varchar(20) NOT NULL DEFAULT '', `inquirer` varchar(100) NOT NULL DEFAULT '', `inqdate` date NOT NULL DEFAULT '0000-00-00', `supplier` varchar(100) NOT NULL DEFAULT '', `suppldate` date NOT NULL DEFAULT '0000-00-00', `supplnumber` varchar(30) NOT NULL DEFAULT '', `invoicedate` date NOT NULL DEFAULT '0000-00-00', `invoicenumber` varchar(30) NOT NULL DEFAULT '', `purchaseprice` decimal(11,2) NOT NULL DEFAULT '0.00', `leased` varchar(1) NOT NULL DEFAULT 'N', `leasingcompany` varchar(100) NOT NULL DEFAULT '', `leasingnumber` varchar(30) NOT NULL DEFAULT '', `notes` text NOT NULL, `inspnotes` text NOT NULL, `inactive` varchar(1) NOT NULL DEFAULT 'N', `maintain` varchar(1) NOT NULL DEFAULT 'Y', `asset` varchar(1) NOT NULL DEFAULT 'Y', `inspection` varchar(1) NOT NULL DEFAULT '', `inspperson` varchar(100) NOT NULL DEFAULT '', `guarantor` varchar(32) NOT NULL DEFAULT '', `saveuser` varchar(32) NOT NULL, `savetime` int(11) NOT NULL, `recordid` varchar(32) NOT NULL, `_state` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`id`), UNIQUE KEY `invnum` (`invnum`), KEY `_state` (`_state`), KEY `site` (`site`) ) ENGINE=InnoDB AUTO_INCREMENT=30707 DEFAULT CHARSET=utf8; CREATE TABLE `mobuto_inv_sites` ( `siteid` int(11) NOT NULL AUTO_INCREMENT, `description` varchar(100) NOT NULL, `saveuser` varchar(32) NOT NULL, `savetime` int(11) NOT NULL, `recordid` varchar(32) NOT NULL, `_state` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`siteid`), KEY `_state` (`_state`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; mysql> SHOW INDEX FROM mobuto_inv_assets; +-------------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +-------------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | mobuto_inv_assets | 0 | PRIMARY | 1 | id | A | 24857 | NULL | NULL | | BTREE | | | | mobuto_inv_assets | 0 | invnum | 1 | invnum | A | 24857 | NULL | NULL | | BTREE | | | | mobuto_inv_assets | 1 | _state | 1 | _state | A | 4 | NULL | NULL | | BTREE | | | +-------------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 中向列site添加了索引(执行速度降低了近半秒)
  • 似乎第一次查询中缺少列mobuto_inv_assets。格式化查询时可能会丢失。当然应该和快速的一样
  • 移除nextinsp加入,因为它未在那里使用(另外保存2秒)并更新了其saveuser(删除了最后一行)
  • EXPLAIN已添加

    SHOW INDEX FROM mobuto_inv_sites

4 个答案:

答案 0 :(得分:0)

您的第一个查询是使用密钥少于第二个查询。解释计划中的possible_keys列显示了可以使用密钥的位置,但key列显示了它们实际使用的位置。

我建议,除了查看数据库的结构之外,还要在JOIN和WHERE子句中更多地使用这些键来加快它的速度。

我们确定当您说您正在修改选择列并且速度不同时,查询不会被缓存。

答案 1 :(得分:0)

12秒首次查询可能是由R​​OWS列线索引起的,仅考虑24857 * 3 * 915 * 1 = 68,232,465总行数。第二次查询ROWS列的线索不到1秒,仅考虑24857 * 1 * 915 * 1 = 22,744,155总行数。第一个查询使用块嵌套循环处理是延迟响应的主要因素 请发布SHOW CREATE TABLE mobuto_inv_assets和mobuto_inv_sites的结果。还请发布SHOW INDEX FROM mobuto_inv_assets和mobuto_inv_sites的结果。有了这些附加信息,有人可能会建议对SELECT ....查询进行改进,这些查询将避免块嵌套循环处理,这是RBAR(通过Agonizing Row处理行)的时间。可能需要额外的索引。

答案 2 :(得分:0)

感谢您发布两个SHOW CREATE TABLE,非常有帮助。 请考虑添加索引 ALTER TABLE mobuto_inv_sites ADD INDEX网站 - 如果您的系统允许空间。 此外,query1显示的EXPLAIN与查询不匹配。 查询没有引用我在EXPLAIN中可以看到的nextinsp或saveused。 当您有机会再次测试并指示所需的执行时间减少时,请在创建索引后替换EXPLAIN for query1。 如果您可以发布结果也很好 显示来自mobuto_inv_sites的索引,以便我们可以看到您的数据和基数的范围。

答案 3 :(得分:0)

如果使用ACCURATE _state数据填充inv_assets行 考虑将query1更改为以下内容:
选择inv_assetsid AS idsitedescription AS sitename,   (选择COALESCE(DATE_FORMAT(CONVERT_TZ(MIN(激励),' UTC'欧洲/维也纳'),'%Y-%m-%d') '&#39)    来自mobuto_inv_inspections AS nextinsp    WHERE((nextinspobjectlink = inv_assetsid             和nextinspinspdate> = NOW()))    )AS nextinsp 来自mobuto_inv_assets AS inv_assets    在哪里inv_assets_state = 2或inv_assets_state = 0
LEFT JOIN mobuto_inv_sites AS site   开(sitesiteid = inv_assetssite   和site_state IN(2,0)) ORDER BY inv_assetstype ASC LIMIT 0,20;

EXPLAIN应避免表扫描和后续的块嵌套循环处理。

如果inv_assets中的_state数据在每一行都不是ACCURATE,那么这将不起作用。

2017-08-10 update 09:42 CT请为所涉及的表发布QUERY,EXPLAIN结果,SHOW CREATE TABLE tblname,并为所涉及的表显示来自tblname的索引。