Mysql 中的 utf-8

96
阳光男孩刚哥哥
2018.12.21 14:58 字数 1345

导读

问题:在用户评论中带有 emoji表情时,utf-8编码的数据库抛出异常


`Incorrect string value: ‘\xF0\x9F\x98\x83 <…’ for column ‘comment’ at row 1`

在网上找解决方案:将相关字段字符集设置为 utf-8mb4,然后问题解决。

疑问:在我的理解中, utf-8 是用来存储各国语言字符的,utf-8mb4 是难道是专门用来存储 emoji的?

字符集及编码简介

要明白这个问题,首先需要先了解什么是字符集及编码方式。

字符集(Character set)是多个字符的集合,字符编码是在字符集内用于比较字符的一套规则,即字符集的排序规则。

首先我们知道,计算机使用 01 存储信息。最早的计算机在设计时采用 8个比特(bit)作为一个字节(byte)。所以,一个字节能表示的最大的整数就是 255(二进制 11111111,十进制 255),0 -255 被用来表示大小写英文字母、数字和一些符号,

这个编码表被称为 ASCII编码,比如大写字母 A 的编码是 65,小写字母 z 的编码是 122

小技巧:键盘按住 alt + 对应 ASCII 码,输入相应字符

时代在进步,计算机的普及和发展导致新的问题出现,对于中文、日文等文字符号,ASCII字符集并不能满足,于是出现了 UNICODE字符集,

通过扩充比特位的方式,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。将字符转为二进制编码的规则就被称为编码方式。

UNICODE通常用两个字节表示一个字符。为什么是 2字节呢?可能是因为在 UNICODE早期,设计 Unicode的团队考查了世界上主要的文字之后,感觉用 2个字节也就是最多 65536个字符应该够用了,设定编码方式为 UCS2。但是后面出现了更多的字符(现在 Unicode包含了 12万 8千个字符),于是只能再使用 UCS4或 UTF-32将字符以 4字节保存。

问题看起来解决了,但是却有个问题:很多字符使用这种编码并不高效,如用 ASCII表示的 00000001用 UTF-32就会存储很多无必要的高比特位。为了解决这个问题,就出现了其他的非固定长度编码方式,也就是本文的主角 ———— UTF-8,最短的 UTF-8字符只需要使用 1个字节(同 ASCII),最长是 4个字节。UTF-8相比 UTF-32更加节约空间。在 UTF-8中,像“1”这样的字符占用 1字节,“💩”这样的 emoji占用 4字节。其他字符占用 2或者 3字节。更小的空间占用也意味着加载与传输速度会快更多。

MYSQL 中的“utf-8”

可以注意到,上文我提到一个例子:UTF-8中 “💩”占用 4字节。这就回到了本文一开始的问题:utf-8编码的数据库在存储 emoji表情时出错。

按照上述的介绍,应该是可以存进去的?但是我们看下 MYSQL文档上对其 utf-8字符编码的解释:


utf8是 utf8mb3 字符集的别名

  该 utf8mb3 字符集有以下特点:

      * 仅支持BMP字符(不支持增补字符)

      * 每个多字节字符最多需要三个字节

划重点:每个多字节字符最多需要三个字节,这就表明了 MYSQL中 utf-8并不是传统意义上的 utf-8。

我们再看下 utf-8mb4


该utfmb4字符集有以下特点:

    * 支持BMP和补充字符。

    * 每个多字节字符最多需要四个字节。

也就是说,对于 MYSQL来说,utf-8mb4(mb4即 most byte 4)才是真正意义上的 utf-8。当我们 MYSQL数据库的字符集编码为 utf-8

存储需要 4字节表示的 emoji表情时,自然会出错了。

后话

MYSQL为什么会用这种奇怪的方式来定义字符集编码,在网上找了许久的答案没有准确的说法,更多的是把它看作 MYSQL开发过程中的一个失误。

如果大家后续清楚了原因,也可以说出来一起交流下。

另外在寻找问题的过程中也了解到其他一些知识点,跟大家简单分享下:

  • MySQL可以使用对种字符集和检验规则来组织字符。MySQL服务器可以支持多种字符集,在同一台服务器,同一个数据库,甚至同一个表的不同字段都可以指定使用不同的字符集。相比 oracle等其他数据库管理系统,在同一个数据库只能使用相同的字符集,MySQL明显存在更大的灵活性。

  • 数据库设置字符集的排序规则,如 utf8_general_ciutf8_unicode_cigeneralunicode 代表的是排序规则,使用 general 更快速,使用 unicode 更精确(德语等比较特殊的语种使用),ci 即 case insensitive, 即“大小写不敏感”。

日记本