mysql - 为什么我的 UPDATE ... WHERE ... ORDER BY .. LIMIT 1 语句需要这么长时间?

标签 mysql sql database optimization query-optimization

<分区>

我正在尝试改进我的查询,以免花费这么长时间。有什么我可以尝试的吗?

我正在使用 InnoDB。

我的 table :

mysql> describe hunted_place_review_external_urls;
+--------------+--------------+------+-----+---------+----------------+
| Field        | Type         | Null | Key | Default | Extra          |
+--------------+--------------+------+-----+---------+----------------+
| id           | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| worker_id    | varchar(255) | YES  | MUL | NULL    |                |
| queued_at    | bigint(20)   | YES  | MUL | NULL    |                |
| external_url | varchar(255) | NO   |     | NULL    |                |
| place_id     | varchar(63)  | NO   | MUL | NULL    |                |
| source_id    | varchar(63)  | NO   |     | NULL    |                |
| successful   | tinyint(1)   | NO   |     | 0       |                |
+--------------+--------------+------+-----+---------+----------------+

mysql> show index from hunted_place_review_external_urls;
+-----------------------------------+------------+--------------------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table                             | Non_unique | Key_name                                   | Seq_in_index | Column_name  | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------------------------------+------------+--------------------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| hunted_place_review_external_urls |          0 | PRIMARY                                    |            1 | id           | A         |     5118685 |     NULL | NULL   |      | BTREE      |         |               |
| hunted_place_review_external_urls |          1 | worker_id                                  |            1 | worker_id    | A         |     5118685 |     NULL | NULL   | YES  | BTREE      |         |               |
| hunted_place_review_external_urls |          1 | queued_at                                  |            1 | queued_at    | A         |     5118685 |     NULL | NULL   | YES  | BTREE      |         |               |
| hunted_place_review_external_urls |          1 | worker_id_and_queued_at                    |            1 | worker_id    | A         |     5118685 |     NULL | NULL   | YES  | BTREE      |         |               |
| hunted_place_review_external_urls |          1 | worker_id_and_queued_at                    |            2 | queued_at    | A         |     5118685 |     NULL | NULL   | YES  | BTREE      |         |               |
| hunted_place_review_external_urls |          1 | place_id_source_id_external_url_successful |            1 | place_id     | A         |     5118685 |     NULL | NULL   |      | BTREE      |         |               |
| hunted_place_review_external_urls |          1 | place_id_source_id_external_url_successful |            2 | source_id    | A         |     5118685 |     NULL | NULL   |      | BTREE      |         |               |
| hunted_place_review_external_urls |          1 | place_id_source_id_external_url_successful |            3 | external_url | A         |     5118685 |     NULL | NULL   |      | BTREE      |         |               |
| hunted_place_review_external_urls |          1 | place_id_source_id_external_url_successful |            4 | successful   | A         |     5118685 |     NULL | NULL   |      | BTREE      |         |               |
+-----------------------------------+------------+--------------------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

我的查询:

mysql> select count(*) from hunted_place_review_external_urls;
+----------+
| count(*) |
+----------+
|  4217356 |
+----------+
1 row in set (0.96 sec)

mysql> select count(*) from hunted_place_review_external_urls where worker_id is null;
+----------+
| count(*) |
+----------+
|   772626 |
+----------+
1 row in set (0.27 sec)

mysql> update hunted_place_review_external_urls set worker_id = "123" where worker_id is null order by queued_at asc limit 1;
Query OK, 1 row affected (4.80 sec)
Rows matched: 1  Changed: 1  Warnings: 0

为什么更新查询需要 4 秒,即使我在 queued_atworker_id 上都有单一索引和复合索引?这在 worker_id = null 的行数要低得多之前从未发生过。使用约 20 万行而不是 78 万行,只需几毫秒。

请注意,使用 SELECT 而不是 UPDATE 的等效查询非常快:

mysql> select * from hunted_place_review_external_urls where worker_id is null order by  queued_at asc limit 1;
1 row in set (0.00 sec)

我的 queued_at 值是以毫秒数表示的时间戳,例如 1398210069531

我已经尝试在 worker_idqueued_at 上删除我的单个索引,但问题仍然存在:

mysql> drop index queued_at on hunted_place_review_external_urls;
Query OK, 0 rows affected (3.75 sec)

mysql> drop index worker_id on hunted_place_review_external_urls;
Query OK, 0 rows affected (3.75 sec)

mysql> show index from hunted_place_review_external_urls;
+-----------------------------------+------------+--------------------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table                             | Non_unique | Key_name                                   | Seq_in_index | Column_name  | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------------------------------+------------+--------------------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| hunted_place_review_external_urls |          0 | PRIMARY                                    |            1 | id           | A         |     5118685 |     NULL | NULL   |      | BTREE      |         |               |
| hunted_place_review_external_urls |          1 | worker_id_and_queued_at                    |            1 | worker_id    | A         |     5118685 |     NULL | NULL   | YES  | BTREE      |         |               |
| hunted_place_review_external_urls |          1 | worker_id_and_queued_at                    |            2 | queued_at    | A         |     5118685 |     NULL | NULL   | YES  | BTREE      |         |               |
| hunted_place_review_external_urls |          1 | place_id_source_id_external_url_successful |            1 | place_id     | A         |     5118685 |     NULL | NULL   |      | BTREE      |         |               |
| hunted_place_review_external_urls |          1 | place_id_source_id_external_url_successful |            2 | source_id    | A         |     5118685 |     NULL | NULL   |      | BTREE      |         |               |
| hunted_place_review_external_urls |          1 | place_id_source_id_external_url_successful |            3 | external_url | A         |     5118685 |     NULL | NULL   |      | BTREE      |         |               |
| hunted_place_review_external_urls |          1 | place_id_source_id_external_url_successful |            4 | successful   | A         |     5118685 |     NULL | NULL   |      | BTREE      |         |               |
+-----------------------------------+------------+--------------------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

这是我的 EXPLAIN SELECT 语句。我正在使用不支持 EXPLAIN UPDATE 的旧版本 MYSQL:

mysql> explain select * from hunted_place_review_external_urls where worker_id is null order by queued_at asc limit 1;
+----+-------------+-----------------------------------+------+-------------------------+-------------------------+---------+-------+---------+-------------+
| id | select_type | table                             | type | possible_keys           | key                     | key_len | ref   | rows    | Extra       |
+----+-------------+-----------------------------------+------+-------------------------+-------------------------+---------+-------+---------+-------------+
|  1 | SIMPLE      | hunted_place_review_external_urls | ref  | worker_id_and_queued_at | worker_id_and_queued_at | 768     | const | 1587282 | Using where |
+----+-------------+-----------------------------------+------+-------------------------+-------------------------+---------+-------+---------+-------------+
1 row in set (0.00 sec)

最佳答案

这是您的查询:

update hunted_place_review_external_urls
    set worker_id = "123"
    where worker_id is null
    order by queued_at asc
    limit 1;

它首先必须找到用于更新的行,这需要应用 where 子句和 order by 子句。它要么完成所有工作(扫描表然后排序),要么使用索引。正确的索引应该是 hunted_place_review_external_urls(worker_id, queued_at)。您可以在末尾添加更多列,但这些列必须是前两列,并按此顺序。

编辑:

考虑到 select 速度很快,试试这个版本:

update hunted_place_review_external_urls toupdate join
       (select
        from hunted_place_review_external_urls
        where worker_id is null
        order by queued_at asc
        limit 1
       ) l
       on toupdate.id = l.id
    set toupdate.worker_id = '123';

我不确定为什么索引在这里可以正确使用,而在 update 中却不能,但希望这会起作用。

关于mysql - 为什么我的 UPDATE ... WHERE ... ORDER BY .. LIMIT 1 语句需要这么长时间?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24002129/

相关文章:

mysql - 设置绑定(bind)地址后无法重启mysql

mysql - "key buffer size"和 "total MyISAM indexes"单位大小是什么意思?

php - 警告:mysqli::query():空查询

Mysql如何在连接中重复时使行为空

python - 在 SQLAlchemy 中,如何在具有多个关系时访问查询中的引用属性?

sql-server - 如何限制列中可以放置的值范围?

mysql - mysql 查询中日期时间的更改不会反射(reflect)在 Jasper studio 中生成报告期间的输出中

php - CakePHP 保存外键问题

sql - 与层次顺序和缩进空间相关的查询

database - 与 MS SQL Server Management Studio 免费版对应的 Oracle 是什么?