MySQL DELETE 语句不使用索引,尽管相同的 SELECT 查询使用索引

The*_*aom 5 mysql performance delete explain mysql-5.7 query-performance

我有一个大约有 3000 万行的表(很快就会增加两倍/三倍),我必须在其中进行定期更新。表结构如下:

id, 
cookie_id VARCHAR(45), 
country VARCHAR(45), 
category VARCHAR(45), 
other_non_relevant_columns
Run Code Online (Sandbox Code Playgroud)

索引看起来像这样:

SHOW INDEX FROM data;
+-------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name               | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| data  |          0 | PRIMARY                |            1 | id          | A         |    24767570 |     NULL | NULL   |      | BTREE      |         |               |
| data  |          1 | cookie_index           |            1 | cookie_id   | A         |    14440214 |     NULL | NULL   |      | BTREE      |         |               |
| data  |          1 | country_category_index |            1 | country     | A         |         498 |     NULL | NULL   |      | BTREE      |         |               |
| data  |          1 | country_category_index |            2 | category    | A         |         997 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
4 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

因此 cookie_id 上有一个非唯一索引,而 Country+category 列上有一个非唯一索引。现在的情况是,每周我都应该运行查询

  1. 删除属于国家='Y'和类别='X'的所有数据(5到2000万行)
  2. 导入新数据(类似数量)

问题是,删除数据需要花费大量时间 - 这就是我在国家+类别列上设置索引的原因。但是,“DELETE”语句仍然不使用索引,而是尝试扫描整个表:

mysql> EXPLAIN DELETE FROM data WHERE country='Y' and category='X';
+----+-------------+-------+------------+------+------------------------+------+---------+------+----------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys          | key  | key_len | ref  | rows     | filtered | Extra       |
+----+-------------+-------+------------+------+------------------------+------+---------+------+----------+----------+-------------+
|  1 | DELETE      | data  | NULL       | ALL  | country_category_index | NULL | NULL    | NULL | 24767570 |   100.00 | Using where |
+----+-------------+-------+------------+------+------------------------+------+---------+------+----------+----------+-------------+
Run Code Online (Sandbox Code Playgroud)

选择效果很好:

mysql> EXPLAIN SELECT id, cookie_id FROM data WHERE country='Y' and category='X';
+----+-------------+-------+------------+------+------------------------+------------------------+---------+-------------+----------+----------+-------+
| id | select_type | table | partitions | type | possible_keys          | key                    | key_len | ref         | rows     | filtered | Extra |
+----+-------------+-------+------------+------+------------------------+------------------------+---------+-------------+----------+----------+-------+
|  1 | SIMPLE      | data  | NULL       | ref  | country_category_index | country_category_index | 365     | const,const | 10130630 |   100.00 | NULL  |
+----+-------------+-------+------------+------+------------------------+------------------------+---------+-------------+----------+----------+-------+
Run Code Online (Sandbox Code Playgroud)

有什么办法可以优化 DELETE 查询吗?

Ric*_*mes 0

通过首先删除数据,您实际上使数据无法访问。您不想避免这种“停机时间”吗?

考虑将替换数据加载到临时表中,然后执行 IODKU 来更新主数据:

INSERT INTO main (...)
        ON DUPLICATE KEY UPDATE 
             col1 = VALUES(col1),
             ...
    SELECT ... FROM temp;
Run Code Online (Sandbox Code Playgroud)

是否可以删除行;IODKU 不会提供该功能。但是,您可以在它之前加上类似的内容

ALTER TABLE temp ADD INDEX (...);  -- to speed up the LEFT JOIN below

DELETE FROM main
       USING main
        LEFT JOIN temp ON ...
       WHERE temp... IS NULL;
Run Code Online (Sandbox Code Playgroud)