我遇到了MySQL的性能问题;似乎我的请求的执行计划远非最佳,但我不知道为什么MySQL选择它,也不知道如何改变它。我在最小的环境中重现了这个问题,这是查询:
SELECT member.id, member_cache.id, section.id, topic.id
FROM topic
INNER JOIN (section
INNER JOIN (member
LEFT JOIN (member_cache) ON member_cache.id = member.id
) ON member.id = section.last_member
) ON section.id = topic.section
WHERE topic.last_time IS NOT NULL
ORDER BY topic.last_time DESC
LIMIT 0, 1
以下是此查询中使用的表格:
CREATE TABLE `member` (`id` int(10) unsigned NOT NULL)
ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `member_cache` (`id` int(10) unsigned NOT NULL)
ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `section` (`id` int(10) unsigned NOT NULL, `last_member` int(10) unsigned NOT NULL DEFAULT '0')
ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `topic` (`id` int(10) unsigned NOT NULL, `section` int(10) unsigned NOT NULL, `last_time` int(10) unsigned NOT NULL)
ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
ALTER TABLE `member` ADD PRIMARY KEY (`id`);
ALTER TABLE `member_cache` ADD PRIMARY KEY (`id`);
ALTER TABLE `section`ADD PRIMARY KEY (`id`);
ALTER TABLE `topic` ADD PRIMARY KEY (`id`), ADD KEY `section__last_time` (`section`,`last_time`), ADD KEY `last_time` (`last_time`);
现在这里是执行计划,使用“EXPLAIN<上面的查询>”获得:
+----+-------------+--------------+--------+------------------------------+--------------------+---------+-------------------------------+------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------------+--------+------------------------------+--------------------+---------+-------------------------------+------+---------------------------------+
| 1 | SIMPLE | section | ALL | PRIMARY | NULL | NULL | NULL | 2188 | Using temporary; Using filesort |
| 1 | SIMPLE | member | eq_ref | PRIMARY | PRIMARY | 4 | temporary.section.last_member | 1 | Using index |
| 1 | SIMPLE | member_cache | eq_ref | PRIMARY | PRIMARY | 4 | temporary.section.last_member | 1 | Using index |
| 1 | SIMPLE | topic | ref | section__last_time,last_time | section__last_time | 4 | temporary.section.id | 106 | Using index condition |
+----+-------------+--------------+--------+------------------------------+--------------------+---------+-------------------------------+------+---------------------------------+
正如您所看到的那样,它首先扫描整个“部分”表,使用临时表并导致糟糕的表现。我真的不明白为什么会发生这种情况,因为“topic.last_time”(在WHERE子句中使用)和“section.id”(在第一个INNER JOIN中使用)都存在索引。我也做了几次测试,结果非常不稳定:
以下是我应用上述三项变更后的执行计划:
+----+-------------+--------------+--------+---------------+-----------+---------+-------------------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------------+--------+---------------+-----------+---------+-------------------------------+------+-------------+
| 1 | SIMPLE | topic | index | last_time | last_time | 4 | NULL | 1 | Using where |
| 1 | SIMPLE | section | eq_ref | PRIMARY | PRIMARY | 4 | temporary.topic.section | 1 | NULL |
| 1 | SIMPLE | member | eq_ref | PRIMARY | PRIMARY | 4 | temporary.section.last_member | 1 | Using index |
| 1 | SIMPLE | member_cache | eq_ref | PRIMARY | PRIMARY | 4 | temporary.section.last_member | 1 | Using index |
+----+-------------+--------------+--------+---------------+-----------+---------+-------------------------------+------+-------------+
我还试图“OPTIMIZE TABLE”所有表格或切换到InnoDB引擎,但这些都没有改变任何东西。问题转载于MySQL版本5.5.35和5.6.15;我还上传了测试环境here的快照,其中可以轻松复制上述查询。
你知道什么可以解释这个执行计划吗?
答案 0 :(得分:0)
考虑在section.last_member,section.id。
上添加索引ALTER TABLE section ADD KEY(last_member, id);
如果他们是innodb,你可以省略ID,因为它已经是PK。