字符集GBK升級UTF8

     在生產環境中,數據庫字符集由於各類緣由須要升級,好比爲了支持漢字,從latin1字符集升級到GBK,後面爲了支持多個語言文字,須要將GBK升級到UTF8等。遷移過程網上有不少,我今天主要想講下字符集轉換後,可能對業務產生的影響,我以GBK轉換到UTF8爲例說明。主要有兩點:mysql

  1. 漢字在GBK編碼中佔2個字節,在UTF8編碼中佔3個字節,而mysql的索引要求總長度不超過767個字節,所以索引字符數會被縮短(383->255),特別的,對於惟一索引,要求索引字段長度小於256個字符。
  2. 編碼轉換後,致使字段排序發生變化。

這篇文章主要爲了說明編碼轉換後,字段排序如何受影響,會結合mysql源代碼給出緣由和分析。首先看測試用例,假設cmp_t(GBK編碼)和cmp_t2(UTF8編碼)分別是遷移先後的表。算法

測試用例:sql

 

操做數據庫

cmp_t(GBK)函數

cmp_t2(UTF8)測試

1編碼

 

GBK表:spa

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     

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

2

GBK表:

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 

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

 

從上面操做返回的結果咱們能夠獲得如下幾點信息:

  1. 漢字在GBK編碼中佔2個字節,在UTF8編碼中佔3個字節
  2. 數字和英文字符在GBK和UTF8編碼中都只佔一個字節
  3. 漢字在UTF8編碼和GBK編碼不一樣,排序後順序變化了。

原理分析:

Mysql利用sortcmp函數對字符串進行比較,對於GBK的字符串和UTF8的字符串分別採用接口my_strnncollsp_gbk和my_strnncollsp_utf8比較,這兩個函數分別在ctype-gbk.c和ctype-utf8.c中實現,兩個函數實現邏輯相似,只是各有本身一套比較大小的規則,下面我主要描述下my_strnncollsp_utf8的比較邏輯和比較大小的規則。

比較邏輯:

  1. 獲取字符串的第一個字節
  2. 根據UTF8的編碼規則(附1),肯定字符由幾個字節組成
  3. 根據必定的算法算出字符的加權值(附2),比較大小;若不符合UTF8編碼規範,認爲是亂碼,採用二進制比較(附3)
  4. 跳過步驟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)的風險,主要是索引或主鍵中包含字符串字段須要特別關注,若是字符串中肯定只包含有數字和字符,則不會存在問題。

相關文章
相關標籤/搜索