MySQL InnoDB索引减慢了排序

时间:2013-09-08 01:39:00

标签: mysql performance sorting innodb myisam

我在FreeBSD上使用MySQL 5.6并且最近刚从使用MyISAM表切换到InnoDB以获得外键约束和事务的进展。

在切换之后,我发现在一个表上有100,000行的查询,以前需要花费.003秒,现在需要3.6秒。查询看起来像这样:

SELECT *
    -> FROM USERS u 
    -> JOIN MIGHT_FLOCK mf ON (u.USER_ID = mf.USER_ID) 
    ->  WHERE u.STATUS = 'ACTIVE' AND u.ACCESS_ID >= 8  ORDER BY mf.STREAK DESC LIMIT 0,100

我注意到如果我删除了 ORDER BY 子句,执行时间会缩短到.003秒,所以问题显然在于排序。

然后我发现如果我添加了 ORDER BY 但删除了查询中引用的列的索引( STATUS ACCESS_ID ),查询执行时间将正常.003秒。

然后我发现如果我在 STATUS ACCESS_ID 列上添加了索引,但是使用了 IGNORE INDEX(STATUS,ACCESS_ID) ,查询仍然会在正常的.003秒内执行。

当我在WHERE子句中引用一个我不理解的索引列时,是否有关于InnoDB和排序结果的内容?

或者我做错了什么?

慢速查询

EXPLAIN 会返回以下结果:

+----+-------------+-------+--------+--------------------------+---------+---------+---------------------+-------+---------------------------------------------------------------------+
| id | select_type | table | type   | possible_keys            | key     | key_len | ref                 | rows  | Extra                                                               |
+----+-------------+-------+--------+--------------------------+---------+---------+---------------------+-------+---------------------------------------------------------------------+
|  1 | SIMPLE      | u     | ref    | PRIMARY,STATUS,ACCESS_ID | STATUS  | 2       | const               | 53902 | Using index condition; Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | mf    | eq_ref | PRIMARY                  | PRIMARY | 4       | PRO_MIGHT.u.USER_ID |     1 | NULL                                                                |
+----+-------------+-------+--------+--------------------------+---------+---------+---------------------+-------+---------------------------------------------------------------------+
快速查询

EXPLAIN 会返回以下结果:

+----+-------------+-------+--------+---------------+---------+---------+----------------------+------+-------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref                  | rows | Extra       |
+----+-------------+-------+--------+---------------+---------+---------+----------------------+------+-------------+
|  1 | SIMPLE      | mf    | index  | PRIMARY       | STREAK  | 2       | NULL                 |  100 | NULL        |
|  1 | SIMPLE      | u     | eq_ref | PRIMARY       | PRIMARY | 4       | PRO_MIGHT.mf.USER_ID |    1 | Using where |
+----+-------------+-------+--------+---------------+---------+---------+----------------------+------+-------------+

非常感谢任何帮助。

1 个答案:

答案 0 :(得分:1)

在缓慢的情况下,MySQL假设STATUS上的索引将极大地限制它必须排序的users的数量。 MySQL错了。大概您的大多数用户都是ACTIVE。 MySQL正在获取5万个用户行,检查他们的ACCESS_ID,加入MIGHT_FLOCK,对结果进行排序并获得前100个(超过50k)。

在快速的情况下,你告诉MySQL它不能使用USERS上的任何一个索引。 MySQL正在使用其下一个最佳索引,它使用MIGHT_FLOCK索引(已经排序)从STREAK获取前100行,然后加入USERS并获取用户行,然后检查您的用户是ACTIVE并且ACCESS_ID等于或高于8.这速度要快得多,因为从磁盘读取只有100行(两个表为x2)。

我建议:

  • 删除STATUS上的索引,除非您经常需要检索INACTIVE个用户(不是ACTIVE个用户)。这个索引对你没有帮助。
  • 阅读this question以了解为什么你的种类如此缓慢。您可以tune InnoDB for better sort performance来预防这类问题。
  • 如果{8}或8以上的ACCESS_ID用户很少,您应该会看到一个显着的改进。如果不是,您可能必须在select子句中使用STRAIGHT_JOIN

以下示例:

SELECT *
FROM MIGHT_FLOCK mf 
STRAIGHT_JOIN USERS u ON (u.USER_ID = mf.USER_ID) 
WHERE u.STATUS = 'ACTIVE' AND u.ACCESS_ID >= 8  ORDER BY mf.STREAK DESC LIMIT 0,100

STRAIGHT_JOIN强制MySQL根据您在查询中指定这两个表的顺序访问MIGHT_FLOCK表之前的USERS表。

要回答“为什么行为发生了变化”这一问题,您应该首先了解MySQL对每个索引的统计信息:http://dev.mysql.com/doc/refman/5.6/en/myisam-index-statistics.html。如果统计数据不是最新的,或者InnoDB没有向MySQL提供足够的信息,那么查询优化器可以(并且确实)做出关于如何连接表的愚蠢决定。