在MySQL中,不要使用“utf8”。使用“utf8mb4”

銀河1號發表於2019-04-03
今天的錯誤:我試圖將一個UTF-8字串儲存在MariaDB“utf8”編碼的資料庫中,並且引發了一個奇怪的錯誤:
Incorrect string value: ‘\xF0\x9F\x98\x83 <…’ for column ‘summary’ at row 1複製程式碼
這是UTF-8客戶端和UTF-8伺服器,位於UTF-8資料庫中,具有UTF-8編碼規則。字串“?”是有效的UTF-8。
但問題是:MySQL的“ utf8 ” 不是UTF-8。
“utf8”編碼僅支援每個字元三個位元組。真正的UTF-8編碼 - 每個人都使用,包括你 - 每個字元最多需要四個位元組。
MySQL開發人員從未修復過這個bug。他們在2010年釋出了一個解決方法:一個名為“ utf8mb4 ” 的新字符集。
當然,他們從未公佈過這個(可能是因為這個bug太尷尬了)。現在,Web上的指南建議使用者使用“utf8”。所有這些指南都是錯誤的。
簡而言之:
· MySQL的“utf8mb4”表示“UTF-8”。
· MySQL的“utf8”意味著“專有字元編碼”。此編碼不能編碼許多Unicode字元。
我將在這裡做一個徹底的陳述:目前使用“utf8”的所有 MySQL和MariaDB使用者實際上應該使用“utf8mb4”。沒有人應該使用“utf8”。

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

)。我會縮減它。
Computer(計算機)將文字儲存為1和0。本段中的第一個字母儲存為“01000011”,你的計算機顯示為“C”。你的計算機分兩步選擇“C”:
1. 你的計算機讀取“01000011”並確定它是數字67.這是因為67被編碼為“01000011”。
2. 你的計算機在Unicode 字符集中查詢字元編號67 ,並且發現67表示“C”。
當我鍵入“C”時,我的結果發生了同樣的事情:
1. 我的計算機將Unicode字符集中的“C”對映到67。
2. 我的計算機編碼為 67,向此Web伺服器傳送“01000011”。
字符集是一個解決的問題。幾乎網際網路上的每個程式都使用Unicode字符集,因為沒有動機使用另一個。
但編碼更像是一種判斷。Unicode具有超過一百萬個字元的插槽。(C和“?”是兩個字元)
最簡單的編碼(utf-32)使每個字元佔用32位。這很簡單,因為計算機已經把32位的組當作數字處理了很多年,而且他們真的很擅長。但它沒用:這是浪費空間。
UTF-8節省空間。在UTF-8中,像“C”這樣的常見字元佔8位,而像“其他字元需要16或24位。像這樣的部落格文章在UTF-8中佔用的空間比在UTF-32中少四倍。所以載入速度快四倍。
你可能沒有意識到,但我們的計算機在幕後同意了UTF-8。如果他們沒有,然後當我輸入
“?”時,你會看到一堆隨機資料。
MySQL的“utf8”字符集與其他程式不一致。當他們說“?”時,它會猶豫。

一點MySQL的歷史

為什麼MySQL開發人員使“utf8”無效?我們可以通過檢視提交日誌來猜測。
MySQL從版本4.1開始支援UTF-8 。那是2003年 - 在今天的UTF-8標準之前,RFC 3629。
以前的UTF-8標準RFC 2279每個字元最多支援6個位元組。MySQL開發人員在2002年3月28日的MySQL 4.1的第一個預發行版本中編寫了RFC 2279 。
然後在9月對MySQL的原始碼進行了一次神祕的,一位元組的調整:“UTF8現在只能處理3個位元組的序列。”
是誰提交了這個?為什麼?我說不出來。MySQL的程式碼庫在採用Git時似乎丟失了舊的作者名稱。(MySQL過去常常使用BitKeeper,就像Linux核心一樣。)2003年9月左右的郵件列表中沒有任何內容可以解釋這一變化。
但我可以猜到。
早在2002年,如果使用者可以保證表中的每一行具有相同的位元組數,MySQL就會為使用者提供速度提升。為此,使用者會將文字列宣告為“CHAR”。“CHAR”列始終具有相同的字元數。如果你輸入的字元太少,它會在末尾新增空格; 如果你輸入太多的字元,它會截斷最後的字元。
當MySQL開發人員第一次嘗試使用UTF-8時,每個字元的後六個位元組,他們可能會猶豫不決:一個CHAR(1)列需要六個位元組; CHAR(2)列需要12個位元組; 等等。
讓我們明確一點:從未釋出的初始行為是正確的。它得到了很好的記錄和廣泛採用,任何理解UTF-8的人都會同意這是正確的。
但顯然,MySQL開發人員(或商人)擔心一兩個使用者會做兩件事:
1.選擇CHAR列。(CHAR格式現在是遺物。當時,使用CHAR列,MySQL速度更快。直到2005年,它不是。)
2.選擇將這些CHAR列編碼為“utf8”。
我的猜測是MySQL開發人員打破了他們的“utf8”編碼來幫助這些使用者:1)試圖優化空間和速度的使用者; 2)未能優化速度和空間。
沒人贏。想要速度和空間的使用者使用“utf8”CHAR列仍然是錯誤的,因為那些列仍然比它們原來更大更慢。想要正確性的開發人員使用“utf8”是錯誤的,因為它無法儲存
“?”
一旦MySQL釋出了這個無效的字符集,它就永遠無法解決它:這將迫使每個使用者重建每個資料庫。MySQL最終在2010年釋出了UTF-8支援,名稱不同:“utf8mb4”。

為什麼這麼令人沮喪

很明顯,本週我很沮喪。我的bug很難找到,因為我被“utf8”這個名字所迷惑。而且我不是唯一一個 - 我在網上發現的幾乎所有文章都將“utf8”稱為UTF-8。
名稱“utf8”始終是錯誤的。這是一個專有的字符集。它創造了新問題,並沒有解決它要解決的問題。
這是虛假的廣告。
My take-away lessons
1.Database systems have subtle bugs and oddities, and you can avoid a lot of bugs by avoiding database systems.
2.If you need a database, don’t use MySQL or MariaDB. Use PostgreSQL.
3.If you need to use MySQL or MariaDB, never use “utf8”. Always use “utf8mb4” when you want UTF-8. Convert your database now to avoid headaches later.
更多文章歡迎訪問: http://www.apexyun.com
公眾號:銀河系1號
聯絡郵箱:public@space-explore.com
(未經同意,請勿轉載)

相關文章