迹忆客 专注技术分享

当前位置:主页 > 学无止境 > 数据库 > MySQL >

在 MySQL 中,永远不要使用“utf8”, 使用“utf8mb4”。

作者:迹忆客 最近更新:2023/01/07 浏览次数:

今天的错误:我试图将 UTF-8 字符串存储在 MariaDB“utf8”编码的数据库中,Rails 引发了一个奇怪的错误:

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

这是一个 UTF-8 客户端和一个 UTF-8 服务器,位于具有 UTF-8 排序规则的 UTF-8 数据库中。 字符串😃 <...是有效的 UTF-8

但问题是:MySQL 的“utf8”不是 UTF-8。

utf8”编码仅支持每个字符三个字节。 真正的 UTF-8 编码——每个人都使用,包括你——每个字符最多需要四个字节。

MySQL 开发人员从未修复此错误。 他们在 2010 年发布了一个解决方法:一个名为“utf8mb4”的新字符集。

当然,他们从来没有宣传过这个(可能是因为这个 bug 太尴尬了)。 现在,网络指南建议用户使用“utf8”。 所有这些指南都是错误的。

简而言之:

  • MySQL 的“utf8mb4”表示“UTF-8”。
  • MySQL 的“utf8”意思是“专有字符编码”。 这种编码不能编码很多 Unicode 字符。

我将在这里做一个笼统的声明:所有当前使用“utf8”的 MySQL 和 MariaDB 用户实际上应该使用“utf8mb4”。 任何人都不应该使用“utf8”。

什么是编码? 什么是 UTF-8?

计算机将文本存储为 1 和 0。 这一段的第一个字母被存储为“01000011”,你的电脑画了“C”。 计算机分两步选择“C”:

  1. 你的计算机读取“01000011”并确定它是数字 67。那是因为 67 被编码为“01000011”。
  2. 你的计算机在 Unicode 字符集中查找字符编号 67,发现 67 表示“C”。

当我输入“C”时,同样的事情发生在我这边:

  1. 我的计算机将“C”映射到 Unicode 字符集中的 67
  2. 我的计算机编码为 67,向该 Web 服务器发送“01000011”。

字符集是一个已解决的问题。 几乎 Internet 上的每个程序都使用 Unicode 字符集,因为没有动力去使用另一个。

但编码更像是一种判断。 Unicode 有超过一百万个字符的插槽。 (“C”和“💩”是两个这样的字符。)最简单的编码 UTF-32 使每个字符占用 32 位。 Unicode 字符集和 UTF-8、UTF-16、UTF-32 编码 这篇文章对几种编码进行了详细的介绍。 这很简单,因为计算机长期以来一直将 32 位组视为数字,而且它们非常擅长。 但它没有用:这是浪费空间。

UTF-8 节省空间。 在 UTF-8 中,像“C”这样的常见字符占用 8 位,而像“💩”这样的稀有字符占用 32 位。 其他字符占用 16 或 24 位。 像这样的博文在 UTF-8 中占用的空间比在 UTF-32 中少四倍。 所以它加载速度快四倍。

你可能没有意识到,但我们的计算机在幕后同意使用 UTF-8。 如果他们没有,那么当我输入“💩”时,你会看到一堆乱七八糟的随机数据。

MySQL 的“utf8”字符集与其他程序不一致。 当他们说“💩”时,它会犹豫。

一点 MySQL 的历史

为什么 MySQL 开发人员要使“utf8”无效? 我们可以通过查看提交日志来猜测。

MySQL 从 4.1 版开始支持 UTF-8。 那是 2003 年——在今天的 UTF-8 标准 RFC 3629 出现之前。

以前的 UTF-8 标准 RFC 2279 支持每个字符最多六个字节。 MySQL 开发人员于 2002 年 3 月 28 日在 MySQL 4.1 的第一个预发布版本中编写了 RFC 2279。

然后在 9 月份对 MySQL 的源代码进行了一个神秘的单字节调整:“UTF8 现在最多只能处理 3 个字节的序列。”

谁要求进行此更改? 为什么? 我不知道。 2003 年 9 月前后的邮件列表中没有任何内容可以解释这一变化。 (RFC 2279 于 2003 年 11 月宣布过时,为当前的 UTF-8 标准 RFC 3629 让路。)

但是我可以猜到为什么 MySQL 违反了标准。

早在 2002 年,如果用户可以保证表中的每一行都具有相同的字节数,MySQL 就可以提高用户的速度。 为此,用户会将文本列声明为“CHAR”。 “CHAR”列中每条记录的值都具有相同数量的字符。 如果输入的字符太少,MySQL 会在末尾添加空格; 如果你给它太多的字符,MySQL 会截断最后的字符。

当 MySQL 开发人员第一次尝试 UTF-8 时,当时每个字符有 6 个字节,他们可能会犹豫:一个 CHAR(1) 列需要 6 个字节; 一个 CHAR(2) 列需要 12 个字节; 等等。

让我们明确一点:从未发布的初始行为是正确的。 它有据可查并被广泛采用,任何了解 UTF-8 的人都会同意它是正确的。

但显然,MySQL 开发人员(或用户或业务人员)担心一两个用户会做两件事:

  1. 选择 CHAR 列。 (CHAR 格式现在已经过时了。当时,使用 CHAR 列的 MySQL 速度更快。自 2005 年以来,情况就不是这样了。)
  2. 选择将这些 CHAR 列编码为“utf8”。

我的猜测是 MySQL 开发人员打破了他们的“utf8”编码来帮助这些用户: 1) 试图优化空间和速度的用户; 2)忽视了速度和空间的优化。

没有人赢。 想要速度和空间的用户使用“utf8”CHAR 列仍然是错误的,因为这些列仍然比他们必须的更大和更慢。 而想要正确性的开发人员使用“utf8”是错误的,因为它不能存储“💩”。

一旦 MySQL 发布了这个无效的字符集,它就永远无法修复它:这将迫使每个用户重建每个数据库。 MySQL 终于在 2010 年发布了 UTF-8 支持,但名称不同:“utf8mb4”。

为什么如此令人沮丧

显然这周我很沮丧。 我的 bug 很难找到,因为我被“utf8”这个名字骗了。 而且我不是唯一一个——我在网上找到的几乎所有文章都将“utf8”吹捧为 UTF-8

名称“utf8”始终是一个错误。 这是一个专有字符集。 它创造了新的问题,并没有解决它想要解决的问题。

这是虚假宣传。


我的观点

  1. 数据库系统有细微的错误和怪癖,你可以通过避免使用数据库系统来避免很多错误。
  2. 如果你需要数据库,请不要使用 MySQL 或 MariaDB。 使用 PostgreSQL。
  3. 如果你需要使用 MySQL 或 MariaDB,切勿使用“utf8”。 当你需要 UTF-8 时,请始终使用“utf8mb4”。 立即转换数据库以避免以后出现麻烦。

转载请发邮件至 1244347461@qq.com 进行申请,经作者同意之后,转载请以链接形式注明出处

本文地址:

相关文章

使用 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() 的组合。

在 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)。

扫一扫阅读全部技术教程

社交账号
  • https://www.github.com/onmpw
  • qq:1244347461

最新推荐

教程更新

热门标签

扫码一下
查看教程更方便