【问题标题】:Why adding duplicate indexes to MySQL table caused longer query execution time?为什么向 MySQL 表添加重复索引会导致查询执行时间变长?
【发布时间】:2016-09-16 08:19:06
【问题描述】:

也许索引不相关,但我遇到了一个奇怪的问题。

这是我的选择查询:

SELECT DISTINCT completeAddress FROM DB_M3_Medium.AvailableAddressesV3 where postNr = 1050 ORDER BY completeAddress ASC;

我的索引:

create index postNrAndAddress_idx on DB_M3_Medium.AvailableAddressesV3 (completeAddress);
create index postNr_idx on DB_M3_Medium.AvailableAddressesV3 (completeAddress);
create index completeAddress_idx on DB_M3_Medium.AvailableAddressesV3 (completeAddress);

除此之外,我还有一个自动增量 ID (idIndex) 的 PK。

在任何手动创建的索引出现之前,选择查询的执行时间为 2.4 秒。

然后我创建了索引(一个一个):

  • 第一个索引 - 选择语句执行时间 - 2.1s
  • 第二个索引 - 选择 语句执行时间 - 2.8s
  • 第三个索引 - 选择语句 执行时间 - 12.7 秒

刚刚发生了什么?

编辑:

谢谢你们的cmets。我的解释语句结果:

+----+-------------+----------------------+-------+-----------------------------------------------------+---------------------+---------+-----+---------+-------------+
| id | select_type |        table         | type  |                    possible_keys                    |         key         | key_len | ref |  rows   |    Extra    |
+----+-------------+----------------------+-------+-----------------------------------------------------+---------------------+---------+-----+---------+-------------+
|  1 | SIMPLE      | AvailableAddressesV3 | index | postNrAndAddress_idx,postNr_idx,completeAddress_idx | completeAddress_idx |     363 |     | 3526406 | Using where |
+----+-------------+----------------------+-------+-----------------------------------------------------+---------------------+---------+-----+---------+-------------+

表结构:

+------------------+--------------+------+-----+---------+----------------+
|      Field       |     Type     | Null | Key | Default |     Extra      |
+------------------+--------------+------+-----+---------+----------------+
| vej_Navn         | varchar(70)  | YES  |     |         |                |
| husNr            | varchar(20)  | YES  |     |         |                |
| husbogstav       | varchar(50)  | YES  |     |         |                |
| etage            | varchar(30)  | YES  |     |         |                |
| side_DoerNr      | varchar(20)  | YES  |     |         |                |
| stedNavn         | varchar(50)  | YES  |     |         |                |
| postNr           | varchar(15)  | YES  | MUL |         |                |
| postDistrikt     | varchar(50)  | YES  |     |         |                |
| lev_Adresse_UUID | varchar(50)  | YES  |     |         |                |
| fiberstatus      | varchar(15)  | YES  |     |         |                |
| kommune_nr       | varchar(35)  | YES  |     |         |                |
| vej_Kode         | varchar(35)  | YES  |     |         |                |
| completeAddress  | varchar(120) | YES  | MUL |         |                |
| randomSalt       | varchar(5)   | YES  |     |         |                |
| id               | int(11)      | NO   | PRI |         | auto_increment |
+------------------+--------------+------+-----+---------+----------------+

创建表查询:

  CREATE TABLE `AvailableAddressesV3` (
  `vej_Navn` varchar(70) COLLATE utf8_unicode_ci DEFAULT NULL,
  `husNr` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL,
  `husbogstav` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
  `etage` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL,
  `side_DoerNr` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL,
  `stedNavn` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
  `postNr` varchar(15) CHARACTER SET utf8 DEFAULT NULL,
  `postDistrikt` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
  `lev_Adresse_UUID` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
  `fiberstatus` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL,
  `kommune_nr` varchar(35) COLLATE utf8_unicode_ci DEFAULT NULL,
  `vej_Kode` varchar(35) COLLATE utf8_unicode_ci DEFAULT NULL,
  `completeAddress` varchar(120) COLLATE utf8_unicode_ci DEFAULT NULL,
  `randomSalt` varchar(5) COLLATE utf8_unicode_ci DEFAULT NULL,
  `id` int(11) NOT NULL AUTO_INCREMENT,
  UNIQUE KEY `idIndex` (`id`),
  KEY `postNrAndAddress_idx` (`postNr`,`completeAddress`),
  KEY `postNr_idx` (`postNr`),
  KEY `completeAddress_idx` (`completeAddress`)
) ENGINE=InnoDB AUTO_INCREMENT=3552718 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

【问题讨论】:

  • 你的第一站为什么是我的查询做这个问题:dev.mysql.com/doc/refman/5.7/en/explain.html
  • 我猜这是一个复制和粘贴错误,但你添加了三倍相同的索引(总是在completeAddress)。除了按照 CBroe 的建议显示 EXPLAIN 语句结果之外,还要发布您的架构。
  • 谢谢,请查看编辑。
  • 请注意,每个索引可能会加快 SELECT 查询,但会减慢 UPDATE/INSERT/DELETE,因为必须更新此索引。很容易“猜测”添加所有可能的索引并希望一切运行得更快。
  • @Drew 可以。但是在大表上放10个不必要的索引,然后看看速度是如何变慢的。不是几秒钟,但查询可能需要 0.3 秒而不是 0.001 秒。对于密集的工作,这是有价值的。而对于百万行表,时间可以不是 0.3 秒,而是 3 秒。

标签: mysql


【解决方案1】:

根据您的 EXPLAIN 输出,查询使用completeAddress_idx,可能是因为排序/不同,但我猜postNr = 1050 的行很少(在哥本哈根,对吗?)所以它使用postNr_idxpostNrAndAddress_idx 应该更有效(几百行的排序/区分应该几乎是即时的)。某些事情使查询执行计划者错过了最佳查询。

我自己从未尝试过,但您可以尝试更新表统计信息的 ANALYZE TABLE 语句,例如键基数,这可能会改变优化器的工作方式。

要么这样,要么我错过了一些简单的东西 - 这似乎很可能:)

编辑

在调试时,强制 MySQL 使用特定索引会很有用。试试FORCE/USE INDEX hint

【讨论】:

  • 非常感谢@AndreLaszlo 我已经尝试过仅使用postNr 索引,现在执行时间为2.6 秒,这仍然比完全没有索引要高。好奇怪。
  • 有趣,该查询的 EXPLAIN 输出是什么?
  • 嗯,但现在执行 EXPLAIN 后,当我只有 postNr 索引时,它说它是一个可能的键,但不是一个选择的键。选择的密钥为空。我怎样才能强制它使用那个键?
  • 答案已更新。另外,您如何测量查询时间。它仅适用于 SELECT 本身?不包括 CREATE 和 INSERT 对吗?
  • 对不起。读得太快了。太糟糕了!
【解决方案2】:

我从没想过这会成为问题,或者至少我会收到WorkBenchJDBC 的错误通知或至少是警告。

我的选择查询应该如下所示:

SELECT DISTINCT completeAddress FROM DB_M3_Medium.AvailableAddressesV3 where postNr = '4000' ORDER BY completeAddress ASC;

区别在于postNr 的数据类型。在我没有将它包裹在 ' 之前。

这极大地改善了选择,然后当我删除 ORDER BY 时,执行时间下降到 0.07 秒。

所以基本上发生了什么,SELECT 查询没有使用任何索引,因为没有一个索引是合适的。当我执行EXPLAIN 时,我的 Key 列收到 NULL。我试图FORCE 它,但它没有任何区别。

然后我发现了这个:Why isn't MySQL using any of these possible keys?

他在第二个答案中提到的地方。

【讨论】:

  • 我只是在试验,回来回答完全相同的问题! :) 不错!
  • 为这样的努力提供 +1!谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-07
  • 1970-01-01
  • 2019-02-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多