zoukankan      html  css  js  c++  java
  • [MySQL] 字符集

    这阵子在做MySQL字符集改造(gbk->utf8)的一些事情, 以及业务上乱码数据的清理工作, 借此机会梳理了下字符集相关的概念, 做个总结.

    1 字符集基础概念

    1.1 常见字符集

    • Latin-1, 全称ISO 8859-1, Latin-1对ASCII的拉丁语扩展, 向下兼容ASCII, 其编码范围是0x00-0xFF, 0x00-0x7F之间完全和ASCII一致, 0x80-0x9F之间是控制字符, 0xA0-0xFF之间是文字符号.

    • ASCII, 0x00-0x7f.

    • Unicode表.

    • UTF8, Unicode表的一种实现.

      Unicode都是2字节, 用来存储ASCII的效率很低, 要比ASCII编码多处一倍的空间.

      UTF8的范围是1-6个字节

      GBK->UTF8: 需要Unicode转换

        GBK查表 -> Unicode转换 -> UTF8
      

      在java中一切以Unicode为基准, Char a = '诺' 和 Char b = 'N'都是可以的, 一个Char两个字节就是可以表示的Unicode. 如果要表示复杂的文字, 那就需要Char数组.

      Unicode->UTF8:

        1. 确定UTF8编码的字节数
        2. 用Unicode编码从地位到高位依次按规则填入空位, 不足的高位以0补充
      

    MySQL编码

    1. MySQL字符参数

    +--------------------------+-----------------------------+
    | Variable_name            | Value                       |
    +--------------------------+-----------------------------+
    | character_set_client     | utf8                        |
    | character_set_connection | utf8                        |
    | character_set_database   | utf8                        |
    | character_set_filesystem | binary                      |
    | character_set_results    | utf8                        |
    | character_set_server     | utf8                        |
    | character_set_system     | utf8                        |
    | character_sets_dir       | /u01/my3406/share/charsets/ |
    +--------------------------+-----------------------------+
    
    • character_set_client 客户端来源数据字符集

    • character_set_connection 连阶层字符集

        MySQL使用character_set_connection字符集讲client端的字符串转换为character_set_connection表示的字符集
      
    • character_set_results 查询结果数据字符集

        MySQL将数据存储的字符集转换为character_set_results字符集返回给前端
      
    • character_set_database 当前数据库的字符集

    • character_set_server 数据库server字符集

    • character_set_system 系统字符集

        SET NAMES xxx;
        等价于
        set character_set_client = xxx;
        set character_set_connection = xxx;
        set character_set_results = xxx;
      

    2. MySQL字符集转换过程

    大致过程如下:

    • MySQL Server收到请求时, 将请求数据从character_set_client转换成character_set_connection;

    • 进行Server内部操作(譬如: 存储)时, 将请求数据从character_set_connection转换成内部字符集;

      MySQL内部字符集是有3层继承关系的:

      • 使用每个表数据字段的character set设定值;
      • 如没上述设定值, 则使用表的default character set设定值;
      • 如没上述设定值, 则使用库的character set设定值;
    • 将操作结果从内部字符集转换为character_set_results返回;

    3. MySQL乱码

    • hex定位column的16进制字符;

        select hex(column) from table_a;
      
    • 如果hex值包含一串3F, 那就说明彻底乱码了, 无法转换回去了;

    • 通过set names xxx; 以及调整终端字符集等手段转码;

    4. 几种乱码case

    • latin1->latin1->utf8

      • 向utf8的表里插入latin1的源数据

          set names latin1;
          即
          set character_set_client = latin1;
          set character_set_connection = latin1;
          set character_set_results = latin1;
        
      • 读取

          set names utf8;
          select name, hex(name) from renotest;
          +---------------+----------------------------+
          | name          | hex(name)                  |
          +---------------+----------------------------+
          | 雷诺        | C3A9E280BAC2B7C3A8C2AFC2BA |
          +---------------+----------------------------+
          
          set names latin1;
          select name, hex(name) from renotest;
          +--------+----------------------------+
          | name   | hex(name)                  |
          +--------+----------------------------+
          | 雷诺   | C3A9E280BAC2B7C3A8C2AFC2BA |
          +--------+----------------------------+
        
      • 乱码

          插入时, 汉字从原始的3个字节变成6个字节保存.
          
          读取时, utf8->utf8的字符集转换过程, 将保存的6字节原封不动的返回, 产生乱码.
        
    • utf8->utf8->latin1

      • 向latin1的表里插入utf8的源数据

          set names utf8;
          即
          set character_set_client = utf8;
          set character_set_connection = utf8;
          set character_set_results = utf8;
        
      • 读取

          set names utf8;
          select name, hex(name) from renotest2;
          +------+-----------+
          | name | hex(name) |
          +------+-----------+
          | ??   | 3F3F      |
          +------+-----------+
          
          set names latin1;
          select name, hex(name) from renotest2;
          +------+-----------+
          | name | hex(name) |
          +------+-----------+
          | ??   | 3F3F      |
          +------+-----------+
        
      • 乱码

          插入时, utf8->utf8->latin1的字符集转换,如果原始数据中含有u0000~u00ff范围以外的Unicode字符, 会因为无法在latin1字符集中表示而被转换为"?"(0x3F)符号.
          
          读取时, 无论怎么转换0x3F都没用, 彻底乱码了.
        

    5. 建议

    建议业务使用统一的字符集, 保证MySQL Server内部从server到database, 再到table, 再到column使用一套相同的字符编码.

  • 相关阅读:
    mongodb3.6 query plan机制变更导致慢查询问题排查
    zoj 3822 概率期望dp入门
    poj 4513 吉哥系列故事――完美队形II 最长回文子串
    poj 3974 Palindrome O(n)回文子串(Manacher)算法
    hdu 4405 Aeroplane chess 概率dp入门题
    hdu 5001 walk 概率dp入门题
    hdu 3586 Information Disturbing 树形dp+二分
    hdu 2296 Ring AC自动机+DP
    poj 3691 DNA repair AC自动机+DP
    hdu 1520 Anniversary party 树形dp水题
  • 原文地址:https://www.cnblogs.com/renolei/p/4451098.html
Copyright © 2011-2022 走看看