mysql 学习 - 乱码问题

本篇已收录在 MySQL 是怎样运行的 学习笔记系列

字符集

mysql 存储数据比如字符串时, 是将字符串的内容根据定义好的字符集转义成为二进制序列, 再进行存储的. 这种转义的行为被称为编码. 如果字符串中有字符是不存在于字符集当中的, 那么会被认为是乱码, 即使存进数据库, 再获取出来时也不会正常显示.

字符比较规则

由于字符串之间是可以进行比较的, 其比较规则是通过字符编码为二进制后进行比较. 这种简单的比较规则也可以被称为二进制比较规则,英文名为binary collation

二进制比较简单粗暴. 但是往往不能满足所有需求, 比如 aA 在某些场合是可以相等的. 所以每个字符集需要有多种比较规则才可以.

常见的字符集

ASCII字符集

共收录128个字符,包括空格、标点符号、数字、大小写字母和一些不可见字符。由于总共才128个字符,所以可以使用1个字节来进行编码,我们看一些字符的编码方式:

'L' -> 01001100(十六进制:0x4C,十进制:76)
'M' -> 01001101(十六进制:0x4D,十进制:77)

ISO 8859-1字符集

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

GB2312字符集

收录了汉字以及拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母。其中收录汉字6763个,其他文字符号682个。同时这种字符集又兼容ASCII字符集,所以在编码方式上显得有些奇怪:

如果该字符在ASCII字符集中,则采用1字节编码。
否则采用2字节编码。

这种表示一个字符需要的字节数可能不同的编码方式称为变长编码方式。比方说字符串'爱u',其中'爱'需要用2个字节进行编码,编码后的十六进制表示为0xB0AE,'u'需要用1个字节进行编码,编码后的十六进制表示为0x75,所以拼合起来就是0xB0AE75

utf8字符集

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

'L' -> 01001100(十六进制:0x4C)
'啊' -> 111001011001010110001010(十六进制:0xE5958A)

MySQL中支持的字符集和排序规则

utf8和utf8mb4

utf8mb3:阉割过的utf8字符集,只使用1~3个字节表示字符。在mysql中就叫utf8
utf8mb4:正宗的utf8字符集,使用1~4个字节表示字符。如果大家有使用4字节编码一个字符的情况,比如存储一些emoji表情啥的,那请使用utf8mb4

查看支持的字符集

SHOW CHARSET

Default collation列表示这种字符集中一种默认的比较规则,
Maxlen,它代表该种字符集表示一个字符最多需要几个字节

查看字符集支持的比较规则

SHOW COLLATION

比较规则名称以与其关联的字符集的名称开头
后边紧跟着该比较规则主要作用于哪种语言,比如utf8_polish_ci表示以波兰语的规则比较,utf8_spanish_ci是以西班牙语的规则比较,utf8_general_ci是一种通用的比较规则。
名称后缀意味着该比较规则是否区分语言中的重音、大小写啥的,具体可以用的值如下:

SHOW COLLATION的返回结果中的Default列的值为YES的就是该字符集的默认比较规则,比方说utf8字符集默认的比较规则就是utf8_general_ci

查看不同级别的字符集和排序规则

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

服务器级别查询:

数据库级别的设定是在创建时, 也可以通过 alter 语句修改, 表级别同理.

列级别比较特殊, 同一个表中的不同的列也可以有不同的字符集和比较规则,我们在创建和修改列定义的时候可以指定该列的字符集和比较规则

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

只修改字符集,则比较规则将变为修改后的字符集默认的比较规则。
只修改比较规则,则字符集将变为修改后的比较规则对应的字符集。

从发送请求到接收结果过程中发生的字符集转换

客户端使用操作系统的字符集编码请求字符串,向服务器发送的是经过编码的一个字节串.


服务器将客户端发送来的字节串采用character_set_client代表的字符集进行解码,将解码后的字符串再按照character_set_connection代表的字符集进行编码.

如果character_set_connection代表的字符集和具体操作的列使用的字符集一致,则直接进行相应操作,否则的话需要将请求中的字符串从character_set_connection代表的字符集转换为具体操作的列使用的字符集之后再进行操作

将从某个列获取到的字节串从该列使用的字符集转换为character_set_results代表的字符集后发送到客户端。

客户端使用操作系统的字符集解析收到的结果集字节串.

这个过程是比较复杂的, 可能会涉及到了多次字符集转换, 下面这张图可能会比较好理解一些

最后记住一个结论:

posted @ 2020-04-02 21:30  YanyuWu  阅读(350)  评论(0编辑  收藏  举报