utf8mb4 和 utf8 是 MySQL 中兩種常用的字符集,它們都可以用來(lái)存儲(chǔ) Unicode 字符,但是有一些區(qū)別和聯(lián)系。本文將從以下幾個(gè)方面對(duì)比 utf8mb4 和 utf8:
編碼范圍和存儲(chǔ)空間
utf8 是 MySQL 中最早支持的 Unicode 字符集,它使用 1 到 3 個(gè)字節(jié)來(lái)編碼每個(gè)字符,最大能表示的 Unicode 碼點(diǎn)是 U+FFFF,也就是 Unicode 的基本多文種平面(BMP)。這意味著 utf8 不能存儲(chǔ)一些超出 BMP 的字符,例如 Emoji 表情、部分罕用漢字、新增的 Unicode 字符等。這些字符需要 4 個(gè)字節(jié)來(lái)編碼,所以 utf8 會(huì)在遇到這些字符時(shí)報(bào)錯(cuò)或者出現(xiàn)亂碼。
utf8mb4 是 MySQL 在 5.5.3 版本之后增加的一個(gè)新的字符集,它是 utf8 的超集,也就是說(shuō) utf8 可以表示的字符 utf8mb4 都可以表示,而且 utf8mb4 還可以表示一些 utf8 不能表示的字符。utf8mb4 使用 1 到 4 個(gè)字節(jié)來(lái)編碼每個(gè)字符,最大能表示的 Unicode 碼點(diǎn)是 U+10FFFF,也就是 Unicode 的所有 17 個(gè)平面。這意味著 utf8mb4 可以存儲(chǔ)任何合法的 Unicode 字符,包括 Emoji 表情、部分罕用漢字、新增的 Unicode 字符等。
由于 utf8mb4 可以使用 4 個(gè)字節(jié)來(lái)編碼字符,所以它占用的存儲(chǔ)空間會(huì)比 utf8 略大一些。例如,一個(gè) CHAR(10) 類型的字段,如果使用 utf8 字符集,那么它需要保留 10 * 3 = 30 個(gè)字節(jié)的空間;如果使用 utf8mb4 字符集,那么它需要保留 10 * 4 = 40 個(gè)字節(jié)的空間。對(duì)于 VARCHAR 類型的字段,如果使用 utf8 字符集,那么它需要額外使用一個(gè)字節(jié)來(lái)記錄字符串的長(zhǎng)度;如果使用 utf8mb4 字符集,那么它需要額外使用兩個(gè)字節(jié)來(lái)記錄字符串的長(zhǎng)度。
排序規(guī)則和性能
排序規(guī)則(collation)是指在比較和排序字符串時(shí)所遵循的規(guī)則。不同的字符集可以有不同的排序規(guī)則,甚至同一個(gè)字符集也可以有多種排序規(guī)則。MySQL 中常見(jiàn)的排序規(guī)則有以下幾種:
- _bin:按照二進(jìn)制方式比較字符串,區(qū)分大小寫(xiě)和重音符號(hào)。
- _general_ci:按照一般方式比較字符串,不區(qū)分大小寫(xiě)和重音符號(hào)。
- _unicode_ci:按照 Unicode 標(biāo)準(zhǔn)方式比較字符串,不區(qū)分大小寫(xiě)和重音符號(hào)。
- _ci:按照特定語(yǔ)言或地區(qū)方式比較字符串,不區(qū)分大小寫(xiě)和重音符號(hào)。
utf8 和 utf8mb4 都有以上幾種排序規(guī)則,但是有一些細(xì)微的差別。例如,在 utf8_general_ci 排序規(guī)則下,'a' 等于 'A',而在 utf8mb4_general_ci 排序規(guī)則下,'a' 小于 'A'。這是因?yàn)?utf8_general_ci 排序規(guī)則只考慮了 BMP的字符,而 utf8mb4_general_ci 排序規(guī)則考慮了所有的 Unicode 字符。因此,在 utf8mb4_general_ci 排序規(guī)則下,'a' 的 Unicode 碼點(diǎn)是 U+0061,而 'A' 的 Unicode 碼點(diǎn)是 U+0041,所以 'a' 小于 'A'。
排序規(guī)則的不同會(huì)影響字符串的比較和排序的結(jié)果,進(jìn)而影響索引的效率和查詢的性能。一般來(lái)說(shuō),_bin 排序規(guī)則的性能最高,因?yàn)樗恍枰凑斩M(jìn)制方式比較字符串,不需要考慮字符的大小寫(xiě)和重音符號(hào)等因素。
_general_ci 排序規(guī)則的性能次之,因?yàn)樗恍枰凑找话惴绞奖容^字符串,不需要考慮字符的語(yǔ)言或地區(qū)等因素。*_unicode_ci 和 *_ci 排序規(guī)則的性能最低,因?yàn)樗鼈冃枰凑?Unicode 標(biāo)準(zhǔn)或特定語(yǔ)言或地區(qū)的方式比較字符串,需要考慮字符的大小寫(xiě)和重音符號(hào)等因素。
兼容性和安全性
utf8 和 utf8mb4 的兼容性和安全性也有一些區(qū)別。由于 utf8mb4 是 utf8 的超集,所以從 utf8 切換到 utf8mb4 一般不會(huì)有問(wèn)題,只需要注意存儲(chǔ)空間的增加和排序規(guī)則的變化。但是從 utf8mb4 切換到 utf8 就可能會(huì)有問(wèn)題,因?yàn)?utf8mb4 可能包含一些 utf8 不能表示的字符,這些字符在切換后會(huì)被丟棄或者轉(zhuǎn)換成問(wèn)號(hào)等符號(hào)。
另外,utf8mb4 也比 utf8 更安全,因?yàn)樗梢苑乐挂恍阂獾墓簟@纾幸环N攻擊叫做 UTF-8 編碼注入攻擊(UTF-8 Encoding Injection Attack),它是利用 MySQL 中 utf8 字符集對(duì) 4 字節(jié)字符的處理方式來(lái)繞過(guò)一些安全檢查的。
具體來(lái)說(shuō),當(dāng) MySQL 遇到一個(gè) 4 字節(jié)的 UTF-8 字符時(shí),它會(huì)把它拆分成兩個(gè) 2 字節(jié)的字符,并且忽略第一個(gè)字符。這樣就可能導(dǎo)致一些原本不合法或者不安全的字符串變成合法或者安全的字符串。
例如,假設(shè)有一個(gè)字符串是 "xF0x90x80xE2x80xAEabc",它實(shí)際上包含了一個(gè) 4 字節(jié)的 UTF-8 字符 U+10400 和一個(gè)右至左覆蓋符號(hào) U+202E。如果使用 utf8 字符集來(lái)存儲(chǔ)這個(gè)字符串,那么 MySQL 會(huì)把它拆分成 "xC0x80xE2x80xAEabc",并且忽略第一個(gè)字符 "xC0x80"。這樣就相當(dāng)于把右至左覆蓋符號(hào) U+202E 插入到了字符串中,從而改變了字符串的顯示方向。
這可能會(huì)被用來(lái)進(jìn)行釣魚(yú)或者欺騙等攻擊。如果使用 utf8mb4 字符集來(lái)存儲(chǔ)這個(gè)字符串,那么 MySQL 會(huì)保留原始的字符串,并且報(bào)錯(cuò)或者出現(xiàn)亂碼。
結(jié)論
utf8mb4 和 utf8 都可以用來(lái)存儲(chǔ) Unicode 字符,但是 utf8mb4 支持更廣泛的字符范圍,能夠存儲(chǔ) Emoji 表情、罕用漢字、新增的 Unicode 字符等。utf8mb4 比 utf8 占用的存儲(chǔ)空間略大一些,但是在性能和安全性方面更優(yōu)。因此,如果需要支持更廣泛的字符范圍,或者需要更高的安全性和兼容性,那么應(yīng)該使用 utf8mb4 字符集。當(dāng)然,在選擇字符集的時(shí)候,還需要考慮具體的業(yè)務(wù)需求和實(shí)際情況,選擇最合適的字符集才是最重要的。