Select 按日期时间范围在 MySQL 中非常慢

Select by datetime range is very slow in MySQL

我有 table posts 超过 650 万条记录。每个 post 使用固定长度 name 表示。我使用 MySQL 社区 5.7,具有大约 10K-20K IOPS 和 1GB 内存的 SSD 磁盘,key-buffer-size 设置为 512M(顺便说一句,大多数情况下我使用默认 MySQL 配置驱动)。我的资源有限,因此我选择了 MyISAM 作为我的存储引擎。我的基准测试表明,在我的情况下,MyISAM 更快。我也不太关心数据,因为它可以更新。

所以,这是我的方案信息:

+------------+--------+------------+
| TABLE_NAME | ENGINE | row_format |
+------------+--------+------------+
| posts      | MyISAM | Fixed      |
+------------+--------+------------+

+---------------------+---------------------+------+-----+---------+----------------+
| Field               | Type                | Null | Key | Default | Extra          |
+---------------------+---------------------+------+-----+---------+----------------+
| id                  | int(11) unsigned    | NO   | PRI | NULL    | auto_increment |
| name                | char(30)            | NO   | UNI | NULL    |                |
| worker_id           | tinyint(4) unsigned | NO   | MUL | NULL    |                |
| processing_priority | tinyint(4) unsigned | NO   | MUL | 0       |                |
| last_processed_at   | datetime            | YES  | MUL | NULL    |                |
| scraped_at          | datetime            | NO   | MUL | NULL    |                |
+---------------------+---------------------+------+-----+---------+----------------+

+-------+------------+---------------------+--------------+---------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name            | Seq_in_index | Column_name         | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+---------------------+--------------+---------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| posts |          0 | PRIMARY             |            1 | id                  | A         |     6579588 |     NULL | NULL   |      | BTREE      |         |               |
| posts |          0 | name                |            1 | name                | A         |     6579588 |     NULL | NULL   |      | BTREE      |         |               |
| posts |          1 | last_processed_at   |            1 | last_processed_at   | A         |     6579588 |     NULL | NULL   | YES  | BTREE      |         |               |
| posts |          1 | processing_priority |            1 | processing_priority | A         |           3 |     NULL | NULL   |      | BTREE      |         |               |
| posts |          1 | worker_id           |            1 | worker_id           | A         |          50 |     NULL | NULL   |      | BTREE      |         |               |
| posts |          1 | scraped_at          |            1 | scraped_at          | A         |      234985 |     NULL | NULL   |      | BTREE      |         |               |
+-------+------------+---------------------+--------------+---------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

查询我运行:

SELECT COUNT(*) FROM `posts` WHERE `posts`.`worker_id` = 1 AND (last_processed_at >= '2017-11-04 22:20:27.203761')

MySQL 需要 3676.4ms 来执行这个查询。

查询解释:

+----+-------------+-------+------------+------+-----------------------------+-----------+---------+-------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys               | key       | key_len | ref   | rows   | filtered | Extra       |
+----+-------------+-------+------------+------+-----------------------------+-----------+---------+-------+--------+----------+-------------+
|  1 | SIMPLE      | posts | NULL       | ref  | last_processed_at,worker_id | worker_id | 1       | const | 232621 |    37.45 | Using where |
+----+-------------+-------+------------+------+-----------------------------+-----------+---------+-------+--------+----------+-------------+

您对如何优化它有什么想法吗?

您可以使用 worker_idlast_processed_at 创建组合键,替换 worker_id 键。

如果您因为 space 感到局促:

  • CHAR(30) 对于可变长度 names 可能会使您的 .MYD 文件比 Ascii 或 latin1 所需的大 50%,或者 3 如果 utf8.
  • 则需要多大
  • 不要盲目地索引每一列;在 .MYI 文件中浪费 space。特别是,INDEX(processing_priority) 可能从未使用过。

让我们看看 SHOW CREATE TABLE 和一些进一步批评 space 设置的查询。

1GB RAM 和 512MB key_buffer 真的很糟糕。首先,key_buffer 仅用于缓存索引块(每个 1KB)。其次,您需要缓存数据的空间,OS 就是这样做的。第三,您需要为代码和其他数据留出空间。推荐不超过 key_buffer_size = 50M.

(至于你的实际问题,@Turo 给出了很好的答案。)但是你删除了现在多余的 INDEX(worker_id) 了吗? (这将节省大约 70MB。)

更多关于索引创建的信息:http://mysql.rjweb.org/doc.php/index_cookbook_mysql