mysql中 utf8 和 utf8mb4 的区别
今年上半年,优化过一批报表查询的功能,在过程中遇到了这个问题,因为两张表字段一个是 utf8 一个是 utf8mb4 ,导致查询一直无法使用索引,最后把字段全都改为 utf8mb4 就好了,当时我就只是认为这两种字符集不同,并没有继续深究下去。
今天突然在一个公众号上讲到mysql中 utf8 和 utf8mb4 的问题,于是我查了一些资料才弄懂这件事情,然后总结了一些后续使用mysql的注意事项。
问题描述
1、我遇到的情况是这样:
假设第一张表是student(学生)表,第二张表是class(班级)表。
studentId和classId分别是两张表的主键,现在我要查询所有学生和他们的班级信息,非常简单的一个问题,在数据量多的情况下,我突然发现这个简单的查询会变的很慢,并且把班级表的关联去掉就会瞬间出结果,我听过EXPLAIN关键字对sql进行了分析,发现的问题是class这张表关联的时候并没有走主键的索引,导致查询会扫描整张class表。
接下来我把所有可能的有可能情况都排除了,还是没有找到问题,后来无意间发现了两张表的classId虽然类型都是varchar,但两者的字符集不同,一个是 utf8 一个是 utf8mb4 ,我把两张表的类型全都改成 utf8mb4 ,就解决了这个问题。
2、我在一个公众号上看到案例:
作者在向数据库插入Emoji 表情的时候数据库报错了,也是改成 utf8mb4 之后解决了问题。
那么通过这两种情况,我们可以知道两件事情,一是 utf8 和 utf8mb4 不一样,二是 utf8 不支持Emoji 表情,那为什么会在mysql里面出现这个问题,我们到底该用哪一种字符集?
资料查询
通过查询资料,我发现这根本就是一个历史遗留问题。
utf8mb4 编码是MySQL在5.5.3之后新增的,mb4就是most bytes 4的意思,是专门拿来兼容四字节unicode,区别就是 utf8mb4 占用四个字节, utf8 占用三个字节。所以第一个问题中虽然两个类型都是varchar,但是他们的编码格式其实是不同的。
为什么会出现这种情况,我们要谈到unicode编码了,在最初的时候unicode编码是16位也就是2个字节,这样他就可以表示65536
个字符,但是大家自己想想,世界上这么多语言,它们的字符数量肯定不止6万多,所以Unicode4.0定义了一组附加字符编码,附加字符编码采用2个16位来表示,也就变成了4个字节。
我们再回到mysql这个问题上, utf8 是mysql中的一种字符集,只支持最长三个字节的UTF-8字符,也就是 Unicode 中的基本多文本平面。这就解释了为什么Emoji 表情为什么存不进去,因为Emoji 表情是四个字节的。
原因很明了了,是因为在mysql最初的时候unicode都还没有辅助平面这么一说,后来需要在mysql中保存4个字节长度的字符时,就出现了 utf8mb4 ,为什么没有直接改变 utf8 呢?有人认为是为了考虑向后兼容性的问题,还有原因是4个字节的字符确实很少用到。
utf8 升级到 utf8mb4 的问题
utf8mb4 是 utf8 的超集,从旧版本的 utf8 升级到 utf8mb4 的时候,不需要担心字符转换或者丢失数据。
那么我们如何去升级呢?
SQL 语句
# 修改数据库:
ALTER DATABASE database_name CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
# 修改表:
ALTER TABLE table_name CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
# 修改表字段:
ALTER TABLE table_name CHANGE column_name column_name VARCHAR(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
修改MySQL配置文件
新增如下参数:
default-character-set = utf8mb4
default-character-set = utf8mb4
character-set-client-handshake = FALSE
character-set-server = utf8mb4
collation-server = utf8mb4 _unicode_ci
init_connect=‘SET NAMES utf8mb4 ‘
检查环境变量 和测试 SQL 如下:
SHOW VARIABLES WHERE Variable_name LIKE ‘character\_set\_%‘ OR Variable_name LIKE ‘collation%‘;
注意:MySQL版本必须为5.5.3以上版本,否则不支持字符集 utf8mb4
建议
个人觉得,虽然 utf8mb4 会多消耗空间,但是为了更好的兼容性,推荐全部使用 utf8mb4,并且使用 varchar 代替 char。