我在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 |
+----+-------------+-------+--------+---------------+---------+---------+----------------------+------+-------------+
非常感谢任何帮助。
答案 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
个用户)。这个索引对你没有帮助。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提供足够的信息,那么查询优化器可以(并且确实)做出关于如何连接表的愚蠢决定。