没有索引,查询运行得更快。为什么?

时间:2015-07-31 14:45:03

标签: mysql sql performance

我有两张桌子。其中一个表有这个模式:

CREATE TABLE `object_master_70974_` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 `id_object` int(10) unsigned NOT NULL DEFAULT '0',
 `id_master` int(10) unsigned NOT NULL DEFAULT '0',
 `id_slave` int(10) unsigned NOT NULL DEFAULT '0',
 `id_field` bigint(20) unsigned NOT NULL DEFAULT '0',
 `id_slave_field` bigint(20) unsigned NOT NULL DEFAULT '0',
 PRIMARY KEY (`id`),
 UNIQUE KEY `id_object`    (`id_object`,`id_master`,`id_slave`,`id_field`,`id_slave_field`),
 KEY `id_object_2` (`id_object`,`id_master`,`id_field`,`id_slave_field`),
 KEY `id_object_3` (`id_object`,`id_slave`,`id_field`),
 KEY `id_object_4` (`id_object`,`id_slave_field`),
 KEY `id_object_5` (`id_object`,`id_master`,`id_slave`,`id_field`),
 KEY `id_object_6` (`id_object`,`id_master`,`id_slave`,`id_slave_field`),
 KEY `id_master` (`id_master`,`id_slave_field`),
 KEY `id_object_7` (`id_object`,`id_field`)
) ENGINE=InnoDB AUTO_INCREMENT=17827 DEFAULT CHARSET=utf8;

如您所见,存在重叠索引KEY id_object_5 (id_object,id_master,id_slave,id_field),并且没有涵盖这三个字段的索引:id_objectid_masterid_field。但是,当我运行这两个查询时:

SELECT f1.id 
FROM object_70974_ f1  
LEFT JOIN object_master_70974_ mss0 ON mss0.id_object IN (70974,71759)  
AND mss0.id_master = 71100 AND mss0.id_slave = 70912 AND mss0.id_field = f1.id

SELECT f1.id 
FROM object_70974_ f1  
LEFT JOIN object_master_70974_ mss0 ON mss0.id_object IN (70974,71759)  
AND mss0.id_master = 71100 AND mss0.id_field = f1.id

它们都返回相同数量的行(因为实际上id_slave字段并不重要) - 3530,但是,第一个查询比第二个查询慢一秒 - 8和分别为7秒。所以,我想我必须提出两个问题 - 1)为什么第二个查询运行得更快,即使它不使用索引而且2)为什么第一个查询运行得如此之慢以及为什么它不使用索引(显然) 。简而言之,到底发生了什么?

修改

这是EXPLAIN命令的结果(两个查询都相同):

"id"    "select_type"   "table" "type"  "possible_keys" "key"   "key_len"   "ref"   "rows"  "Extra"
"1" "SIMPLE"    "f1"    "index" \N  "attr_80420_"   "5" \N  "3340"  "Using index"
"1" "SIMPLE"    "mss0"  "ref"   "id_object,id_object_2,id_object_3,id_object_4,id_object_5,id_object_6,id_master,id_object_7"   "id_master" "4" "const" "3529"  "Using where"

修改

这非常有趣,因为如果我DROP id_master索引(两个查询都使用了某些奇怪的原因),那么它就会开始使用id_object_5索引。

修改

而且,是的,随着id_master索引被删除,两个查询都开始以超快的速度运行。所以,我猜优化器存在一些问题。

修改

我甚至猜测优化器会遇到什么问题 - 它可能会错误地处理密钥中的id_slave_field字段名称,就好像它是两个字段 - id_slaveid_field。在这种情况下,它变得合理,为什么它首先在两个查询中都使用了这个键。

修改

object_70974_

的架构
CREATE TABLE `object_70974_` (
   `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
   `id_inherit` BIGINT(20) NOT NULL DEFAULT '0',
   `id_obj` INT(10) UNSIGNED NOT NULL DEFAULT '0',
   `if_control` TINYINT(1) NOT NULL DEFAULT '0',
   `id_order` BIGINT(20) NOT NULL DEFAULT '0',
   `if_archive` TINYINT(1) NOT NULL DEFAULT '0',
   `id_group` BIGINT(20) NOT NULL DEFAULT '0',
   `if_hist` SMALLINT(6) NOT NULL DEFAULT '0',
   `if_garbage` TINYINT(1) NOT NULL DEFAULT '0',
   `id_color` CHAR(6) DEFAULT NULL,
   `id_text` TINYINT(4) NOT NULL DEFAULT '0',
   `if_default` TINYINT(1) NOT NULL DEFAULT '0',
   `id_parent` BIGINT(20) NOT NULL DEFAULT '0',
   .... a long list of other fields
   PRIMARY KEY (`id`),
   KEY `id_order` (`id_order`)
) ENGINE=INNODB AUTO_INCREMENT=3636 DEFAULT CHARSET=utf8;

1 个答案:

答案 0 :(得分:1)

Why does the SELECT mention f1 at all? It is essentially useless. This would give the same answer, possibly except for some end case:

SELECT  mss0.id_field
    FROM  object_master_70974_ mss0
    WHERE  mss0.id_object IN (70974, 71759)
      AND  mss0.id_master = 71100
      AND  mss0.id_slave = 70912

The optimal index for that is

INDEX(id_master, id_slave, id_object)

where master and slave can be in either order, but id_object is last. Build the 'best' index by starting with any WHERE clause that have = (constant).

Don't use LEFT unless you are want to see NULLs for the 'right' table when there is no match. I think this is part of the problem -- the optimizer was forced to start with f1 when it would be a lot better to start with the other table.

8 vs 7 seconds could be caching.

Note in the EXPLAIN that it needs to hit 3K rows in each table.