参考:《MySQL是怎样运行的》,小孩子
字符集
# 查看字符集
show (character set|charset) [like 匹配的模式];
# utf8(utf8mb3) 与 utf8mb4 有什么不同?
utf8(utf8mb3):用 1-3 字节表示一个字符
utf8mb4:用 1-4 字节表示一个字符,支持 emoji 表情哦
# Maxlen 表示该字符集表示一个字符的最大长度(Byte字节数)
MariaDB [(none)]> show character set like "utf8%";
+---------+---------------+--------------------+--------+
| Charset | Description | Default collation | Maxlen |
+---------+---------------+--------------------+--------+
| utf8 | UTF-8 Unicode | utf8_general_ci | 3 |
| utf8mb4 | UTF-8 Unicode | utf8mb4_general_ci | 4 |
+---------+---------------+--------------------+--------+
2 rows in set (0.00 sec)
# 比较重要的字符集 Character Set 及表达一个字符的最大长度 Maxlen
MariaDB [information_schema]> select * from CHARACTER_SETS where CHARACTER_SET_NAME regexp "utf8|latin1|ascii|gb";
+--------------------+----------------------+---------------------------+--------+
| CHARACTER_SET_NAME | DEFAULT_COLLATE_NAME | DESCRIPTION | MAXLEN |
+--------------------+----------------------+---------------------------+--------+
| latin1 | latin1_swedish_ci | cp1252 West European | 1 |
| ascii | ascii_general_ci | US ASCII | 1 |
| gb2312 | gb2312_chinese_ci | GB2312 Simplified Chinese | 2 |
| gbk | gbk_chinese_ci | GBK Simplified Chinese | 2 |
| utf8 | utf8_general_ci | UTF-8 Unicode | 3 |
| utf8mb4 | utf8mb4_general_ci | UTF-8 Unicode | 4 |
+--------------------+----------------------+---------------------------+--------+
6 rows in set (0.00 sec)
比较规则
# 查询 Utf8 字符集下的校验规则
MariaDB [information_schema]> SHOW COLLATION LIKE 'utf8\_%';
+--------------------------+---------+-----+---------+----------+---------+
| Collation | Charset | Id | Default | Compiled | Sortlen |
+--------------------------+---------+-----+---------+----------+---------+
| utf8_general_ci | utf8 | 33 | Yes | Yes | 1 |
| utf8_bin | utf8 | 83 | | Yes | 1 |
| utf8_unicode_ci | utf8 | 192 | | Yes | 8 |
| utf8_icelandic_ci | utf8 | 193 | | Yes | 8 |
| utf8_latvian_ci | utf8 | 194 | | Yes | 8 |
| utf8_romanian_ci | utf8 | 195 | | Yes | 8 |
| utf8_slovenian_ci | utf8 | 196 | | Yes | 8 |
| utf8_polish_ci | utf8 | 197 | | Yes | 8 |
| utf8_estonian_ci | utf8 | 198 | | Yes | 8 |
| utf8_spanish_ci | utf8 | 199 | | Yes | 8 |
| utf8_swedish_ci | utf8 | 200 | | Yes | 8 |
| utf8_turkish_ci | utf8 | 201 | | Yes | 8 |
| utf8_czech_ci | utf8 | 202 | | Yes | 8 |
| utf8_danish_ci | utf8 | 203 | | Yes | 8 |
| utf8_lithuanian_ci | utf8 | 204 | | Yes | 8 |
| utf8_slovak_ci | utf8 | 205 | | Yes | 8 |
| utf8_spanish2_ci | utf8 | 206 | | Yes | 8 |
| utf8_roman_ci | utf8 | 207 | | Yes | 8 |
| utf8_persian_ci | utf8 | 208 | | Yes | 8 |
| utf8_esperanto_ci | utf8 | 209 | | Yes | 8 |
| utf8_hungarian_ci | utf8 | 210 | | Yes | 8 |
| utf8_sinhala_ci | utf8 | 211 | | Yes | 8 |
| utf8_croatian_ci | utf8 | 213 | | Yes | 8 |
| utf8_general_mysql500_ci | utf8 | 223 | | Yes | 1 |
+--------------------------+---------+-----+---------+----------+---------+
24 rows in set (0.00 sec)
校验规则前缀名表达含义:前缀名表示着所属字符集 。
校验规则中间名表达含义:中间名表示着该比较规则主要作用于哪种语言,比如utf8_polish_ci
表示以波兰语的规则比较 。
校验规则后缀名表达含义:
后缀 | 英文释义 | 描述 |
---|---|---|
_ai |
accent insensitive |
不区分重音 |
_as |
accent sensitive |
区分重音 |
_ci |
case insensitive |
不区分大小写 |
_cs |
case sensitive |
区分大小写 |
_bin |
binary |
以二进制方式比较 |
字符集 & 比较规则 的级别
MySQL
有4个级别的字符集和比较规则,分别是:
- 服务器级别
- 数据库级别
- 表级别
- 列级别
服务器级别
# 查看服务器级别字符集
MariaDB [(none)]> show variables like "character_set_server";
+----------------------+--------+
| Variable_name | Value |
+----------------------+--------+
| character_set_server | latin1 |
+----------------------+--------+
1 row in set (0.00 sec)
# 查看服务器级别字符校验规则
MariaDB [(none)]> show variables like "collation_server";
+------------------+-------------------+
| Variable_name | Value |
+------------------+-------------------+
| collation_server | latin1_swedish_ci |
+------------------+-------------------+
1 row in set (0.00 sec)
# 更改服务器级别字符集
MariaDB [(none)]> set character_set_server="utf8";
Query OK, 0 rows affected (0.00 sec)
# 更改服务器级别校验规则
MariaDB [(none)]> set collation_server="utf8_general_ci";
Query OK, 0 rows affected (0.00 sec)
# 验证结果(字符集)
MariaDB [(none)]> show variables like "character_set_server";
+----------------------+-------+
| Variable_name | Value |
+----------------------+-------+
| character_set_server | utf8 |
+----------------------+-------+
1 row in set (0.00 sec)
# 验证结果(校验规则)
MariaDB [(none)]> show variables like "collation_server";
+------------------+-----------------+
| Variable_name | Value |
+------------------+-----------------+
| collation_server | utf8_general_ci |
+------------------+-----------------+
1 row in set (0.00 sec)
数据库级别
创建数据库时,若不指定数据库级别的字符集和校验规则,则默认使用以下字符集和校验规则:
# 无非还是环境变量啦,想修改 Set 下就好
MariaDB [(none)]> show variables like "character_set_database";
+------------------------+--------+
| Variable_name | Value |
+------------------------+--------+
| character_set_database | latin1 |
+------------------------+--------+
1 row in set (0.00 sec)
MariaDB [(none)]> show variables like "collation_database";
+--------------------+-------------------+
| Variable_name | Value |
+--------------------+-------------------+
| collation_database | latin1_swedish_ci |
+--------------------+-------------------+
1 row in set (0.00 sec)
# ============================== 关于 Set ================================= #
# 以下命令只能改变当前 Session 的环境变量,当前的 Session 即刻生效,其他 Session 不会生效
MariaDB [(none)]> set character_set_database="utf8";
# 以下命令可以改变全局 Session 的环境变量,但已经开启的 Session 不会生效(新开启的 Session 会生效)
MariaDB [(none)]> set global character_set_database="utf8";
# =================== 想修改某个变量时,两个命令全部执行一次即可 ========================= #
# =================== 以上变量在 MySQL 重启后失效 ============================ #
# =================== 修改配置文件后重启即可永久生效 ============================ #
如果不想受制于环境变量,那么就在创建数据库时指定下字符集与校验规则吧:
# 举个反例,utf8mb4_general_ci 无法适配 utf8 字符集(utf8 字符集中没有此校验规则)
MariaDB [(none)]> create database forethan character set utf8 collate utf8mb4_general_ci;
ERROR 1253 (42000): COLLATION 'utf8mb4_general_ci' is not valid for CHARACTER SET 'utf8'
# 指定字符集创建一个新的数据库 forethan
MariaDB [(none)]> create database forethan character set utf8mb4 collate utf8mb4_general_ci;
Query OK, 1 row affected (0.00 sec)
# 看看建库语句
MariaDB [(none)]> show create database forethan;
+----------+----------------------------------------------------------------------+
| Database | Create Database |
+----------+----------------------------------------------------------------------+
| forethan | CREATE DATABASE `forethan` /*!40100 DEFAULT CHARACTER SET utf8mb4 */ |
+----------+----------------------------------------------------------------------+
1 row in set (0.00 sec)
# 指定字符集创建一个新的数据库 forkiki
MariaDB [(none)]> create database forkiki character set gb2312 collate gb2312_chinese_ci;
Query OK, 1 row affected (0.00 sec)
# 看看建库语句
MariaDB [(none)]> show create database forkiki;
+----------+--------------------------------------------------------------------+
| Database | Create Database |
+----------+--------------------------------------------------------------------+
| forkiki | CREATE DATABASE `forkiki` /*!40100 DEFAULT CHARACTER SET gb2312 */ |
+----------+--------------------------------------------------------------------+
1 row in set (0.00 sec)
表级别
Create Table 表名 (列的信息)
[[DEFAULT] CHARACTER SET 字符集名称]
[COLLATE 比较规则名称];
Alter Table 表名
[[DEFAULT] CHARACTER SET 字符集名称]
[COLLATE 比较规则名称];
# Example:
mysql> CREATE TABLE t(
-> col VARCHAR(10)
-> # 设置表级别的字符集和比较规则
-> ) CHARACTER SET utf8 COLLATE utf8_general_ci;
Query OK, 0 rows affected (0.03 sec)
如果建表时,不指定表的字符集和比较规则,则继承数据库级别的字符集和比较规则 。
列级别
同一个表中,不同列可以有不同的字符集和比较规则
Create Table 表名(
列名 字符串类型 [CHARACTER SET 字符集名称] [COLLATE 比较规则名称],
列名 ......
);
Alter Table 表名 Modify 列名 字符串类型
[CHARACTER SET 字符集名称]
[COLLATE 比较规则名称];
# Example:
mysql> ALTER TABLE t MODIFY col VARCHAR(10) CHARACTER SET gbk COLLATE gbk_chinese_ci;
Query OK, 0 rows affected (0.04 sec)
Records: 0 Duplicates: 0 Warnings: 0
每个级别中,仅修改字符集或者比较规则
由于字符集和比较规则是互相有联系的,如果我们只修改了字符集,比较规则也会跟着变化,如果只修改了比较规则,字符集也会跟着变化,具体规则如下:
- 只修改字符集,则比较规则将变为修改后的字符集默认的比较规则。
- 只修改比较规则,则字符集将变为修改后的比较规则对应的字符集。
MySQL 中的字符集的转换
那么,在 MySQL 客户端发起请求 -> MySQL 服务端 -> MySQL 客户端 这条流程中,字符集都经过了哪些转换?
如上所述,一次完整的转换过程中,会被 MySQL 三个系统变量所影响:
系统变量 | 描述 |
---|---|
character_set_client |
服务器解码请求时使用的字符集 |
character_set_connection |
服务器处理请求时会把请求字符串从character_set_client 转为character_set_connection |
character_set_results |
服务器向客户端返回数据时使用的字符集 |
一次请求被 MySQL 服务端处理
服务器会默认认为客户端操作系统使用 character_set_client & character_set_results 字符集,如果不同:
-
如果客户端操作系统字符集和 character_set_client 不一致,服务器可能无法理解我们发送的请求,更别谈处理这个请求了
-
如果客户端操作系统字符集和 character_set_results 不一致,那么返回到客户端的界面的就会使乱码
图中的 character_set_connection 就没多重要,但是一定要注意,该字符集包含的字符范围一定涵盖请求中的字符,要不然会导致有的字符无法使用character_set_connection
代表的字符集进行编码。比如你把character_set_client
设置为utf8
,把character_set_connection
设置成ascii
,那么此时你如果从客户端发送一个汉字到服务器,那么服务器无法使用ascii
字符集来编码这个汉字,就会向用户发出一个警告 。
MySQL
中从发送请求到返回结果过程里发生的各种字符集转换,但是为啥要转来转去的呢?不晕么?
所以我们通常都把 character_set_client 、character_set_connection、character_set_results 这三个系统变量设置成和客户端使用的字符集一致的情况,这样减少了很多无谓的字符集转换。为了方便我们设置,MySQL
提供了一条非常简便的语句:
SET NAMES 字符集名;
这一条语句产生的效果和我们执行这3条的效果是一样的:
SET character_set_client = 字符集名;
SET character_set_connection = 字符集名;
SET character_set_results = 字符集名;
小贴士:
如果你使用的是Windows系统,那应该设置成gbk。
另外,如果你想在启动客户端的时候就把character_set_client
、character_set_connection
、character_set_results
这三个系统变量的值设置成一样的,那我们可以在启动客户端的时候指定一个叫default-character-set
的启动选项,比如在配置文件里可以这么写:
[client]
default-character-set=utf8
比较规则的含义
比较规则是对于字符集中的字符比较大小的一种规则,又称为排序规则、校验规则 。
表t
的列col
使用的字符集是gbk
,使用的比较规则是gbk_chinese_ci
,我们向里边插入几条记录:
-- 确认使用的比较规则是 gbk_chinese_ci
mysql> ALTER TABLE t MODIFY col VARCHAR(10) COLLATE gbk_chinese_ci;
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
-- 插入实验数据
mysql> INSERT INTO t(col) VALUES('a'), ('b'), ('A'), ('B');
Query OK, 4 rows affected (0.00 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql>
我们查询的时候按照t
列排序一下:
-- 默认排序结果
mysql> SELECT * FROM t ORDER BY col;
+------+
| col |
+------+
| a |
| A |
| b |
| B |
+------+
4 rows in set (0.00 sec)
可以看到在默认的比较规则gbk_chinese_ci
中是不区分大小写的,我们现在把列col
的比较规则修改为gbk_bin
:
-- 更改比较规则为 gbk_bin
mysql> ALTER TABLE t MODIFY col VARCHAR(10) COLLATE gbk_bin;
Query OK, 4 rows affected (0.02 sec)
Records: 4 Duplicates: 0 Warnings: 0
由于gbk_bin
是直接比较字符的编码,所以是区分大小写的,我们再看一下排序后的查询结果:
-- 默认排序结果
mysql> SELECT * FROM t ORDER BY col;
+------+
| s |
+------+
| A |
| B |
| a |
| b |
+------+
4 rows in set (0.00 sec)
mysql>
ALTER 语句中的 CHANGE 与 MODIFY
-- CHANGE ~ 既可以更改列名,也可以更改列的类型
ALTER TABLE project_list
CHANGE COLUMN descriptionofproj proj_desc VARCHAR(100),
CHANGE COLUMN contractoronjob con_name VARCHAR(30);
-- MODIFY ~ 使用它可以只更改列的类型
ALTER TABLE project_list
MODIFY COLUMN proj_desc VACHAR(120);
总结:
①. — 既更改列名也更改类型,用 CHANGE
②. — 只修改类型,用 MODIFY
总结
①. — 字符集`指的是某个字符范围的编码规则。
②. — 比较规则
是针对某个字符集中的字符比较大小的一种规则。
③. — 在MySQL
中,一个字符集可以有若干种比较规则,其中有一个默认的比较规则,一个比较规则必须对应一个字符集。
④. — 查看MySQL
中查看支持的字符集和比较规则的语句如下:
SHOW (CHARACTER SET|CHARSET) [LIKE 匹配的模式];
SHOW COLLATION [LIKE 匹配的模式];
⑤. — MySQL有四个级别的字符集和比较规则
-
服务器级别
character_set_server
表示服务器级别的字符集,collation_server
表示服务器级别的比较规则。 -
数据库级别
创建和修改数据库时可以指定字符集和比较规则:
CREATE DATABASE 数据库名 [[DEFAULT] CHARACTER SET 字符集名称] [[DEFAULT] COLLATE 比较规则名称]; ALTER DATABASE 数据库名 [[DEFAULT] CHARACTER SET 字符集名称] [[DEFAULT] COLLATE 比较规则名称];
character_set_database
表示当前数据库的字符集,collation_database
表示当前默认数据库的比较规则,这两个系统变量是只读的,不能修改。如果没有指定当前默认数据库,则变量与相应的服务器级系统变量具有相同的值。 -
表级别
创建和修改表的时候指定表的字符集和比较规则:
CREATE TABLE 表名 (列的信息) [[DEFAULT] CHARACTER SET 字符集名称] [COLLATE 比较规则名称]]; ALTER TABLE 表名 [[DEFAULT] CHARACTER SET 字符集名称] [COLLATE 比较规则名称];
-
列级别
创建和修改列定义的时候可以指定该列的字符集和比较规则:
CREATE TABLE 表名( 列名 字符串类型 [CHARACTER SET 字符集名称] [COLLATE 比较规则名称], 其他列... ); ALTER TABLE 表名 MODIFY 列名 字符串类型 [CHARACTER SET 字符集名称] [COLLATE 比较规则名称];
-
从发送请求到接收结果过程中发生的字符集转换:
- 客户端使用操作系统的字符集编码请求字符串,向服务器发送的是经过编码的一个字节串。
- 服务器将客户端发送来的字节串采用
character_set_client
代表的字符集进行解码,将解码后的字符串再按照character_set_connection
代表的字符集进行编码。 - 如果
character_set_connection
代表的字符集和具体操作的列使用的字符集一致,则直接进行相应操作,否则的话需要将请求中的字符串从character_set_connection
代表的字符集转换为具体操作的列使用的字符集之后再进行操作。 - 将从某个列获取到的字节串从该列使用的字符集转换为
character_set_results
代表的字符集后发送到客户端。 - 客户端使用操作系统的字符集解析收到的结果集字节串。
在这个过程中各个系统变量的含义如下:
系统变量 描述 character_set_client
服务器解码请求时使用的字符集 character_set_connection
服务器处理请求时会把请求字符串从 character_set_client
转为character_set_connection
character_set_results
服务器向客户端返回数据时使用的字符集 一般情况下要使用保持这三个变量的值和客户端使用的字符集相同。
-
比较规则的作用通常体现比较字符串大小的表达式以及对某个字符串列进行排序中。