【问题标题】:Select by datetime range is very slow in MySQL在 MySQL 中按日期时间范围选择非常慢
【发布时间】:2017-11-05 10:27:25
【问题描述】:

我的表 posts 有 650 万多条记录。每个帖子都使用固定长度name 表示。我使用 MySQL Community 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 |
+----+-------------+-------+------------+------+-----------------------------+-----------+---------+-------+--------+----------+-------------+

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

【问题讨论】:

    标签: mysql sql database performance myisam


    【解决方案1】:

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

    【讨论】:

    • 一些细节:目前,MySQL 只使用表posts 上的last_processed_atworker_id 之一。它使用索引通过worker_id 获取所有行,然后遍历所有这些行以一一比较last_processed_at。这需要时间。如果您创建组合索引worker_id + last_processed_at,MySQL 将使用组合索引的第二部分来过滤last_processed_at 以及worker_id,因此它会快得多。见文档MySQL 5.7 Multiple-Column Indexes
    • 谢谢!你帮了很多忙。我在两列 worker_id + last_processed_at 上创建了索引。现在查询需要不超过 10 毫秒。速度提高了 360 倍。
    • @Animir 所以基本上在旧情况下 MySQL 在worker_id 上使用索引,然后从磁盘读取last_processed_at 以在条件下使用,对吧?
    • @yivo 差不多,它使用worker_id 索引过滤数据,然后遍历结果数据集中的所有行以通过last_processed_at 过滤。数据可能存储在磁盘或内存中,这取决于,但索引有助于找到某些数据块,将光标直接“移动”到所需的数据并开始选择,例如如果您的数据有 10 个工作人员 ID,并且您需要通过索引 worker_id 查找工作人员 id = 1 的所有数据,则 MySQL 返回块 1,然后逐行通过 last_processed_at 对其进行过滤。希望,这会有所帮助。
    • @Animir 谢谢你的帮助!
    【解决方案2】:

    如果您空间狭小:

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

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

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

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

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

    【讨论】:

      猜你喜欢
      • 2012-04-12
      • 2011-04-27
      • 1970-01-01
      • 1970-01-01
      • 2019-04-28
      • 1970-01-01
      • 2019-01-21
      • 2018-03-03
      • 2013-11-23
      相关资源
      最近更新 更多