我有一个具有以下属性的表:
+-----------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | <null> | auto_increment |
| c2 | varchar(255) | YES | MUL | <null> | |
| c3 | int(11) | YES | | <null> | |
| c4 | varchar(255) | YES | | <null> | |
| c5 | varchar(255) | YES | | <null> | |
| c6 | int(11) | YES | MUL | <null> | |
| c7 | int(11) | YES | | <null> | |
| c8 | int(11) | YES | | <null> | |
| c9 | datetime | YES | | <null> | |
| c10 | datetime | YES | | <null> | |
| c11 | char(40) | YES | UNI | <null> | |
| c12 | tinyint(1) | NO | MUL | 1 | |
| c13 | text | YES | | <null> | |
| c14 | int(11) | YES | MUL | <null> | |
| c15 | varchar(64) | YES | MUL | <null> | |
+-----------------------+--------------+------+-----+---------+----------------+
显示 table_one 的索引;
显示以下输出:
+-------------------+------------+--------------------------------------------------+--------------+-----------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------------------+------------+--------------------------------------------------+--------------+-----------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| table_one | 0 | PRIMARY | 1 | id | A | 1621972 | NULL | NULL | | BTREE | | |
| table_one | 0 | c11 | 1 | c11 | A | 1621972 | NULL | NULL | YES | BTREE | | |
| table_one | 0 | c2_c6_c8_and_c14_unique | 1 | c2 | A | 1621972 | NULL | NULL | YES | BTREE | | |
| table_one | 0 | c2_c6_c8_and_c14_unique | 2 | c6 | A | 1621972 | NULL | NULL | YES | BTREE | | |
| table_one | 0 | c2_c6_c8_and_c14_unique | 3 | c8 | A | 1621972 | NULL | NULL | YES | BTREE | | |
| table_one | 0 | c2_c6_c8_and_c14_unique | 4 | c14 | A | 1621972 | NULL | NULL | YES | BTREE | | |
| table_one | 1 | c12 | 1 | c12 | A | 1 | NULL | NULL | | BTREE | | |
| table_one | 1 | c6 | 1 | c6 | A | 20794 | NULL | NULL | YES | BTREE | | |
| table_one | 1 | c14 | 1 | c14 | A | 577 | NULL | NULL | YES | BTREE | | |
| table_one | 1 | c15 | 1 | c15 | A | 5 | NULL | NULL | YES | BTREE | | |
+-------------------+------------+--------------------------------------------------+--------------+-----------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
现在,当我运行以下查询时,平均需要大约 5.8 秒:
select * from table_one
where c6 = 12345 and c14 = 12
limit 10 offset 0;
当我对上面的查询运行explain
时,它说它已经使用了index_merge
:
+----+-------------+---------------------+-------------+-----------------------------+---------+---------+-------------------------------+------+-------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------------+-------------+-----------------------------+---------+---------+-------------------------------+------+-------------------------+
| 1 | SIMPLE | table_one | index_merge | ....................... | c14, c6 | 5,5 | NULL | 9 | Using intersect(c14,c6);|
+----+-------------+---------------------+-------------+-----------------------------+---------+---------+-------------------------------+------+-------------------------+
但是,如果我强制表仅在 c6
上使用索引,它会平均在 0.6 秒内返回结果:
select * from table_one force index(c6) where c6 = 12345 and c14 = 12 limit 10 offset 0;
为什么 MySQL 单独使用 index_merge
并使其变慢?我知道我在 c6、c14
上没有复合索引,但它们单独存在。
此外,强制索引的 explain
查询显示执行查询时访问的行数更多,但速度仍然快了 10 倍。
+----+-------------+---------------------+-------------+-----------------------------+---------+---------+-------------------------------+--------+-------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------------+-------------+-----------------------------+---------+---------+-------------------------------+--------+-------------------------+
| 1 | SIMPLE | table_one | ref | ....................... | c6 | 5 | const | 22388 | Using where; |
+----+-------------+---------------------+-------------+-----------------------------+---------+---------+-------------------------------+--------+-------------------------+
当有人高速访问 API 时,这会导致我们的生产量下降。 MySQL 在 59 秒内没有返回任何结果,查询超时。
另外,我无法在不停机的情况下真正添加复合索引或更改架构,因为我们已经有 200 万个条目。
当前的临时修复是将强制索引(c6)
添加到查询中,但我不太确定它的可扩展性如何,或者我们以后是否可能会遇到问题。
编辑 1
缓慢是否是由于 index_merge
完成的顺序造成的?
有关 c6
和 c14
的更多信息:将 c6
视为国家/地区,将 c14
视为州。
编辑 2:2020-06-15 07:52:35 UTC:
我尝试通过强制 c14
的索引来运行查询,结果慢了大约 3 倍:
select * from table_one force index(c14) where c6 = 12345 and c14 = 12 limit 10 offset 0;
查询花费了 2.1 秒。
explain
查询给出以下输出:
+----+-------------+---------------------+-------------+-----------------------------+---------+---------+-------------------------------+--------+-------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------------+-------------+-----------------------------+---------+---------+-------------------------------+--------+-------------------------+
| 1 | SIMPLE | table_one | ref | ....................... | c14 | 5 | const | 730 | Using where; |
+----+-------------+---------------------+-------------+-----------------------------+---------+---------+-------------------------------+--------+-------------------------+
查询要访问的行数比在 c6
上强制索引时少了 30 倍。即,这里的行数为 730,而对于上一个查询,行数为 22k。即使访问的行数较少,哪些因素也会使该索引变慢?
更多信息(如果有任何帮助的话):
mysql> select count(*) from table_one where c14 is null;
+----------+
| count(*) |
+----------+
| 7490 |
+----------+
1 row in set (0.02 sec)
mysql> select count(*) from table_one;
+----------+
| count(*) |
+----------+
| 1936278 |
+----------+
1 row in set (1.68 sec)
mysql> select count(*) from table_one where c6 is null;
+----------+
| count(*) |
+----------+
| 0 |
+----------+
1 row in set (0.00 sec)
最佳答案
考虑 使用(数据库名称); 更改表 table_one 添加索引 table_one_c6_and_c14 (c6,c14);
删除查询中的强制请求。
请告诉我们创建多列索引需要多长时间。 既然有合适的索引可用,就到了完成查询的时间了。
关于MySQL index_merge 导致查询运行速度减慢 10 倍,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62364544/