在MySQL 8.0版本之前,默认字符集为 latin1 ,utf8字符集指向的是 utf8mb3 。网站开发人员在数据库设计的时候往往会将编码修改为utf8字符集。如果遗忘修改默认的编码,就会出现乱码的问题。从MySQL 8.0开始,数据库的默认编码将改为 utf8mb4 ,从而避免上述乱码的问题。
1. 修改MySQL字符集
1.1. 查看默认字符集
show variables like 'character%';
# 或者
show variables like '%char%';MySQL5.7中执行如下:

MySQL 5.7 默认的客户端和服务器都用了 latin1 ,不支持中文,保存中文会报错。示例如下:
ysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| testdb |
+--------------------+
5 rows in set (0.00 sec)
mysql> create database dbdemo1;
Query OK, 1 row affected (0.00 sec)
mysql> use dbdemo1;
Database changed
mysql> create table t_emp(id int,name varchar(15));
Query OK, 0 rows affected (0.00 sec)
mysql> insert into t_emp(id,name) values(1001,'ZhangSan');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t_emp(id,name) values(1002,'李四');
ERROR 1366 (HY000): Incorrect string value: '\xE6\x9D\x8E\xE5\x9B\x9B' for column 'name' at row 1因为默认情况下,创建表使用的是 latin1
mysql> show create table t_emp;
+-------+----------------------------------------------------------------------------------+
| Table | Create Table |
+-------+----------------------------------------------------------------------------------+
| t_emp | CREATE TABLE `t_emp` (
`id` int(11) DEFAULT NULL,
`name` varchar(15) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------+----------------------------------------------------------------------------------+
1 row in set (0.00 sec)1.2. 修改字符集
1、编辑配置文件,在mysqld模块下添加如下内容
# vim /etc/my.cnf
[mysqld]
...
character_set_server=utf8
collation-server=utf8_general_ci2、重启数据库
# systemctl restart mysqld注意:已存在的原库、原表的设定不会发生变化,参数修改只是对新建的数据库生效。
3、修改已有库&表字符集的变更
-- 修改已创建数据库的字符集
alter database dbtest1 character set 'utf8';
-- 修改已创建数据表的字符集
alter table t_emp convert to character set 'utf8';范例:
# 修改已经创建数据库的字符集
-- 修改之前查看字符集
mysql> show create database dbdemo1;
+----------+--------------------------------------------------------------------+
| Database | Create Database |
+----------+--------------------------------------------------------------------+
| dbdemo1 | CREATE DATABASE `dbdemo1` /*!40100 DEFAULT CHARACTER SET latin1 */ |
+----------+--------------------------------------------------------------------+
1 row in set (0.01 sec)
-- 进行修改
mysql> alter database dbdemo1 character set 'utf8';
Query OK, 1 row affected (0.00 sec)
-- 修改之后查看字符集
mysql> show create database dbdemo1;
+----------+------------------------------------------------------------------+
| Database | Create Database |
+----------+------------------------------------------------------------------+
| dbdemo1 | CREATE DATABASE `dbdemo1` /*!40100 DEFAULT CHARACTER SET utf8 */ |
+----------+------------------------------------------------------------------+
1 row in set (0.00 sec)
# 修改已经创建数据表的字符集
-- 修改之前查看字符集
mysql> show create table t_emp;
+-------+------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+------------------------------------------------------------------------------------------------------------------------------+
| t_emp | CREATE TABLE `t_emp` (
`id` int(11) DEFAULT NULL,
`name` varchar(15) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------+------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
-- 进行修改
mysql> alter table t_emp convert to character set 'utf8';
Query OK, 1 row affected (0.02 sec)
Records: 1 Duplicates: 0 Warnings: 0
-- 修改之后查看字符集
mysql> show create table t_emp;
+-------+----------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+----------------------------------------------------------------------------------------------------------------------------+
| t_emp | CREATE TABLE `t_emp` (
`id` int(11) DEFAULT NULL,
`name` varchar(15) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+-------+----------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)注意:原有的数据如果是用非'utf8'编码的话,数据本身编码不会发生改变。已有数据需要导出或删除,然后重新插入。
3.2 各级别的字符集
MySQL有4个级别的字符集和比较规则,分别是:
服务器级别
数据库级别
表级别
列级别
执行如下SQL语句:
mysql> show variables like 'character%';
+--------------------------+----------------------------+
| Variable_name | Value |
+--------------------------+----------------------------+
| character_set_client | utf8 |
| character_set_connection | utf8 |
| character_set_database | utf8 |
| character_set_filesystem | binary |
| character_set_results | utf8 |
| character_set_server | latin1 |
| character_set_system | utf8 |
| character_sets_dir | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
8 rows in set (0.01 sec)character_set_server:服务器级别的字符集。
character_set_database:当前数据库的字符集。
character_set_client:服务器解码请求时使用的字符集。
character_set_connection:服务器处理请求时会把请求字符集从 character_set_client 转为 character_set_connection。
character_set_results:服务器向客户端返回数据时使用的字符集。
2.1 服务器级别
character_set_server:服务器级别的字符集。
我们可以在启动服务器程序时通过启动选项或者在服务器程序运行过程中使用 SET 语句修改这两个变量的值。比如我们可以在配置文件中这样写:
[mysqld]
character_set_server=utf8mb4 # 默认字符集
collation_server=utf8mb4_general_ci # 对应的默认的比较规则当服务器启动的时候读取这个配置文件后这两个系统变量的值便修改了。
2.2 数据库级别
character_set_database:当前数据库的字符集。
我们在创建和修改数据库的时候可以指定该数据库的字符集和比较规则,具体语法如下:
CREATE DATABASE 数据库名
[[DEFAULT] CHARACTER SET 字符集名称]
[[DEFAULT] COLLATE 比较规则名称];
ALTER DATABASE 数据库名
[[DEFAULT] CHARACTER SET 字符集名称]
[[DEFAULT] COLLATE 比较规则名称];
范例:
mysql> create database dbdemo2 character set 'utf8mb4';
Query OK, 1 row affected (0.00 sec)
mysql> show create database dbdemo2;
+----------+---------------------------------------------------------------------+
| Database | Create Database |
+----------+---------------------------------------------------------------------+
| dbdemo2 | CREATE DATABASE `dbdemo2` /*!40100 DEFAULT CHARACTER SET utf8mb4 */ |
+----------+---------------------------------------------------------------------+
1 row in set (0.00 sec)2.3. 表级别
我们也可以在创建和修改表的时候指定表的字符集和比较规则,语法如下:
CREATE TABLE 表名 (列的信息)
[[DEFAULT] CHARACTER SET 字符集名称]
[COLLATE 比较规则名称]]
ALTER TABLE 表名
[[DEFAULT] CHARACTER SET 字符集名称]
[COLLATE 比较规则名称]
范例:
mysql> create table t_user (id int,name varchar(10)) character set 'utf8mb4' collate 'utf8mb4_general_ci';
Query OK, 0 rows affected (0.01 sec)
mysql> show create table t_user;
+--------+--------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+--------+--------------------------------------------------------------------------------------------------------------------------------+
| t_user | CREATE TABLE `t_user` (
`id` int(11) DEFAULT NULL,
`name` varchar(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 |
+--------+--------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)
如果创建和修改表的语句中没有指明字符集和比较规则,将使用该表所在数据库的字符集和比较规则作为该表的字符集和比较规则。
2.4. 列级别
对于存储字符串的列,同一个表中的不同的列也可以有不同的字符集和比较规则。我们在创建和修改列定义的时候可以指定该列的字符集和比较规则,语法如下:
CREATE TABLE 表名(
列名 字符串类型 [CHARACTER SET 字符集名称] [COLLATE 比较规则名称],
其他列...
);
ALTER TABLE 表名 MODIFY 列名 字符串类型 [CHARACTER SET 字符集名称] [COLLATE 比较规则名称];对于某个列来说,如果在创建和修改的语句中没有指明字符集和比较规则,将使用该列所在表的字符集和比较规则作为该列的字符集和比较规则。
在转换列的字符集时需要注意,如果转换前列中存储的数据不能用转换后的字符集进行表示会发生错误。比方说原先列使用的字符集是utf8,列中存储了一些汉字,现在把列的字符集转换为ascii的话就会出错,因为ascii字符集并不能表示汉字字符。
小结:
这4个级别字符集和比较规则的联系如下:
如果
创建或修改列时没有显式的指定字符集和比较规则,则该列默认用表的字符集和比较规则如果
创建表时没有显式的指定字符集和比较规则,则该表默认用数据库的字符集和比较规则如果
创建数据库时没有显式的指定字符集和比较规则,则该数据库默认用服务器的字符集和比较规则
知道了这些规则之后,对于给定的表,我们应该知道它的各个列的字符集和比较规则是什么,从而根据这个列的类型来确定存储数据时每个列的实际数据占用的存储空间大小了。比方说我们向表 t 中插入一条记录:
mysql> INSERT INTO t(col) VALUES('我们');
Query OK, 1 row affected (0.00 sec)
mysql> SELECT * FROM t;
+--------+
| s |
+--------+
| 我们 |
+--------+
1 row in set (0.00 sec)首先列 col 使用的字符集是 gbk ,一个字符 '我' 在 gbk 中的编码为 0xCED2 ,占用两个字节,两个字符的实际数据就占用4个字节。如果把该列的字符集修改为 utf8 的话,这两个字符就实际占用6个字节。
3.3 字符集和比较规则
3.1. utf8与utf8mb4
utf8 字符集表示一个字符需要使用1~4个字节,但是我们常用的一些字符使用1~3个字节就可以表示了。而字符集表示一个字符所用的最大字节长度,在某些方面会影响系统的存储和性能,所以设计 MySQL的设计者偷偷的定义了两个概念:
utf8mb3:阉割过的utf8字符集,只使用1~3个字节表示字符。utf8mb4:正宗的utf8字符集,使用1~4个字节表示字符。
3.2. 比较规则
上表中,MySQL版本一共支持41种字符集,其中的 Default collation 列表示这种字符集中一种默认的比较规则,里面包含着该比较规则主要作用于哪种语言,比如 utf8_polish_ci 表示以波兰语的规则比较, utf8_spanish_ci 是以西班牙语的规则比较, utf8_general_ci 是一种通用的比较规则。
后缀表示该比较规则是否区分语言中的重音、大小写。具体如下:
最后一列 Maxlen ,它代表该种字符集表示一个字符最多需要几个字节。
常用操作1:
-- 查看GBK字符集的比较规则
SHOW COLLATION LIKE 'gbk%';
-- 查看UTF-8字符集的比较规则
SHOW COLLATION LIKE 'utf8%';常用操作2:
-- 查看服务器的字符集和比较规则
SHOW VARIABLES LIKE '%_server';
-- 查看数据库的字符集和比较规则
SHOW VARIABLES LIKE '%_database';
-- 查看具体数据库的字符集
SHOW CREATE DATABASE dbtest1;
-- 修改具体数据库的字符集
ALTER DATABASE dbtest1 DEFAULT CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';常用操作3:
-- 查看表的字符集
SHOW CREATE TABLE employees;
-- 查看表的比较规则
SHOW TABLE STATUS FROM testdb LIKE 'employees';
-- 修改表的字符集和比较规则
ALTER TABLE emp1 DEFAULT CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';3.3. 请求到响应字符集的变化
这几个系统变量在我的计算机上的默认值如下(不同操作系统的默认值可能不同):

为了体现出字符集在请求处理过程中的变化,我们这里特意修改一个系统变量的值:
mysql> set character_set_connection = gbk;
Query OK, 0 rows affected (0.00 sec)现在假设我们客户端发送的请求是下边这个字符串:
SELECT * FROM t WHERE s = '我';为了方便理解这个过程,我们只分析字符 '我' 在这个过程中字符集的转换。
现在看一下在请求从发送到结果返回过程中字符集的变化:
1、客户端发送请求所使用的字符集
一般情况下客户端所使用的字符集和当前操作系统一致,不同操作系统使用的字符集可能不一样,如下:
类
Unix系统使用的是utf8Windows使用的是gbk
当客户端使用的是 utf8 字符集,字符 '我' 在发送给服务器的请求中的字节形式就是: 0xE68891
如果你使用的是可视化工具,比如navicat之类的,这些工具可能会使用自定义的字符集来编 码发送到服务器的字符串,而不采用操作系统默认的字符集。
2、服务器接收到客户端发送来的请求其实是一串二进制的字节,它会认为这串字节采用的字符集是 character_set_client ,然后把这串字节转换为 character_set_connection 字符集编码的字符。
由于我的计算机上 character_set_client 的值是 utf8 ,首先会按照 utf8 字符集对字节串 0xE68891 进行解码,得到的字符串就是 '我' ,然后按照 character_set_connection 代表的字符集,也就是 gbk 进行编码,得到的结果就是字节串 0xCED2 。
3、因为表 t 的列 col 采用的是 gbk 字符集,与 character_set_connection 一致,所以直接到列中找字节值为 0xCED2 的记录,最后找到了一条记录。
如果某个列使用的字符集和character_set_connection代表的字符集不一致的话,还需要进行一次字符集转换。
4、上一步骤找到的记录中的 col 列其实是一个字节串 0xCED2 , col 列是采用 gbk 进行编码的,所以首先会将这个字节串使用 gbk 进行解码,得到字符串 '我' ,然后再把这个字符串使用 character_set_results 代表的字符集,也就是 utf8 进行编码,得到了新的字节串: 0xE68891 ,然后发送给客户端。
5、由于客户端是用的字符集是 utf8 ,所以可以顺利的将 0xE68891 解释成字符 我 ,从而显示到我们的显示器上,所以我们人类也读懂了返回的结果。
总结图如下:

从这个分析中我们可以得出这么几点需要注意的地方:
服务器认为客户端发送过来的请求时用
character_set_client编码的。
假设你的客户端采用的字符集和 character_set_client 不一样的话,这就会出现识别不准确的情况。比如我的客户端使用的是 utf8 字符集,如果把系统变量 character_set_client 的值设置为 ascii 的话,服务器可能无法理解我们发送的请求,更别谈处理这个请求了。
服务器将把得到的结果集使用
character_set_results编码后发送给客户端。
假设你的客户端采用的字符集和 character_set_results 不一样的话,这就可能会出现客户端无法解码结果集的情况,结果就是在你的屏幕上出现乱码。比如我的客户端使用的是 utf8 字符集,如果把系统变量 character_set_results 的值设置为 ascii 的话,可能会产生乱码。
character_set_connection只是服务器在将请求的字节串从character_set_client转换为character_set_connection时使用,一定要注意,该字符集包含的字符范围一定涵盖请求中的字符,要不然会导致有的字符无法使用character_set_connection代表的字符集进行编码。
经验:
开发中通常把 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 = 字符集名;比如说我的客户端使用的是 utf8 字符集,所以需要把这几个系统变量的值都设置为 utf8;
mysql> SET NAMES utf8;另外,如果你想在启动客户端的时候就把 character_set_client、 character_set_connection、character_set_results 这三个系统变量的值设置成一样的,那我们可以在启动客户端的时候指定一个叫 default-character-set 的启动选项,比如在配置文件里可以这么写:
[client]
default-character-set=utf8它起到的效果和执行一遍 SET NAMES utf8 是一样一样的,都会将那三个系统变量的值设置成 utf8。