字符集GBK公升級UTF8

2022-02-02 12:57:00 字數 2711 閱讀 7130

在生產環境中,資料庫字符集因為各種原因需要公升級,比如為了支援漢字,從latin1字符集公升級到gbk,後面為了支援多個語言文字,需要將gbk公升級到utf8等。遷移過程網上有很多,我今天主要想講下字符集轉換後,可能對業務產生的影響,我以gbk轉換到utf8為例說明。主要有兩點:

漢字在gbk編碼中佔2個位元組,在utf8編碼中佔3個位元組,而mysql的索引要求總長度不超過767個位元組,因此索引字元數會被縮短(383->255),特別的,對於唯一索引,要求索引字段長度小於256個字元。

編碼轉換後,導致字段排序發生變化。

這篇文章主要為了說明編碼轉換後,字段排序如何受影響,會結合mysql源

**給出原因和分析。首先看測試用例,假設cmp_t(gbk編碼)和cmp_t2(utf8編碼)分別是遷移前後的表。

測試用例:

操作cmp_t(gbk)

cmp_t2(utf8)

1gbk表:

select c1,hex(c1) from cmp_t;

utf8表:

select c1,hex(c1) from cmp_t2;

+------+---------+

| c1   | hex(c1) |

+------+---------+

| 一  | d2bb   

| 二  | b6fe

| 三  | c8fd   

| a    | 61     

| 1    | 31

+------+---------+     

+------+---------+

| c1   | hex(c1) |

+------+---------+

| 一  | e4b880 

| 二  | e4ba8c 

| 三  | e4b889 

| a    | 61     

| 1    | 31     

+------+---------+

2gbk表:

select c1,hex(c1) from cmp_t where c1>』a』 order by c1;

utf8表:

select c1,hex(c1) from cmp_t2 where c1>』a』 order by c1;

+------+---------+

| c1   | hex(c1) |

+------+---------+

| 二  | b6fe    |

| 三  | c8fd   

| 一  | d2bb   

+------+---------+

+------+---------+

| c1   | hex(c1) |

+------+---------+

| 一  | e4b880 

| 三  | e4b889 

| 二  | e4ba8c 

+------+---------+

從上面操作返回的結果我們可以得到以下幾點資訊:

漢字在gbk編碼中佔2個位元組,在utf8編碼中佔3個位元組

數字和英文本元在gbk和utf8編碼中都只佔乙個位元組

漢字在utf8編碼和gbk編碼不同,排序後順序變化了。

原理分析:

mysql利用sortcmp函式對字串進行比較,對於gbk的字串和utf8的字串分別採用介面my_strnncollsp_gbk和my_strnncollsp_utf8比較,這兩個函式分別在ctype-gbk.c和ctype-utf8.c中實現,兩個函式實現邏輯類似,只是各有自己一套比較大小的規則,下面我主要描述下my_strnncollsp_utf8的比較邏輯和比較大小的規則。

比較邏輯:

獲取字串的第乙個位元組

根據utf8的編碼規則(附1),確定字元由幾個位元組組成

根據一定的演算法算出字元的加權值(附2),比較大小;若不符合utf8編碼規範,認為是亂碼,採用二進位制比較(附3)

跳過步驟2返回的位元組數,比較下乙個字元。

附1:【介面: my_utf8_uni】

根據utf8編碼規則,符合編碼規範的字元占用1-6個位元組。

取字串第乙個位元組 s

if s<0x80

表示字元佔1個位元組

elif s<0xe0

表示字元佔2個位元組

elif s<0xf0  

表示字元佔3個位元組

else s<0xf8

表示字元佔4個位元組

elif s<0xfc

表示字元佔5個位元組

elif s<0xfe

表示字元佔6個位元組

英文本元和數字字元編碼相容ascii,編碼值小於0x80,因此都只佔1個位元組;漢字的utf8編碼的首位元組都在[0xe0,0xf0]之間,所以佔3個位元組。

附2:utf8編碼比較大小規則

value = ((s[0] & 0x0f) << 12)| ((s[1] ^ 0x80) << 6) | (s[2] ^ 0x80)

s[0],s[1],s[2]表示組成漢字的三個位元組,對參與比較的漢子字元進行計算得到value1和value2,通過比較value1和value2的大小,判斷字元大小。

附3:二進位制比較【介面: bincmp】

memcmp函式比較,即逐字節比較。

因此,如果業務上面需要依賴漢字比較的場景,需要考慮字符集公升級(gbk->utf8)的風險,主要是索引或主鍵中包含字串字段需要特別關注,如果字串中確定只包含有數字和字元,則不會存在問題。

字符集GBK和UTF8的區別說明

gbk就是在儲存你的帖子的時候,乙個漢字占用兩個位元組。外國人看會出現亂碼,此為我中華為自己漢字編碼而形成之解決方案。utf8就是在儲存你的帖子的時候,乙個漢字占用3個位元組。但是外國人看的話不會亂碼,此為西人為了解決多位元組字元而形成之解決方案。ascii iso 8859 1 是鼻祖,最簡單的方...

字符集GBK和UTF8的區別說明

gbk就是在儲存你的帖子的時候,乙個漢字占用兩個位元組。外國人看會出現亂碼,此為我中華為自己漢字編碼而形成之解決方案。utf8就是在儲存你的帖子的時候,乙個漢字占用3個位元組。但是外國人看的話不會亂碼,此為西人為了解決多位元組字元而形成之解決方案。ascii iso 8859 1 是鼻祖,最簡單的方...

字符集GBK和UTF8的區別說明

gbk就是在儲存你的帖子的時候,乙個漢字占用兩個位元組。外國人看會出現亂碼,此為我中華為自己漢字編碼而形成之解決方案。utf8就是在儲存你的帖子的時候,乙個漢字占用3個位元組。但是外國人看的話不會亂碼,此為西人為了解決多位元組字元而形成之解決方案。ascii iso 8859 1 是鼻祖,最簡單的方...