微信nickname亂碼(emoji)及mysql編碼格式設置(utf8mb4)解決的過程

本身的練習項目中涉及保存微信的nickname,以前一直正常使用,可是忽然遇到一個以前沒有遇到的問題。通過調試發現錯誤以下:

Incorrect string value: '\xF0\x9F\x99\x88\xF0\x9F...' for column 'nickname' at row 1
通過仔細查看發現能夠得到nickname的數據,可是沒法保存到mysql數據庫,查看用戶的微信發如今nickname中使用了emoji字符。
到百度(只能用這個,其餘的麻煩呀。)上查找發現主要解決方案就是MySQL的編碼設置由utf8轉爲utf8mb4。
具體解釋可見:[詳細emoji表情與utf8mb4的關係][1] ,寫的很是全面詳細。

網上的解決辦法大可能是修改my.cnf參數,設置mysql的編碼爲utf8mb4,這種方法雖然完全,可是一般要重啓mysql,會形成生產系統臨時當機。我認爲寫的比較好的方法是:mysql/Java服務端對emoji的支持,通常可參考以上方法。文章中的關鍵點也說的比較清楚。html

下面是個人處理方法:
要求:java

1.MySQL的版本不能過低,低於5.5.3的版本不支持utf8mb4編碼。select version();
2.JDBC驅動版本不能過低,mysql connector版本高於5.1.13。
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.38</version>
</dependency>

3.將表中的對應字段,好比會員表的呢稱字段,其字符集修改爲utf8mb4。
4.最後修改druid數據源的配置,增長一行:


<property name="connectionInitSqls" value="set names utf8mb4;"/>

5.檢查下jdbc鏈接串的設置:

jdbc:mysql://localhost:3306/dbname?useUnicode=true&characterEncoding=utf8

這裏要注意:有人建議刪除useUnicode=true&characterEncoding=utf8,但好像我這裏會發生保存數據時發生亂碼的現象。

本文重要參考:mysql : utf8mb4 的問題mysql

 

JAVA解決Emoji表情存儲至Mysql報錯問題

保存微信暱稱時,Mysql報錯。git

複製代碼

Caused by: java.sql.SQLException: Incorrect string value: '\xF0\x9F\x98...' for column 'nick_name' at row 1
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1074)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4096)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4028)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2490)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2651)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2734)
    at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2155)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2458)

複製代碼

報錯緣由:github

UTF-8編碼有多是兩個、三個、四個字節。Emoji表情是4個字節,而Mysql的utf8編碼最多3個字節,因此數據插不進去。

 

網上解決辦法:spring

一、修改my.ini [mysqld] character-set-server=utf8mb4在後臺配置mysql鏈接參數中,不要加characterEncoding參數。 不加這個參數時,默認值就時autodetect。將已經建好的表也轉換成utf8mb4。命令:(將TABLE_NAME替換成你的表名)sql

ALTER TABLE `TABLE_NAME` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
將須要使用emoji的字段設置類型爲utf8mb4_general_ci:
ALTER TABLE `TABLE_NAME`MODIFY COLUMN `COLUMN_NAME`  text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

 

照作以後,發現並無解決問題。找不到緣由。等之後空閒時間慢慢調錯。先將此次BUG修復。選用另外一種方法。數據庫

一、存儲nickname的時候,先將nickname用base64編碼。我照作之後,發現仍是有問題。最後,使用了一種稍麻煩的辦法。segmentfault

String encodeNickname = new String(Base64.getEncoder().encode("Nickname".getBytes()));

二、取出的時候,先將nickname用base64解碼。服務器

String decodeNickname = new String(Base64.getDecoder().decode(encodeNickname.getBytes()));

三、數據庫中的nickname手動用base64編碼更新。

 

結束上述步驟後,查看後臺發現傻了眼。新增的用戶nickname能夠正常顯示emoji表情了。可是以前手動編碼的nickname所有亂碼了。

沒有找到緣由。覺得是沒有加上編碼格式的緣由。更改代碼:

String encodeNickname = new String (Base64.getEncoder().encode("Nickname".getBytes()),"utf-8");
System.out.println("編碼後:"+encodeNickname);
String decodeNickname = new String (Base64.getDecoder().decode(encodeNickname.getBytes()),"utf-8");
System.out.println("解碼後:"+decodeNickname);

仍是亂碼!百思不得其解。福至心靈,拿gbk將以前編碼的數據解碼試了下。發現顯示正常了。

new String (Base64.getDecoder().decode(encodeNickname.getBytes()),"gbk");

從新把以前nickname用gbk解碼獲取用戶暱稱,而後用utf-8編碼存儲,utf-8解碼。一切正常了。

 

記一次生產事故踩坑。血淋淋的慘痛教訓

衆所周知 mysql 存 emoji 表情要用 utf8mb4 這個字符集

OK 沒問題,設置 nick_name 爲 utf8mb4 varchar(50)

測試的結果:

    emoji 表情儲存成功

沒有問題  徹底oj8k  發生產!

微信公衆號作了推送,爲了抗住流量,還準備了100臺服務器。

晚上監控流量,服務器各項指標正常。

可是一看日誌,發現日誌瘋狂報錯:部分敏感信息及參數已刪除

exception[order=UserInfoRequestType{activityId=, uid=, nickName=wing.?, headImgUrl=}]
org.springframework.jdbc.UncategorizedSQLException: 
### Error updating database.  Cause: java.sql.SQLException: Incorrect string value: '\xF0\x9F\x90\x9D' for column 'nick_name' at row 1
### The error occurred while setting parameters
### SQL: insert into s_user_info         (id,nick_name,uid,support_detail,popularity,img_url,DataChange_CreateTime,DataChange_LastTime,activity_id)         values (?, ?, ?,         ?, ?, ?,         ?,?,?)
### Cause: java.sql.SQLException: Incorrect string value: '\xF0\x9F\x90\x9D' for column 'nick_name' at row 1
; uncategorized SQLException for SQL []; SQL state [HY000]; error code [1366]; Incorrect string value: '\xF0\x9F\x90\x9D' for column 'nick_name' at row 1; nested exception is java.sql.SQLException: Incorrect string value: '\xF0\x9F\x90\x9D' for column 'nick_name' at row 1   at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:84)

看到一堆的報錯,立刻就慌了。

nick_name 存不進去,仔細調研發現是 部分emoji表情的用戶 的 暱稱儲存失敗。OK,立刻fixbug,字符串存不了,我轉base64總能夠了吧,改完發測試環境,測試測了這個接口沒有問題,又發生產,結果引發了另一個接口的報錯,瞬間又是一堆錯誤日誌,整我的瞬間斯巴達了 emmm......  因而立刻回退到上一個版本,讓部分特殊emoji表情的用戶沒法活動。再繼續fixbug。

如今庫裏既有base64的暱稱 也有未 base64 的暱稱 。真是讓人頭大。

通過週末兩天的加班,終於把這個問題穩定的解決了:

數據庫存 base64 encode 的 暱稱, 從DB取出來時  decode一下。

總結:

  • 一、我設置了utf8mb4 仍是沒法儲存部分 moji 表情的緣由是:新的moji 表情愈來愈多,mysql 版本卻沒有跟上,致使新的moji 表情存不進去。
  • 二、mysql 存 moji 表情 不要徹底依賴 utf8mb4這個字符集來幫你處理 , 能夠就選用utf8 長度給長一點 轉 base64 後存,取的時候再轉成字符串就行,固然轉base64 的 encode  和 decode 操做都會消耗你 CPU 的 性能,在高併發場景下要多測試,而後進行方案的取捨。
  • 三、在GitHub上找到 emoji-java 這個來解決,下次我應該就會用這個來處理emoji表情了。
  • 四、就算是高併發場景,遇到生產環境大量報錯不要慌,不要急,要穩住心態,只要一些硬性指標(好比訂單量)沒有降低就還能撐住。
  • 五、學會批判的看事情,百度獲得的答案不必定對,相信不少人遇到要存moji 表情 都是 直接谷歌或是百度 mysql 如何存 emoji表情,而後看到一堆文章,找了篇看起來不少的,就拿着參考開始實踐了。咱們還須要站到對立面多思考一個問題:Emoji表情存儲至Mysql報錯問題,因而你就能找到這篇:JAVA解決Emoji表情存儲至Mysql報錯問題 就不會踩坑了

這是個人一點踩坑經歷,但願能給看到文章的你一點幫助。

 

ps:

https://segmentfault.com/a/1190000004594385?utm_medium=referral&utm_source=tuicool

https://www.cnblogs.com/yugure/articles/7773013.html

https://cloud.tencent.com/developer/article/1393147

相關文章
相關標籤/搜索