【SQL】记住,永远不要在MySQL中使用“utf8”

2023-11-04

缘由

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

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

我用的是 UTF-8 编码的客户端,服务器也是 UTF-8 编码的,数据库也是,就连要保存的这个字符串“ <…”也是合法的 UTF-8。

问题的症结在于,MySQL 的“utf8”实际上不是真正的 UTF-8。

“utf8”只支持每个字符最多三个字节,而真正的 UTF-8 是每个字符最多四个字节。

MySQL 一直没有修复这个 bug,他们在 2010 年发布了一个叫作“utf8mb4”的字符集,绕过了这个问题。

当然,他们并没有对新的字符集广而告之(可能是因为这个 bug 让他们觉得很尴尬),以致于现在网络上仍然在建议开发者使用“utf8”,但这些建议都是错误的。

简单概括如下:

  • MySQL 的“utf8mb4”是真正的“UTF-8”。
  • MySQL 的“utf8”是一种“专属的编码”,它能够编码的 Unicode 字符并不多。

我要在这里澄清一下:所有在使用“utf8”的 MySQL 和 MariaDB 用户都应该改用“utf8mb4”,永远都不要再使用“utf8”。

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

我们都知道,计算机使用 0 和 1 来存储文本。比如字符“C”被存成“01000011”,那么计算机在显示这个字符时需要经过两个步骤:

  1. 计算机读取“01000011”,得到数字 67,因为 67 被编码成“01000011”。
  2. 计算机在 Unicode 字符集中查找 67,找到了“C”。

同样的:

  1. 我的电脑将“C”映射成 Unicode 字符集中的 67。
  2. 我的电脑将 67 编码成“01000011”,并发送给 Web 服务器。

几乎所有的网络应用都使用了 Unicode 字符集,因为没有理由使用其他字符集。

Unicode 字符集包含了上百万个字符。最简单的编码是 UTF-32,每个字符使用 32 位。这样做最简单,因为一直以来,计算机将 32 位视为数字,而计算机最在行的就是处理数字。但问题是,这样太浪费空间了。

UTF-8 可以节省空间,在 UTF-8 中,字符“C”只需要 8 位,一些不常用的字符,比如“”需要 32 位。其他的字符可能使用 16 位或 24 位。一篇类似本文这样的文章,如果使用 UTF-8 编码,占用的空间只有 UTF-32 的四分之一左右。

MySQL 的“utf8”字符集与其他程序不兼容,它所谓的“”,可能真的是一坨……

MySQL 简史

为什么 MySQL 开发者会让“utf8”失效?我们或许可以从提交日志中寻找答案。

MySQL 从 4.1 版本开始支持 UTF-8,也就是 2003 年,而今天使用的 UTF-8 标准(RFC 3629)是随后才出现的。

旧版的 UTF-8 标准(RFC 2279)最多支持每个字符 6 个字节。2002 年 3 月 28 日,MySQL 开发者在第一个 MySQL 4.1 预览版中使用了 RFC 2279。

同年 9 月,他们对 MySQL 源代码进行了一次调整:“UTF8 现在最多只支持 3 个字节的序列”。

是谁提交了这些代码?他为什么要这样做?这个问题不得而知。在迁移到 Git 后(MySQL 最开始使用的是 BitKeeper),MySQL 代码库中的很多提交者的名字都丢失了。2003 年 9 月的邮件列表中也找不到可以解释这一变更的线索。

不过我可以试着猜测一下。

2002 年,MySQL 做出了一个决定:如果用户可以保证数据表的每一行都使用相同的字节数,那么 MySQL 就可以在性能方面来一个大提升。为此,用户需要将文本列定义为“CHAR”,每个“CHAR”列总是拥有相同数量的字符。如果插入的字符少于定义的数量,MySQL 就会在后面填充空格,如果插入的字符超过了定义的数量,后面超出部分会被截断。

MySQL 开发者在最开始尝试 UTF-8 时使用了每个字符 6 个字节,CHAR(1) 使用 6 个字节,CHAR(2) 使用 12 个字节,并以此类推。

应该说,他们最初的行为才是正确的,可惜这一版本一直没有发布。但是文档上却这么写了,而且广为流传,所有了解 UTF-8 的人都认同文档里写的东西。

不过很显然,MySQL 开发者或厂商担心会有用户做这两件事:

  1. 使用 CHAR 定义列(在现在看来,CHAR 已经是老古董了,但在那时,在 MySQL 中使用 CHAR 会更快,不过从 2005年以后就不是这样子了)。
  2. 将 CHAR 列的编码设置为“utf8”。

我的猜测是 MySQL 开发者本来想帮助那些希望在空间和速度上双赢的用户,但他们搞砸了“utf8”编码。

所以结果就是没有赢家。那些希望在空间和速度上双赢的用户,当他们在使用“utf8”的 CHAR 列时,实际上使用的空间比预期的更大,速度也比预期的慢。而想要正确性的用户,当他们使用“utf8”编码时,却无法保存像“”这样的字符。

在这个不合法的字符集发布了之后,MySQL 就无法修复它,因为这样需要要求所有用户重新构建他们的数据库。最终,MySQL 在 2010 年重新发布了“utf8mb4”来支持真正的 UTF-8。

为什么这件事情会让人如此抓狂

因为这个问题,我整整抓狂了一个礼拜。我被“utf8”愚弄了,花了很多时间才找到这个 bug。但我一定不是唯一的一个,网络上几乎所有的文章都把“utf8”当成是真正的 UTF-8。

“utf8”只能算是个专有的字符集,它给我们带来了新问题,却一直没有得到解决。

总结

如果你在使用 MySQL 或 MariaDB,不要用“utf8”编码,改用“utf8mb4”。

这里( https://mathiasbynens.be/notes/mysql-utf8mb4#utf8-to-utf8mb4 )提供了一个指南用于将现有数据库的字符编码从“utf8”转成“utf8mb4”。

英文原文:https://medium.com/@adamhooper/in-mysql-never-use-utf8-use-utf8mb4-11761243e434

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【SQL】记住,永远不要在MySQL中使用“utf8” 的相关文章

  • 插入多行并返回主键时 Sqlalchemy 的奇怪行为

    插入多行并返回主键时 我注意到一些奇怪的事情 如果我在 isert 查询中添加使用参数值 我会得到预期的行为 但是当将值传递给游标时 不会返回任何内容 这可能是一个错误还是我误解了什么 我的sqlachemy版本是0 9 4 下面如何重现错
  • 对于数据库来说,选择正确的数据类型会影响性能吗?

    如果是这样 为什么 我的意思是 tinyint 的搜索速度比 int 快吗 如果是这样 性能上的实际差异是什么 是的 根据数据类型 它确实有所不同 int vs tinyint不会在速度上产生明显的差异 但会在数据大小上产生差异 假设tin
  • PHP 绑定“bigint”数据类型(MySQLi 准备好的语句)

    studentId 57004542323382 companyOfferId 7 sql INSERT INTO studentPlacement companyOfferId studentId VALUES if stmt db gt
  • 复选框上的数据绑定

    我目前正在将数据从 SQL 数据库之一提取到我的应用程序中 我可以让它适用于我的文本框和其他项目 但是 我似乎无法让它适用于复选框 这是我正在使用的代码 DataTable dt new DataTable dt using SqlConn
  • 如何将可视选择的文本通过管道传输到 UNIX 命令并将输出附加到 Vim 中的当前缓冲区

    使用 Vim 我尝试将在可视模式下选择的文本通过管道传输到 UNIX 命令 并将输出附加到当前文件的末尾 例如 假设我们有一个 SQL 命令 例如 SELECT FROM mytable 我想做如下的事情
  • Mysql 检索所有有限制的行

    我想检索特定用户的所有行 限制为 0 x 所以我只是想问是否有任何方法可以检索 mysql 中的所有行 而不调用返回 x 的 count id 的方法 而不重载现有函数 该函数在查询中根本没有限制 与我们的 string Relace 功能
  • 如何在 Visual Studio 中更改 Azure 数据库表的列顺序

    我整个下午都在寻找在 MS Visual Studio 2022 中重新排序 Azure 数据库表列的方法 没有运气 在其他应用程序中 可以通过拖动或剪切和粘贴轻松重新排列列 这里无能为力 此时 我什至不确定可以在 VS 中移动列 我只对
  • 分层查询

    我希望我能够解释困扰我的问题 我有以下分层数据集 这只是 34K 记录的子集 PARENT ID CHILD ID EXAM TUDA12802 TUDA12982 N TUDA12982 TUDA12984 J TUDA12984 TUD
  • 使用 JSON 参数的 Postgres 批量 INSERT 函数

    这是一个plpgsqlpostgres 的函数9 6 它试图INSERT一行 如果插入没有失败 由于违反键约束 那么它会运行更多命令 CREATE FUNCTION foo int text text RETURNS void AS BEG
  • 在一个数据访问层中处理多个连接字符串

    我有一个有趣的困境 我目前有一个数据访问层 它必须与多个域一起使用 并且每个域都有多个数据库存储库 具体取决于所调用的存储过程 目前 我只需使用 SWITCH 语句来确定应用程序正在运行的计算机 并从 Web config 返回适当的连接字
  • sql server 按组排名

    问题看似简单 但我却无法理解 这是针对 sql 服务器的 what I have in a table What I need as a output cksum id cksum id 2162514679 204 2162514679
  • 对多个数据库执行 SQL 查询

    我知道我的帖子与该论坛中的其他帖子的标题非常相似 但我真的找不到我需要的答案 这是我的问题 我的 Windows Server 上运行着 SQL Server 在我的 SQL Server 中 我有大约 30 个数据库 它们都具有相同的表和
  • PHP 通过 SSL 连接到 MS SQL

    我想要实现的目标非常简单 我想通过安全连接从 PHP 脚本连接到外部 MS SQL 数据库 然而 这已被证明是有问题的 到目前为止 经过三个小时的研究 我不知所措 客户端的平台是Ubuntu 这意味着我无法使用SQLSRV 安全连接已经在不
  • 单独的逗号分隔值并存储在sql server的表中

    我有一个存储过程 它将逗号分隔的值作为输入 我需要将其分开并需要将其作为单独的行存储在表中 令 SP 的输入为 Rule ID ListType ID Values 1 2 319 400 521 8465 2013 我需要将它存储在一个名
  • 使用 ADODB 连接从关闭的工作簿中检索数据。某些数据被跳过?

    我目前正在编写一些代码 可以通过 ADODB 连接访问单独的工作簿 由于速度的原因 我选择了这种方法而不是其他方法 下面是我的代码 Sub GetWorksheetData strSourceFile As String strSQL As
  • 初级SQL部分:避免重复表达式

    我对 SQL 完全陌生 但我们可以说StackExchange 数据浏览器 https data stackexchange com 我只想按信誉列出前 15 位用户 我写了这样的内容 SELECT TOP 15 DisplayName I
  • 在 C#.NET 应用程序中使用 SQL Server 时间数据类型?

    如何使用 SQLtimeSQL Server 2008 中 C NET 中引入的数据类型 我一直在努力让它发挥作用 但没有成功 这是一个MSDN 文章 http msdn microsoft com en us library bb6751
  • 为什么我可以像调用实例方法一样调用类方法?

    我正在查看这个例子 class SQLObject def self columns return columns if columns columns DBConnection execute2 lt lt SQL first SELEC
  • 在 postgres 查询中使用列表

    我有一个动态列表 list a b c d 所以长度可能会改变 我想在查询中比较这些列表值 select from student where name in all the list values 我想将列表值传递到此查询中 我怎样才能做
  • 如何获得顶部带有千位分隔符的数字?

    SELECT count FROM table A 假设结果是8689 我怎样才能将它转换为8 689在 SQL Server 上 尝试这样 select replace convert varchar convert Money coun

随机推荐

  • GCC 的使用及介绍

    一 GCC介绍 Linux系统下的GCC是GNU推出的功能强大 性能优越的多平台编译器 它可以在多种硬件平台上编译处可执行程序的超级编译器 其执行效率比一般的编译器的效率要高20 30 Gcc编译器能将C C 语言源程序 汇程式化序和目标程
  • FPGA时序约束理论之多周期路径(6)

    1 单周期路径 前面的时钟周期约束 都是按照单周期关系进行分析数据路径 即数据的发起沿和采样沿是最邻近的一对时钟沿 如下图所示 默认情况下 保持时间的检查是以建立时间的检查为前提 即总是在建立时间的前一个时钟周期确定保持时间检查 也就是说
  • 基于Matlab的多线激光中心坐标值提取

    本文是基于给定的两张多线激光图片 如下图所示 需将图片中的激光线的中心线坐标提取出来并绘制激光中心线图形 因为是Matlab课程训练研究大作业 所以全文代码为Matlab 希望可以为相似作业的非专业同学提供一些帮助 文章目录 1 问题分析
  • docker-compose常用命令

    docker compose up d nginx 构建建启动nignx容器 docker compose exec nginx bash 登录到nginx容器中 docker compose down 删除所有nginx容器 镜像 doc
  • python 多分类逻辑回归_机器学习实践:多分类逻辑回归(softmax回归)的sklearn实现和tensorflow实现...

    本文所有代码及数据可下载 Scikit Learn 篇 Light 版 scikit learn内置了逻辑回归 对于小规模的应用较为简单 一般使用如下代码即可 from sklearn linear model logistic impor
  • Spring框架(SpringBoot)中redis报错(Could not get a resource from the pool、java.net.SocketTimeoutException)

    Spring框架 SpringBoot 中redis报错 在使用SpringBoot框架的时候 Spring一直会报两个特别纠结特别的烦的错误 尝试了很多种方法 都是失败的 不能成功 经过我坚持不懈的努力寻找 终于把问题给解决了 一 第一个
  • 灭鼠先锋

    奇技淫巧 cout lt lt LLLV
  • 编译器构造中自底向上的LALR(1)语法分析的语法分析表生成的实现

    提示 阅读本文需掌握编译原理的相关基础知识 本文中使用C 语言系统地实现了龙书中LALR 1 语法分析表的构造算法 首先计算增广文法的LR 0 项集族 每一个项集只包含内核项 计算过程中自动生成了LR 0 自动机 该自动机使用基于十字链表存
  • 【server组件】——mysql连接池的实现原理

    目录 1 池化技术 2 数据库连接池的定义 3 为什么要使用连接池 4 数据库连接池的运行机制 5 连接池与线程池的关系 6 CResultSet的设计 6 1构造函数 7 CDBConn的设计 6 1 构造函数 6 2 init 初始化连
  • 最大间隔问题

    问题描述 给定n个实数x1 x2 x3 xn 求这n个数在实轴上相邻2个数之间的最大间隔 假设对任何实数取整耗时O 1 设计解最大间隙问题的线性时间算法 算法设计 对于给定的n个实数x1 x2 x3 xn 计算它们的最大间隙 数据输入 输入
  • proc*c sqlcode=[-1405];sqlerr=[ORA-01405: fetched column value is NULL

    指示变量的值 及其代表的含义如下 0 操作成功 1 该指示变量对应的宿主变量返回了或插入 更新成了NULL值 2 从数据库存放数据到对应的宿主变量时 数据超长 并且不能推断出截断了多少字节的长度 gt 0 在FETHC或SELECT语句时
  • 5g空分复用技术_5G通信技术原理总结

    5G通信的基本流程框图 如上图 5G智能手机MIC输入的模拟信号会通过基带中的AD数模转换电路 完成采样 量化 编码 变成数字信号 具体过程如下如所示 信源编码 信源编码 上图中的编码 我们称之为信源编码 信源编码 说白了 就是把声音 画面
  • (2018-2021年)Uncertainty 相关SOTA文献笔记整理

    Uncertainty 文献笔记 ACL Word Level Uncertainty Estimation for Black Box Text Classifiers using RNNs Unsupervised Quality Es
  • Java口算练习软件(随机生成10道100以内的数学练习题)

    Java口算练习软件 使用Java编写口算练习软件 实现以下功能点 1 随机生成10道100以内的加减法练习题 2 设计GUI界面 包含 生成题目 上一题 检查答案 导出结果 导入题目 等按钮 这是一个Java程序 实现了一个口算练习软件
  • 第一章 计算机基础知识

    文章目录 第一章 计算机基础知识 1 计算机引论 1 1计算机发展的四个阶段 1 2计算机的发展现状计算机的未来 2 计算机硬件和软件基础 2 1计算机组成体系 2 2计算机硬件的组成 2 3常见的几种操作系统 3 计算机DOS命令行操作
  • Vue.config.js开发环境与生产环境配置

    Vue config js开发环境与生产环境配置 首先在项目根目录创建 env development 开发环境 env production 生产环境 VUE APP SERVICE URL VUE APP BASE API 生产环境配置
  • Java的 字节流 InputStream 类及其子类 FileInputStream 的使用

    import java io FileInputStream import java io FileNotFoundException import java io InputStream public class InputStreamT
  • 实验一 构建虚拟局域网

    实验一 构建虚拟局域网 实验目的 实验环境与设备 实验要求 过程如下 实验目的 为网络安全试验做准备 构建虚拟局域网的网络环境 利用VMware Workstation Pro 12虚拟机安装Windows 10 Windows8 实验环境
  • uni-app开发微信小程序,checkbox、radio等原生组件样式设置问题解决方案

    它们是原生的组件 修改样式只能在app vue里面修改 目前只知道这一种解决办法 如果你的UI给的图比较特殊 用css写比较困难 推荐使用图片代替 请看下面示例 正确设置 在App vue文件里设置 test vue文件 这里假设你使用ch
  • 【SQL】记住,永远不要在MySQL中使用“utf8”

    文章目录 缘由 那么什么是编码 什么是 UTF 8 MySQL 简史 为什么这件事情会让人如此抓狂 总结 缘由 最近我遇到了一个 bug 我试着通过 Rails 在以 utf8 编码的 MariaDB 中保存一个 UTF 8 字符串 然后出