我们正在升级到mysql 5.7并且发现它比它的5.6计数器部分慢得多。虽然两者的配置几乎完全相同,但5.6版本以毫秒为单位执行大部分sqls,而另一个版本对于中间复杂sql(例如下面的那个)大约需要1秒或更长时间。
-- Getting most recent users that are email-verified and not banned
SELECT
`u`.*
FROM
`user` AS `u`
INNER JOIN `user` user_table_alias ON user_table_alias.`id` = `u`.`id`
LEFT JOIN `user_suspend` user_suspend_table_alias ON user_suspend_table_alias.`userId` = `user_table_alias`.`id`
WHERE
(
`user_suspend_table_alias`.`id` IS NULL
)
AND
`user_table_alias`.`emailVerify` = 1
ORDER BY
`u`.`joinStamp` DESC
LIMIT 1, 18
这两个表都很简单,索引很好:
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`email` varchar(128) NOT NULL DEFAULT '',
`username` varchar(32) NOT NULL DEFAULT '',
`password` varchar(64) NOT NULL DEFAULT '',
`joinStamp` int(11) NOT NULL DEFAULT '0',
`activityStamp` int(11) NOT NULL DEFAULT '0',
`accountType` varchar(32) NOT NULL DEFAULT '',
`emailVerify` tinyint(2) NOT NULL DEFAULT '0',
`joinIp` int(11) unsigned NOT NULL,
`locationId` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`),
UNIQUE KEY `username` (`username`),
KEY `accountType` (`accountType`),
KEY `joinStamp` (`joinStamp`),
KEY `activityStamp` (`activityStamp`)
) ENGINE=MyISAM AUTO_INCREMENT=89747 DEFAULT CHARSET=utf8 COMMENT='utf8_general_ci';
-- ----------------------------
-- Table structure for user_suspend
-- ----------------------------
DROP TABLE IF EXISTS `user_suspend`;
CREATE TABLE `user_suspend` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`userId` int(11) DEFAULT NULL,
`timestamp` int(11) DEFAULT NULL,
`message` text NOT NULL,
`expire` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `userId` (`userId`)
) ENGINE=MyISAM AUTO_INCREMENT=513 DEFAULT CHARSET=utf8;
这些表分别有大约100K和1K行。我注意到了两个有趣的行为,我想和#34;修复" :
注意:我们有缓存查询:
显示状态' Qcache%'
Qcache_free_blocks 19408
Qcache_free_memory 61782816
Qcache_hits 31437169
Qcache_inserts 2406719
Qcache_lowmem_prunes 133483
Qcache_not_cached 43555
Qcache_queries_in_cache 41691
Qcache_total_blocks 103951
我用谷歌搜索并发现了5.7上报告的许多问题,但不知道为什么这个奇怪的行为在这个sql上(还有很多其他的sqls在5.7上运行得慢得多)。
以下是Neville K建议的EXPLAIN:
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE user_table_alias NULL ALL PRIMARY NULL NULL NULL 104801 10.00 Using where; Usingtemporary; Usingfilesort
1 SIMPLE u NULL eq_ref PRIMARY PRIMARY 4 knn.base_user_table_alias.id 1 100.00 NULL
1 SIMPLE user_suspend_table_alias NULL ref userId userId 5 knn.base_user_table_alias.id 1 10.00 Using where;
答案 0 :(得分:2)
INNER JOIN
看起来毫无用处。它只与自身连接,并且在查询的其余部分中不使用该技术。user
user_table_alias ON user_table_alias.id
= u
.id
user
上没有索引。这由EXPLAIN的第一行表示。 ('使用where'表示不使用索引)
此查询不能很好地扩展表的大小,因为在分隔最近用户'之前必须查看完整表。是。因此,myisam使用的内部缓冲区现在可能已溢出。 这就是'使用临时'手段。使用filesort意味着order by太大,它使用的是临时文件,这对性能有害。
答案 1 :(得分:1)
好的,感谢NevilleK on Explain。
我想出了如何只修复这个SQL:
user_table_alias.emailVerify = 1
到
u.emailVerify = 1
我不知道为什么,但在MySQL5.6中,两者都是以毫秒为单位执行的。
我想我将不得不审查所有SQL(来自其他开发人员),这要归功于MySQL的落后改进
答案 2 :(得分:1)
自我加入看起来多余。
我认为您可以按如下方式重新编写查询:
SELECT
`u`.*
FROM
`user` AS `u`
LEFT JOIN `user_suspend` user_suspend_table_alias ON user_suspend_table_alias.`userId` = `u`.`id`
WHERE `user_suspend_table_alias`.`id` IS NULL
AND `u`.`emailVerify` = 1
ORDER BY
`u`.`joinStamp` DESC
LIMIT 1, 18
我假设“emailVerify”是一个只包含少量值(0和1)的列,因此不应编入索引。我还假设“joinStamp”是某种时间戳(尽管数据类型是整数)。如果这是真的,你可以创建一个索引来进一步提高速度。
create index id_joinstamp on user (id, joinstamp)
答案 3 :(得分:0)
作为一种解决方法,您可以在首次选择后尝试使用关键字STRAIGHT_JOIN。此关键字强制mysql从左到右连接(MYSQL - What does STRAIGHT_JOIN do in this code?)。