专栏首页银河系资讯在MySQL中,不要使用“utf8”。使用“utf8mb4”

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

今天的错误:我试图将一个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编码规则。字符串“?”是有效的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和“?”是两个字符)

最简单的编码(utf-32)使每个字符占用32位。这很简单,因为计算机已经把32位的组当作数字处理了很多年,而且他们真的很擅长。但它没用:这是浪费空间。

UTF-8节省空间。在UTF-8中,像“C”这样的常见字符占8位,而像“其他字符需要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每个字符最多支持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”是错误的,因为它无法存储

“?”

一旦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.

本文分享自微信公众号 - 银河系1号(gh_19a1776ab1d8),作者:银河系1号

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-04-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 6种技术将使您成为理想的前端开发人员

    世界各地对前端开发工程师有巨大需求。所以大多数学生都希望成为一名前端开发人员。尽管对前端开发人员的需求很大,但真正掌握市场需要的前端技能人员的不足,使有抱负的开...

    银河1号
  • 使用Go构建区块链 第1部分:基本原型

    区块链是21世纪最具革命性的技术之一,至今仍在发展,很多潜力尚未完全实现。从本质上讲,区块链只是一个分布式数据库。但是它的独特之处在于它不是私人数据库,而是公共...

    银河1号
  • Java XML和JSON:Java SE的文档处理 第2部分

    本文中的示例将向您介绍JSON-B,JSON绑定API for Java。在快速概述和安装说明之后,我将向您展示如何使用JSON-B来序列化和反序列化Java对...

    银河1号
  • 记住,永远不要在MySQL中使用“utf8”

    最近我遇到了一个 bug,我试着通过 Rails 在以“utf8”编码的 MariaDB 中保存一个 UTF-8 字符串,然后出现了一个离奇的错误:

    纯洁的微笑
  • Linux 命令

    查看该目录下每个文件夹的大小ll-I”..”|awk’{print$9}’|xargs-I{}du-…

    白凡
  • Css3 Animation 动画原则三

    演出布局是确保对象在场景中得以聚焦,让场景中的其它对象和视觉在主动画发生的地方让位。这意味着要么把主动画放到突出的位置,要么模糊其它元件来让用户专注于看他们需要...

    grain先森
  • Uber开源深度概率编程语言Pyro,AI实验室蛰伏一年首现身

    安妮 编译整理 量子位 出品 | 公众号 QbitAI 昨天,Uber AI实验室与斯坦福研究团队共同开源了概率编程语言Pyro。Pyro是一个深度概率建模工具...

    量子位
  • LookupError: unknown encoding: cp65001

    解决方案:try to run set PYTHONIOENCODING=UTF-8 after execute pip --version 

    week
  • React Native组件只Image

    不管在Android还是在ios原生的开发中,图片都是作为控件给出来的,在RN中也有这么一个控件(Image)。根据官网的资料,图片分为本地静态图片,网络图片和...

    xiangzhihong
  • Hadoop并非完美:8个代替 HDFS的绝佳方案

    HDFS(Hadoop Distributed File System)是Hadoop项目的核心子项目,是分布式计算中数据存储管理的基础,坦白说HDFS是一个...

    小莹莹

扫码关注云+社区

领取腾讯云代金券

玩转腾讯云 有奖征文活动