你知道的越多,不知道的就越多,业余的像一棵小草!

你来,我们一起精进!你不来,我和你的竞争对手一起精进!

编辑:业余草

来源:https://sourl.cn/kKbzpH

推荐:https://www.xttblog.com/?p=5107

记得去年我在往 MySQL 存入 emoji ????????表情时,一直出错,无法导入。后来找到办法,通过把 utf8 改成 utf8mb4 就可以了,当时并没有深究。

一年后,我看到一篇文章讲到 emoji 文字占 4 个字节,通常要用 utf-8 去接收才行,其他编码可能会出错。

我突然想到去年操作 MySQL 把 utf8 改成 utf8mb4 的事儿。

嗯?他本身不就是 utf8 编码么!那我当时还改个锤子?难道,MySQL 的 utf8 不是真正的 UTF-8 编码吗??! 卧槽,这 MySQL 有 Bug!

带着疑问查询了很多相关材料,才发现这竟然是 MySQL 的一个历史遗留问题。我笑了,没想到这么牛 B 的 MySQL 也会有这段往事。

报错回顾

将 emoji 文字直接写入 SQL 中,执行 insert 语句报错:

INSERT INTO `csjdemo`.`student` (`ID`, `NAME`, `SEX`, `AGE`, `CLASS`, `GRADE`, `HOBBY`) VALUES ('20', '陈哈哈????', '男', '20', '181班', '9年级', '看片儿');
[Err] 1366 - Incorrect string value: '\xF0\x9F\x98\x93' for column 'NAME' at row 1

改了数据库编码、系统编码以及表字段的编码格式→utf8mb4 之后,就可以了:

INSERT INTO `student` (`ID`, `NAME`, `SEX`, `AGE`, `CLASS`, `GRADE`, `HOBBY`) VALUES (null, '陈哈哈????????', '男', '20', '181班', '9年级', '看片儿');

MySQL 中 utf8 的趣事

MySQL 的“utf8”实际上不是真正的 UTF-8。

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

在 utf8 编码中,中文是占 3 个字节,其他数字、英文、符号占一个字节。

但 emoji 符号占 4 个字节,一些较复杂的文字、繁体字也是 4 个字节。所以导致写入失败,应该改成 utf8mb4。

如上图中所示,这是编码改成 utf8mb4 后入库的数据,大家可以清晰的对比一下所占的字符数、字节数。

正因如此,4 字节的内容往 utf8 编码中插入,肯定是不行的,插不进去啊,是吧(大潘摊手)。

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

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

utf8mb4 才是真正的UTF-8

是的,MySQL 的“utf8mb4”才是真正的“UTF-8”。

MySQL 的“utf8”是一种“专属的编码”,它能够编码的 Unicode 字符并不多。

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

那么什么是编码?什么是 UTF-8?我们都知道,计算机使用 0 和 1 来存储文本,比如字符“C”被存成“01000011”。

那么计算机在显示这个字符时需要经过两个步骤:

  • 计算机读取“01000011”,得到数字 67,因为 67 被编码成“01000011”。

  • 计算机在 Unicode 字符集中查找 67,找到了“C”。

同样的:

  • 我的电脑将“C”映射成 Unicode 字符集中的 67。

  • 我的电脑将 67 编码成“01000011”,并发送给 Web 服务器。

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

Unicode 字符集包含了上百万个字符。最简单的编码是 UTF-32,每个字符使用 32 位。

这样做最简单,因为一直以来,计算机将 32 位视为数字,而计算机最在行的就是处理数字。但问题是,这样太浪费空间了。

UTF-8 可以节省空间,在 UTF-8 中,字符“C”只需要 8 位,一些不常用的字符,比如“????”需要 32 位。其他的字符可能使用 16 位或 24 位。

一篇类似本文这样的文章,如果使用 UTF-8 编码,占用的空间只有 UTF-32 的四分之一左右。

utf8 的简史

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

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 开发者或厂商担心会有用户做这两件事:

  • 使用 CHAR 定义列(在现在看来,CHAR 已经是老古董了,但在那时,在 MySQL 中使用 CHAR 会更快,不过从 2005 年以后就不是这样子了)。

  • 将 CHAR 列的编码设置为“utf8”。

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

所以结果就是没有赢家。那些希望在空间和速度上双赢的用户,当他们在使用“utf8”的 CHAR 列时,实际上使用的空间比预期的更大,速度也比预期的慢。

而想要正确性的用户,当他们使用“utf8”编码时,却无法保存像“????”这样的字符,因为“????”是 4 个字节的。

在这个不合法的字符集发布了之后,MySQL 就无法修复它,因为这样需要要求所有用户重新构建他们的数据库。

最终,MySQL 在 2010 年重新发布了“utf8mb4”来支持真正的 UTF-8。

总结

主要是目前网络上几乎所有的文章都把 “utf8” 当成是真正的 UTF-8,包括之前我写的文章以及做的项目(捂脸);因此希望更多的朋友能够看到这篇文章。

相信还有很多跟我在同一条船上的人,这是必然的。

所以,大家以后再搭建 MySQL、MariaDB 数据库时,记得将数据库相应编码都改为 utf8mb4。

终有一天,接你班儿的程序员发或你的领导现这个问题后,一定会在心里默默感到你的技术牛 B。

李逵和李鬼,求求你,别在MySQL中使用UTF-8了!相关推荐

  1. “李逵”还是“李鬼”,傻傻分不清楚

    由于现在现代科技的进步以及国家政策的大力扶持,我国光伏产业发展持续向好,产业规模稳步增长,也得到了越来越多的人的认可和支持. 一些人认为光伏就是一个发电的系统,只要能发电就可以了,那可能你还没有真正的 ...

  2. 宏分析(李逵和李鬼)

    函数(C语言的核心) 专题六:函数(C语言的核心).包括以下章节: 认清函数的真面目 可变参数列表 李逵和李鬼 函数调用行为 函数递归详解 函数设计技巧 李逵 VS 李鬼 3-1.c #include ...

  3. 青松资讯:李逵遇李鬼,浅谈Android上的“冒牌货”恶意软件

    提起古典名著<水浒传>,想必中国人都是耳熟能详.这本书刻画了诸多在中国家庭里可谓家喻户晓的人物,梁山好汉李逵便是其中之一.<水浒传>第四十三回中,李逵下山探亲,结果路遇一黑脸大 ...

  4. 定义类型名称的李逵与李鬼(析typedef与typeof)

    其实说不上是遇到李逵了,还是遇到李鬼了,只是自己的一个误用,所以就把typedef 与typeof混用了,编译不过在找原因的过程中才发现是typedef声明误用了typeof typedef用来定义类 ...

  5. C# HttpUtility.UrlEncode 与 Java URLEncoder.encode的转换方法,李逵与李鬼

    两种语言编码结果不一致,部分内容小大写需要转换, 李鬼方法如下(只转换了普通内容,在涉及到空格等字符时还是会编码不一致) //public static string UrlEncode(string ...

  6. HTTPS是李逵还是李鬼?全看你用啥证书

    "钓鱼"网站居然也有HTTPS 由于信息泄露.流量劫持."钓鱼"网站等不安全现象在网络上泛滥,HTTPS这一网络传输加密协议得到越来越多的应用.我们也逐步在潜意 ...

  7. 李逵VS李鬼: 易混淆山寨应用盘点

    提到山寨很多成功的应用都感觉到非常头疼,好不容易开发出了一款不错的应用,立马就会有人模仿你,要是简单的模仿一下还无可厚非,但是有甚者的山寨也许就是换汤不换药,甚至连应用的名字都十分相似就更别提内容了, ...

  8. open cyper还是open cypher,李逵or李鬼?

    一.open cyper.open cypher混用情况 故事的起因是做的运营内容里面领导提出了异议,提出图数据库语言open cypher这里写错了,应该是open cyper. 百度搜索了一下发现 ...

  9. 混战的低代码江湖,如何区分「李逵」和「李鬼」?

    作者:APICloud 创始人刘鑫 这两年,无论是资本层面,还是企业IT部门的关注,"低代码"都是绝对的热点.互联网圈也似在一夜之间冒出了各种各样的低代码公司. 到底什么是低代码? ...

最新文章

  1. jquery radio 取值
  2. 深度!移动机器人(AGV)产业链全分析
  3. 浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路(1)
  4. 1155 Heap Paths (30 分)【难度: 一般 / 知识点: 堆 堆的遍历】
  5. java 锁旗标_Java多线程
  6. 欢乐纪中某B组赛【2018.12.22】
  7. dhtmlXTree 指南与实例(二)
  8. Python3实现常用的数据结构
  9. Ortholab has been moved to Google Code
  10. 移动端H5解惑-页面适配
  11. html5页面风格,H5页面设计风格大盘点!
  12. mPEG-Pyrene,甲氧基聚乙二醇芘丁酸
  13. Bat脚本-timeout 命令
  14. html link 怎么设置密码,tplink路由器手机怎么设置密码?
  15. 从端到云——工业物联网项目全栈快速开发实践
  16. 为什么机器学习对嵌入式开发很重要?
  17. yocs_velocity_smoother速度平滑配置与使用
  18. 动态规划经典问题--TSP问题
  19. Spring Boot (六): 为 JPA 插上翅膀的 QueryDSL
  20. IronPython中使用Cecil类库指南

热门文章

  1. 20135234mqy 实验二 Java面向对象程序设计
  2. 2021年西式面点师(初级)最新解析及西式面点师(初级)模拟考试
  3. 数据标准和数据规范到底有什么区别???
  4. ubuntu20.04搭建QGC4.2编译环境
  5. STM32F103C8T6汇编点灯
  6. 并行计算中的Fortran语言
  7. Torrent Freak 点评十大 Torrent 站点 BTbt 站点
  8. 【翻译】PCI Express –信号完整性和EMI
  9. Rust+物联网智能报警方案、Rust腐蚀智能电路教程
  10. pyqt5实现手写中文数字识别