MySQL 如何完整、精確支援現代多國語系

前陣子在調整翻譯相關功能時,該來的還是來了,面對多國語系。

狀況

德語

原本的 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,依照團隊開發習慣,使用 cics 就好,關鍵在中間的 general / unicode

  • unicode: 查詢資料的排序規則按照 unicode 編碼
  • general: 簡化過後的排序規則,理論上查詢速度會比較快

但是!!!

這是古老時候的資訊了,隨著硬體高速發展,現在不值得使用 general 了!!我只能說,隨便一個 join 沒寫好的效能損耗都遠高於上述兩種差異。

對於有需求使用準確多國語系的服務,精確的結果遠重要於這感受不到的速度差異。
可以參考 StackOverflow 的討論串

Reference

  • 作者: MingYi Chou
  • 版權聲明: 轉載不用問,但請註明出處!本網誌均採用 BY-NC-SA 許可協議。