为什么从utf8更改为utf8mb4会降低我的数据库速度?

时间:2018-06-14 04:34:49

标签: php mysql unicode database-performance

我的PHP Web应用程序中的所有MySQL表都是带有utf8编码的MyISAM。由于记录可以在配对应用程序离线时生成,因此我的表键是随机生成的,字母数字VARCHAR;这些字段使用utf8_bin编码设置为二进制,因此它们可以区分大小写。

我最近决定更改所有文本字段的编码,以支持某些用户喜欢输入的表情符号。我继续将所有utf8字段更改为utf8mb4,包括键。我立即开始看到性能问题,其中一个较大的表上的复杂SELECT查询花了一分多钟,然后其他查询排队等待表锁。我将该表上主键字段的编码更改回utf8,性能恢复正常。几天后,我再次将该字段更改为utf8mb4,查询再次开始排队,然后将其更改为恢复正常性能。

所以这顺利进行:

`ID` varchar(8) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT ''

但这会导致问题:

`ID` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT ''

我读过的所有内容都说utf8和utf8mb4应该具有相同的性能,但我看到了我的情况明显不同。这有意义吗?

将关键字段保存在utf8并不是一个真正的问题,因为我预计在那里不会使用超过简单的字母数字字符。但我希望将所有字段设置为相同的编码只是为了保持一致性和简单性(不必记住将用户填充的字段设置为一个编码,将键字段设置为另一个编码)。

关于@MandyShaw的评论

当我使用Sequel Pro Mac应用程序处理数据库时,控制台会不断显示成对的SET NAMES 'utf8'SET NAMES 'utf8mb4'条目,因此确实表明并未正确设置所有内容。但是,这就是我目前所拥有的:

MySQL [(none)]> SHOW GLOBAL VARIABLES WHERE Variable_name LIKE 'character\_set\_%' OR Variable_name LIKE 'collation%';
+--------------------------+--------------------+
| Variable_name            | Value              |
+--------------------------+--------------------+
| character_set_client     | utf8mb4            |
| character_set_connection | utf8mb4            |
| character_set_database   | utf8mb4            |
| character_set_filesystem | binary             |
| character_set_results    | utf8mb4            |
| character_set_server     | utf8mb4            |
| character_set_system     | utf8               |
| collation_connection     | utf8mb4_unicode_ci |
| collation_database       | utf8mb4_unicode_ci |
| collation_server         | utf8mb4_unicode_ci |
+--------------------------+--------------------+

我读到character_set_system无法从utf8更改,character_set_filesystem应该是二进制。

Sequel Pro的连接编码设置为Autodetect,但当我明确地将其更改为utf8mb4,然后打开一个新连接时,我仍然在控制台中看到所有这些编码更改。

我需要更改一些其他内容才能始终如一地使用此编码吗?

1 个答案:

答案 0 :(得分:4)

utf实际上是utfmb3,每个字符可能使用最多3个字节,而utfmb4每个字符可能使用4个字节。对于VARCHAR列,这通常没有多大区别,因为MySQL将只存储所需的字节数(除非您使用ROW_FORMAT = FIXED创建了MyISAM表)。

但是,在查询执行期间,MySQL可能会在MEMORY存储引擎中创建不支持可变长度行的临时表。这些临时表具有最大大小,如果超出该大小,临时表将转换为MyISAM / InnoDB中的表(取决于您的MySQL版本)。每次发生这种情况时,状态变量Created_tmp_disk_tables都会递增。如果是,请尝试查看是否有助于提高max_heap_table_size tmp_table_size的价值。

或者,升级到MySQL 8.0,其中支持可变长度行的新存储引擎用于内部临时表。