(譯)用MySQL的朋友們請不要使用"utf8",請使用"utf8mb4"

原文地址:https://medium.com/@adamhooper/in-mysql-never-use-utf8-use-utf8mb4-11761243e434html

今天打開電腦,導出都是這篇文章,我以爲大多數開發者可能在沒有遇到這個問題時,不會太注意。可是,這麼操蛋的坑,仍是提早知曉爲妙。mysql

用MySQL的朋友們請不要使用"utf8",請使用"utf8mb4"linux

今天我試圖把UTF-8編碼的字符串插入使用「utf8」編碼的MariaDB數據庫中,Rails拋出一個古怪的異常:git

Incorrect string value: ‘\xF0\x9F\x98\x83 <…’ for column ‘summary’ at row 1github

一切都很UTF-8:UTF-8 client,UTF-8的服務器,UTF-8編碼的數據庫,使用UTF-8的字符集。「😃 <…」是個有效的UTF-8字符串。web

可是問題的關鍵是:MySQL數據庫的 「utf8」並非真正概念裏的 UTF-8。sql

MySQL中的「utf8」編碼只支持最大3字節每字符。真正的你們正在使用的UTF-8編碼是應該能支持4字節每一個字符。數據庫

MySQL的開發者沒有修復這個bug。他們在2010年增長了一個變通的方法:一個新的字符集「utf8mb4服務器

固然,他們並無對外公佈(可能由於這個bug有點尷尬)。如今不少指南推薦用戶使用「utf8」其實都錯了。併發

簡單的說:

MySQL中的 「utf8mb4」 纔是 真正意義上的「UTF-8」。

MySQL的「utf8」是個「特殊的字符編碼」。這種編碼不少Unicode字符保存不了。

我強烈建議MySQL和MariaDB用戶使用「utf8mb4」而不是「utf8」。

編碼是什麼?什麼是UTF-8?

Joel on Software上有一遍我最喜歡的介紹,我精簡描述以下:

計算機使用0和1存儲文字。好比第一段第一個字符存儲爲「01000011」表示「C」,計算機經過如下兩個步驟選擇用「C」表示:

計算機讀取到「01000011」後計算出這是數字67。

計算機經過查找Unicode字符集來確認67表明的「C」。

一樣的事情發生在我打字輸入C的時候。

計算機經過Unicode字符集將「C」 映射爲67。

計算機把67編碼爲「01000011」發送給web服務器。

幾乎全部的程序和互聯網應用使用Unicode字符集。

Unicode字符集裏有超過100萬個字符(「C」 和 「💩」 是兩種不一樣的字符。)。UTF-32是最簡單的編碼方式,它在表示每一個字符的時候使用32個bits。這樣編碼簡單,可是並不實用,明顯浪費了太多的空間。

UTF-8相比UTF-32更加節約空間。在UTF-8中,像「C」這樣的字符佔用8bits,「💩」這樣的佔用32 bits。其餘字符佔用16或者24 bits。如本篇這樣的文章用UTF-8存儲比用UTF-32節省4倍左右的空間。更小的空間佔用也意味着加載速度會快上4倍。

而MySQL中的 「utf8」字符集則和其餘應用行爲不同。好比根本無法表示「💩」。

一點關於MySQL的歷史

爲何MySQL的開發者開發了一個奇怪的「utf8」。咱們能夠經過提交的日誌來揣測一下。

MySQL從4.1版開始支持UTF-8。那是在比今天UTF-8 RFC 3629標準更早的2003年。

在此以前的UTF-8標準,RFC 2279中規定6個bytes表示一個字符。MySQL的開發者在2002.3.28編碼實現了RFC 2279 。併發布了pre-pre-release 的 MySQL 4.1

而後在9月出現了一個神祕的字節調整。「UTF8 now works with up to3 byte sequences only.」

是誰提交了此次更新?爲何?我沒法解答。MySQL的源碼移到Git後丟失了舊的做者信息(MySQL 曾經和linux內核同樣使用BitKeeper)

可是我大概能猜出來緣由。

回到2002年,若是用戶能夠保證表中的每一行具備相同的字節數,MySQL就能夠提升用戶的速度。爲了獲得這個提高,用戶就須要定義保存文字的列爲「CHAR」。一個「CHAR」列老是擁有相同的字符數。若是存入的字符較少則會在最後補齊空白。若是存入的數據過多則會被拋棄多餘的字符。

當MySQL的開發者第一次嘗試以6字節每字符實現UTF-8時,他們意識到CHAR(1)的列會佔用6字節,CHAR(2)會佔用12字節,以此類推。

顯而易見的是,這個沒有被使用的實現方式是正確的,任何一個理解UTF-8的開發者將會認同這一點。

個人猜想是:MySQL的開發者違背了「utf8」編碼去幫助那些1)試圖去優化空間和速度的人,2)嘗試優化空間和速度失敗的人。

這是個無人獲益的改動。那些想要更快性能,更小空間的獲得的依然是比他們曾經使用版本更大更慢的實現,而那些想要正確的「utf8」的人獲得的是個「💩」都存儲不了的實現。

MySQL發佈了這個錯誤的版本後,在也沒有修復它:由於那樣不少使用者將被迫重建他們的數據庫。MySQL最終在2010年更新了一個以「utf8mb4」命名的UTF-8實現。

Why it’s so frustrating

爲何這麼操蛋

這周我過得很操蛋。我遇到一個很難發現的bug,就由於我被「utf8」這個名字給愚弄了。並且我也不是個案,我發現幾乎每篇推薦使用「utf8」的文章都錯了。

「utf8」的命名在mysql依然是錯的。這是個專有的實現。這形成了新的問題,並且沒有解決他應該解決的問題。

若是你使用MySQL或者 MariaDB,不要使用「utf8」,應該老是使用「utf8mb4」,不然總有一天會遇到頭疼的事情。

做者:brightwang 連接:http://www.jianshu.com/p/ab9aa8d4df7d 來源:簡書 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
相關文章
相關標籤/搜索