我utf8用得好好的為什麼要多一個utf8mb4?
一般情況下,使用MySQL的utf8就夠用了,但隨著網路資訊的快速進步,需求越來越多元,有些特殊漢字或是常用的emoji(一種特殊的Unicode編碼,在ios跟安卓手機都會看過),這其實都是需要使用四個位元組才能完整表現的,相對MySQL支援的utf8只有三個位元組,大部分的中文漢字雖然夠用,但是卻無法滿足到所有的字元,若是要使用特殊漢字或是表情符號,就無法使用MySQL的utf8字符集儲存,造成錯誤。
UTF-8原本不就支援四個位元組嗎?為什在MySQL裡只能支援三個位元,還要多此一舉端出一個utf8mb4?
是的,你所知道的UTF-8字符集是支援最多四個位元組沒有錯,但因為MysSQL最初開發時,認為三個位元組就夠用了不需要支援到四個位元組,於是把utf8設定為最多支援三個位元組,並不是我們所熟知的UTF-8支援四位元組的字符集,可說是此MySQL utf8非彼UTF-8啊!!
這情況,導致在使用MySQL utf8編碼時使用到生僻字特殊漢字,或是表情符號時,會出錯誤插入失敗(因為只支援到三個位元組),以為是編碼錯誤,找了老半天竟然是系統不支援字符的窘境。
這個漏洞,一直到2010年才得以修復,MySQL端出了一個叫做utf8mb4的字符集來在MySQL內部使用,後面的mb4意思是most bytes 4,表示支援最多四個位元組啦,這樣就可以順利插入生僻字或是Emoji表情符號了。有趣的是官方端出utf8mb4時(這才是真正的UTF-8字符集)就統一將原本的utf8改名為utf8mb3了,也許是容易分辨吧!
那我如果想要從utf8轉換到utf8mb4會不會不相容,空間與速度的變化?
utf8與utf8mb4差別是在後者多了一個位元組,但兩者都是相同的儲存特性、相同的編碼,所以在升級到utf8mb4時,是不用擔心字元轉換出現亂碼或資料遺失的問題。官方資料也有針對這項疑問作出解釋:
For a BMP character, utf8mb4 and utf8mb3 have identical storage characteristics: same code values, same encoding, same length.
也就是強調不用擔心轉換字符的問題,程式編碼等都是相同的。
再者是utfmb4多一個位元組的儲存空間與速度的問題。
首先我們先來搞清楚什麼是char 與varchar,他們是一個資料庫型別的表示法,但有些不同,舉例來說,我要儲存 ‘nice ’ 若是設定char(10),就會按照原本的設定儲存10個字符,但若是設定varchar(10),就會自動去掉後面的四個空格,判斷儲存為4個字符。
所以說,使用char的話若是不確定字元長度,空間占比就會比較大,是不可變的長度,設定長度多少就會儲存多少,使用varchar的話會由型別自行判斷實際長度再去做儲存,空間會相對char節省不少。
而這邊要注意的是,省了空間不代表省了時間。
使用char時,不需要去判斷實際長度多少,程式可以快速地去做儲存,時間相對直接快速,反之使用varchar就需要花多一點的時間去做判斷長度、刪除多於空格,再做儲存,空間效率高但犧牲掉時間成本。
回到utf8mb4,因為多一個字節,儲存空間相對變大,若是要節省空間就可以使用上述所說明的varchar替代char來儲存,不但可以滿足支援多種字符的要求也可以節省空間,小缺點則是可能多花一些時間,這部分可以按照使用者需要的狀況去做調整。不過這項問題MySQL也在版本8.0時做出了效能的優化,並且將舊的utf8mb3設定為deprecated,未來將會被移除掉,直接預設utf8mb4為預設的編碼,效能因為全面的優化,也比utf8mb3快了許多,讓使用者可以不但省空間也可以有效率的使用utf8mb4字符集。
utf8mb4_unicode_ci vs utf8mb4_general_ci
xxx_general_ci與xxx_unicode_ci 是一種連線校對的規則,這兩項的差別,也是很多人在操作時不知道如何選擇的地方,在這邊會詳細介紹他的功能和兩者的差別,幫助你選擇。
general版本比unicode來的快速,但是相對的準確率沒有unicode來的精準,會存在一些的錯誤,原因是因為Unicode支持擴展字符集,他的文字涵蓋率可說無論全球哪種文字,只要保存成Unicode編碼都可以正常解釋,但也因為編碼體積比較大,佔電腦空間較多,所以速度會稍微慢一點;general不支持擴展字集,是比較古老的校對規則,準確率沒有這麼高但是速度也相對快速。
這兩項連線校對規則最經典的不同就是發生在以下這個字元「ß」
若在utf8_general_ci 他會顯示為ß = s
在utf8_unicode_ci 則會顯示為ß = ss
而在MySQL官方網站則將這現象做了一個總結方便使用者做選擇:
utf8_general_ci also is satisfactory for both German and French, except that ß is equal to s, and not to ss. If this is acceptable for your application, you should use utf8_general_ci because it is faster. If this is not acceptable (for example, if you require German dictionary order), use utf8_unicode_ci because it is more accurate.
因為在德文與法文翻譯時,general會出現小錯誤,但官方認為若是一般使用可以選擇general版本,因為速度較快,建議若是有字典,需要正確率高的文字需求,則建議使用準確率極高的Unicode版本。
而前述都是舉例原本的utf8做解釋,若套用成utf8mb4_general_ci或是utf8mb_unicode_ci的連線校對也是跟utf8的舉例一樣的,只是就如同前面所說的,生僻字與特殊符號能否正確顯示的差別而已。
這邊再做出濃縮重點整理複習一下
MySQL utf8無法顯示生僻字與Emoji表情符號,utf8mb4可以
原本MySQL內的utf8不是一般的UTF-8,他只有三個位元組,utf8mb4才是四個位元組,是真正的UTF-8字符集。
utf8轉utf8mb4不會發生不相容的問題。
utf8mb4_unicode_ci 準確但比較慢utf8mb4_general_ci 速度快但相對不準確。
看完文章後,是不是更了解了呢?
趕快分享給身邊還在霧煞煞的朋友吧!
Comments