目录
suibi

查看数据库字符集格式语句(一文读懂数据库的字符集)


MySQL 数据库支持多种字符集,你可以在数据库中存储多国文字,例如英文、中文、俄文,甚至各种emoji图片。不过作为数据库管理员仅仅知道这些远远不够,我们还需要了解一些字符集背后的一些内幕,这样才能高效的理解和使用MySQL数据库。

本文我们将讨论以下主题:

  1. MySQL数据库字符集的级别
  2. 查看和修改MySQL字符集
  3. 三元组(MySQL客户端字符集、连接字符集和结果字符集)
  4. 查看和修改操作系统/SSH客户端/程序代码级别的字符集

本文的测试环境为MySQL 8,不过也适用于其它版本的MySQL。

本文涉及到很多概念,建议大家参考MySQL官方文档中的解释。

官方文档链接:
https://dev.mysql.com/doc/refman/8.0/en/,官方还提供了PDF版本的下载链接:https://downloads.mysql.com/docs/refman-8.0-en.a4.pdf

数据库信息

在正式开始之前,建议输入 status 命令了解一下MySQL服务器的基本信息。

这里显示了数据库的版本、数据库实例字符集(Server characterset)、数据库字符集(Db characterset)、客户端字符集(Client characterset)、连接字符集(Conn. characterset),可以看到MySQL 8字符集开始改为通用的UTF8MB4。

mysql> status--------------mysql  Ver 8.0.26 for Linux on x86_64 (Source distribution)Connection id:          15Current database:Current user:           root@localhostSSL:                    Not in useCurrent pager:         stdoutUsing outfile:           ''Using delimiter:        ;Server version:             8.0.26 Source distributionProtocol version:          10Connection:              Localhost via UNIX socketServer characterset:     utf8mb4Db     characterset:     utf8mb4Client characterset:    utf8mb4Conn.  characterset:     utf8mb4UNIX socket:            /var/lib/mysql/mysql.sockBinary data as:         HexadecimalUptime:                 6 days 17 hours 51 min 29 secThreads: 2  Questions: 12  Slow queries: 0  Opens: 135  Flush tables: 3  Open tables: 54  Queries per second avg: 0.000--------------

其中比较关键的几个字符集的解释

character_set_client: 服务器认为 character_set_client 系统变量 是客户端SQL语句采用的字符集,不过实际上可能不是这样。character_set_connection: 服务器接收到SQL语句后将其转换为character_set_connection字符集character_set_database: 当前选择/使用中的数据库的字符集character_set_filesystem: 把os上文件名转化成此字符集,即把character_set_client转换character_set_filesystem,默认binary是不做任何转换的character_set_results: 服务器向MySQL客户端返回的查询结果(例如:结果数据(如列值)、结果元数据(如列名)和错误消息)的字符集。character_set_server: 服务器默认使用的字符集character_set_system: MySQL服务器存储系统元数据所使用的字符集,总是utf8mb3

字符集和校对规则

显示数据库支持的字符集(character set)和排序/校对规则(collation)。

本文仅介绍字符集,它的孪生兄弟排序规则的重要性要小一些,留待以后介绍。

mysql> show character set;+----------+---------------------------------+---------------------+--------+| Charset  | Description                     | Default collation   | Maxlen |+----------+---------------------------------+---------------------+--------+| armscii8 | ARMSCII-8 Armenian              | armscii8_general_ci |      1 || ascii    | US ASCII                        | ascii_general_ci    |      1 || big5     | Big5 Traditional Chinese        | big5_chinese_ci     |      2 || binary   | Binary pseudo charset           | binary              |      1 || cp1250   | Windows Central European        | cp1250_general_ci   |      1 || cp1251   | Windows Cyrillic                | cp1251_general_ci   |      1 || cp1256   | Windows Arabic                  | cp1256_general_ci   |      1 || cp1257   | Windows Baltic                  | cp1257_general_ci   |      1 || cp850    | DOS West European               | cp850_general_ci    |      1 || cp852    | DOS Central European            | cp852_general_ci    |      1 || cp866    | DOS Russian                     | cp866_general_ci    |      1 || cp932    | SJIS for Windows Japanese       | cp932_japanese_ci   |      2 || dec8     | DEC West European               | dec8_swedish_ci     |      1 || eucjpms  | UJIS for Windows Japanese       | eucjpms_japanese_ci |      3 || euckr    | EUC-KR Korean                   | euckr_korean_ci     |      2 || gb18030  | China National Standard GB18030 | gb18030_chinese_ci  |      4 || gb2312   | GB2312 Simplified Chinese       | gb2312_chinese_ci   |      2 || gbk      | GBK Simplified Chinese          | gbk_chinese_ci      |      2 || geostd8  | GEOSTD8 Georgian                | geostd8_general_ci  |      1 || greek    | ISO 8859-7 Greek                | greek_general_ci    |      1 || hebrew   | ISO 8859-8 Hebrew               | hebrew_general_ci   |      1 || hp8      | HP West European                | hp8_english_ci      |      1 || keybcs2  | DOS Kamenicky Czech-Slovak      | keybcs2_general_ci  |      1 || koi8r    | KOI8-R Relcom Russian           | koi8r_general_ci    |      1 || koi8u    | KOI8-U Ukrainian                | koi8u_general_ci    |      1 || latin1   | cp1252 West European            | latin1_swedish_ci   |      1 || latin2   | ISO 8859-2 Central European     | latin2_general_ci   |      1 || latin5   | ISO 8859-9 Turkish              | latin5_turkish_ci   |      1 || latin7   | ISO 8859-13 Baltic              | latin7_general_ci   |      1 || macce    | Mac Central European            | macce_general_ci    |      1 || macroman | Mac West European               | macroman_general_ci |      1 || sjis     | Shift-JIS Japanese              | sjis_japanese_ci    |      2 || swe7     | 7bit Swedish                    | swe7_swedish_ci     |      1 || tis620   | TIS620 Thai                     | tis620_thai_ci      |      1 || ucs2     | UCS-2 Unicode                   | ucs2_general_ci     |      2 || ujis     | EUC-JP Japanese                 | ujis_japanese_ci    |      3 || utf16    | UTF-16 Unicode                  | utf16_general_ci    |      4 || utf16le  | UTF-16LE Unicode                | utf16le_general_ci  |      4 || utf32    | UTF-32 Unicode                  | utf32_general_ci    |      4 || utf8     | UTF-8 Unicode                   | utf8_general_ci     |      3 || utf8mb4  | UTF-8 Unicode                   | utf8mb4_0900_ai_ci  |      4 |+----------+---------------------------------+---------------------+--------+41 rows in set (0.04 sec)

查看数据库当前的字符集设置

mysql> show variables like 'character_set%';+--------------------------+----------------------------+| Variable_name            | Value                      |+--------------------------+----------------------------+| character_set_client     | utf8mb4                    || character_set_connection  | utf8mb4                    || character_set_database   | utf8mb4                    || character_set_filesystem  | binary                     || character_set_results     | utf8mb4                    || character_set_server      | utf8mb4                    || character_set_system              | utf8mb3                    || character_sets_dir                   | /usr/share/mysql/charsets/ |+--------------------------+----------------------------+8 rows in set (0.03 sec)

设置数据库字符集

MySQL字符集比较复杂,有些不在MySQL服务器的控制范围之内,包括:

  1. 操作系统级别
  2. 操作系统SSH客户端级别
  3. 程序代码级别

下面这些则在MySQL服务器的控制范围之内,包括:

  1. MySQL实例级别
  2. 数据库中的库级别
  3. 表级别(含字段级别)
  4. MySQL客户端级别

操作系统级别

尽量将系统的字符集和系统中软件的字符集进行统一

# localeen_US.UTF-8# echo $LANG如果想修改系统的字符集,需要修改对应的配置文件,对于Rocky Linux 8 操作系统# cat /etc/locale.confLANG="en_US.UTF-8"# source /etc/locale.conf对于较早的版本可能位于:/etc/sysconfig/i18n

MySQL客户端级别

要想与MySQL数据库进行交互,那么必须通过各种各样的客户端,例如Linux中的bash终端、诸如运行在Windows上的MobaXterm、XShell等、诸如dbeaver之类的数据库管理工具或者通过自己编写代码。这些可以统称为MySQL客户端,它们在向MySQL发送SQL语句之前,需要将SQL语句进行编码,其使用的编码就是我们这里所说的字符集。

例如MobaXterm的字符集在这里:

Settings - Terminal 标签页 - Default terminal font settings

其中的Term Charset 显示了MobaXterm终端设置的字符集

如果将鼠标放到Term charset上面,可以看到一段解释文字:

除非您知道自己在做什么,否则不要更改此设置。大多数Linux的现代发行版都会期望默认设置为UTF-8。在终端会话(SSH、telnet、rsh,…)期间,MobaXterm从服务器接收8位的字节流,为了在终端上显示它们,它需要知道使用什么字符集解释他们。类似地,MobaXterm需要知道如何将键盘输入转换成服务器期望的编码。不幸的是,对于MobaXterm终端和服务器来说,没有令人满意的机制传递该信息,因此必须手动配置该信息。

我们有很多字符集可供选择。默认情况下,MobaXterm将使用UTF-8编码,它可以代表几乎任何字符,来自服务器的数据被解释为UTF-8,和键盘输入使用UTF-8编码然后发送给服务器。这是大多数现代Linux发行版所期望的。但是,如果这对您的服务器来说是错误的,您可以使用它选择一个不同的字符集。

MySQL数据库实例级别字符集

对于MySQL 8,/etc/my.cnf 配置文件的默认内容为:

## This group is read both both by the client and the server# use it for options that affect everything#[client-server]## include all files from the config directory#!includedir /etc/my.cnf.d

MySQL客户端和服务器程序都会读取 [client-server] 中的内容,它还会包含 /etc/my.cnf.d 目录中的3个配置文件:

  1. client.cnf
  2. mysql-default-authentication-plugin.cnf
  3. mysql-server.cnf

我们可以在
/etc/my.cnf.d/mysql-server.cnf 中修改 Server characterset 服务器级别字符集的设置。例如将其修改为utf8。

character-set-server=utf8

此时再次执行 status 命令,可以看到:

Server characterset:    utf8mb3Db     characterset:    utf8mb3Client characterset:    utf8mb4Conn.  characterset:    utf8mb4

注意:Db characterset 也自动改为了与Server characterset一样的 utf8mb3 ,至于原因下面再说。

MySQL数据库中的库级别字符集

Db characterset 或者 character_set_database 表示当前选择/使用中的数据库的数据库级别的字符集。当选择的数据库更改时,MySQL服务器都会设置此变量。如果没有选择数据库,则该变量的值与 character_set_server 的值相同(这也是为什么当你设置了character_set_server=utf8之后,character_set_database也为utf8)。

备注:当前选择/使用中的数据库指的是当前正在操作的数据库,例如执行了 use mysql; ,再次执行 status 或者 show variables like '%character_set%'; 之后,可以看到 Db characterset 已经变为 utf8mb4。

备注:在MySQL 8中,内置的 mysql 数据库的数据库字符集已经变为 utf8mb4,可以输入命令 show create database mysql; 核实。

Server characterset:    utf8mb3Db     characterset:    utf8mb4Client characterset:    utf8mb4Conn.  characterset:    utf8mb4

注意:根据MySQL手册,全局 character_set_database 和 collation_database 系统变量已经弃用,并且将在 MySQL 的未来版本中删除。

关于character_set_server和character_set_database的说明已经完毕,我们将其改回默认值utf8mb4。

我们也可以在创建数据库的时候指定字符集,MySQL命令中的字符集优先级要高于character_set_server变量的值。

创建一个字符集为latin1的数据库create database demo DEFAULT CHARACTER SET latin1;

注意:关于character_set_database的资料很少,我在这里(
https://stackoverflow.com/questions/62698928/what-is-the-use-of-mysql8-variable-character-set-database)找到一点说明,然后在官方手册(https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_character_set_database)中找到了解释,这里也将最重要的一段摘录在下面。

The character set used by the default database. The server sets this variable whenever the default database changes. If there is no default database, the variable has the same value as character_set_server.

As of MySQL 8.0.14, setting the session value of this system variable is a restricted operation. The session user must have privileges sufficient to set restricted session variables. See Section 5.1.9.1, “System Variable Privileges”.

The global character_set_database and collation_database system variables are deprecated; expect them to be removed in a future version of MySQL.

Assigning a value to the session character_set_database and collation_database system variables is deprecated and assignments produce a warning. Expect the session variables to become read-only (and assignments to them to produce an error) in a future version of MySQL in which it remains possible to access the session variables to determine the database character set and collation for the default database.

表级别(含字段级别)

创建表时可以显式指定表的字符集,这里为 utf8 。

create table students(  id int not null,  name varchar(20) not null,  PRIMARY KEY (id) )DEFAULT CHARACTER SET = utf8;

注意:虽然成功创建了students表,但是出现一个警告,使用下面的命令查看警告信息:

'utf8' 当前是字符集 UTF8MB3 的别名,但在未来版本中将是 UTF8MB4 的别名。 请考虑使用 UTF8MB4 以便明确。

mysql> show warnings;+---------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+| Level   | Code | Message                                                                                                                                                                     |+---------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+| Warning | 3719 | 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous. |+---------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+1 row in set (0.00 sec)

不过如果将utf8改为utf8mb3,系统又给出警告:'utf8mb3' is deprecated and will be removed in a future release. Please use utf8mb4 instead.

输入命令确认表格的字符集

mysql> show create table students;

在mysql> 命令提示符后面输入下面的语句,查看字段name的字符集

select character_set_name from information_schema.columns where table_schema='demo' and table_name='students' and column_name='name';

输出结果

网上部分文章介绍了下面的命令

mysql> show full columns from students;

但是实际上发现此命令只描述了列的 校对/Collation 方式

那么如何在创建表时指定列的字符集呢?

首先删除之前创建的students表mysql> drop table students;创建新的students表create table students(  id int not null auto_increment,  name varchar(20) not null,  address varchar(200) CHARACTER SET gbk,  primary key(id)  )DEFAULT CHARSET=utf8mb3;

然后查看students表的各个字段的字符集

select column_name,character_set_name from information_schema.columns where table_schema='demo' and table_name='students';

输出结果如下图所示,可以看到address字段的字符集成功设置为gbk,而name字段的字符集则继承自数据库demo的字符集utf8。

下面再来看看如何修改表和字段的字符集

将students表的字符集改为utf8mb4

ALTER TABLE students DEFAULT CHARACTER SET utf8mb4;

将students表中name字段的字符集改为gbk

ALTER TABLE students CHANGE name name VARCHAR(30) CHARACTER SET gbk;

此外还可以通过一个命令将students表和它的所有字段的字符集都改为 gb2312:

ALTER TABLE students CONVERT TO CHARACTER SET gb2312;

输入命令 show create table students; 检查一下

最后还可以将数据库demo的字符集修改为utf8mb4

注意:此修改只会影响此后创建的表的字符集,对于已经创建的表的字符集没有影响。

ALTER DATABASE demo DEFAULT CHARACTER SET utf8mb4;

MySQL客户端级别字符集

客户端字符集设置涉及到以下3个变量:

  • character_set_client
  • character_set_connection
  • character_set_results

这3个变量设置不当时很容易导致乱码,实践中我们至少需要将它们都设置为相同的字符集,例如utf8mb4。

由于这个操作比较,因此MySQL特意设计了一个快捷命令:

mysql> set names uf8mb4;

当我们在MySQL命令行中插入一条数据时,例如

mysql> insert into students values(10,"孙悟空", "花果山");Query OK, 1 row affected (0.02 sec)

背后都发生了什么事情呢?

  1. 终端(这里是bash,也可以是其它SQL客户端程序)根据操作系统(或者SQL客户端程序)字符集设置将MySQL语句进行编码转换成二进制流
  2. MySQL Server 接收到查询语句后,按照character_set_client字符集将终端发来的二进制流转化为character_set_connection设定的字符集。(也就是说,MySQL服务器认为客户端发来的语句是以character_set_client字符集进行编码的,不过现实情况中可能不一致。)
  3. MySQL服务器执行SQL语句,将数据插入表中。这里如果character_set_connection和表/字段的字符集不一致,那么还会进行一次编码转换,将character_set_connection字符集转换为表/字段字符集。
  4. SQL客户端程序在收到MySQL服务器的查询结果之后,以操作系统(或者SQL客户端程序)字符集设置进行解码和显示。

这里面有几个要点:

MySQL服务器并不知道客户端发来的SQL语句的字符集格式,它只能认为它与你设置的character_set_client字符集一致,因此不存在字符集转换,即MySQL服务器认为客户端发来的二进制流就是character_set_client字符集。想到这里我想如何它们之间进行协商一下就完美了。

character_set_client → character_set_connection → 表/字段字符集 之间如果不一致,那么会发生字符集转换,因为MySQL服务器知道它们都是什么字符集,因此会尽力进行转换,当然如果转换不成功,那么就会发生乱码。

最后再来看一下
character-set-client-handshake 和

skip-character-set-client-handshake。


character-set-client-handshake 
的含义是在握手阶段是否考虑MySQL客户端的编码方式,在MySQL 8中,默认TRUE。也就是说,当MySQL客户端连接到服务器时,它会向服务器发送它想要使用的字符集的名称(我感觉只有部分MySQL客户端,例如mysql,才会和MySQL服务器进行协商,而其它客户端可能不会)。MySQL服务器使用该字符集来设置 character_set_client、character_set_results 和 character_set_connection 系统变量。 实际上,MySQL服务器执行的是命令 SET NAMES。

不过,有时候你希望忽略客户端字符集设置,此时可以使用命令行参数
--skip-character-set-client-handshake 启动mysqld,此时当客户端再次连接到服务器时,虽然它也会向服务器发送它想要使用的字符集的名称。但是,这次服务器会忽略来自客户端的此请求。

以上描述来自MySQL参考手册:
https://dev.mysql.com/doc/refman/8.0/en/faqs-cjk.html,问题:A.11.10. I've upgraded to MySQL 8.0. How can I revert to behavior like that in MySQL 4.0 with regard to character sets? 其中还给出了一个例子解释了这两个变量的用法,主要是为了解决非英语国家MySQL客户端字符集与数据库字符集不同从而导致乱码的问题。

想了解各个变量的含义,请参考
https://dev.mysql.com/doc/refman/8.0/en/server-option-variable-reference.html。

终于将数据库字符集的相关知识梳理了一遍,希望对您有所帮助。当然文中的描述也不一定全对,欢迎讨论。


写笔记

咨询

您的电子邮箱地址不会被公开。