【发布时间】: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 配置文件中的索引缓冲区太低,所以从磁盘读取索引?
标签: mysql sql performance indexing