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_id
和 last_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
我有 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_id
和 last_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