前陣子在調整翻譯相關功能時,該來的還是來了,面對多國語系。
狀況
德語
原本的 MySQL 版本還維持著舊版本,雖然有料想到多國語系支援問題,所以把語系設定為utf8
,以為這樣就能夠萬無一失了,沒想到發現德國語系出現不預期的問題,像是 ü
這個字,雖然能夠順利的寫進資料欄位,這時候我們再寫入 u
會發生什麼事呢?
什麼事都不會發生
我們的資料庫會有這樣的兩筆資料
Id | FieldA |
---|---|
1 | iamu |
2 | iamü |
如果我們想查詢其中一筆時,輸入SELECT * FROM TABLEA WHERE FIELDA = 'iamü'
接著不預期的狀況來了,MySQL 會同時把這兩筆資料給查出來!我馬上換個查詢使用 iamu
結果也是一樣!
這邊提供幾個德語都是容易造成問題的:
ä
、ö
、ü
、ß
爛漫測試精神的QA - emoji
不得不說,能夠擔任 QA 的人都具有獨特的敏銳度,簡單的 boundary test 就不用說了。有一次他們在測試後台的某個 text box 元件,除了長度、各種特殊字之外,居然輸入了 emoji
符號,果不其然再寫入 mysql 時跳出了錯誤。
當時在修正這個錯誤時,怎麼測試都覺得很奇怪,明明 utf8
明明支援 emoji
呀!為什麼會寫不進資料庫咧!而且我還確認 charset 確實是 utf8 呀~
MySQL 對於 utf8,各版本各自表述
1 | 問題出在 MySQL 早期的版本(5.5以前) utf8 只坐了半套 |
Before MySQL 5.5
5.5 版本以前的 MySQL utf8 僅用了 三個位元
來儲存,在這個時代多了表情符號、罕見漢字,通通都需要 四個位元
,因此以前預設的版本 utf8 就不夠空間使用。
這邊的我不打算講太多技術細節,在文末的 Reference 會附一些參考連結,裡面有更詳細的解釋,有興趣的人可以再自己深入鑽研。
After MySQL 5.5
這個時期推出了 四個位元
的 真。UTF8 版本,為了避免相同文字造成的混淆,官方特別加上了後綴,現在完全體叫做 utf8mb4
,而舊版,你要稱為 utf8 或 utf8mb3
都可以。
全部改用 utf8mb4 就完事了!?
事情當然沒這麼簡單,改不改還是需要評估一些事情。
首先要確認你們的系統是否有這種需求,如果沒需求,其實隨著版本支援度走就好。很多線上服務或者對於資訊沒有太強烈要求的公司,伺服器內的 MySQL 恐怕都還是很古老版本,冒然升級絕對不是一件好事。
其次,官方有提到在 MySQL
8.0
之後的版本才有大幅度的效能提升!才有!才有!才有!因為很重要所以說三次!
這也是為什麼網路上很多資訊都還停留在改用utf8mb4
查詢效能會略微降低,因為當時官方還沒釋出 8.0 版本啊~所以第二重要的事,就是先確認你們的 MySQL 是哪個版本
這個是 MySQL官方開發帖提供的數據,8.0 版本後的 utf8mb4 效能大幅度提升。
我們需要且可升級版本
走到這裡,如果你們確定有多語系判斷的需要且能夠升級 MySQL 版本,那我再推薦這篇官方的The Devil in the details,這裡說的是更多 utf8mb4
對於各種語系的分支與支援度,依據你們的市場來選擇吧!
MySQL 8.0
我只想提醒幾件事:
- utf8mb3 已棄用
- 預設就是
utf8mb4
那連線該用哪一種? utf8mb4_general_ci or utf8mb4_unicode_ci
看到這裡,前面的 utf8mb4 我們已經理解了,先來看看後面的 ci
是什麼?
ci
: case insensitive,查詢時不分大小寫cs
: case sensitive,區分大小寫bin
: binary,使用二進位來判斷、比較
這個很好處理,其實不用特別使用 bin
,依照團隊開發習慣,使用 ci
或 cs
就好,關鍵在中間的 general
/ unicode
unicode
: 查詢資料的排序規則按照 unicode 編碼general
: 簡化過後的排序規則,理論上查詢速度會比較快
但是!!!
這是古老時候的資訊了,隨著硬體高速發展,現在不值得使用 general 了!!我只能說,隨便一個 join 沒寫好的效能損耗都遠高於上述兩種差異。
對於有需求使用準確多國語系的服務,精確的結果遠重要於這感受不到的速度差異。
可以參考 StackOverflow 的討論串