手机上众多输入法和键盘支持输入 emoji 表情,给早期设计的程序形成了愈来愈多的干扰。html
- 移动端购物的流行,2018 年 “双十一”全网移动端交易达到 93.6%
- 微信年度报告里 80 后爱用的“龇牙”表情,早在 2017 年 QQ 发布的统计数据就超过 303 亿。
最近咱们团队就遇到了一个线上问题,就是因为用户下单备注使用 emoji 表情引发的问题。java
经过解决这个问题,主要了解下面 3 个方面内容:mysql
- Mysql 编码概念
- Mysql“乱码”是怎么来的:字符编码转换
- Emoji 在 Mysql 使用问题
昨天忽然接到业务反馈,监控告警了,有几个订单卡单了,一直在某个系统里面没有推到下游,最终致使错过了配送时间。sql
查询日志中心,ES 搜索到了某系统 Job 拉数据保存数据库的异常信息。数据库
展开详细日志发现了关键日志:微信
ERROR 1366: Incorrect string value: '\xF0\x9F\x99\x8F...' for column 'Remark' at row 2。网络
试图将一个 4 字节的字符写入到一个 3 字节的列 Remark, 固然是报错了。运维
而后咱们看了一下用户的备注 Remark 信息,**上午家里没人,请下午送,谢谢😊dom
这个谢谢真心不容易啊,老铁。emoji 表情,下单彻底没有问题。DBA 搜索了一下历史订单数据库,是支持的。找了一下报错的 Job 服务,发现操做的库是最近从 SQL Server 转到 mysql 的,拉取打印发货单信息的时候出错了。ui
Unicode 字符集有好几种编码方式,好比常见的 utf-8,utf-16,utf-32 等。 utf-8 是它是一种变长的编码方式,采用 1-4 个字节表示字符,utf-16 采用固定的 2 个字节,utf-32 则采用 4 个字节存储。
C#里面 Encoding.Unicode 实现为 2 个字节的 Little-Endian UTF-16,若是是 4 个字节使用 UTF-32。
Unicode 在第二个面板(Plane 1,SMP)规定了 emoji 的码点和含义。每个表情使用 4 个字节。 所谓 Emoji 就是一种在 Unicode 位于、u1F601-\u1F64F 区段的字符。
SQL Server 的数据库表,nvarchar 类型字符串默认就是可变长度 Unicode 字符串。
MySQL 版本 5.5.3 如下版本 utf8 字符集 utf8 最多表示 3 个字节,5.5.3 以上版本支持 4 个字节的 utf8 编码字符集 utf8mb4,MySQL8.0 版默认字符集为 utf8mb4。
MYSQL 存储 utf8 字符默认为无 BOM 的 Big-Endian 方式编码。
了解字符编码的转换规则,咱们就能够理解为什么会产生乱码以及字符插入失败等问题。
咱们先查看一下系统配置的相关结果:
mysql> show variables like '%char%';
+--------------------------+----------------------------------------------+
| Variable_name | Value |
+--------------------------+----------------------------------------------+
| character_set_client | utf8 |
| character_set_connection | utf8 |
| character_set_database | utf8mb4 |
| character_set_filesystem | binary |
| character_set_results | utf8 |
| character_set_server | utf8mb4 |
| character_set_system | utf8 |
| character_sets_dir | D:\mysql\mysql-8.0.11-winx64\share\charsets\ |
+--------------------------+------------------------------------- ---------+
8 rows in set
复制代码
包括库,表,字段三个层级都要明确,不然按照从低到高原则使用 my.ini 默认配置。具体的建立语句不细说,请自行搜索。
目的是减小没必要要的转换,除非有特殊要求。特别注意保证转换的时候不会因为字符集不兼容而致使不可逆的转换。例如部分 Unicode 字符在 Utf8 里面是没有的,部分 gbk 编码也是。 具体来讲就是:
客户端, character-set-client,table charset 三个字符集彻底一致就能够保证不会产生乱码。
SQL 语句中的裸字符串会受到 character_set_connection 字符集或 introducer 设置的影响,对于比较之类的操做可能产生彻底不一样的结果,须要当心!解决方法是在发送查询前执行一下下面这句: SET NAMES '***'
至关于下面的三句指令:
SET character_set_client = utf8;
SET character_set_results = utf8;
SET character_set_connection = utf8;
经过 ALTER TABLE ... CHARSET=xxx 或 ALTER TABLE … CONVERT TO CHARACTER SET … 有可能把数据彻底破坏,可行的作法能够参考卢钧轶的博客(参见引文5)。
关于 emoji 表情,随着手机输入的支持和年轻人的热爱,想要不出问题必须思考好如下几个问题:
- 功能设计明确该输入框是否须要支持 emoji 表情
- 上下游链路约定好如何存储和展现,字符串截取
- 运维和升级数据库字符集相关问题须要谨慎,防止数据丢失
- 主从同步,注意低版本不支持的字符集 utf8mb4 的状况会致使同步失败
本文同步发布在公众号:MYSQL乱码问题整理