目录

《MySQL是怎样运行的:从根儿上理解MySQL》读书笔记(一)

目录
摘要
《MySQL是怎样运行的:从根儿上理解MySQL》读书笔记(一)。
警告

涉及大量摘录,均以引用格式表明,包括标题在内,内容版权属于原作者!!

非摘录格式内容为崔叉叉原创总结。


第0章 楔子

第1章 装作自己是个小白——初识MySQL

1.1 MySQL 的客户端/服务器架构

  1. 启动 MySQL 服务器程序。
  2. 启动 MySQL 客户端程序,并连接到服务器程序。
  3. 在客户端程序中输入命令语句 , 并将其作为请求发送给服务器程序.服务器程序在收 到这些请求后,根据请求的内容来操作具体的数据,并将结果返回给客户端。

1.2 MySQL 的安装

1.3 启动 MySQL 服务器程序

  1. mysqldmysqld 可执行文件就表示 MySQL 服务器程序 , 运行这个可执行文件就可以直接启动一个 MySQL 服务器进程.但这个可执行文件并不常用。
  2. mysqld_safemysqld_safe 是一个启动脚本, 它会间接调用 mysqld 并持续监控服务器的运行状态.当服 务器进程出现错误时 ,它还可以帮助重启 服务器程序.另外,使用 mysqld_safe 启动 MySQL 服务器程序时, 它会将服务器程序的 出错信息和其他诊断信息输出 到错误日志,以方便后期查找发生错误的原因。
  3. mysql.servermysql.server 也是一个启动脚本 , 它会间接地调用 mysqld_safe。这个 mysql.server 文件其实是一个链接文件,它的实际文件是 ../support-files/mysql.server
    1. 启动:mysql.server start
    2. 关闭:mysql.server stop

1.4 启动MySQL客户端程序

1
mysql -h主机名  -u用户名 -p密码

1.5 客户端与服务器连接的过程

运行着的服务器程序和客户端程序本质上都是计算机上的一个进程,所以客户端进程向服务器进程发送请求并得到回复的过程本质上是一个进程间通信的过程

MySQL服务器会默认监听3306端口。

1
mysql -h127.0.0.1 -uroot -P3307 -p (大写的P指定端口,小写的p指定密码)

1.6 服务器处理客户端请求

其实不论客户端进程和服务器进程是采用哪种方式进行通信,最后实现的效果都是:客户端进程向服务器进程发送一段文本(MySQL语句),服务器进程处理后再向客户端进程一段文本(处理结果)

/images/How_MySQL_Works/%E6%9F%A5%E8%AF%A2%E8%AF%B7%E6%B1%82%E6%89%A7%E8%A1%8C%E8%BF%87%E7%A8%8B.png
查询请求执行过程

1.6.1 连接管理

每当有一个客户端进程连接到服务器进程时,服务器进程都会创建一个线程来专门处理与这个客户端的交互,当该客户端退出时会与服务器断开连接,服务器并不会立即把与该客户端交互的线程销毁掉,而是把它缓存起来,在另一个新的客户端再进行连接时,把这个缓存的线程分配给该新客户端。这样就起到了不频繁创建和销毁线程的效果,从而节省开销。

1.6.2 解析与优化

1.6.2.1 查询缓存

查询缓存可以在不同客户端之间共享

  1. 如果两个查询请求在任何字符上的不同(例如:空格、注释、大小写),都会导致缓存不会命中
  2. 如果查询请求中包含某些系统函数、用户自定义变量和函数、一些系统表,如 mysql 、information_schema、 performance_schema 数据库中的表,那这个请求就不会被缓存

MySQL的缓存系统会监测涉及到的每张表,只要该表的结构或者数据被修改,如对该表使用了INSERTUPDATEDELETETRUNCATE TABLEALTER TABLEDROP TABLEDROP DATABASE语句,那使用该表的所有高速缓存查询都将变为无效并从高速缓存中删除

虽然查询缓存有时可以提升系统性能,但也不得不因维护这块缓存而造成一些开销,比如每次都要去查询缓存中检索,查询请求处理完需要更新查询缓存,维护该查询缓存对应的内存区域。从MySQL 5.7.20开始,不推荐使用查询缓存,并在MySQL 8.0中删除。

1.6.2.2 语法解析

从指定的文本中提取出我们需要的信息本质上算是一个编译过程。

1.6.2.3 查询优化

MySQL的优化程序会对我们的语句做一些优化,如外连接转换为内连接、表达式简化、子查询转为连接等。

1.6.3 存储引擎

截止到服务器程序完成了查询优化为止,还没有真正的去访问真实的数据表,MySQL服务器把数据的存储和提取操作都封装到了一个叫存储引擎的模块里。

其实这个存储引擎以前叫做表处理器,后来可能人们觉得太土,就改成了存储引擎的叫法,它的功能就是接收上层传下来的指令,然后对表中的数据进行提取或写入操作。

为了管理方便,人们把连接管理查询缓存语法解析查询优化这些并不涉及真实数据存储的功能划分为MySQL server的功能,把真实存取数据的功能划分为存储引擎的功能。各种不同的存储引擎向上边的MySQL server层提供统一的调用接口(也就是存储引擎API),包含了几十个底层函数。

server 层在判断某奈记录符合妥求之后,其实是先将其发送到一个缓冲区,待到该 缓冲区满了,才向客户端发送真正的记录.该缓冲区大小由.%统变量 net_buffer_Iength 控制。

1.7 常用存储引擎

1.8 关于存储引擎的一些操作

1.9 总结

第2章 MySQL的调控按钮一启动选项和系统变量

2.1 启动选项和配置文件

2.1.1 在命令行上使用选项

我们在命令行中指定启动选项时需要在选项名前加上--前缀。另外,如果选项名是由多个单词构成的,它们之间可以由短划线-连接起来,也可以使用下划线_连接起来。

1
mysqld --skip-networking
1
mysqld --skip_networking
1
--启动选项1[=值1] --启动选项2[=值2] ... --启动选项n[=值n]

选项名、=、选项值之间不可以有空白字符

短形式的选项名只有一个字母,与使用长形式选项时需要在选项名前加两个短划线--不同的是,使用短形式选项时在选项名前只加一个短划线-前缀。

使用短形式指定启动选项时,选项名和选项值之间可以没有间隙,或者用空白字符隔开(-p选项有些特殊,-p和密码值之间不能有空白字符)

2.1.2配置文件中使用选项

2.1.2.1 配置文件的路径
    路径名     备注
/etc/my.cnf
/etc/mysql/my.cnf
SYSCONFDIR/my.cnf SYSCONFDIR表示在使用CMake构建MySQL时使用SYSCONFDIR选项指定的目录。默认情况下,这是位于编译安装目录下的etc目录。
$MYSQL_HOME/my.cnf 特定于服务器的选项(仅限服务器);MYSQL_HOME是一个环境变量,该变量的值是我们自己设置的,我们想设置就设置,不想设置就不设置。
defaults-extra-file 命令行指定的额外配置文件路径;我们在启动程序时可以通过指定defaults-extra-file参数的值来添加额外的配置文件路径
~/.my.cnf 用户特定选项
~/.mylogin.cnf 用户特定的登录路径选项(仅限客户端)

这几个路径中的任意一个都可以当作配置文件来使用。

使用mysqld_safe程序启动服务器时,会间接调用mysqld,所以对于传递给mysqld_safe的启动选项来说,如果mysqld_safe程序不处理,会接着传递给mysqld程序处理。

2.1.2.2 配置文件的内容

配置文件中的启动选项被划分为若干个组,每个组有一个组名,用中括号[]扩起来。

1
2
3
4
[server]
option1     #这是option1,该选项不需要选项值
option2 = value2      #这是option2,该选项需要选项值
...

配置文件中只能使用长形式的选项。在配置文件中指定的启动选项不允许加--前缀,并且每行只指定一个选项,而且=周围可以有空白字符(命令行中选项名、=、选项值之间不允许有空白字符)。另外,在配置文件中,我们可以使用#来添加注释。

  • [server]组下边的启动选项将作用于所有的服务器程序。
  • [client]组下边的启动选项将作用于所有的客户端程序。
启动命令 类别 能读取的组
mysqld 启动服务器 [mysqld]、[server]
mysqld_safe 启动服务器 [mysqld]、[server]、[mysqld_safe]
mysql.server 启动服务器 [mysqld]、[server]、[mysql.server]
mysql 启动客户端 [mysql]、[client]
mysqladmin 启动客户端 [mysqladmin]、[client]
mysqldump 启动客户端 [mysqldump]、[client]
2.1.2.3 特定MySQL版本的专用选项组
2.1.2.4 配置文件的优先级

如果我们在多个配置文件中设置了相同的启动选项,那以最后一个配置文件中的为准

2.1.2.5 同一个配置文件中多个组的优先级

在同一个配置文件中,在组里出现了同样的配置项,将以最后一个出现的组中的启动选项为准。

2.1.2.6 defaults-file的使用

如果我们不想让MySQL到默认的路径下搜索配置文件(就是上表中列出的那些),可以在命令行指定defaults-file选项,比如这样(以UNIX系统为例):

1
mysqld --defaults-file=/tmp/myconfig.txt

这样,在程序启动的时候将只在/tmp/myconfig.txt路径下搜索配置文件。如果文件不存在或无法访问,则会发生错误。

注意defaults-extra-filedefaults-file的区别,使用defaults-extra-file可以指定额外的配置文件搜索路径(也就是说那些固定的配置文件路径也会被搜索)。

2.1.3 命令行和配置文件中启动选项的区别

如果同一个启动选项既出现在命令行中,又出现在配置文件中,那么以命令行中的启动选项为准

2.2 系统变量

2.2.1 系统变量简介

MySQL服务器程序运行过程中会用到许多影响程序行为的变量,它们被称为MySQL系统变量,比如允许同时连入的客户端数量用系统变量max_connections表示,表的默认存储引擎用系统变量default_storage_engine表示,查询缓存的大小用系统变量query_cache_size表示。

2.2.2 查看系统变量

1
SHOW VARIABLES [LIKE 匹配的模式];

更严谨地说, MySQL 服务器实际上允许 max_ connections + 1 个客户端连接 ,额外的1个是给超级用 户准备的(很显然这是超级用户的一个特权)。

2.2.3 设置系统变量

2.2.3.1 通过启动选项设置
  • 通过命令行添加启动选项。
1
mysqld --default-storage-engine=MyISAM --max-connections=10
  • 通过配置文件添加启动选项。
1
2
3
[server]
default-storage-engine=MyISAM
max-connections=10

对于启动选项来说,如果启动选项名由多个单词组成,各个单词之间用短划线-或者下划线_连接起来都可以,但是对应的系统变量之间必须使用下划线_连接起来

2.2.3.2 服务器程序运行过程中设置
设置不同作用范围的系统变量

系统变量的作用范围的概念:

  • GLOBAL(全局范围):影响服务器的整体操作。具有 GLOBAL 作用范围的系统变量可以称为全面变量。
  • SESSION (会话范围) :影响某个客户端连接的操作。具有 SESSION 作用范围的系统变量可以称为会话变量。

服务器在启动时,会将每个全局变量初始化为其默认值(可以通过命令行或配置文件中指 定的选项更改这些默认值)。服务器还为每个连接的客户端维护一组会话变量,客户端的会话变量在连接时使用相应全局变量 的当前值进行初始化。

意思就是说,全局变量是一开始就有的,是个蓝本。一个客户端连过来了,会以这个蓝本,创建一份此客户端特有的会话变量,类似于副本,二者从此互不影响。如果全局变量变了,也只影响变化之后,后续连接过来的客户端。

通过启动选项设置的系统变量的作用范围都是GLOBAL的,也就是对所有客户端都有效的

通过客户端程序设置系统变量的语法:

1
2
SET [GLOBAL|SESSION] 系统变量名 = 值;
SET [@@(GLOBAL|SESSION).]var_name = XXX;

作用范围为GLOBAL的系统变量default_storage_engine的值修改为MyISAM

1
2
语句一:SET GLOBAL default_storage_engine = MyISAM;
语句二:SET @@GLOBAL.default_storage_engine = MyISAM;

只想对本客户端生效:

1
2
3
语句一:SET SESSION default_storage_engine = MyISAM;
语句二:SET @@SESSION.default_storage_engine = MyISAM;
语句三:SET default_storage_engine = MyISAM;

如果在设置系统变量的语句中省略了作用范围,默认的作用范围就是SESSION

查看不同作用范围的系统变量
1
SHOW [GLOBAL|SESSION] VARIABLES [LIKE 匹配的模式];
  • 如果使用 GLOBAL 修饰符,则显示全局系统变量的值。如果某个系统变量没有 GLOBAL 作用范围,则不显示它。
  • 如果使用 SESSION 修饰符,则显示针对当前连接有效的系统变量值。如果某个系统变量没有 SESSION 作用范围,则显示 GLOBAL 作用范围的值。
  • 如果没写修饰符,则与使用 SESSION 修饰符效果一样。
注意事项
  • 并不是所有系统变量都具有GLOBALSESSION的作用范围

    • 有一些系统变量只具有GLOBAL作用范围,比方说max_connections,表示服务器程序支持同时最多有多少个客户端程序进行连接。

    • 有一些系统变量只具有SESSION作用范围,比如insert_id,表示插入值时使用的AUTO_INCREMENT修饰的列的值。

    • 有一些系统变量的值既具有GLOBAL作用范围,也具有SESSION作用范围,比如我们前边用到的default_storage_engine,而且其实大部分的系统变量都是这样的,

  • 有些系统变量是只读的,并不能设置值

    比方说version,表示当前MySQL的版本,我们客户端是不能设置它的值的,只能在SHOW VARIABLES语句里查看。

第3章 字符集和比较规则

3.1 字符集和比较规则简介

3.1.1 字符集简介

存储字符串呢即建立字符与二进制数据的映射关系:

  1. 要把哪些字符映射成二进制数据?也就是界定清楚字符范围。
  2. 怎么映射?将一个字符映射成一个二进制数据的过程也叫做编码,将一个二进制数据映射到一个字符的过程叫做解码

3.1.2 比较规则简介

3.1.3 一些重要的字符集

3.2 MySQL中支持的字符集和比较规则

3.2.1 MySQL中的utf8和utf8mb4

  • utf8mb3:阉割过的utf8字符集,只使用1~3个字节表示字符。
  • utf8mb4:正宗的utf8字符集,使用1~4个字节表示字符。

MySQLutf8utf8mb3的别名,所以之后在MySQL中提到utf8就意味着使用1~3个字节来表示一个字符,如果大家有使用4字节编码一个字符的情况,比如存储一些emoji表情啥的,那请使用utf8mb4

MySQL 8.0 中,设计 MySQL 的大叔已经很大程度地优化 utf8mb4 字符集的性能,而且已经将其设置为默认的字符集。

3.2.2 字符集的查看

MySQL 支持非常多的字符集,可以用下面这个语句来查看当前 MySQL 中 支持的字符集:

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

其中CHARACTER SETCHARSET是同义词,用任意一个都可以。

从输出中可以看到 ,MySQL 中 表示字符集的名称时使用 小写 形式。

Default collation列表示这种字符集中一种默认的比较规则。大家注意返回结果中的最后一列Maxlen,它代表该种字符集表示一个字符最多需要几个字节。

3.2.3 比较规则的查看

可以使用如下命令来查看 MySQL 中支持的比较规则:

1
SHOW COLLATION [LIKE 匹配的模式];

这些比较规则的命名还挺有规律的,具体规律如下:

  • 比较规则名称以与其关联的字符集的名称开头。如上图的查询结果的比较规则名称都是以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结尾的,说明不区分大小写。

每种字符集对应若干种比较规则,每种字符集都有一种默认的比较规则SHOW COLLATION的返回结果中的Default列的值为YES的就是该字符集的默认比较规则,比方说utf8字符集默认的比较规则就是utf8_general_ci

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

3.3.1 各级别的字符集和比较规则

MySQL有4个级别的字符集和比较规则,分别是:

  • 服务器级别
  • 数据库级别
  • 表级别
  • 列级别
3.3.1.1 服务器级别

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

系统变量 描述
character_set_server 服务器级别的字符集
collation_server 服务器级别的比较规则

我们可以在启动服务器程序时通过启动选项或者在服务器程序运行过程中使用SET语句修改这两个变量的值。比如我们可以在配置文件中这样写:

1
2
3
[server]
character_set_server=gbk
collation_server=gbk_chinese_ci
3.3.1.2 数据库级别

我们在创建和修改数据库的时候可以指定该数据库的字符集和比较规则,具体语法如下:

1
2
3
4
5
6
7
CREATE DATABASE 数据库名
    [[DEFAULT] CHARACTER SET 字符集名称]
    [[DEFAULT] COLLATE 比较规则名称];

ALTER DATABASE 数据库名
    [[DEFAULT] CHARACTER SET 字符集名称]
    [[DEFAULT] COLLATE 比较规则名称];

其中的DEFAULT可以省略,并不影响语句的语义。

如果想查看当前数据库使用的字符集和比较规则,可以查看下面两个系统变量的值(前提是使用USE语句选择当前默认数据库,如果没有默认数据库,则变量与相应的服务器级系统变量具有相同的值):

系统变量 描述
character_set_database 当前数据库的字符集
collation_database 当前数据库的比较规则

character_set_databasecollation_database 这两个系统变量是只读的,我们不能通过修改这两个变量的值而改变当前数据库的字符集和比较规则

数据库的创建语句中也可以不指定字符集和比较规则,比如这样:

1
CREATE DATABASE 数据库名;

这样的话将使用服务器级别的字符集和比较规则作为数据库的字符集和比较规则

3.3.1.3 表级别

我们也可以在创建和修改表的时候指定表的字符集和比较规则,语法如下:

1
2
3
4
5
6
7
CREATE TABLE 表名 (列的信息)
    [[DEFAULT] CHARACTER SET 字符集名称]
    [COLLATE 比较规则名称]]

ALTER TABLE 表名
    [[DEFAULT] CHARACTER SET 字符集名称]
    [COLLATE 比较规则名称]

如果创建和修改表的语句中没有指明字符集和比较规则,将使用该表所在数据库的字符集和比较规则作为该表的字符集和比较规则

3.3.1.4 列级别

需要注意的是,对于存储字符串的列,同一个表中的不同的列也可以有不同的字符集和比较规则。我们在创建和修改列定义的时候可以指定该列的字符集和比较规则,语法如下:

1
2
3
4
5
6
CREATE TABLE 表名(
    列名 字符串类型 [CHARACTER SET 字符集名称] [COLLATE 比较规则名称],
    其他列...
);

ALTER TABLE 表名 MODIFY 列名 字符串类型 [CHARACTER SET 字符集名称] [COLLATE 比较规则名称];

对于某个列来说,如果在创建和修改的语句中没有指明字符集和比较规则,将使用该列所在表的字符集和比较规则作为该列的字符集和比较规则

在转换列的字符集时需要注意,如果转换前列中存储的数据不能用转换后的字符集进行表示会发生错误。比方说原先列使用的字符集是utf8,列中存储了一些汉字,现在把列的字符集转换为ascii的话就会出错,因为ascii字符集并不能表示汉字字符。

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

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

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

不论哪个级别的字符集和比较规则,这两条规则都适用

3.3.1.6 各级别字符集和比较规则小结

我们介绍的这4个级别字符集和比较规则的联系如下:

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

知道了这些规则之后,对于给定的表,我们应该知道它的各个列的字符集和比较规则是什么,从而根据这个列的类型来确定存储数据时每个列的实际数据占用的存储空间大小了。

一个字符'我'gbk中的编码为0xCED2,占用两个字节,两个字符的实际数据就占用4个字节。如果把该列的字符集修改为utf8的话,这两个字符就实际占用6个字节啦~

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

3.3.2.1 编码和解码使用的字符集不一致的后果

如果对于同一个字符串编码和解码使用的字符集不一样,会产生意想不到的结果,在我们看来就像是产生了乱码一样。

3.3.2.2 字符集转换的概念

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

3.3.2.3 MySQL 中的字符集转换过程

如果我们仅仅把 MySQL 当作一个软件 , 那么从用户的角度来看 , 客户端发送的请求以及服务器返回的响应都是一个字符串。但是从机器的角度来看,客户端发送的请求和服务器返回的响应本质上就是一个字节序列。在这个“客户端发送请求,服务器返回响应“的过程中,其实经历了多次 的字符集转换。

  1. 这里稍微有一点不好理解。其实客户端与服务器之间,是在相互发送字符串,客户端发送字符串语句,服务器返回字符串结果。但是机器不知道字符串,机器用二进制代表字符串,那么字符串到二进制的过程,就涉及字符集编码、解码、转换的过程。

  2. 客户端有自己的字符集,客户端向服务器发送语句时,会用自己的字符集,把字符串语句,转换为二进制,发送到服务器。

  3. 但是,服务器是不知道客户端用什么字符集的,因此服务器维护了3个系统变量。

    系统变量 描述
    character_set_client 服务器解码请求时使用的字符集
    character_set_connection 服务器运行过程中使用的字符集
    character_set_results 服务器向客户端返回数据时使用的字符集
  4. character_set_client是服务器收到客户端二进制时,使用的解码字符集,服务器认为客户端用的是这个字符集,实际可能不一致,不一致会导致乱码报错,语句肯定不能正确执行。

  5. character_set_connection是服务器自己用的字符集。因为客户端用的字符集,可能和服务器用不同,所以服务器自己用单独的字符集,对客户端发过来的二进制,进行解码再编码(也就是转换)的过程。

  6. character_set_results是服务器对返回结果,进行编码的字符集。

    /images/How_MySQL_Works/%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%B8%8E%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E4%BA%A4%E4%BA%92.png
    客户端与服务器的交互
  7. 实际中最好将所有字符集,保持一致,可以避免不必要的转换。

客户端的字符集

一般情况下,客户端编码请求字符串时使用的字符集与操作系统当前使用的字符集一致。

LC_ALLLC_CTYPELANG 这 3 个环境变量的值决定了操作系统当前使用的是哪种字符集。其中,LC_ALL 的优先级比 LC_CTYPE 高,LC CTYPE 的优先级比 LANG 高。也就是说,如果设置了 LC_ALL,则无论是否设置了 LC_CTYPE 或者 LANG,最终都以 LC_ALL 为准;如果没有设置 LC_ALL , 就以 LC_CTYPE 为准;如果既没有设置 LC_ALL 也没有设置 LC_CTYPE,就以 LANG 为准。

服务器的字符集转换

有的同学可能认为这一步骤多此一举了,但是请考虑下面这个查询语句:

1
mysql> SELECT ' a' = 'A';

这个查询语句的返回结果是 TRUE 还是 FALSE? 其实仅仅根据这个语句是不能确定结果的,这是因为我们并不知道这两个字符串到底采用了什么字符集进行编码,也不知道这里使用 的比较规则是什么。此时, character_set_connection 系统变量就发挥了作用,它表示这些字符串应该使用哪种字符集进行编码。当然,还有 一个与之配套的系统变量 collation_connection ,这个系统变量表示这些字符串应该使用哪种比较规则。

1
SELECT * FROM tt WHERE c = '我';

在执行这个语句前,面临一个很重要的问题:字符串 ’我‘ 是使用 gbk 字符集进行编码的,比较规则是gbk_chinese_ci;而列 c 是采用 utf8 字符集进行编码的,比较规则为 utf8_general_ci,这该怎么比较呢?设计 MySQL 的大叔规定,在这种情况下,列的字符集和排序规则的优先级更高。因此,这里需要将请求中的字符串 ‘我’ 先从 gbk 字符集转换为 utf8 字符集,然后再使用列 c 的比较规则 utf8_general_ci 进行比较。

我们通常都把 character_set_clientcharacter_set_connectioncharacter_set_results 这三个系统变量设置成和客户端使用的字符集一致的情况,这样减少了很多无谓的字符集转换。为了方便我们设置,MySQL提供了一条非常简便的语句:

1
SET NAMES 字符集名;

这一条语句产生的效果和我们执行这3条的效果是一样的:

1
2
3
SET character_set_client = 字符集名;
SET character_set_connection = 字符集名;
SET character_set_results = 字符集名;

3.3.3 比较规则的应用

比较规则的作用通常体现比较字符串大小的表达式以及对某个字符串列进行排序中,所以有时候也称为排序规则。比方说表t的列col使用的字符集是gbk,使用的比较规则是gbk_chinese_ci

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
mysql> SELECT * FROM t ORDER BY col;
+------+
| col  |
+------+
| a    |
| A    |
| b    |
| B    |
| 我   |
+------+
5 rows in set (0.00 sec)

可以看到在默认的比较规则gbk_chinese_ci中是不区分大小写的。

我们现在把列col的比较规则修改为gbk_bin

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
mysql> SELECT * FROM t ORDER BY s;
+------+
| s    |
+------+
| A    |
| B    |
| a    |
| b    |
| 我   |
+------+
5 rows in set (0.00 sec)

所以如果以后大家在对字符串做比较或者对某个字符串列做排序操作时没有得到想象中的结果,需要思考一下是不是比较规则的问题。