utf8mb4 和 utf8深度对比分析

utf8mb4 和 utf8 是 MySQL 中两种常用的字符集,它们都可以用来存储 Unicode 字符,但是有一些区别和联系。本文将从以下几个方面对比 utf8mb4 和 utf8:

编码范围和存储空间

utf8 是 MySQL 中最早支持的 Unicode 字符集,它使用 1 到 3 个字节来编码每个字符,最大能表示的 Unicode 码点是 U+FFFF,也就是 Unicode 的基本多文种平面(BMP)。这意味着 utf8 不能存储一些超出 BMP 的字符,例如 Emoji 表情、部分罕用汉字、新增的 Unicode 字符等。这些字符需要 4 个字节来编码,所以 utf8 会在遇到这些字符时报错或者出现乱码。

utf8mb4 是 MySQL 在 5.5.3 版本之后增加的一个新的字符集,它是 utf8 的超集,也就是说 utf8 可以表示的字符 utf8mb4 都可以表示,而且 utf8mb4 还可以表示一些 utf8 不能表示的字符。utf8mb4 使用 1 到 4 个字节来编码每个字符,最大能表示的 Unicode 码点是 U+10FFFF,也就是 Unicode 的所有 17 个平面。这意味着 utf8mb4 可以存储任何合法的 Unicode 字符,包括 Emoji 表情、部分罕用汉字、新增的 Unicode 字符等。

由于 utf8mb4 可以使用 4 个字节来编码字符,所以它占用的存储空间会比 utf8 略大一些。例如,一个 CHAR(10) 类型的字段,如果使用 utf8 字符集,那么它需要保留 10 * 3 = 30 个字节的空间;如果使用 utf8mb4 字符集,那么它需要保留 10 * 4 = 40 个字节的空间。对于 VARCHAR 类型的字段,如果使用 utf8 字符集,那么它需要额外使用一个字节来记录字符串的长度;如果使用 utf8mb4 字符集,那么它需要额外使用两个字节来记录字符串的长度。

排序规则和性能

排序规则(collation)是指在比较和排序字符串时所遵循的规则。不同的字符集可以有不同的排序规则,甚至同一个字符集也可以有多种排序规则。MySQL 中常见的排序规则有以下几种:

  • _bin:按照二进制方式比较字符串,区分大小写和重音符号。
  • _general_ci:按照一般方式比较字符串,不区分大小写和重音符号。
  • _unicode_ci:按照 Unicode 标准方式比较字符串,不区分大小写和重音符号。
  • _ci:按照特定语言或地区方式比较字符串,不区分大小写和重音符号。

utf8 和 utf8mb4 都有以上几种排序规则,但是有一些细微的差别。例如,在 utf8_general_ci 排序规则下,’a’ 等于 ‘A’,而在 utf8mb4_general_ci 排序规则下,’a’ 小于 ‘A’。这是因为 utf8_general_ci 排序规则只考虑了 BMP的字符,而 utf8mb4_general_ci 排序规则考虑了所有的 Unicode 字符。因此,在 utf8mb4_general_ci 排序规则下,’a’ 的 Unicode 码点是 U+0061,而 ‘A’ 的 Unicode 码点是 U+0041,所以 ‘a’ 小于 ‘A’。

排序规则的不同会影响字符串的比较和排序的结果,进而影响索引的效率和查询的性能。一般来说,_bin 排序规则的性能最高,因为它只需要按照二进制方式比较字符串,不需要考虑字符的大小写和重音符号等因素。_general_ci 排序规则的性能次之,因为它只需要按照一般方式比较字符串,不需要考虑字符的语言或地区等因素。*_unicode_ci 和 *_ci 排序规则的性能最低,因为它们需要按照 Unicode 标准或特定语言或地区的方式比较字符串,需要考虑字符的大小写和重音符号等因素。

兼容性和安全性

utf8 和 utf8mb4 的兼容性和安全性也有一些区别。由于 utf8mb4 是 utf8 的超集,所以从 utf8 切换到 utf8mb4 一般不会有问题,只需要注意存储空间的增加和排序规则的变化。但是从 utf8mb4 切换到 utf8 就可能会有问题,因为 utf8mb4 可能包含一些 utf8 不能表示的字符,这些字符在切换后会被丢弃或者转换成问号等符号。

另外,utf8mb4 也比 utf8 更安全,因为它可以防止一些恶意的攻击。例如,有一种攻击叫做 UTF-8 编码注入攻击(UTF-8 Encoding Injection Attack),它是利用 MySQL 中 utf8 字符集对 4 字节字符的处理方式来绕过一些安全检查的。具体来说,当 MySQL 遇到一个 4 字节的 UTF-8 字符时,它会把它拆分成两个 2 字节的字符,并且忽略第一个字符。这样就可能导致一些原本不合法或者不安全的字符串变成合法或者安全的字符串。例如,假设有一个字符串是 “\xF0\x90\x80\xE2\x80\xAEabc”,它实际上包含了一个 4 字节的 UTF-8 字符 U+10400 和一个右至左覆盖符号 U+202E。如果使用 utf8 字符集来存储这个字符串,那么 MySQL 会把它拆分成 “\xC0\x80\xE2\x80\xAEabc”,并且忽略第一个字符 “\xC0\x80″。这样就相当于把右至左覆盖符号 U+202E 插入到了字符串中,从而改变了字符串的显示方向。这可能会被用来进行钓鱼或者欺骗等攻击。如果使用 utf8mb4 字符集来存储这个字符串,那么 MySQL 会保留原始的字符串,并且报错或者出现乱码。

结论

utf8mb4 和 utf8 都可以用来存储 Unicode 字符,但是 utf8mb4 支持更广泛的字符范围,能够存储 Emoji 表情、罕用汉字、新增的 Unicode 字符等。utf8mb4 比 utf8 占用的存储空间略大一些,但是在性能和安全性方面更优。因此,如果需要支持更广泛的字符范围,或者需要更高的安全性和兼容性,那么应该使用 utf8mb4 字符集。当然,在选择字符集的时候,还需要考虑具体的业务需求和实际情况,选择最合适的字符集才是最重要的

发表评论