数据库与编码

以 MySQL 为例。在 MySQL 中,utf8 编码并不是真正意义上的 utf8,而是 utf8mb3。utf8mb3 支持的字符集是 0~65535 之间的 BMP 的字符,最多支持 3 个字节,一些生僻字和 emoji 是不支持。

以 MySQL 为例。在 MySQL 中,utf8 编码并不是真正意义上的 utf8,而是 utf8mb3。utf8mb3 支持的字符集是 0~65535 之间的 BMP 的字符,最多支持 3 个字节,一些生僻字和 emoji 是不支持。

这会导致什么问题呢?如果你数据库使用的是 utf8mb3 编码,那么在 insert 一些 4 字节的字符的时候,会报错:

insert into t (name) value('😂')
SQL 错误 [1366] [HY000]: Incorrect string value: '\xF0\x9F\x98\x82' for column 'name' at row 1

insert into t (name) value('𡋾')
SQL 错误 [1366] [HY000]: Incorrect string value: '\xF0\xA1\x88\xBE' for column 'name' at row 1

其实,这并不是 MySQL 自己的问题,而是 Unicode 在一开始的时候就没确定好到底用几个字节存储,当时的方案并不完善;等到了后来,Unicode 标准才正式发布,确定了存储方案。

于是,在 2010 年,MySQL 发布了 MySQL 5.53,在创建数据库时可以用一个新的字符集 utf8mb4,支持 4 个字节的字符

官方文档醒目的提醒了大家说 utf8mb3 会在未来的版本中删除,并建议用户优先使用 utf8mb4。所以在创建数据库的时候,确保的使用是 utf8mb4,而不是 utf8mb3。

官方的一些说明:MySQL :: MySQL 8.0 Reference Manual :: 10.9.2 The utf8mb3 Character Set (3-Byte UTF-8 Unicode Encoding)

Applications that use UTF-8 data but require supplementary character support should use utf8mb4​ rather than utf8mb3​ (see Section 10.9.1, “The utf8mb4 Character Set (4-Byte UTF-8 Unicode Encoding)”).

可以通过查看数据库的建表语句来检查:

show create database 数据库名\G;
-- 例如
mysql> show create database dblog\G;
*************************** 1. row ***************************
       Database: dblog
Create Database: CREATE DATABASE `dblog` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */
1 row in set (0.00 sec)

笔者在 MySQL 8.0.27 下尝试了下,默认都是用的 utf8mb4 编码了,而在很久之前,默认编码是 utf8mb3。

如果是早期的数据库表,有需求要兼容 4 个字节的字符的话,可以修改对应的字段的编码或数据库的编码。

编码的重要性

编码是一件非常重要的事情。现代很多系统都要求支持 Unicode。如果不支持,有什么后果呢?

举个例子,如果某个人名字带有生僻字,银行的系统不支持,那么这个客户就不能去该银行做业务,直接损失了一名客户。

例如:在 有没有谁的朋友名字中含有“䶮”字的? - 知乎 里有人提到:

本人有两张储蓄卡,招商和工商,招商的户名是某䶮,工商银行的户名是某(YAN3),亲身经历证明:招商储蓄卡无法变更为公积金联名卡,主要原因是姓名不一致,因此肯定没有办法提取公积金

一个名字叫“䶮”的人的苦恼:开不了银行账户,用不了微信、支付宝

近日,多位人士向记者表示,自己的名字中含有 “䶮”等汉字,在办理公积金、银行卡、第三方支付、手机卡等金融、通信业务时,名字无法被验证,导致业务无法办理。

甚至,因为没法申请银行账户,微信、支付宝也拒绝使用,有的银行建议名字带有“䶮”字的用户改回繁体字“龑”。

生僻字带来金融交易中的麻烦,这一情况并不鲜见。

演示

实践出真知,我们来演示下,本文是在 Windows 下的 MySQL 5 演示。

首先创建一个数据库,并指定字符集

mysql> create database testunicode default character set utf8 collate utf8_general_ci;
Query OK, 1 row affected (0.00 sec)

创建完后,可以检查下该数据库的编码

mysql> show create database testunicode;
+------------+--------------------------------------------------------+
| Database    | Create Database                                                      |
+------------+--------------------------------------------------------+
| testunicode | CREATE DATABASE `testunicode` /*!40100 DEFAULT CHARACTER SET utf8 */ |
+-------------+--------------------------------------------------------+
1 row in set (0.00 sec)

使用该数据库并创建表,可以看到该表的默认编码是 UTF8

mysql> use testunicode;
Database changed

mysql> create table t (name varchar(50));
Query OK, 0 rows affected (0.25 sec)

mysql> show create table t
CREATE TABLE `t` (
  `name` varchar(50) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8

尝试 insert 一个 emoji,确实有报错:

insert into t (name) value('😂')

SQL 错误 [1366] [HY000]: Incorrect string value: '\xF0\x9F\x98\x82' for column 'name' at row 1

接下来我们尝试更换字符集,并创建一个新表:

mysql> ALTER DATABASE `testunicode`
DEFAULT CHARACTER SET utf8mb4
DEFAULT COLLATE utf8mb4_bin;

mysql> create table tu (name varchar(50))

mysql> show create table t
CREATE TABLE `t` (
  `name` varchar(50) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8

尝试重新 insert,结果是能成功 insert(没有报错就是成功):

mysql> insert into tu (name) value('😂')

小结

虽然目前 MySQL 默认都是使用 utf8mb4 了,但一些早期的项目还是使用的 utf8mb3,希望读者们如果遇到了该编码问题能有思路,知道是怎么回事、怎么修复。

触类旁通,其他关系型数据库,例如 Oracle 等,也需要考虑编码问题

参考

英文版In MySQL, never use “utf8”. Use “utf8mb4”. | by Adam Hooper | Medium

中文版:记住,永远不要在 MySQL 中使用“utf8”_数据库_Adam Hooper_InfoQ 精选文章

一个名字叫“䶮”的人的苦恼:开不了银行账户,用不了微信、支付宝

曝光 MySQL 的 一个坑,不要再使用 UTF-8 了?

posted @ 2024-06-19 10:02  peterjxl  阅读(16)  评论(0编辑  收藏  举报