最近有同窗反饋了這樣一個問題:php
上述語句在腳本中 load 入庫的時候會 hang 住,web 前端、命令行操做則要麼拋出 html
Incorrect string value: '\xF0\xA1\x8B\xBE\xE5\xA2...' for column 'name',前端
要麼存入MYSQL數據庫的內容會被截斷或者亂碼,而換作其它的中文則一切正常。java
嗯,看起來有點奇怪哈,按理說 utf8 編碼是覆蓋了全部中文的,不該該出現上述問題。python
那咱們先來看看插入異常的中文和正常的中文有啥區別:mysql
能夠看到上面插入異常的文字佔了 4 個字節,而咱們插入正常的則只佔了 3 個字節。可是 utf8 字符編碼不就是可變長,支持 1-4 字節的麼?會和這個有關?android
嗯,看看官方文檔就知道了:ios
10.1.10.6 The utf8mb4 Character Set (4-Byte UTF-8 Unicode Encoding)
The character set named utf8 uses a maximum of three bytes per character and contains only BMP characters. As of MySQL 5.5.3, the utf8mb4 character set uses a maximum of four bytes per character supports supplemental characters:web
For a BMP character, utf8 and utf8mb4 have identical storage characteristics: same code values, same encoding, same length.sql
For a supplementary character, utf8 cannot store the character at all, while utf8mb4 requires four bytes to store it. Since utf8 cannot store the character at all, you do not have any supplementary characters in utf8 columns and you need not worry about converting characters or losing data when upgrading utf8 data from older versions of MySQL.
utf8mb4 is a superset of utf8.
由官方文檔可知,mysql 支持的 utf8 編碼最大字符長度爲 3 字節,若是遇到 4 字節的寬字符就會插入異常了。三個字節的 UTF-8 最大能編碼的 Unicode 字符是 0xffff,也就是 Unicode 中的基本多文種平面(BMP)。也就是說,任何不在基本多文本平面的 Unicode字符,都沒法使用 Mysql 的 utf8 字符集存儲。包括 Emoji 表情(Emoji 是一種特殊的 Unicode 編碼,常見於 ios 和 android 手機上),和不少不經常使用的漢字,以及任何新增的 Unicode 字符等等。
最初的 UTF-8 格式使用一至六個字節,最大能編碼 31 位字符。最新的 UTF-8 規範只使用一到四個字節,最大能編碼21位,正好可以表示全部的 17個 Unicode 平面。
utf8 是 Mysql 中的一種字符集,只支持最長三個字節的 UTF-8字符,也就是 Unicode 中的基本多文本平面。
Mysql 中的 utf8 爲何只支持持最長三個字節的 UTF-8字符呢?我想了一下,多是由於 Mysql 剛開始開發那會,Unicode 尚未輔助平面這一說呢。那時候,Unicode 委員會還作着 「65535 個字符足夠全世界用了」的好夢。Mysql 中的字符串長度算的是字符數而非字節數,對於 CHAR 數據類型來講,須要爲字符串保留足夠的長。當使用 utf8 字符集時,須要保留的長度就是 utf8 最長字符長度乘以字符串長度,因此這裏理所固然的限制了 utf8 最大長度爲 3,好比 CHAR(100) Mysql 會保留 300字節長度。至於後續的版本爲何不對 4 字節長度的 UTF-8 字符提供支持,我想一個是爲了向後兼容性的考慮,還有就是基本多文種平面以外的字符確實不多用到。
要在 Mysql 中保存 4 字節長度的 UTF-8 字符,須要使用 utf8mb4 字符集,但只有 5.5.3 版本之後的才支持(查看版本: select version();)。我以爲,爲了獲取更好的兼容性,應該老是使用 utf8mb4 而非 utf8. 對於 CHAR 類型數據,utf8mb4 會多消耗一些空間,根據 Mysql 官方建議,使用 VARCHAR 替代 CHAR。
知道緣由了,固然得談談有哪些方案能夠解決開頭的問題。
升級你的 mysql 到 5.5.3 以後便可,查看當前環境版本:
select version();
MySQL在5.5.3以後增長了這個utf8mb4的編碼,mb4就是most bytes 4的意思,專門用來兼容四字節的unicode。好在utf8mb4是utf8的超集,除了將編碼改成utf8bp4外不須要作其餘轉換。固然,爲了節省空間,通常狀況下使用utf8也就夠了。
因此好的技術就是,採用對當前而言最好的解決方案,而後再逐步迭代知足新的需求。
-- 方法一,若是遇到某個列字符集轉換完後字節數超限了,會提示錯誤 --一、修改數據庫字符集,或修改表默認字符集 alter table j1 default character set utf8mb4; ALTER DATABASE test CHARACTER SET = utf8mb4; --二、隨後再修改全部字符型列的字符集 alter table j1 modify name varchar(20) character set utf8mb4 not null default ''; ALTER TABLE `test` CHANGE COLUMN `name` `name` varchar(12) CHARACTER SET utf8mb4; -- 方法二,若是遇到某個列字符集轉換完後字節數超限了,則會將這個列數據類型轉換成能夠容納更大長度的類型,好比從 TEXT 轉成 LONGTEXT 等。 --直接轉換表字符集 alter table test convert to character set utf8mb4; --方法三 --若是不放心,能夠用mysqldump邏輯備份方式,用utf8mb4字符集把數據備份出來,新建表,恢復回去,應該也能夠的。
[client] default-character-set = utf8mb4 [mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_unicode_ci [mysql] default-character-set = utf8mb4
P.S. 若是你使用的是java語言,須要將jdbc驅動包升級到 mysql-connector-java-5.1.14.jar。
從業務和技術的角度綜合考慮,能夠作個折中,將生僻字符串提早過濾掉,由於這類字符串原本就使用的不多,即便存進數據庫了,展現、查詢的時候也會多少有其它的問題,不如直接過濾掉,mysql 不支持四字節的 utf8 一方面多是歷史包袱,另外一方面估計也是爲了省空間。
好比,我們能夠直接先用 sed、awk、python、perl 處理下要 load 入庫的腳本,將四字節的生僻字全過濾再入庫:
判斷MySql支持Unicode字符的方法,僞碼爲:
for i=1->n int c=str.codePointAt(i); if (c<0x0000||c>0xffff) { return false; }
稍做修改便可。
爲避免web頁面或者終端自己不支持utf8四字節,能夠採用二進制的方式來操做
create table t1(name varchar(20) charset utf8mb4); insert into t1 values(0xF0A09080); set charset binary; select * from t1;
最後順便總結下4字節utf8字符的系統支持狀況:
windows xp: 我所測試的xp系統都不支持4字節utf8字符, 瀏覽器用佔位符顯示
windows 7: 支持4字節utf8字符
mac os x: 支持4字節utf8字符
iPhone/iPad: 支持4字節utf8字符
許多的數據庫軟件或者shell終端都不支持4字節utf8字符, 好比 Navicat、SecureCRT
以 php 場景爲例說明:
php鏈接會話設置編碼utf8, mysql後端字段爲text character set utf8: 寫入內容從4字節utf8字符處被截斷
php鏈接會話設置編碼utf8mb4, mysql後端字段爲text character set utf8: 內容能夠完整寫入, 可是4字節utf8字符被替換爲問號"?"
php鏈接會話設置編碼utf8mb4, mysql後端字段爲text character set utf8mb4: 完整支持4字節utf8字符
從平臺支持上來看, 隨着winxp的逐步淘汰, 對4字節utf8字符的支持仍是有必要的.
官方手冊對utf8mb4字符的說明中指出, utf8mb4是utf8的超集, 所以可放心升級.
看到這裏,不知道細心的你有沒有發現,本文的代碼爲毛都是圖呢?要知道我本身寫文章不多把代碼搞成圖的,那是由於。。。
哇哈哈,真是哪壺不開提哪壺啊。。。。。。。。。
[1] 談談字符集與字符編碼
http://my.oschina.net/leejun2005/blog/232732#OSC_h3_4
[2] sed matching any ascii code/hex byte
http://forums.gentoo.org/viewtopic-t-770576-start-0.html
[3] 10.1.10.6 The utf8mb4 Character Set (4-Byte UTF-8 Unicode Encoding)
http://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html
[4] Mysql 中的 utf8
[5] Mysql中4字節UTF8字符集問題
http://bbs.chinaunix.net/thread-3766853-1-1.html
[6] MySQL,UTF-8和emoji
http://shadyxu.com/mysql_utf8.html
[7] 關於MYSQL截斷內容問題解決
http://www.momotime.me/2014/10/mysql-utf8mb4/
[8] MySQL儲存4字節字符
http://www.web-tinker.com/article/20643.html
[9] 關於UTF-8編碼的MySql拋出incorrect string value的問題
http://blog.csdn.net/tannasu/article/details/8064021
[10] 關於MYSQL截斷內容問題解決
http://daiweiyang.com/mysql_data_truncated/
[11] mysql漢字16進制編碼轉換方法
http://blogread.cn/it/article/3016?f=wb
[12] MySQL多字節字符集形成主從數據不一致問題
http://backend.blog.163.com/blog/static/20229412620133274030845/
[13] 如何轉義emoji表情,讓它能夠存入utf8的數據庫?
http://segmentfault.com/q/1010000003711491
[14] MySQL表字段字符集不一樣致使的索引失效問題