先了解一下字符集
我们的计算机只能读懂0和1组成的二进制数据,那么我们看到的字符,计算机是怎么存储,传输,展示的呢?这就需要建立一张二进制数据与字符之间的映射关系表,当我们需要存储一个字符的时候,把字符对应的二进制数据存储到计算机,当我们需要传输一个字符的时候,传输字符对应的二进制数据。而这个映射关系就叫字符集,简单来说字符集就是一个范围内字符的编码规则。另外每一种字符集都对应多个比较规则,比如不区分大小的比较是变成大写或者小写字母再比较,但是本质上都是比较字符编码。
常见字符集
ASCII 字符集,共收录128个字符,包括空格、标点符号、数字、大小写字母和一些不可见字符。使用一个字节进行编码,最高位空余没有使用。
ISO 8859-1字符集 ,在ASCII字符集的基础上进行了扩充,共256个字符,一个字节进行编码,使用了最高位。这个字符集在mysql中有个别名,叫latin1。
GB2312字符集 ,收录了汉字以及拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母。兼容了ASCII编码,如果一个字符在ASCII字符集中使用一个字节编码,否则使用两个字节编码。最高位是否位1判断本字节是一字节编码字符还是两字节编码字符。
GBK
字符集, GBK
字符集只是在收录字符范围上对GB2312
字符集作了扩充,编码方式上兼容GB2312
utf8
字符集,能收录地球上所想到得所有字符,还在不断扩充。兼容ASCII字符集,使用1~4个字节进行 编码。准确的说utf8是Unicode字符集编码方案的一种,另外还有utf16(使用2或者4个字节编码一个字符),utf32(使用4个字节编码一个字符)。在mysql中没有区分字符集和编码方案。
MySQL中支持的字符集和排序规则
mysql中使用得uft8是阉割过的utf8,正常情况下是1~4字节编码的,但是我们常用的字符使用1~3字节就够了。
utf8mb3:1到3字节编码,也就是默认使用的utf8
utf8mb4:正常的utf8编码,使用1到4字节编码。
mysql之所以这么做是因为编码一个字符所用字节最大长度会影响系统的存储和性能。
mysql支持的常用比较规则如下:
字符集 | Maxlen(表示一个字符最多需要字节数) |
ascii | 1 |
latin1 | 1 |
gb2312 | 2 |
gbk | 2 |
utf8 | 3 |
utf8mb4 | 4 |
每一种字符集对应多种排序规则,这个对字符如何比较大小很重要。show collation like'utf8\_%';
字符集和比较规则的应用
字符集和比较规则使用的时候要分级别:服务器级别,数据库级别,表级别,列级别
服务器级别:通过character_set_server可以在启动选项或者运行时设置编码。
数据库级别:在创建数据库的时候设置编码,如果没有指定,则默认使用服务器级别的编码方案。使用只读变量character_set_database 查看当前数据的编码方式
表级别:建表时设置,如果没有指定,使用数据库级别的编码方案。
列级别:建表时,在列名后面指定编码方案,如果没有指定,则使用表级别编码方案
客户端和服务器通信中的字符集转换
乱码
我们肯定遇到过一些乱码问题,遇到的比较多的是中文乱码,那么乱码是怎么发生的呢?比如中文字符"我",在utf8字符集中编码是 0xE68891,如果另一个程序使用gbk字符集来解码这个这个字符,就会出现乱码。utf8中“我”的编码是三字节,gbk使用两字节编码。gbk解码的时候会把前两个字节0xE68解码成一个字符'鎴',0x91 由于大于0x7F,gbk认为这个一个两字节编码字符的第一个字节,但是又没有另一个字节了,如果是一个整个字符串的话,就会顺序读取其他字符的字节,解码成其他字符,这个时候整个字符串都会乱码。
什么是字符集转换呢
比如说要把utf8编码的字符,转变成gbk编码的字符。‘我’在utf8中编码是 0xE68891,先使用utf8解码,然后gbk编码成0xCED2,‘我’在gbk中的编码就是0xCED2,这个过程就是字符集转换。
mysql中发生的字符集转换
从客户端向服务器发送请求,到服务器返回结果,中间可能有多次字符集转换。主要涉及三个系统变量:
character_set_client | 服务器解码请求时使用的字符集 |
character_set_connection | 服务器处理请求时会把请求字符串从character_set_client 转为character_set_connection, 该字符集包含的字符范围一定涵盖请求中的字符,要不然会导致有的字符无法使用character_set_connection 代表的字符集进行编码 |
character_set_results | 服务器向客户端返回数据时使用的字符集 |
字符集转换过程:
服务器按照character_set_client解码客户端发送来请求(假如客户端发送的请求编码和这个变量设置不一致,就有可能出现乱码啊),然后再按照character_set_connection进行编码,交给服务器处理(如果某个列使用的字符集和character_set_connection代表的字符集不一致的话,还需要进行一次字符集转换)。处理完成后按照character_set_results编码返回结果返回给客户端。
通常会把character_set_client,character_set_connection,character_set_results 设置一致,减少很多无畏的字符集转换。
如果你想在启动客户端的时候就把character_set_client
、character_set_connection
、character_set_results
这三个系统变量的值设置成一样的,那我们可以在启动客户端的时候指定一个叫default-character-set
的启动选项,比如在配置文件里可以这么写:
这个三个变量既有session级别,也有global级别。客户端启动连接到mysqlserver的时候可以读取上面的配置设置对应的编码规则,建立指定了编码规则的一个session。