3.1 字符集和比较规则
3.1.1 字符集简介
计算机中实际存储的是二进制数据,怎么存储字符串呢?就需要建立字符串与二进制的关系:
- 要把哪些字符映射成二进制数据?也就是界定字符范围
- 怎么映射?将字符映射成二进制数据的过程叫做编码,将二进制数据映射到字符的过程叫做解码。
3.1.2 比较规则简介
二进制比较规则:直接比较二进制的大小
但是很多场合下,英文字符不区分大小写,就不能直接用二进制进行比较了,也就是说同一种字符集可以有多种比较规则。稍后将介绍现实生活中使用的各种字符集以及它们的部分比较规则。
3.1.3 一些重要的字符集
- ASCII字符集:共收录128个字符,包括空格、标点符号、数字、大小写字母和一些不可见的字符。由于总共128个字符,所以可以使用一个字节来进行编码。
- ISO 8859-1 字符集(别名:Latin1):共收录256个字符,它在ASCII字符集的基础上又扩充了128个西欧常用字符(包括德法两国的字母)。该字符集也可以使用一个字节来进行编码
- GB2312字符集:收录了汉字以及拉丁字母、希腊字母、日文平假名以及片假名字母、俄语西里尔字母,收录汉字6763个,收录其他文字符号682个。这种字符集同时又兼容ASCII字符集,所以在编码方式上显得有些奇怪:如果该字符在ASCII字符集中,则采用一字节编码;否则采用两字节编码。
这种使用不同字节数来表示一个字符的编码方式称为变长编码方式。比如字符串“爱u”,其中的“爱”需要2个字节进行编码,编码后的十六进制表示0xB0AE;‘u’需要1字节进行编码,编码后的十六进制为0x75,所以拼合起来是0xB0AE75。
ASCII字符集只收录128个字符,使用0127就可以表示全部字符。所以,如果某个字节是在0127之内(该字节的最高位是0),就意味着一个字节代表一个单独的字符,否则(该字节的最高位为1)就是两个字节代表一个字符。
- GBK 字符集: GBK字符集只是在收录的字符范围上对GB2312 字符集进行了扩充,编码方式兼容GB2312 字符集。
- UTF-8 字符集:几乎收录了当今世界各个国家/地区使用的字符,而且还在不断地扩充。这种字符集兼容ASCII 字符集,采用变长编码方式,编码一个字符时需要使用1~4字节。
其实准确地说,UTF-8只是Unicode 字符集的一种编码方案,Unicode 字符集可以采用UTF-8、UTF-16、UTF-32这几种编码方案。utf-8使用14字节编码一个字符,utf-16使用24字节编码一个字符,utf-32 使用4个字节编码一个字符。
3.2 Mysql 中支持的字符集和比较规则
3.2.1 Mysql 中的utf8和utf8mb4
- utf8mb3:“阉割”过的utf-8字符集,只使用1~3字节表示字符。在mysql中utf8是其别名。
- utf8mb4:正宗的utf-8字符集,使用1~4字节表示字符
3.2.2 字符集的查看
show (character set|charset) [like 匹配模式]
, 其中character set 和 charset 是同义词,用任意一个都可以。
default collation 列表示这种字符集默认的比较规则;maxlen 列表示这种字符集最多需要几个字节来表示一个字符。
3.2.3 比较规则的查看
show collation [like 匹配模式]
当前版本的数据库查询出来有107条数据。
3.3 字符集和比较规则的应用
3.3.1 各级别的字符集和比较规则
Mysql有四个级别的字符集和比较规则,分别是服务器级别、数据库级别、表级别、列级别。
- 服务器级别
show variables like 'character_set_server'; show variables like 'collation_server'
可以在配置文件中进行配置 - 数据库级别
创建和修改数据库是可以指定该数据库的字符集和比较规则
create database 数据库名 [[default] character set 字符集名称] [[default] collate 比较规则名称];
如果想当前数据库使用的字符集(使用use语句选择当前的数据库),也可以使用show来查询
(show variables like 'character_set_database'
);
如果创建数据库不指定字符集和比较规则时,则默认使用服务器级别的字符集与比较规则。 - 表级别
可以在创建和修改表的时候指定表的字符集和比较规则:
create table 表名(列的信息) [[default] character set 字符集名称] [collate 比较规则名称];
- 列级别
同一个表中的不同列也可以有不同的字符集和比较规则。
create table 表名 (列名 字符串类型 [character set 字符集名称] [collate 比较规则], 其他列);
也可以修改列的字符集 和比较规则: alter table t modify col varchar(10) character set gbk collate bk_chinese_ci;如果修改时未指定字符集则默认使用表级别的字符集与规则。但是如果列中存储的数据不能用修改后的字符集进行表示,则会发生错误。例如,uft8转ASCII的话就会出错。
- 仅修改字符集或仅修改比较规则
由于字符集和比较规则之间互相关联,因此如果只修改字符集,比较规则会跟着变化,如果只修改比较规则,字符集也会跟着变化- 只修改字符集,则比较规则将变为修改后的字符集默认的比较规则;
- 只修改比较规则,则字符集将变为修改后的比较规则对应的字符集。
- 各级别字符集和比较规则小结
- 如果创建或修改列没有显式指定字符集和比较规则,则该列默认使用表的字符集和比较规则;
- 如果创建表时没有显式指定字符集和比较规则,则该表默认使用数据库的字符集和比较规则;
- 如果创建数据库时没有显式指定字符集和比较规则,则该数据库默认使用服务器的字符集和比较规则。
3.3.2 客户端和服务器通信过程中使用的字符集
-
编码和解码使用的字符集不一致
-
字符集转换的概念
-
MySQL中的字符集转换过程
客户端发送请求-
当使用类UNIX操作系统时
LC_ALL, LC_CTYPE, LANG这3个环境变量的值决定了操作系统当前使用的是哪种字符集。其中LC_ALL的优先级比LC_CTYPE高,LC_CTYPE的优先级比LANG高。也就是说,如果设置了LC_ALL,最终都以LC_ALL为准。
如果这3个环境变量都没有,那么操作系统使用的字符集就是其默认的字符集。 -
当使用Windows操作系统时
在Windows中,字符集称为代码页(code page),一个代码页与一个唯一的数据相关联。比如,936代表GBK字符集,65001代表UTF-8字符集
在windows操作系统中,如果在启动MySQL客户端程序时携带了default-character-set启动选项,那么Mysql客户端将以该启动选项指定的字符集对请求的字符串进行编码(这一点并不适用于类UNIX操作系统)。
服务器接收请求
从本质上来说,服务器接收到的请求就是一个字节序列。服务器将这个字节序列看作是使用系统变量character_set_client代表的字符集进行编码的字节序列(每个客户端与服务器建立连接后,服务器都会为该客户端维护一个单独的character_set_client变量,这个变量是SESSION级别的 )。假如客户端实际使用UTF-8字符集来编码请求的字符串,我们就可以将character_set_client设置为utf-8:set character_set_client = utf8
;服务器处理请求
在真正处理请求时,又会转化为使用SESSION级别的系统变量character_set_connection对应的字符集进行编码的字节序列。
select ‘a’ = ‘A’; 这个返回结果是True还是False?其实仅仅根据这个语句不能进行判断。因为我们并不知道两个字符串使用的是什么字符集编码,也不知道比较规则。
此时,character_set_connection 系统变量就发挥了作用,它表示这些字符串应该使用哪种字符集进行编码。当然,与之对应的还有collation_connection,这个系统变量表示这些字符串应该使用哪种比较规则。
create table tt ( c varchar(100)) engine=innodb charset = utf8
, 假设当前的character_set_connection 和collation_connection 分别为gbk和gbk_chinese_ci。然后执行select * from tt where c = '我';
字符串“我” 是使用gbk字符集进行编码的,比较规则是 gbk_chinese_ci ;而列c是采用utf8字符集进行编码的,比较规则是utf8_general_ci。这该怎么比较呢?在这种情况下,列的字符集和排序规则的优先级更高。因此,这里需要将请求中的字符串“我” 先从 gbk 字符集转化为utf8字符集, 然后再使用列c的比较规则utf8_general_ci进行比较。服务器生成响应
“我”在列c中的存放格式为 0xE68891 ,执行select * from tt; 时,是直接将该值读出后发给客户端吗?这可不一定,取决于SESSION级别的系统变量 character_set_results 的值。服务器会先将会字符串“我” 从utf8 字符集编码的 0xE68891 转化为 character_set_results 系统变量对应的字符集编码后的字节序列,之后再发送给客户端。系统变量 描述 character_set_client 服务器认为请求是按照该系统变量指定的字符集进行编码的 character_set_connection 服务器在处理请求时,会把请求字节序列从character_set_client转化为character_set_connection character_set_results 服务器采用该系统变量指定的字符集对返回给客户端的字符串进行编码 这3个系统变量在服务器中的作用范围都是session级别的。每个客户端在与服务器建立连接后,服务器都会为这个连接维护这3个变量。
每个Mysql客户端都维护着一个客户端默认字符集,客户端在启动是会自动检测所在操作系统当前使用的字符集,并按照一定的规则映射成Mysql自持的字符集,然后将该字符集作为客户端默认的字符集。如果在启动Mysql客户端时设置了default-character-set启动选项,那么客户端会忽视操作系统当前使用的字符集,直接使用该配置。客户端接收到响应
客户端收到的响应其实也是一个字节序列。对于类UNIX操作系统来说,收到的字节序列基本上相当于直接写到黑框框中,再由黑框框将这个字节序列解释为人类能看懂的字符。对于windows系统来说,客户端会使用客户端的默认字符集来解释这个字节序列。- 客户端发送的请求字节序列是采用哪种字符集进行编码的;
- 服务器收到请求字节序列后会认为它采用哪种字符集进行编码的;
- 服务器在运行过程中会把请求的字节序列转化为以哪种字符集编码的字节序列;
- 服务器在向客户端返回字节序列时,是采用哪种字符集进行编码的;
- 客户端在收到响应字节序列后,是怎么把它们进行转换的。
-
3.3.3 比较规则的应用
3.4 总结
字符集指的是某个字符范围的编码规则。
比较规则是对某个字符集中的字符比较大小的一种规则。
在MySQL中,一个字符集可以有若干种比较规则,其中有一个默认的比较规则,一个比较规则必须对应的一个字符集。
在MySQL中查看支持的字符集和比较规则在语句如下:
-
show (character set | charset) [like 匹配的模式];
-
show collation [like 匹配的模式];
MySQL有4个级别的字符集和比较规则,具体如下:
- 服务器级别
character_set_server 表示服务器级别的字符集,collation_server表示服务器级别的比较规则。 - 数据库级别
创建和修改数据库时可以指定字符集和比较规则: - 表级别
创建和修改表的时候指定表的字符集和比较规则: - 列级别
创建和修改列的时候指定列的字符集和比较规则:
从发送请求到接收响应的过程中发生的字符集转换如下:
- 客户端发送的请求字节序列是采用哪种字符集编码的。
这一步骤主要取决于操作系统当前使用的字符集;对于Windows操作系统来说,还与客户端启动时设置的default-character-set启动项有关; - 服务器收到请求字节序列后会认为它是采用哪种字符集进行编码的。
这一步骤取决于系统变量 character_set_client 的值。 - 服务器在运行过程中会把请求的字节序列转换为以哪种字符集编码的字节序列。
这一步骤取决与系统变量character_set_connection 的值 - 服务器在向客户端返回字节序列时,是采用哪种字符集进行编码的。
这一步骤取决于系统变量 character_set_results 的值 - 客户端在收到响应字节序列后,是怎么把它们写到黑框框中的。
这一步骤主要取决于操作系统当前使用的字符集;对于Windows系统来说,还与客户端启动时设置的default-character-set启动项有关
- 服务器级别