MYSQL數據庫與Emoji表情的故事

問題背景

手機上衆多輸入法和鍵盤支持輸入 emoji 表情,給早期設計的程序形成了愈來愈多的干擾。html

  1. 移動端購物的流行,2018 年 「雙十一」全網移動端交易達到 93.6%
  2. 微信年度報告裏 80 後愛用的「齜牙」表情,早在 2017 年 QQ 發佈的統計數據就超過 303 億。

最近咱們團隊就遇到了一個線上問題,就是因爲用戶下單備註使用 emoji 表情引發的問題。java

經過解決這個問題,主要了解下面 3 個方面內容:mysql

  1. Mysql 編碼概念
  2. Mysql「亂碼」是怎麼來的:字符編碼轉換
  3. 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 中的字符集轉換過程

字符集轉換
圖片來源於網絡

瞭解字符編碼的轉換規則,咱們就能夠理解爲什麼會產生亂碼以及字符插入失敗等問題。

https://www.ibm.com/developerworks/cn/java/analysis-and-summary-of-common-random-code-problems/index.html

解決方案

1. 瞭解當前字符編碼設置

咱們先查看一下系統配置的相關結果:

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
複製代碼

2. 設計數據庫時明確指定字符集

包括庫,表,字段三個層級都要明確,不然按照從低到高原則使用 my.ini 默認配置。具體的建立語句不細說,請自行搜索。

3. 統一字符集

目的是減小沒必要要的轉換,除非有特殊要求。特別注意保證轉換的時候不會因爲字符集不兼容而致使不可逆的轉換。例如部分 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;

4. 修復編碼損壞數據不可強行轉換字符集

經過 ALTER TABLE ... CHARSET=xxx 或 ALTER TABLE … CONVERT TO CHARACTER SET … 有可能把數據徹底破壞,可行的作法能夠參考盧鈞軼的博客(參見引文5)。

寫在後面

關於 emoji 表情,隨着手機輸入的支持和年輕人的熱愛,想要不出問題必須思考好如下幾個問題:

  1. 功能設計明確該輸入框是否須要支持 emoji 表情
  2. 上下游鏈路約定好如何存儲和展現,字符串截取
  3. 運維和升級數據庫字符集相關問題須要謹慎,防止數據丟失
  4. 主從同步,注意低版本不支持的字符集 utf8mb4 的狀況會致使同步失敗

資料來源

  1. 前瞻產業研究院報告解讀:移動支付便利化 十張圖瞭解 2018 年全球購物狂歡節高速增加背後的女人
  2. 阮一峯入門文章:Emoji 簡介
  3. 36 氪一篇有趣的文章:一波又一波 Emoji 表情推出,你的表情符號鍵盤還好嗎?
  4. 有意思的國外相關數據:你正在參與的語言革命|Emoji:再建巴別塔
  5. 盧鈞軼:10分鐘學會理解和解決MySQL亂碼問題:10分鐘學會理解和解決MySQL亂碼問題
  6. MYSQL Help About Unicode:charset-unicode
  7. 常見亂碼問題分析和總結:亂碼分析

本文同步發佈在公衆號:MYSQL亂碼問題整理

相關文章
相關標籤/搜索