39

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

 5 years ago
source link: http://www.apexyun.com/zai-mysqlzhong-bu-yao-shi-yong-utf8-shi-yong-utf8mb4/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

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

这是UTF-8客户端和UTF-8服务器,位于UTF-8数据库中,具有UTF-8编码规则。字符串“:smiley:”是有效的UTF-8。

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

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

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

当然,他们从未公布过这个(可能是因为这个bug太尴尬了)。现在,Web上的指南建议用户使用“utf8”。所有这些指南都是错误的。

简而言之:

MySQL的“utf8mb4”表示“UTF-8”。

MySQL的“utf8”意味着“专有字符编码”。此编码不能编码许多Unicode字符。

我将在这里做一个彻底的陈述:目前使用“utf8”的所有 MySQL和MariaDB用户实际上应该使用“utf8mb4”。没有人应该使用“utf8”。

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

Joel on Software写了我最喜欢的介绍( https://www.joelonsoftware.com/2003/10/08/the-absolute-minimum-every-software-developer-absolutely-positively-must-know-about-unicode-and-character-sets-no-excuses/

)。我会缩减它。

Computer(计算机)将文本存储为1和0。本段中的第一个字母存储为“01000011”,你的计算机显示为“C”。你的计算机分两步选择“C”:

1.你的计算机读取“01000011”并确定它是数字67.这是因为67被编码为“01000011”。

2.你的计算机在Unicode 字符集中查找字符编号67 ,并且发现67表示“C”。

当我键入“C”时,我的结果发生了同样的事情:

1.我的计算机将Unicode字符集中的“C”映射到67。

2.我的计算机编码为 67,向此Web服务器发送“01000011”。

字符集是一个解决的问题。几乎互联网上的每个程序都使用Unicode字符集,因为没有动机使用另一个。

但编码更像是一种判断。Unicode具有超过一百万个字符的插槽。(C和“:hankey:”是两个字符)最简单的编码(utf-32)使每个字符占用32位。这很简单,因为计算机已经把32位的组当作数字处理了很多年,而且他们真的很擅长。但它没用:这是浪费空间。

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

你可能没有意识到,但我们的计算机在幕后同意了UTF-8。如果他们没有,然后当我输入

“:hankey:”时,你会看到一堆随机数据。

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

一点MySQL的历史

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

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

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

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

是谁提交了这个?为什么?我说不出来。MySQL的代码库在采用Git时似乎丢失了旧的作者名称。(MySQL过去常常使用BitKeeper,就像Linux内核一样。)2003年9月左右的邮件列表中没有任何内容可以解释这一变化。

但我可以猜到。

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

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

让我们明确一点:从未发布的初始行为是正确的。它得到了很好的记录和广泛采用,任何理解UTF-8的人都会同意这是正确的。

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

1.选择CHAR列。(CHAR格式现在是遗物。当时,使用CHAR列,MySQL速度更快。直到2005年,它不是。)

2.选择将这些CHAR列编码为“utf8”。

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

没人赢。想要速度和空间的用户使用“utf8”CHAR列仍然是错误的,因为那些列仍然比它们原来更大更慢。想要正确性的开发人员使用“utf8”是错误的,因为它无法存储

“:hankey:”

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

为什么这么令人沮丧

很明显,本周我很沮丧。我的bug很难找到,因为我被“utf8”这个名字所迷惑。而且我不是唯一一个 - 我在网上发现的几乎所有文章都将“utf8”称为UTF-8。

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

这是虚假的广告。

My take-away lessons

1.Database systems have subtle bugs and oddities, and you can avoid a lot of bugs by avoiding database systems.

2.If you need a database, don’t use MySQL or MariaDB. Use PostgreSQL.

3.If you need to use MySQL or MariaDB, never use “utf8”. Always use “utf8mb4” when you want UTF-8. Convert your database now to avoid headaches later.

点击英文原文链接

更多文章欢迎访问: http://www.apexyun.com

公众号:银河系1号

联系邮箱:[email protected]

(未经同意,请勿转载)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK