如何在 MySQL 数据库中支持完整的 Unicode
你在你的数据库中使用 MySQL 的 utf8 字符集吗? 在这篇文章中,我将解释为什么你应该改用 utf8mb4,以及如何去做。
UTF-8
UTF-8编码可以表示Unicode字符集中的每一个符号,范围从U+000000
到U+10FFFF
。 那是 1,114,112 个可能的符号。 (并非所有这些 Unicode 代码点都已分配字符,但这并不妨碍 UTF-8 能够对它们进行编码。)
UTF-8 是一种可变宽度编码;可以参考Unicode 字符集和 UTF-8、UTF-16、UTF-32 编码这篇文章 它使用一到四个 8 位字节对每个符号进行编码。 具有较低数字代码点值的符号使用较少的字节进行编码。 这样,UTF-8 针对使用 ASCII 字符和其他 BMP 符号(其代码点范围从 U+000000
到 U+00FFFF
)的常见情况进行了优化——同时仍然允许星体符号(其代码点范围从 U+010000
到 U+10FFFF
) 被存储。
MySQL 中的 utf8
很长一段时间,我一直在为数据库、表和列使用 MySQL 的 utf8 字符集,假设它映射到上述 UTF-8 编码。 通过使用 utf8,我可以在我的数据库中存储我想要的任何符号——或者我是这么认为的。
在写 JavaScript 的内部字符编码时,我注意到无法将 U+1D306 TETRAGRAM FOR CENTER (𝌆)
符号插入到该站点后面的 MySQL 数据库中。 我尝试更新的列具有 utf8_unicode_ci
排序规则,并且连接字符集设置为 utf8。
mysql> SET NAMES utf8; # just to emphasize that the connection charset is set to `utf8`
Query OK, 0 rows affected (0.00 sec)
mysql> UPDATE database_name.table_name SET column_name = 'foo𝌆bar' WHERE id = 9001;
Query OK, 1 row affected, 1 warning (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 1
mysql> SELECT column_name FROM database_name.table_name WHERE id = 9001;
+-------------+
| column_name |
+-------------+
| foo |
+-------------+
1 row in set (0.00 sec)
内容在第一个 astral Unicode
符号处被截断,在本例中为 𝌆
— 因此,尝试插入 foo𝌆bar
实际上插入的是 foo
,导致数据丢失(并可能引入安全问题;见下文)。 MySQL 也返回一条警告消息:
mysql> SHOW WARNINGS;
+---------+------+------------------------------------------------------------------------------+
| Level | Code | Message |
+---------+------+------------------------------------------------------------------------------+
| Warning | 1366 | Incorrect string value: '\xF0\x9D\x8C\x86' for column 'column_name' at row 1 |
+---------+------+------------------------------------------------------------------------------+
1 row in set (0.00 sec)
事实证明,MySQL 的 utf8 字符集仅部分实现了正确的 UTF-8 编码。 它只能存储由一到三个字节组成的 UTF-8 编码符号; 不支持占用四个字节的编码符号。
由于星体符号(其代码点范围从 U+010000
到 U+10FFFF
)在 UTF-8 中均由四个字节组成,因此我们不能使用 MySQL 的 utf8 实现来存储它们。
这不仅会影响 𝌆
字符,还会影响更重要的符号,例如 U+01F4A9 PILE OF POO (💩)
。 总共有 1,048,575 个可能的代码点是我们不能使用的。 事实上,MySQL 的 utf8 只允许我们存储所有可能的 Unicode 代码点的 5.88% (0x00FFFF + 1) / (0x10FFFF + 1)
。 正确的 UTF-8 可以编码所有 Unicode 代码点的 100% 。
MySQL 的
utf8
编码命名很笨拙,因为它与正确的UTF-8
编码不同。 它不提供完整的 Unicode 支持,这可能会导致数据丢失或安全漏洞。
MySQL 中的 utf8mb4
幸运的是,MySQL 5.5.3(2010 年初发布)引入了一种名为 utf8mb4 的新编码,它映射到正确的 UTF-8,因此完全支持 Unicode,包括星体符号。
我们可以参考 MySQL 从 utf8 切换到 utf8mb4 这篇文章来对编码进行切换。
总结
永远不要在 MySQL 中使用 utf8——总是使用 utf8mb4。 更新数据库和代码可能需要一些时间,但绝对值得付出努力。 为什么要任意限制可以在数据库中使用的符号集? 为什么每次用户输入星体符号作为评论或消息的一部分或我们存储在数据库中的任何内容时,都会丢失数据? 没有理由不在所有地方争取完全的 Unicode
支持。 做正确的事,使用 utf8mb4。 🍻
相关文章
使用 Mysqldump 备份 MySQL 中的数据
发布时间:2023/05/09 浏览次数:192 分类:MySQL
-
本篇文章将介绍如何使用 mysqldump 只备份数据。 在这里,我们将探讨 --no-create-info 、--compact 、--skip-triggers 和 --no-create-db 选项。
更新 MySQL 表中的主键
发布时间:2023/05/09 浏览次数:61 分类:MySQL
-
本篇文章介绍如何更新 MySQL 表中的主键。 我们将使用 ALTER 命令对主键进行任何更改。更新 MySQL 表中的主键 我们可以在多种情况下更新 MySQL 表中的主键。
在 MySQL 中获取命令历史记录
发布时间:2023/05/09 浏览次数:150 分类:MySQL
-
本文重点介绍了在 Windows 和 Linux 中获取我们已执行的 MySQL 命令历史记录的各种方法。MySQL命令历史
Oracle 的 decode 函数在 MySQL 中的等价物
发布时间:2023/05/09 浏览次数:115 分类:MySQL
-
本篇文章介绍了三种替代实现,我们可以将它们用作 MySQL 中 Oracle 的 decode() 函数的等价物。 为此,我们将使用 IF()、CASE 以及 FIELD() 和 ELT() 的组合。
使用 Ubuntu 连接远程 MySQL 服务器的不同方法
发布时间:2023/05/09 浏览次数:97 分类:MySQL
-
在本文中我们将学习如何使用 Ubuntu 连接远程 MySQL 服务器来操作数据以及启动和停止 MySQL 服务器。
在 Linux 中安装 MySQL 客户端
发布时间:2023/05/09 浏览次数:72 分类:MySQL
-
在 Linux 中安装 MySQL 客户端的命令。Linux 和 Unix 等环境作为命令行界面工作,仅在命令的帮助下运行。
在 MySQL 中转换为十进制
发布时间:2023/05/09 浏览次数:150 分类:MySQL
-
有时,我们可能需要将一种数据类型转换为另一种数据类型。 下面是我们如何使用带有 DECIMAL(M,D) 的 CAST() 和 CONVERT() 函数在 MySQL 中转换为十进制。
在 MySQL 中获取当前日期和时间
发布时间:2023/05/09 浏览次数:145 分类:MySQL
-
本篇文章我们将学习 NOW()、CURRENT_TIMESTAMP()(也写为 CURRENT_TIMESTAMP)和 SYSDATE() 来获取 MySQL 中的当前日期和时间。 我们还将看到这三个功能之间的比较。在 MySQL 中获取当前日期和时间
更改 MySQL 服务器中的 max_allowed_packet Size
发布时间:2023/05/09 浏览次数:142 分类:MySQL
-
本篇文章介绍如何更改 MySQL 服务器中的 max_allowed_packet 大小。 为了了解这一点,我们将使用两个操作系统,Windows 10 和 Linux (Ubuntu)。