字符集
字符集的概念
计算机是通过二进制存储数据的,但是MySQL需要存字符串等,因此就要用字符集
字符集就是可编码的字符串范围+字符串如何映射到二进制数据(如何编码和解码)
常见字符集
一些重要的字符集包括:
ASCII字符集:只收录了128个字符,包括空格、标点、数字、大小写字面、一些不可见字符。由于只有128个,因此1个字节足够用了,比如'L'编码为01001100(0x4C,十进制为76)
ISO 8859-1(latin1):收录256个字符,除了ASCII,还收录了128个西欧字母。256也可以1个字节编码。
GB2312:收录了6763个汉字,以及其他字母,还兼容ASCII,这个字符集数量很大,因此1个字节不够用了,它会判断字符是否在ASCII中,如果在用1个字节,否则用2个字节,这叫变长编码。例如'爱u'表示为0xCED275。
这就产生一个问题:怎么判断GB2312的1个字节是ASCII字符还是其他字符的一部分?因为ASCII的编码一定是0~127,即高位一定是0,GB2312的其他字符只占据128~255,即高位一定是1
GBK:扩充的GB2312,编码方式兼容GB2312,也是1个字符用1~2字节
utf8:最常用,收录全世界所有字符,兼容ASCII,也是变长编码,编码1个字符需要1~4个字节
utf16、utf32与utf8差不多,都是unicode编码方案,utf16固定用2个或4个字节编码一个字符,utf32固定用4个字节,因此使用utf8在MySQL中的好处是节省空间
乱码产生的原理
不同字符集的字符范围和编码类型不一样,但最终都是通过二进制存储
如果一组二进制数据在解码时选错了字符集,就有可能解码到意想不到的字符上,产生乱码
MySQL中的字符集
因为常用数据大部分1~3字节就够了,因此MySQL对utf8做了简化,从而降低存储空间:
utf8mb3:阉割版utf8字符集,只使用1~3字节表示字符,最常用,简称utf8
utf8b4:正宗的utf8字符集,使用1~4字节表示字符,有存储emoji的需求,必须用这种
MySQL可以使用如下命令查看字符集:
SHOW CHARSET [LIKE XXX]
查到的结果包括如下几个维度:
Charset:字符集
Description:描述
Default collation:该字符集默认的排序规则
Maxlen:该字符集描述一个字符需要的最大字节数
其中一些常用字符集的Maxlen比较有用:
比较规则
MySQL中的比较规则
MySQL要用于查询和比较,查到的结果必然就会有一定顺序,比较规则就是为了解决这个事情
可以使用如下命令查看比较规则:
SHOW COLLATION [LIKE xxx]
可以看到比较规则命名是比较整齐的,例如utf8_general_ci、utf8_bin、utf8_spanish_ci……
一般是2~3段式命名,含义如下:
位置1:字符集名称,例如utf8,表示这个比较规则是给哪种字符集使用的
位置2:语言,表示该比较规则是以哪种语言比较,例如上面的spanish是西班牙语,而general是指的通用规则,而utf8_bin是基于二进制比较的,因此没有语言这一说
位置3:简述比较规则,一般是一些标志词,包括:
ai:accent insensitive,表示不区分重音
as:accent sensitive,区分重音
ci:case insensitive,不区分大小写
cs:case sensitive,区分大小写
bin:binary,以二进制方式比较
因此utf8_general_ci是一种不区分大小写比较的通用规则
比如按照插入数据存入了a、b、A、B、我五个字符
按照utf8_general_ci查询结果是a、b、A、B、我
按照gbk_bin查询结果则是A、B、a、b、我,因为转成gbk字符集的二进制结果,A=65,B=66,a=97,b=98,我=25105
想要知道每种字符集的默认比较规则,就在前面SHOW CHARSET [LIKE XXX]
展示的Default collation列中
比较规则的级别
四个级别:服务器级别、数据库级别、表级别、列级别
列级别不指定,去找所在表的级别;表级别不指定,去找所在库的级别;库级别不指定,去找所在服务器级别;服务器级别也不指定,取字符集的默认比较规则
服务器级别
通过两个系统变量可以查询和设置:
# 查询服务器级别的字符集
SHOW VARIABLES LIKE 'character_set_server';
# 查询服务器级别的比较规则
SHOW VARIABLES LIKE 'collation_server';
可以通过启动选项修改这两个参数:
[server]
character_set_server=gbk
collation_server=gbk_chinese_ci
数据库级别
在create database
语句和alter database
语句中可以通过指定character set和collate关键词指定或修改,例如:
CREATE DATABASE charset_demo_db
CHARACTER SET gb2312
COLLATE gb2312_chinese_ci;
也可以使用SHOW语法查看当前库的配置,但是要先使用USE进入当前库
USE charset_demo_db;
# 查看数据库字符集
SHOW VARIABLES LIKE 'character_set_database';
# 查看数据库比较规则
SHOW VARIABLES LIKE 'collation_database';
character_set_database 和 collation_database 这两个系统变量是只读的,我们不能通过修改这两个变量的值 而改变当前数据库的字符集和比较规则
表级别
与库级别差不多,创建语法:
CREATE TABLE t(
VARCHAR(10)
) CHARACTER SET utf8 COLLATE utf8_general_ci;
列级别
与表级别也差不多:
CREATE TABLE 表名(
列名 字符串类型 [CHARACTER SET 字符集名称] [COLLATE 比较规则名称],
其他列...
);
当表和列都设置了字符集,列显然会优先取本列的
修改字符集和比较规则
只修改字符集,则比较规则将变为修改后的字符集默认的比较规则。
只修改比较规则,则字符集将变为修改后的比较规则对应的字符集。
MySQL客户端与服务器端通信时的字符集转换
MySQL有三个配置项:
character_set_client:服务器解码请求时使用的字符集
character_set_connection:服务器处理请求时会把请求字符串从 character_set_client 转为 character_set_connection
character_set_results:服务器向客户端返回数据时使用的字符集
简单来说,就是:
character_set_client对应客户端所在操作系统的字符集,因为它是用来解码客户端发来请求的,肯定经过客户端编码
character_set_connection是服务器端使用的默认字符集,一般与表/列字符集一致,否则查询时还要再转一次
character_set_results是服务器端返回结果时编码的字符集,因此也要跟客户端操作系统字符集一致,否则编码的结果客户端解不出来
根据这个逻辑,大致可以分析出来,一次请求需要3~4次字符集转换:
服务器解码客户端的二进制码,按照character_set_client转换一次
服务器把请求按自己的默认字符集转码一次,按照character_set_connection转换
服务器在查询时按照表/列字符集转换一次,将character_set_connection转成目标字符集(不一定发生)
服务器返回时,按照character_set_results转换一次,给客户端解码用
因此尽量把这三个变量设置成与客户端和表一致,大部分情况都是使用utf8的就可以
评论区