3

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

切换后,我发现以前需要 0.003 秒的 100,000 行表的查询现在需要 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子句,执行时间会下降到 0.003 秒,所以问题显然出在排序上。

然后我发现如果我添加了ORDER BY但删除了查询中引用的列上的索引(STATUSACCESS_ID),查询执行时间将需要正常的 0.003 秒。

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

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

还是我做错了什么?

查询的解释返回以下结果:

+----+-------------+-------+--------+--------------------------+---------+---------+---------------------+-------+---------------------------------------------------------------------+
| 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 |
+----+-------------+-------+--------+---------------+---------+---------+----------------------+------+-------------+

任何帮助将不胜感激。

4

1 回答 1

1

在缓慢的情况下,MySQL 假设索引STATUS将大大限制users它必须排序的数量。MySQL 是错误的。大概您的大多数用户都是ACTIVE. MySQL 正在获取 50k 用户行,检查他们的ACCESS_ID、加入MIGHT_FLOCK、排序结果并获取前 100 行(从 50k 中)。

在快速情况下,您告诉 MySQL 它不能在USERS. MySQL 正在使用它的下一个最佳索引,它从MIGHT_FLOCK使用STREAK索引(已经排序)中获取前 100 行,然后加入USERS并提取用户行,然后检查您的用户是否ACTIVEACCESS_ID8 或以上. 这要快得多,因为只从磁盘读取 100 行(两个表为 x2)。

我会推荐:

  • STATUS除非您经常需要检索INACTIVE用户(不是ACTIVE用户) ,否则请删除索引。该索引对您没有帮助。
  • 阅读此问题以了解您的排序为何如此缓慢。您可能可以调整 InnoDB 以获得更好的排序性能以防止此类问题。
  • 如果您只有很少的用户ACCESS_ID达到或超过 8,您应该已经看到了显着的改进。如果不是,您可能必须在您的选择子句中使用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 提供足够的信息,查询优化器可以(并且确实)做出关于如何连接表的愚蠢决定。

于 2013-09-08T03:26:27.457 回答