在生产环境中,数据库字符集由于各类缘由须要升级,好比为了支持汉字,从latin1字符集升级到GBK,后面为了支持多个语言文字,须要将GBK升级到UTF8等。迁移过程网上有不少,我今天主要想讲下字符集转换后,可能对业务产生的影响,我以GBK转换到UTF8为例说明。主要有两点:mysql
这篇文章主要为了说明编码转换后,字段排序如何受影响,会结合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 +------+---------+ |
从上面操做返回的结果咱们能够获得如下几点信息:
原理分析:
Mysql利用sortcmp函数对字符串进行比较,对于GBK的字符串和UTF8的字符串分别采用接口my_strnncollsp_gbk和my_strnncollsp_utf8比较,这两个函数分别在ctype-gbk.c和ctype-utf8.c中实现,两个函数实现逻辑相似,只是各有本身一套比较大小的规则,下面我主要描述下my_strnncollsp_utf8的比较逻辑和比较大小的规则。
比较逻辑:
附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)的风险,主要是索引或主键中包含字符串字段须要特别关注,若是字符串中肯定只包含有数字和字符,则不会存在问题。