在生产环境中,数据库字符集因为各种原因需要升级,比如为了支持汉字,从latin1字符集升级到GBK,后面为了支持多个语言文字,需要将GBK升级到UTF8等。迁移过程网上有很多,我今天主要想讲下字符集转换后,可能对业务产生的影响,我以GBK转换到UTF8为例说明。主要有两点:

  1. 汉字在GBK编码中占2个字节,在UTF8编码中占3个字节,而mysql的索引要求总长度不超过767个字节,因此索引字符数会被缩短(383->255),特别的,对于唯一索引,要求索引字段长度小于256个字符。
  2. 编码转换后,导致字段排序发生变化。

这篇文章主要为了说明编码转换后,字段排序如何受影响,会结合mysql源代码给出原因和分析。首先看测试用例,假设cmp_t(GBK编码)和cmp_t2(UTF8编码)分别是迁移前后的表。

测试用例:

操作

cmp_t(GBK)

cmp_t2(UTF8)

1

GBK表:

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)的风险,主要是索引或主键中包含字符串字段需要特别关注,如果字符串中确定只包含有数字和字符,则不会存在问题。

字符集GBK升级UTF8的更多相关文章

  1. MySQL字符集 GBK、GB2312、UTF8区别 解决 MYSQL中文乱码问题 收藏 MySQL中涉及的几个字符集

    MySQL中涉及的几个字符集 character-set-server/default-character-set:服务器字符集,默认情况下所采用的.character-set-database:数据 ...

  2. PHP通过iconv将字符串从GBK转换为UTF8字符集

    PHP通过iconv将字符串从GBK转换为UTF8字符集的方法,需要的朋友可以参考下. 1. iconv()介绍 iconv函数可以将一种已知的字符集文件转换成另一种已知的字符集文件.例如:从GB23 ...

  3. GB2312、GBK和UTF-8三种编码以及QT中文显示乱码问题

    1.GB2312.GBK和UTF-8三种编码的简要说明 GB2312.GBK和UTF-8都是一种字符编码,除此之外,还有好多字符编码.只是对于我们中国人的应用来说,用这三种编码 比较多.简单的说一下, ...

  4. ASCII、Unicode、GBK和UTF-8字符编码的区别联系(转载)

    ASCII.Unicode.GBK和UTF-8字符编码的区别联系 转载自:http://dengo.org/archives/901 很久很久以前,有一群人,他们决定用8个可以开合的晶体管来组合成不同 ...

  5. 字符编码GB2312、GBK、UTF-8的区别

    本文来自:javaeye网站 UTF8是国际编码,它的通用性比较好,外国人也可以浏览论坛 GBK是国家编码,通用性比UTF8差,不过UTF8占用的数据库比GBK大~ 提示:如果您的网站客户群体主要是面 ...

  6. 网络编码 GB2312、GBK与UTF-8的区别

    GB2312.GBK与UTF-8的区别  这是一个异常经典的问题,有无数的新手站长每天都在百度这个问题,而我,作为一个“伪老手”站长,在明白这个这个问题的基础上,有必要详细的解答一下.  首先,我们要 ...

  7. 解决mysql中表字符集gbk,列字符集Latin1,python查询乱码问题

    最近在公司碰到一个异常蛋疼的情况,mysql数据库中,数据库和表的字符集都是'gbk',但是列的字符集却是'latin1',于是蛋疼的事情出现了. 无论我连接字符串的`charset`设置为`gbk` ...

  8. 【JAVA编码专题】JAVA字符编码系列一:Unicode,GBK,GB2312,UTF-8概念基础

    这两天抽时间又总结/整理了一下各种编码的实际编码方式,和在Java应用中的使用情况,在这里记录下来以便日后参考. 为了构成一个完整的对文字编码的认识和深入把握,以便处理在Java开发过程中遇到的各种问 ...

  9. 告别乱码,针对GBK、UTF-8两种编码的智能URL解码器的java实现(转)

    效果图 字符 字符是早于计算机而存在,从人类有文明那时起,人们就用一个个符号代表世间万象.如ABC,如“一.二.三”. 字符集 字符集是所有字符的集合. XXX字符集 给字符集中的每一个字符套上一个序 ...

随机推荐

  1. form表单及其中元素

    <form method=get/post action="提交路径"> 单行文本域:<input type="text" name=&quo ...

  2. Java集合源码分析(七)HashMap<K, V>

    一.HashMap概述 HashMap基于哈希表的 Map 接口的实现.此实现提供所有可选的映射操作,并允许使用 null 值和 null 键.(除了不同步和允许使用 null 之外,HashMap  ...

  3. PHP内核探索之变量(5)- session的基本原理

    这次说说session. session可以说是当前互联网提到的最多的名词之一了.它的含义很宽泛,可以指任何一次完整的事务交互(会话):如发送一次HTTP请求并接受响应,执行一条SQL语句都可以看做一 ...

  4. 设计模式/原则篇- Unit of Work

    概念 Unit of Work 即工作单元. 用来维护一组受业务影响的对象列表,将多个操作放在一个单元中,把操作原子化,通过事务统一完成一次提交,如果某个过程出现异常,就将所有修改进行回滚,保证数据的 ...

  5. 【精心推荐】几款极好的 JavaScript 文件上传插件

    文件上传功能作为网页重要的组成部分,几乎无处不在,从简单的单个文件上传到复杂的批量上传.拖放上传,需要开发者花费大量的时间和精力去处理,以期实现好用的上传功能.这篇文章向大家推荐几款很棒的 JavaS ...

  6. sublime text3 之snippet编写代码片段

    sublime text 3 中有个强大的功能就是可以编写各种文件类型的snippet代码片段,可以节省大量的时间. 文件名为:jekyll-top.sublime-snippet(.sublime- ...

  7. jquery easyui tabs单击刷新右键刷新

    单击刷新 $(".tabs-inner").click(function(){var currTab = self.parent.$('#tabs').tabs('getSelec ...

  8. [读码]HTML5像素文字爆炸重组

    [边读码,边学习,技术也好,思路也罢] [一款基于HTML5技术的文字像素爆炸重组动画特效,我们可以在输入框中指定任意文字,点击确定按钮后,就会将原先的文字爆炸散去,新的文字以像素点的形式组合起来,看 ...

  9. 杭电acm2029-Palindromes _easy version

    Problem Description “回文串”是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就是回文串.请写一个程序判断读入的字符串是否是“回文”.   Input 输入包 ...

  10. CSS基础知识之文本属性二三事

    line-height 可以给某个元素指定一个不带单位的缩放因子,这样它的后代元素就会继承这个缩放因子,再根据自身的字号大小来计算自己的行高(line-height)值, body { font-si ...