使用索引查询的速度慢于没有索引的

时间:2016-05-01 18:12:36

标签: mysql optimization indexing innodb mysql-workbench

我使用的是MySQL 5.6,而我的存储引擎是InnoDB。

我有一个包含100万行的表格,其中包含以下列:

  • ID(主键)
  • 名字
  • foreign_key_id(外键,非空)
  • foreign_key_id2(另一个外键,默认为NULL)

这些行分开在:

  • 25% foreign_key_id 值1和 foreign_key_id2 NULL
  • 25% foreign_key_id 值1和 foreign_key_id2 NOT NULL
  • 25% foreign_key_id 值2和 foreign_key_id2 NULL
  • 25% foreign_key_id 值2和 foreign_key_id2 NOT NULL

使用以下索引:

    foreign_key_id
  • 索引 foreign_key_idx
  • 索引 foreign_key_2_idx foreign_key_id2
  • (foreign_key_idx,foreign_key_2_idx)
  • 上的复合索引 foreign_key_comp_idx

我执行以下查询:

  

查询1 - 没有索引:

SELECT *
FROM table tbl
IGNORE INDEX(foreign_key_idx, foreign_key_2_idx, foreign_key_comp_idx)
WHERE tbl.foreign_key_id = 1 AND tbl.foreign_key_id2 IS NOT NULL
  

查询2 - 使用索引(无复合索引):

SELECT *
FROM table tbl
IGNORE INDEX(foreign_key_comp_idx)
WHERE tbl.foreign_key_id = 1 AND tbl.foreign_key_id2 IS NOT NULL
  

查询3 - 使用复合索引(无其他索引):

SELECT *
FROM table tbl
IGNORE INDEX(foreign_key_idx, foreign_key_2_idx)
WHERE tbl.foreign_key_id = 1 AND tbl.foreign_key_id2 IS NOT NULL

结果:

  

查询1(无索引)执行全表扫描并使用 100万条记录   总持续时间 0.37秒。

     

查询2(索引,无复合索引)对 foreign_key_idx索引执行非唯一键查找,   使用500K记录,总持续时间 0.6秒

     

查询3(仅限复合索引)对复合索引执行索引范围扫描并使用480K   记录总持续时间 0.13秒

我真正不理解的是:为什么查询2 (带索引)的执行速度始终低于查询1 (没有索引)?我真的很困难,需要一些帮助...

我用不同的行测试了上面的查询,例如1k,10k,20k,50k,100k,200k,250k,500k,1M等,总是具有相同的比例(25%),并且结果相同(查询2总是表现缓慢)

提前感谢您,非常感谢任何形式的投入!

编辑(2016年5月2日)

显示创建表命令:

CREATE TABLE `table` (
   `ID` int(11) NOT NULL AUTO_INCREMENT,
   `FirstName` varchar(255) NOT NULL,
   `LastName` varchar(255) NOT NULL,
   `foreign_key_id` int(11) NOT NULL,
   `foreign_key_id2` int(11) DEFAULT NULL,

   PRIMARY KEY (`ID`),
   KEY `foreign_key_idx` (`foreign_key_id`),
   KEY `foreign_key_2_idx` (`foreign_key_id2`),
   KEY `foreign_key_comp_idx ` (`foreign_key_id`,`foreign_key_id2`),

   CONSTRAINT `foreign_key_idx` FOREIGN KEY (`foreign_key_id`) REFERENCES `table2` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
   CONSTRAINT `foreign_key_2_idx` FOREIGN KEY (`foreign_key_id2`) REFERENCES `table3` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
 ) ENGINE=InnoDB AUTO_INCREMENT=1515998 DEFAULT CHARSET=latin1

EXPLAIN PLANS: enter image description here

不确定是否重要,但是table2有20条记录,table3也有100万条。

3 个答案:

答案 0 :(得分:2)

让我感到惊讶的是查询3比查询1更快: - )

您需要25%的表格记录。因此,简单地按顺序阅读表应该是最快的方法。 (至少这是我要做的,以及大多数DBMS在那种情况下所做的事情。)

使用复合索引是可以的,因为它足以知道要选择哪些记录。但是要经历一棵树只能最终获得必须逐一访问的所有记录的25%,这似乎是一项艰巨的任务。如上所述,令人惊讶的是,这比您的全表扫描运行速度更快。也许物理记录恰好按照需要进行排序,因此您不必从一个部分到另一个部分来回,这是从索引来的时候通常会发生的事情。 (说明:假设您在磁盘上的表部分A的索引中找到匹配的记录引用,下一个匹配恰好在扇区B中,第三个再次在扇区A中,...这可能需要很长时间如果你很幸运,你会发现一个扇区中的所有记录先在另一个扇区中。通过全表扫描,你可以逐个扇区地读取,而不必从一个扇区切换到另一个扇区,然后返回。所以全表扫描保证相当快,而通过索引的acccess可能是快或慢。)

现在查询2:索引仅指向可能匹配的记录(表中只有一半匹配的记录的50%)。这意味着你必须按照描述通过树,只是为了仍然读取表的一半记录。这太过分了。

答案 1 :(得分:1)

FOREIGN KEY是一只红鲱鱼;索引(KEYs)是相关的。

索引存储在BTree中。 BTree可以有效地查找单个项目以及扫描具有相同或连续值的项目范围。这就是您的测试用例正在做的事情。

但是,一旦在索引中找到了一个项目,查询就需要进入数据'因为您要求其他列*(在SELECT *中)。这意味着要覆盖数据BTree,它是根据PRIMARY KEY订购的。

索引和数据之间的这种反复有点代价。通常,如果需要超过20%的行,则简单地扫描表("表扫描"),忽略索引更有效。 (警告:" 20%"取决于月亮的阶段;它可能是10%,或30%或其他东西。)

通常优化器将在使用索引(需要一小部分)和进行表扫描之间正确选择。所以,通常你不应该担心。

另一个问题......一个常见的问题是运行时间是“缓存”。这使得相同的查询在第二次运行时花费的时间更少。或者让其他查询运行得更快(因为它缓存了他们需要的东西)。这增加了混乱。

有时值得运行ANALYZE TABLE tbl;来重新计算"统计数据"用于决定进行表扫描或使用索引。但我不会过分相信标准的修复&#39 ;;它也会让事情变得更糟。 (ANALYZE做了一些随机调查来猜测统计数据。)

一千次中只有一次我看到了一个真正需要FORCE INDEX或其中一个类似提示的查询。所以,我建议反对。

您打算在客户端使用250K或500K行做什么?这将扼杀大多数客户。它听起来不像你经常做的事情吗?

答案 2 :(得分:0)

指数非常糟糕,因为每个指数只有两个可能的值。所以我想,与完全不使用索引相比,使用索引会减少要检查的行数,并且只需检查所有条目。

组合索引至少将表分为4个部分,因此只能搜索表的四分之一,这足以补偿首先询问索引。