zoukankan      html  css  js  c++  java
  • 《MySQL是怎样运行的:从根儿上理解MySQL》笔记3

    第三节:字符集和比较规则

    -----------------------------------------------------------
    字符集和比较规则简介

    字符集
      在计算机中只能存储二进制数据,那该怎么存储字符串呢?当然是建立字符与二进制数据的映射关系了,建立这个关系最起码要搞清楚两件事儿:
        1.你要把哪些字符映射成二进制数据?也就是界定清楚字符范围。
        2.怎么映射?
      将一个字符映射成一个二进制数据的过程也叫做编码,将一个二进制数据映射到一个字符的过程叫做解码。

      人们抽象出一个字符集的概念来描述某个字符范围的编码规则。比方说我们来自定义一个名称为xiaohaizi的字符集,它包含的字符范围和编码规则如下:
    包含字符'a'、'b'、'A'、'B'。
      编码规则如下:
        采用1个字节编码一个字符的形式,字符和字节的映射关系如下:
        'a' -> 00000001 (十六进制:0x01)
        'b' -> 00000010 (十六进制:0x02)
        'A' -> 00000011 (十六进制:0x03)
        'B' -> 00000100 (十六进制:0x04)
      有了xiaohaizi字符集,我们就可以用二进制形式表示一些字符串了,下边是一些字符串用xiaohaizi字符集编码后的二进制表示:
        'bA' -> 0000001000000011 (十六进制:0x0203)
        'baB' -> 000000100000000100000100 (十六进制:0x020104)
        'cd' -> 无法表示,字符集xiaohaizi不包含字符'c'和'd'

      二进制比较规则,英文名为binary collation:直接比较这两个字符对应的二进制编码的大小。
      对于某一种字符集来说,比较两个字符大小的规则可以制定出很多种,也就是说同一种字符集可以有多种比较规则

    -----------------------------------------------------------
    一些重要的字符集

    ASCII字符集 (American Standard Code for Information Interchange)
      共收录128个字符,包括空格、标点符号、数字、大小写字母和一些不可见字符。由于总共才128个字符,所以可以使用1个字节来进行编码

    ISO 8859-1字符集
      共收录256个字符,是在ASCII字符集的基础上又扩充了128个西欧常用字符(包括德法两国的字母),也可以使用1个字节来进行编码。这个字符集也有一个别名latin1。

    GB2312字符集
      收录了汉字以及拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母。其中收录汉字6763个,其他文字符号682个。同时这种字符集又兼容ASCII字符集,所以在编码方式上显得有些奇怪:
        >如果该字符在ASCII字符集中,则采用1字节编码。
        >否则采用2字节编码。
      这种表示一个字符需要的字节数可能不同的编码方式称为变长编码方式。比方说字符串'爱u',其中'爱'需要用2个字节进行编码,编码后的十六进制表示为0xB0AE,'u'需要用1个字节进行编码,编码后的十六进制表示为0x75,所以拼合起来就是0xB0AE75。
      我们怎么区分某个字节代表一个单独的字符还是代表某个字符的一部分呢?别忘了`ASCII`字符集只收录128个字符,使用0~127就可以表示全部字符,所以如果某个字节是在0~127之内的,就意味着一个字节代表一个单独的字符,否则就是两个字节代表一个单独的字符。

    GBK字符集
      GBK字符集只是在收录字符范围上对GB2312字符集作了扩充,编码方式上兼容GB2312。

    utf8字符集
      收录地球上能想到的所有字符,而且还在不断扩充。这种字符集兼容ASCII字符集,采用变长编码方式,编码一个字符需要使用1~4个字节

    utf8mb3:阉割过的utf8字符集,只使用1~3个字节表示字符。
    utf8mb4:正宗的utf8字符集,使用1~4个字节表示字符。
      有一点需要大家十分的注意,在MySQL中utf8是utf8mb3的别名,所以之后在MySQL中提到utf8就意味着使用1~3个字节来表示一个字符,如果大家有使用4字节编码一个字符的情况,比如存储一些emoji表情啥的,那请使用utf8mb4。

    -----------------------------------------------------------
    字符集的查看

      SHOW (CHARACTER SET|CHARSET) [LIKE 匹配的模式];

    -----------------------------------------------------------
    比较规则的查看

      SHOW COLLATION [LIKE 匹配的模式];
      MySQL支持的字符集就已经非常多了,所以支持的比较规则更多,我们先只查看一下utf8mb4字符集下的比较规则:
      mysql> SHOW COLLATION LIKE 'utf8mb4\_%';

    +------------------------+---------+-----+---------+----------+---------+
    | Collation | Charset | Id | Default | Compiled | Sortlen |

    +------------------------+---------+-----+---------+----------+---------+
    | utf8mb4_general_ci | utf8mb4 | 45 | Yes | Yes | 1 |
    | utf8mb4_bin | utf8mb4 | 46 | | Yes | 1 |
    | utf8mb4_unicode_ci | utf8mb4 | 224 | | Yes | 8 |
    | utf8mb4_icelandic_ci | utf8mb4 | 225 | | Yes | 8 |
    | utf8mb4_latvian_ci | utf8mb4 | 226 | | Yes | 8 |
    | utf8mb4_romanian_ci | utf8mb4 | 227 | | Yes | 8 |
    | utf8mb4_slovenian_ci | utf8mb4 | 228 | | Yes | 8 |
    | utf8mb4_polish_ci | utf8mb4 | 229 | | Yes | 8 |
    | utf8mb4_estonian_ci | utf8mb4 | 230 | | Yes | 8 |
    | utf8mb4_spanish_ci | utf8mb4 | 231 | | Yes | 8 |
    | utf8mb4_swedish_ci | utf8mb4 | 232 | | Yes | 8 |
    | utf8mb4_turkish_ci | utf8mb4 | 233 | | Yes | 8 |
    | utf8mb4_czech_ci | utf8mb4 | 234 | | Yes | 8 |
    | utf8mb4_danish_ci | utf8mb4 | 235 | | Yes | 8 |
    | utf8mb4_lithuanian_ci | utf8mb4 | 236 | | Yes | 8 |
    | utf8mb4_slovak_ci | utf8mb4 | 237 | | Yes | 8 |
    | utf8mb4_spanish2_ci | utf8mb4 | 238 | | Yes | 8 |
    | utf8mb4_roman_ci | utf8mb4 | 239 | | Yes | 8 |
    | utf8mb4_persian_ci | utf8mb4 | 240 | | Yes | 8 |
    | utf8mb4_esperanto_ci | utf8mb4 | 241 | | Yes | 8 |
    | utf8mb4_hungarian_ci | utf8mb4 | 242 | | Yes | 8 |
    | utf8mb4_sinhala_ci | utf8mb4 | 243 | | Yes | 8 |
    | utf8mb4_german2_ci | utf8mb4 | 244 | | Yes | 8 |
    | utf8mb4_croatian_ci | utf8mb4 | 245 | | Yes | 8 |
    | utf8mb4_unicode_520_ci | utf8mb4 | 246 | | Yes | 8 |
    | utf8mb4_vietnamese_ci | utf8mb4 | 247 | | Yes | 8 |

    +------------------------+---------+-----+---------+----------+---------+
    26 rows in set (0.00 sec)

    说明:
      比较规则名称以与其关联的字符集的名称开头。如上图的查询结果的比较规则名称都是以utf8开头的。
      后边紧跟着该比较规则主要作用于哪种语言,比如utf8_polish_ci表示以波兰语的规则比较,utf8_spanish_ci是以西班牙语的规则比较,utf8_general_ci是一种通用的比较规则。
      名称后缀意味着该比较规则是否区分语言中的重音、大小写啥的,具体可以用的值如下:
      后缀 英文释义 描述
        _ai accent insensitive 不区分重音
        _as accent sensitive 区分重音
        _ci case insensitive 不区分大小写
        _cs case sensitive 区分大小写
        _bin binary 以二进制方式比较
      比如utf8_general_ci这个比较规则是以ci结尾的,说明不区分大小写。

    -----------------------------------------------------------
    字符集和比较规则的应用

      各级别的字符集和比较规则,MySQL有4个级别的字符集和比较规则,分别是:
        服务器级别
        数据库级别
        表级别
        列级别

    服务器级别
      MySQL提供了两个系统变量来表示服务器级别的字符集和比较规则:
        ----系统变量----描述----
        ----character_set_server----服务器级别的字符集----
        ----collation_server----服务器级别的比较规则----

    mysql> SHOW VARIABLES LIKE 'character_set_server';

    +----------------------+---------+
    | Variable_name | Value |

    +----------------------+---------+
    | character_set_server | utf8mb4 |

    +----------------------+---------+
    1 row in set (0.01 sec)

    mysql> SHOW VARIABLES LIKE 'collation_server';

    +------------------+--------------------+
    | Variable_name | Value |

    +------------------+--------------------+
    | collation_server | utf8mb4_unicode_ci |

    +------------------+--------------------+
    1 row in set (0.00 sec)

      我们可以在启动服务器程序时通过启动选项或者在服务器程序运行过程中使用SET语句修改这两个变量的值。比如我们可以在配置文件中这样写:
    [server]
    character_set_server=gbk
    collation_server=gbk_chinese_ci
      当服务器启动的时候读取这个配置文件后这两个系统变量的值便修改了。

    数据库级别
      我们在创建和修改数据库的时候可以指定该数据库的字符集和比较规则。
      character_set_database 和 collation_database 这两个系统变量是只读的,我们不能通过修改这两个变量的值而改变当前数据库的字符集和比较规则。

      数据库的创建语句中也可以不指定字符集和比较规则,比如这样:
        CREATE DATABASE 数据库名;
      这样的话将使用服务器级别的字符集和比较规则作为数据库的字符集和比较规则。

    表级别
      我们也可以在创建和修改表的时候指定表的字符集和比较规则
      如果创建和修改表的语句中没有指明字符集和比较规则,将使用该表所在数据库的字符集和比较规则作为该表的字符集和比较规则。

    列级别
      同一个表中的不同的列也可以有不同的字符集和比较规则
      我们在创建和修改列定义的时候可以指定该列的字符集和比较规则,语法如下:
        CREATE TABLE 表名(
        列名 字符串类型 [CHARACTER SET 字符集名称] [COLLATE 比较规则名称],
        其他列...
        );
      ALTER TABLE 表名 MODIFY 列名 字符串类型 [CHARACTER SET 字符集名称] [COLLATE 比较规则名称];
      在转换列的字符集时需要注意,如果转换前列中存储的数据不能用转换后的字符集进行表示会发生错误。比方说原先列使用的字符集是utf8,列中存储了一些汉字,现在把列的字符集转换为ascii的话就会出错,因为ascii字符集并不能表示汉字字符。

    -----------------------------------------------------------
    仅修改字符集或仅修改比较规则

      由于字符集和比较规则是互相有联系的,如果我们只修改了字符集,比较规则也会跟着变化,如果只修改了比较规则,字符集也会跟着变化,具体规则如下:

      只修改字符集,则比较规则将变为修改后的字符集默认的比较规则。
      只修改比较规则,则字符集将变为修改后的比较规则对应的字符集。
      不论哪个级别的字符集和比较规则,这两条规则都适用

    mysql> SET character_set_server = gb2312;
    Query OK, 0 rows affected (0.00 sec)

    mysql> SHOW VARIABLES LIKE 'character_set_server';

    +----------------------+--------+
    | Variable_name | Value |

    +----------------------+--------+
    | character_set_server | gb2312 |

    +----------------------+--------+
    1 row in set (0.00 sec)

    mysql> SHOW VARIABLES LIKE 'collation_server';

    +------------------+-------------------+
    | Variable_name | Value |

    +------------------+-------------------+
    | collation_server | gb2312_chinese_ci |

    +------------------+-------------------+
    1 row in set (0.00 sec)

      我们只修改了character_set_server的值为gb2312,collation_server的值自动变为了gb2312_chinese_ci。
      只修改比较规则,则字符集将变为修改后的比较规则对应的字符集。

    mysql> SET collation_server = utf8_general_ci;
    Query OK, 0 rows affected (0.00 sec)

    mysql> SHOW VARIABLES LIKE 'character_set_server';

    +----------------------+-------+
    | Variable_name | Value |

    +----------------------+-------+
    | character_set_server | utf8 |

    +----------------------+-------+
    1 row in set (0.00 sec)

    mysql> SHOW VARIABLES LIKE 'collation_server';

    +------------------+-----------------+
    | Variable_name | Value |

    +------------------+-----------------+
    | collation_server | utf8_general_ci |

    +------------------+-----------------+
    1 row in set (0.00 sec)

      如果创建或修改列时没有显式的指定字符集和比较规则,则该列默认用表的字符集和比较规则
      如果创建或修改表时没有显式的指定字符集和比较规则,则该表默认用数据库的字符集和比较规则
      如果创建或修改数据库时没有显式的指定字符集和比较规则,则该数据库默认用服务器的字符集和比较规则

    -----------------------------------------------------------
    客户端和服务器通信中的字符集

    编码和解码使用的字符集不一致的后果
      说到底,字符串在计算机上的体现就是一个字节串,如果你使用不同字符集去解码这个字节串,最后得到的结果可能让你挠头。
      我们知道字符'我'在utf8字符集编码下的字节串长这样:0xE68891,如果一个程序把这个字节串发送到另一个程序里,另一个程序用不同的字符集去解码这个字节串,假设使用的是gbk字符集来解释这串字节,解码过程就是这样的:
      首先看第一个字节0xE6,它的值大于0x7F(十进制:127),说明是两字节编码,继续读一字节后是0xE688,然后从gbk编码表中查找字节为0xE688对应的字符,发现是字符'鎴'
      继续读一个字节0x91,它的值也大于0x7F,再往后读一个字节发现木有了,所以这是半个字符。
      所以0xE68891被gbk字符集解释成一个字符'鎴'和半个字符。

    字符集转换的概念
      如果接收0xE68891这个字节串的程序按照utf8字符集进行解码,然后又把它按照gbk字符集进行编码,最后编码后的字节串就是0xCED2,我们把这个过程称为字符集的转换,也就是字符串'我'从utf8字符集转换为gbk字符集。

    MySQL中字符集的转换
      我们知道从客户端发往服务器的请求本质上就是一个字符串,服务器向客户端返回的结果本质上也是一个字符串,而字符串其实是使用某种字符集编码的二进制数据。这个字符串可不是使用一种字符集的编码方式一条道走到黑的,从发送请求到返回结果这个过程中伴随着多次字符集的转换,在这个过程中会用到3个系统变量,我们先把它们写出来看一下:
    系统变量 描述
      character_set_client 服务器解码请求时使用的字符集
      character_set_connection 服务器处理请求时会把请求字符串从character_set_client转为character_set_connection
      character_set_results 服务器向客户端返回数据时使用的字符集

    从发送请求到接收结果过程中发生的字符集转换:
      客户端使用操作系统的字符集编码请求字符串,向服务器发送的是经过编码的一个字节串。
      服务器将客户端发送来的字节串采用character_set_client代表的字符集进行解码,将解码后的字符串再按照character_set_connection代表的字符集进行编码。
      如果character_set_connection代表的字符集和具体操作的列使用的字符集一致,则直接进行相应操作,否则的话需要将请求中的字符串从character_set_connection代表的字符集转换为具体操作的列使用的字符集之后再进行操作。
      将从某个列获取到的字节串从该列使用的字符集转换为character_set_results代表的字符集后发送到客户端。
      客户端使用操作系统的字符集解析收到的结果集字节串。

      我们通常都把 character_set_client 、character_set_connection、character_set_results 这三个系统变量设置成和客户端使用的字符集一致的情况,这样减少了很多无谓的字符集转换。为了方便我们设置,MySQL提供了一条非常简便的语句:
        SET NAMES 字符集名;
      这一条语句产生的效果和我们执行这3条的效果是一样的:
        SET character_set_client = 字符集名;
        SET character_set_connection = 字符集名;
        SET character_set_results = 字符集名;

      如果你使用的是Windows系统,那应该设置成gbk。
      比较规则的作用通常体现比较字符串大小的表达式以及对某个字符串列进行排序中,所以有时候也称为排序规则。如果以后大家在对字符串做比较或者对某个字符串列做排序操作时没有得到想象中的结果,需要思考一下是不是比较规则的问题~

  • 相关阅读:
    Ubuntu打开终端的方法三种
    javascript 获取随机数
    HTML5中类jQuery选择器querySelector的使用
    PHP stream_context_create()作用和用法分析
    一些常用的api接口、
    怎么样学好C++
    指针访问与数组访问的效率分析
    架构师
    Java 之 StringTokenizer
    类型转换操作符static_cast、const_cast、dynamic_cast、reinterpret_cast
  • 原文地址:https://www.cnblogs.com/bruceChan0018/p/14898554.html
Copyright © 2011-2022 走看看