按 id 排序的复合索引上的并发查询非常慢

Posted

技术标签:

【中文标题】按 id 排序的复合索引上的并发查询非常慢【英文标题】:Concurrent queries on composite index with order by id drastically slow 【发布时间】:2018-05-07 03:45:18 【问题描述】:

我有一个表定义如下:

| book | CREATE TABLE `book` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `provider_id` int(10) unsigned DEFAULT '0',
  `source_id` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
  `title` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `description` longtext COLLATE utf8_unicode_ci,
  PRIMARY KEY (`id`),
  UNIQUE KEY `provider` (`provider_id`,`source_id`),
  KEY `idx_source_id` (`source_id`),
) ENGINE=InnoDB AUTO_INCREMENT=1605425 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |

当有大约 10 个并发读取时使用以下 sql:

SELECT * FROM `book`  WHERE (provider_id = '1' AND source_id = '1037122800') ORDER BY `book`.`id` ASC LIMIT 1  

它变慢了,大约需要 100 毫秒。

但是如果我把它改成

SELECT * FROM `book`  WHERE (provider_id = '1' AND source_id = '221630001') LIMIT 1  

那么就正常了,需要几个ms。

我不明白为什么按 id 添加订单会使查询变慢?谁能解释一下?

【问题讨论】:

首先,你的查询条件不同。如果您想将一件事与另一件事进行比较,请不要更改它们。现在它可能与排序无关,而与条件有关。其次,如果您想要任何单个结果,数据库可以按任何顺序运行,并在找到一个后停止。如果您对它进行排序,它必须找到特定的,并且可能必须遍历所有数据。 100ms也很慢?取决于数据量。检查查询计划。 @SamiKuhmonen,在海量查询条件下,source_id 没有区别。在这两种情况下(无论是否按 id 排序)的单个查询中,只需要几个毫秒。 提示 1:不要将字符串与整数进行比较 @Strawberry,我把“provider_id = '1'”改成“provider_id = 1”,结果是一样的,应该是mysql优化的吧。 如果反转 provider_id,source_id 索引会发生什么?此外,鉴于此索引是唯一的,order by(和 limit)子句似乎没有任何作用 【参考方案1】:

尝试添加所需的列(选择列名,..)而不是 * 或参考这个。

Why is my SQL Server ORDER BY slow despite the ordered column being indexed?

【讨论】:

【参考方案2】:

我不是 mysql 专家,无法进行详细分析,但我的猜测是,因为您在 WHERE 子句中为 UNIQUE KEY 提供值,引擎可以去获取它直接使用索引行。

但是,当您向ORDER BY 询问id 列(即PRIMARY KEY)时,会更改访问路径。引擎现在猜测,由于它在id 上有一个索引,并且您想按id 排序,因此最好以PK 顺序获取该数据,这样可以避免排序。但是,在这种情况下,它会导致结果变慢,因为它必须将每一行与标准进行比较(表扫描)。

请注意,这只是推测。你需要EXPLAIN这两个语句才能看到发生了什么。

【讨论】:

解释结果完全一样,只扫描了一行。我猜这个问题可能与表锁有关。

以上是关于按 id 排序的复合索引上的并发查询非常慢的主要内容,如果未能解决你的问题,请参考以下文章

应该按哪个顺序(表列或查询)复合索引?

复合索引顺序 MySQL 查询

在 IndexedDB 中,有没有办法进行排序复合查询?

MongoDB + C#:未选择/使用 GUID 字段上的复合索引

MongoDB - 唯一索引与复合索引

MongoDB OR与正则表达式不使用复合索引