我有两张桌子。其中一个表有这个模式:
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_object
,id_master
,id_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_slave
和id_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;
答案 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.