首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >[MYSQL] 参数/变量浅析(4) -- 字符集

[MYSQL] 参数/变量浅析(4) -- 字符集

原创
作者头像
大大刺猬
发布2026-07-30 15:48:12
发布2026-07-30 15:48:12
1884
举报
文章被收录于专栏:大大刺猬大大刺猬

导读

之前我们解析frm中comment的时候,发现有部分编码比较怪,是"编码的编码",最终发现其原因竟然和字符集参数(character_set_client)有关. 所以本期我们来讲讲mysql字符集相关的参数.

字符集

我们可以在数据库中使用show variables like '%char%';来查询字符集相关的变量,发现有一堆:

代码语言:txt
复制
(root@127.0.0.1) [(none)]> show variables like '%char%';
+--------------------------+--------------------------------------------------+
| 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     | utf8                                             |
| character_set_system     | utf8                                             |
| character_sets_dir       | /soft/mysql_5744/mysqlbase/mysql/share/charsets/ |
+--------------------------+--------------------------------------------------+

好家伙,这么多,看着相当的懵. 从变量命名来看应该是作用域不同.

我们按顺序一个个的看(这里是按照变量名称的顺序)

character_set_client

先来看看character_set_client, 客户端发送SQL语句给服务端,网络传输的是二进制的数据,服务端接收到后就需要先解码,那么按照哪种编码来解码呢? 就由参数character_set_client设置.

比如客户端发送了包b"C\x00\x00\x00\x03create table db1.t20260730(id int,c1 varchar(20)) comment '\xe6\xb5\x8b\xe8\xaf\x95'",对于这个SQL服务端会使用character_set_client设置的编码来解码.

输入的编码由客户端决定,比如我的OS是LANG="en_US.UTF-8", 所以会先将"测试"使用utf8编码为'\xe6\xb5\x8b\xe8\xaf\x95',然后发送

如果设置的编码(character_set_client)和实际的编码不一致的话,就gg了. 比如:

utf8的"测试",即'\xe6\xb5\x8b\xe8\xaf\x95', 如果让服务端以latin1去解码的话就是'测试',如果该字段字符集是utf8的话,则存储的时候会再encode一下,变成b'\xc3\xa6\xc2\xb5\xe2\x80\xb9\xc3\xa8\xc2\xaf\xe2\x80\xa2',读者可使用ibd2sql去验证.看起来虽然怪怪的, 但再按照这一套编码过程解析的话还是能解析出来的.前提是知道character_set_client的值.(由于该值为会话变量,不会记录在元数据信息里面,所以实际上是无法解析的,当然也可以当作默认值来处理, character_set_client在5.5/5.6默认为latin1,在5.7默认是utf8,在8.0默认是utf8mb4).

mysql的frm中comment是以utf8存储的,如果character_set_client是latin1的话,就会出现comment乱码:

代码语言:txt
复制
13:39:43 [root@ddcw21 ~]#mysqlfrm --diagnostic /data/mysql_5744/mysqldata/db1/t20260730.frm | grep -v "^#"

CREATE TABLE `db1`.`t20260730` (
  `id` int(11) DEFAULT NULL, 
  `c1` varchar(60) DEFAULT NULL 
) ENGINE=InnoDB COMMENT '测试';

有的小伙伴可能会纳闷,既然老版本的mysql默认是latin1,那为啥select/show create table看到的都是正常的呢? 别急,等到character_set_results的时候就知道了.

简单来说character_set_client就是服务端认为的客户端的编码(The character set for statements that arrive from the client)

character_set_connection

我们知道表的各字段都有字符集,不同的字符集是无法做比较的,不然会报错,比如

代码语言:txt
复制
ERROR 1267 (HY000): Illegal mix of collations (latin1_swedish_ci,IMPLICIT) and (gbk_chinese_ci,IMPLICIT) for operation '>'

那么如果不是对表的字段进行比较,而是对常量进行比较呢? 比如

代码语言:txt
复制
(root@127.0.0.1) [(none)]> select 'a' > 'A';
+-----------+
| 'a' > 'A' |
+-----------+
|         1 |
+-----------+
1 row in set (0.00 sec)

不但没报错,反而结果还是True. 也就是说服务端知道那两个字符串常量的字符集和排序规则,并使用该排序规则进行比较. 那么它咋知道是啥呢? 当然是我们的变量character_set_connection了,还有对应的排序规则变量collation_connection. 比如:

代码语言:txt
复制
(root@127.0.0.1) [(none)]> select @@collation_connection;
+------------------------+
| @@collation_connection |
+------------------------+
| latin1_general_cs      |
+------------------------+
1 row in set (0.00 sec)

(root@127.0.0.1) [(none)]> select 'a' > _gbk 'A';
ERROR 1267 (HY000): Illegal mix of collations (latin1_general_cs,COERCIBLE) and (gbk_chinese_ci,COERCIBLE) for operation '>'

从报错我们就可以看出: 使用latin1_general_cs的'a'无法和使用gbk_chinese_ci的'b'进行比较.

字符串常量可以在其前面加上 _charset,后面加上 collate collation的方式来指定其排序规则, 比如 _utf8mb4'a' COLLATE utf8mb4_bin; 字段和常量比较则转常量为字段字符集

简单来说character_set_connection就是字符串常量使用的字符集.

character_set_database

character_set_database表示当前数据库的默认字符集,如果没有use任何数据库,则和character_set_server值相同. 可以修改该值,但没啥意义,就一条warning:"Updating 'character_set_database' is deprecated. It will be made read-only in a future release."

collation_database表示当前数据库的默认排序规则.

character_set_filesystem

character_set_filesystem表示load data/select into outfile时操作系统上的文件名的编码方式. 比如:

代码语言:sql
复制
select * from db1.t20260730_01 into outfile '/tmp/t20260730/测试.sql';
-- 对应二进制包如下:
-- b"H\x00\x00\x00\x03select * from db1.t20260730_01 into outfile '/tmp/t20260730/\xe6\xb5\x8b\xe8\xaf\x95.sql'"

会先根据character_set_client将'/tmp/t20260730/\xe6\xb5\x8b\xe8\xaf\x95.sql'做decode,然后使用character_set_filesystem指定的编码在OS创建相关文件. 如果os上的LANG编码也一样的话,就能正常看到文件(其实还和终端编码有关,不然看到的是乱码.):

代码语言:txt
复制
14:59:55 [root@ddcw21 ibd2sql-ibd2sql-v2.x]#ll /tmp/t20260730/*.sql
-rw-rw-rw- 1 mysql mysql 6 Jul 30 14:54 /tmp/t20260730/????.sql
14:59:56 [root@ddcw21 ibd2sql-ibd2sql-v2.x]#LANG='zh_CN.gbk'
15:00:00 [root@ddcw21 ibd2sql-ibd2sql-v2.x]#ll /tmp/t20260730/*.sql
-rw-rw-rw- 1 mysql mysql 6 7月  30 14:54 /tmp/t20260730/测试.sql

简单来说character_set_filesystem表示服务器上的文件名的编码方式.

character_set_results

character_set_client表示服务端认为客户端的编码, 而character_set_results表示服务端返回的时候需要怎么编码.

比如上面使用latin1创建的表db1.t20260730,对应的comment存储的则是b'\xc3\xa6\xc2\xb5\xe2\x80\xb9\xc3\xa8\xc2\xaf\xe2\x80\xa2', 会先使用utf8解码(frm使用的是utf8)得到'测试',然后将其编码为character_set_results并返回给客户端.

代码语言:txt
复制
# 当character_set_results=latin1时,返回内容如下:
b"\x98\x00\x00\x04\tt20260730\x8dCREATE TABLE `t20260730` (\n  `id` int(11) DEFAULT NULL,\n  `c1` varchar(20) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='\xe6\xb5\x8b\xe8\xaf\x95'"
由于终端是使用的utf8编码,则能够将'\xe6\xb5\x8b\xe8\xaf\x95'解码为'测试', 所以我们看到的是正常的.

# 当character_set_results=utf8时,返回内容如下(其实就是存储的内容):
b"\xa0\x00\x00\x04\tt20260730\x95CREATE TABLE `t20260730` (\n  `id` int(11) DEFAULT NULL,\n  `c1` varchar(20) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='\xc3\xa6\xc2\xb5\xe2\x80\xb9\xc3\xa8\xc2\xaf\xe2\x80\xa2'"
终端使用utf8进行解码,得到'测试'

# 当character_set_results=gbk时,返回内容如下:
b"\x99\x00\x00\x04\tt20260730\x8eCREATE TABLE `t20260730` (\n  `id` int(11) DEFAULT NULL,\n  `c1` varchar(20) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='???\xa8\xa8??'"
由于部分字符不在gbk里面,故使用?代替

简单来说character_set_results就是服务端返回数据时使用的编码.

character_set_server

character_set_server表示服务端默认字符集, 就是create database时使用的字符集.直接看例子吧:

代码语言:txt
复制
(root@127.0.0.1) [(none)]> set character_set_server=gbk;
Query OK, 0 rows affected (0.00 sec)

(root@127.0.0.1) [(none)]> create database db20260730_gbk;
Query OK, 1 row affected (0.01 sec)

(root@127.0.0.1) [(none)]> show create database db20260730_gbk;
+----------------+------------------------------------------------------------------------+
| Database       | Create Database                                                        |
+----------------+------------------------------------------------------------------------+
| db20260730_gbk | CREATE DATABASE `db20260730_gbk` /*!40100 DEFAULT CHARACTER SET gbk */ |
+----------------+------------------------------------------------------------------------+
1 row in set (0.00 sec)

(root@127.0.0.1) [(none)]> set character_set_server=latin1;
Query OK, 0 rows affected (0.00 sec)

(root@127.0.0.1) [(none)]> create database db20260730_latin1;
Query OK, 1 row affected (0.00 sec)

(root@127.0.0.1) [(none)]> show create database db20260730_latin1;
+-------------------+------------------------------------------------------------------------------+
| Database          | Create Database                                                              |
+-------------------+------------------------------------------------------------------------------+
| db20260730_latin1 | CREATE DATABASE `db20260730_latin1` /*!40100 DEFAULT CHARACTER SET latin1 */ |
+-------------------+------------------------------------------------------------------------------+
1 row in set (0.00 sec)

对应的排序规则变量为:collation_server

character_set_system

我们刚才不是说存储的元数据信息使用的utf8编码的么, 其实就是这个变量指定的, 所以这个变量一旦变更,影响巨大,故为只读(实际上无法修改).

character_sets_dir

安装字符集的目录. 不可修改. 其目录下是一些字符集的排序规则和对应的utf8编码文件.

其实ibd2sql也有类似的编码转换文件. 毕竟mysql支持的编码还是挺多的,而py并不都自带.

总结

至此字符集相关的变量就讲完了.比较乱,我们简单总结下:

变量

描述

character_set_client

服务端收到客户端的包时,使用的解码字符集

character_set_connection

服务端处理一些没有字符集的常量时,使用的字符集

character_set_database

显示当前数据库的字符集

character_set_filesystem

对应的文件系统上文件名的编码方式

character_set_results

服务端返回数据给客户端时,使用的编码

character_set_server

服务端默认字符集,就是create database的默认字符集

character_set_system

元数据信息使用的编码,固定为utf8.无法修改

character_sets_dir

字符集目录.无法修改.

再来画个图整理下

参考:

https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html

https://dev.mysql.com/doc/refman/8.0/en/charset-connection.html

https://dev.mysql.com/doc/refman/8.0/en/charset-introducer.html

https://dev.mysql.com/doc/refman/8.0/en/charset-metadata.html

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 导读
  • 字符集
    • character_set_client
    • character_set_connection
    • character_set_database
    • character_set_filesystem
    • character_set_results
    • character_set_server
    • character_set_system
    • character_sets_dir
  • 总结
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档