简体   繁体   English

在中等sql中,mysql 5.7比mysql 5.6慢得多

[英]mysql 5.7 is much slower than mysql 5.6 in medium sql

We are upgrading to mysql 5.7 and just discover that it is much slower than its 5.6 counter part. 我们正在升级到mysql 5.7,只是发现它比5.6计数器要慢得多。 While both have almost identical config, the 5.6 version execute most of the sqls in milliseconds, while the other takes around 1 sec or more for a middle complex sql like the one below for example. 虽然两者的配置几乎相同,但5.6版本以毫秒为单位执行大多数sql,而对于中等复杂的sql(例如下面的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

Both tables are pretty simple and well indexed: 这两个表都非常简单并且索引良好:

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

The tables have around 100K and 1K rows respectively. 这些表分别具有约100K和1K行。 I noticed two interesting behaviors that I would like to "fix" : 我注意到了两个我想“修复”的有趣行为:

  1. Removing ORDER BY bring the exec time from ~1.2 sec to 0.0015 sec !! 删除ORDER BY可使执行时间从〜1.2秒变为0.0015秒!
  2. The sql is not cached by mysql 5.7 mysql 5.7未缓存SQL

Note: we do have cache query : 注意:我们有缓存查询:

SHOW STATUS LIKE 'Qcache%' 显示状态,例如“ 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

I googled and found out many issues reported on 5.7 but don't get why this strange behaviors on this sql (still plenty of other sqls that run much slower on 5.7). 我用谷歌搜索发现了关于5.7的许多问题,但是却不明白为什么这个sql出现这种奇怪的行为(仍然有很多其他的sql在5.7上运行得慢得多)。

Here is the EXPLAIN, suggested by Neville K: 这是Neville K建议的解释:

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;

The INNER JOIN user user_table_alias ON user_table_alias. id = u . id INNER JOIN user user_table_alias ON user_table_alias. id = u . id INNER JOIN user user_table_alias ON user_table_alias. id = u . id INNER JOIN user user_table_alias ON user_table_alias. id = u . id looks useless. INNER JOIN user user_table_alias ON user_table_alias. id = u . id看起来没用。 It only joins against itself and that technique is not used in the rest of the query. 它仅与自身连接,在其余的查询中未使用该技术。

There is no index on emailVerify . emailVerify上没有索引。 Which is indicated by the first row of the EXPLAIN. 由EXPLAIN的第一行指示。 ('using where' means no index is used) (“在哪里使用”表示不使用索引)

This query does not scale well with the size of the table, because the full table must be looked at before delimiting what 'recent users' are. 此查询不能很好地扩展表的大小,因为在界定“最近的用户”之前,必须先查看整个表。 So probably some internal buffer used by myisam is overflowed now. 因此,myisam使用的某些内部缓冲区现在可能已溢出。 That is what 'using temporary' means. 这就是“使用临时”的意思。 Using filesort means the order by is so big it uses a tempfile, which is bad for performance. 使用文件排序意味着顺序很大,因此使用了临时文件,这对性能不利。

Ok, thanks to NevilleK on Explain. 好的,感谢NevilleK在Explain上的发言。

I figured out how to just ONLY fix this SQL: 我想出了如何仅修复此SQL:

 user_table_alias.emailVerify = 1 

to

u.emailVerify = 1

I do not know why, but in MySQL5.6, both are executed in milliseconds. 我不知道为什么,但是在MySQL5.6中,两者都以毫秒为单位执行。

I guess I will have to review all the SQL (from other devs) , thanks to MySQL backward improvement 我想我得回顾所有SQL(来自其他开发者),这要归功于MySQL的向后改进

The self-join looks redundant. 自连接看起来很多余。

I think you can re-write the query as follows: 我认为您可以按以下方式重新编写查询:

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

I assume "emailVerify" is a column with just a handful of values (0 and 1), and should therefore not be indexed. 我假设“ emailVerify”是仅包含少量值(0和1)的列,因此不应该对其进行索引。 I also assume that "joinStamp" is some kind of time stamp (though the data type is integer). 我还假定“ joinStamp”是某种时间戳(尽管数据类型是整数)。 If that is true, you could create an index to speed this up further. 如果是这样,您可以创建一个索引来进一步加快速度。

create index id_joinstamp on user (id, joinstamp)

As a workaround you can try to use the Keyword STRAIGHT_JOIN after your first select. 作为解决方法,您可以在首次选择后尝试使用关键字STRAIGHT_JOIN。 This keyword force mysql to join from left to right ( MYSQL - What does STRAIGHT_JOIN do in this code? ). 此关键字强制mysql从左到右连接( MYSQL-STRAIGHT_JOIN在此代码中做什么? )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM