非常简单的 MySQL 索引查询运行非常缓慢

Posted

技术标签:

【中文标题】非常简单的 MySQL 索引查询运行非常缓慢【英文标题】:Very simple MySQL index query running very slowly 【发布时间】:2021-04-23 17:05:53 【问题描述】:

我有一个非常简单的查询,尽管已编入索引,但运行速度非常慢。

我的表如下:

mysql> show create table mytable
CREATE TABLE `mytable` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `start_time` datetime DEFAULT NULL,
  `status` varchar(64) DEFAULT NULL,
  `user_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `ix_status_user_id_start_time` (`status`,`user_id`,`start_time`),
  ### other columns and indices, not relevant
) ENGINE=InnoDB AUTO_INCREMENT=115884841 DEFAULT CHARSET=utf8

那么下面的查询需要运行超过 10 秒

select id from mytable USE INDEX (ix_status_user_id_start_time) where status = 'running';

表中大约有 700 万行,其中大约 200 行的状态为 running

我希望这个查询不到十分之一秒。它应该在索引中找到状态为running 的第一行。然后扫描接下来的 200 行,直到找到第一个非running 行。它不需要查看索引之外的内容。

当我解释查询时,我得到了一个非常奇怪的结果:

mysql> explain select id from mytable USE INDEX (ix_status_user_id_start_time) where status = 
'running';
+----+-------------+---------+------------+------+------------------------------+------------------------------+---------+-------+---------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys                | key                          | key_len | ref   | rows    | filtered | Extra       |
+----+-------------+---------+------------+------+------------------------------+------------------------------+---------+-------+---------+----------+-------------+
|  1 | SIMPLE      | mytable | NULL       | ref  | ix_status_user_id_start_time | ix_status_user_id_start_time | 195     | const | 2118793 |   100.00 | Using index |
+----+-------------+---------+------------+------+------------------------------+------------------------------+---------+-------+---------+----------+-------------+

估计扫描超过 200 万行!此外,status 索引的基数似乎不正确。只有大约 5 或 6 种不同的状态,而不是 344 种。

其他信息

    此表的插入和更新有些频繁。每秒插入大约 2 行,每秒更新 10 个状态。我不知道这有多大影响,但我不希望它值 30 秒。

    如果我同时使用statususer_id 进行查询,有时会很快(低于 0.1 秒),有时会很慢(> 1 秒),具体取决于user_id。这似乎不依赖于结果集的大小(有些用户有 20 行很快,其他有 4 行很慢)

谁能解释这里发生了什么以及如何解决它?

我使用的是 mysql 版本 5.7.33

【问题讨论】:

为什么不单独在状态列上使用索引呢?如果索引在 3 列上,那么仅搜索状态对您没有帮助 show create table tablename 比描述 + 显示索引更具可读性(和完整) 考虑将您的状态更改为整数或枚举? 看起来索引不是用作索引而是用作紧凑表的副本。 IE。服务器对索引进行全扫描。测试确实CREATE INDEX ix_status ON mytable (status) 改进了。测试SELECT id FROM mytable WHERE status BETWEEN 'running' AND 'running' 很快。 由于你没有说表上有多少索引,并且表中有 7 个中间行,可能是你 mysql 配置文件中的索引缓冲区太低,所以从磁盘读取索引? 【参考方案1】:

正如评论中已经提到的,您在一张大表上使用了许多索引。所以这个索引所需的内存非常高。 您可以通过将 innodb_buffer_pool_size 更改为更高的值来增加 my.cnf 中的索引缓冲区大小。 但是如果不是绝对需要,使用更少的索引并且不使用组合索引可能更有效。 我的猜测是,如果您删除所有索引并仅在状态上创建一个索引,此查询将在 1 秒内运行。

【讨论】:

警告:buffer_pool 增加太多可能会导致交换,这会极大地损害性能。

以上是关于非常简单的 MySQL 索引查询运行非常缓慢的主要内容,如果未能解决你的问题,请参考以下文章

Postgresql 通过网络非常缓慢地发送查询结果

Mysql“不是NULL”工作非常缓慢

使用 sum 和 group by 选项的 Mysql 查询运行速度非常慢

MySQL索引简述

mysql索引总结----mysql 索引类型以及创建

mysql索引总结-mysql 索引类型以及创建