非常简单的 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 秒。
如果我同时使用status
和user_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 索引查询运行非常缓慢的主要内容,如果未能解决你的问题,请参考以下文章