10.4 连接字符集和排序规则

“连接”是 Client 端程序在连接到服务器时开始与服务器交互的会话的过程。Client 端通过会话连接发送 SQL 语句,例如查询。服务器通过连接将响应(例如结果集或错误消息)发送回 Client 端。

连接字符集和排序规则系统变量

几个字符集和整理系统变量与 Client 端与服务器的交互有关。其中一些已在前面的部分中提到:

在处理 Client 端和服务器之间的连接通信时,还涉及其他字符集和排序规则系统变量。每个 Client 端都有特定于会话的与连接有关的字符集和排序规则系统变量。这些会话系统变量值在连接时初始化,但可以在会话中更改。

关于 Client 端连接的字符集和排序规则处理的几个问题可以通过系统变量来回答:

  • 语句离开 Client 时使用什么字符集?

服务器将character_set_client系统变量作为字符集,Client 端在其中发送语句。

Note

某些字符集不能用作 Client 端字符集。参见不允许的 Client 字符集

  • 服务器在收到语句后应将其转换为什么字符集?

为了确定这一点,服务器使用character_set_connectioncollation_connection系统变量:

character_set_results系统变量 table 示服务器将查询结果返回给 Client 端的字符集。这包括结果数据(例如列值),结果元数据(例如列名)和错误消息。

要告诉服务器不执行结果集或错误消息的转换,请将character_set_results设置为NULLbinary

SET character_set_results = NULL;
SET character_set_results = binary;

有关字符集和错误消息的更多信息,请参见第 10.6 节“错误消息字符集”

要查看适用于当前会话的字符集和排序规则系统变量的值,请使用以下语句:

SELECT * FROM performance_schema.session_variables
WHERE VARIABLE_NAME IN (
'character_set_client', 'character_set_connection',
'character_set_results', 'collation_connection'
) ORDER BY VARIABLE_NAME;

以下更简单的语句还显示连接变量,但也包括其他相关变量。它们对于查看* all *字符集和归类系统变量很有用:

SHOW SESSION VARIABLES LIKE 'character\_set\_%';
SHOW SESSION VARIABLES LIKE 'collation\_%';

Client 端可以微调这些变量的设置,或者取决于默认值(在这种情况下,您可以跳过本节的其余部分)。如果不使用默认值,则必须更改与服务器的每个连接的字符设置。

不允许的 Client 字符集

character_set_client系统变量不能设置为某些字符集:

ucs2
utf16
utf16le
utf32

尝试将任何这些字符集用作 Client 端字符集都会产生错误:

mysql> SET character_set_client = 'ucs2';
ERROR 1231 (42000): Variable 'character_set_client'
can't be set to the value of 'ucs2'

如果在以下上下文中使用了任何这些字符集,则会发生相同的错误,所有这些都导致尝试将character_set_client设置为命名字符集:

Client 端程序连接字符集配置

当 Client 端连接到服务器时,它指示它要用于与服务器通信的字符集。 (实际上,Client 端指示该字符集的默认排序规则,服务器可以从中确定字符集.)服务器使用此信息将character_set_clientcharacter_set_resultscharacter_set_connection系统变量设置为字符集,并将collation_connection设置为字符设置默认排序规则。实际上,服务器执行等效于SET NAMES的操作。

如果服务器不支持所请求的字符集或排序规则,它会退回到使用服务器字符集和排序规则来配置连接。有关此后备行为的更多详细信息,请参见连接字符集错误处理

mysqlmysqladminmysqlcheckmysqlimportmysqlshowClient 端程序确定要使用的默认字符集,如下所示:

  • 在没有其他信息的情况下,每个 Client 端都使用内置的默认字符集,通常为latin1

  • 每个 Client 端都可以基于 os 设置自动检测要使用的字符集,例如 Unix 系统上的LANGLC_ALL语言环境变量的值或 Windows 系统上的代码页设置。对于从 OS 可以在其上使用语言环境的系统,Client 端使用它来设置默认字符集,而不是使用已编译的默认字符集。例如,将LANG设置为ru_RU.KOI8-R会导致使用koi8r字符集。因此,用户可以在其环境中配置区域设置,以供 MySQLClient 端使用。

如果没有完全匹配,则 OS 字符集将 Map 到最接近的 MySQL 字符集。如果 Client 端不支持匹配的字符集,则使用默认编译器。例如,不支持将ucs2作为连接字符集,因此它 Map 为已编译的默认值。

在连接到服务器之前,C 应用程序可以通过调用mysql_options()来基于 os 设置使用字符集自动检测:

mysql_options(mysql,
              MYSQL_SET_CHARSET_NAME,
              MYSQL_AUTODETECT_CHARSET_NAME);
  • 每个 Client 端都支持--default-character-set选项,该选项使用户可以显式指定字符集,以覆盖 Client 端否则确定的默认值。

Note

某些字符集不能用作 Client 端字符集。尝试将它们与--default-character-set一起使用会产生错误。参见不允许的 Client 字符集

使用mysqlClient 端,要使用与默认字符集不同的字符集,可以在每次连接到服务器时显式执行SET NAMES语句(请参见Client 端程序连接字符集配置)。要更轻松地完成相同的结果,请在选项文件中指定字符集。例如,以下选项文件设置每次调用mysql时会将三个与连接有关的字符集系统变量设置为koi8r

[mysql]
default-character-set=koi8r

如果您使用的是启用了自动重新连接的mysqlClient 端(不建议这样做),则最好使用charset命令而不是SET NAMES。例如:

mysql> charset koi8r
Charset changed

charset命令发出SET NAMES语句,并更改mysql在断开连接后重新连接时使用的默认字符集。

配置 Client 端程序时,还必须考虑它们在其中执行的环境。参见第 10.5 节“配置应用程序字符集和排序规则”

用于连接字符集配置的 SQL 语句

构建连接后,Client 端可以更改当前会话的字符集和整理系统变量。可以使用SET语句分别更改这些变量,但是还有两个更方便的语句会成组地影响与连接相关的字符集系统变量:

  • SET NAMES 'charset_name' [COLLATE 'collation_name']

SET NAMES指示 Client 端将使用什么字符集将 SQL 语句发送到服务器。因此,设置名称'cp1251'告诉服务器,“该 Client 端的 Future 传入消息位于字符集cp1251中。”它还指定服务器用于将结果发送回 Client 端的字符集。 (例如,如果您使用产生结果集的SELECT语句,则它指示用于列值的字符集。)

SET NAMES“字符集名称”语句等效于以下三个语句:

SET character_set_client = charset_name;
SET character_set_results = charset_name;
SET character_set_connection = charset_name;

character_set_connection设置为* charset_name 也会将collation_connection隐式设置为 charset_name *的默认排序规则。不必显式设置该排序规则。要指定用于collation_connection的特定排序规则,请添加COLLATE子句:

SET NAMES 'charset_name' COLLATE 'collation_name'
  • SET CHARACTER SET 'charset_name '

设置字符集SET NAMES类似,但是将character_set_connectioncollation_connection设置为character_set_databasecollation_database(如前所述,它们指示默认数据库的字符集和排序规则)。

SET CHARACTER SET charset_name语句等效于以下三个语句:

SET character_set_client = charset_name;
SET character_set_results = charset_name;
SET collation_connection = @@collation_database;

设置collation_connection还会将character_set_connection隐式设置为与排序规则关联的字符集(等同于执行SET character_set_connection = @@character_set_database)。不必显式设置character_set_connection

Note

某些字符集不能用作 Client 端字符集。尝试将它们与SET NAMES设置字符集一起使用会产生错误。参见不允许的 Client 字符集

示例:假设column1被定义为CHAR(5) CHARACTER SET latin2。如果您不说SET NAMES设置字符集,那么对于SELECT column1 FROM t,服务器将使用 Client 端在连接时指定的字符集发回column1的所有值。另一方面,如果在发出SELECT语句之前说SET NAMES 'latin1'SET CHARACTER SET 'latin1',则服务器会在将结果发送回之前将latin2的值转换为latin1。对于不在两个字符集中的字符,转换可能会造成损失。

连接字符集错误处理

尝试使用不合适的连接字符集或排序规则会产生错误,或导致服务器退回到给定连接的默认字符集和排序规则。本节介绍配置连接字符集时可能出现的问题。构建连接或在已构建的 Connecting 更改字符集时,可能会出现这些问题。

连接时错误处理

某些字符集不能用作 Client 端字符集;某些字符集不能用作 Client 端字符集。参见不允许的 Client 字符集。如果您指定了有效但不能作为 Client 端字符集的字符集,则服务器将返回错误:

shell> mysql --default-character-set=ucs2
ERROR 1231 (42000): Variable 'character_set_client' can't be set to
the value of 'ucs2'

如果指定了 Client 端无法识别的字符集,则会产生错误:

shell> mysql --default-character-set=bogus
mysql: Character set 'bogus' is not a compiled character set and is
not specified in the '/usr/local/mysql/share/charsets/Index.xml' file
ERROR 2019 (HY000): Can't initialize character set bogus
(path: /usr/local/mysql/share/charsets/)

如果指定了 Client 端可以识别但服务器不能识别的字符集,则服务器将退回到其默认字符集和排序规则。假设服务器配置为使用latin1latin1_swedish_ci作为其默认值,并且不能将gb18030识别为有效字符集。指定--default-character-set=gb18030的 Client 端可以连接到服务器,但是生成的字符集不是 Client 端想要的:

mysql> SHOW SESSION VARIABLES LIKE 'character\_set\_%';
+--------------------------+--------+
| Variable_name            | Value  |
+--------------------------+--------+
| character_set_client     | latin1 |
| character_set_connection | latin1 |
...
| character_set_results    | latin1 |
...
+--------------------------+--------+
mysql> SHOW SESSION VARIABLES LIKE 'collation_connection';
+----------------------+-------------------+
| Variable_name        | Value             |
+----------------------+-------------------+
| collation_connection | latin1_swedish_ci |
+----------------------+-------------------+

您可以看到连接系统变量已设置为反映latin1latin1_swedish_ci的字符集和排序规则。发生这种情况是因为服务器无法满足 Client 端字符集请求,并退回到其默认值。

在这种情况下,Client 端无法使用所需的字符集,因为服务器不支持它。Client 端必须要么愿意使用其他字符集,要么连接到支持所需字符集的其他服务器。

在更微妙的上下文中也会发生相同的问题:当 Client 端告诉服务器使用服务器可以识别的字符集,但是服务器端不知道 Client 端上该字符集的默认排序规则。例如,当 MySQL 8.0Client 端希望使用utf8mb4作为 Client 端字符集连接到 MySQL 5.7 服务器时,就会发生这种情况。指定--default-character-set=utf8mb4的 Client 端可以连接到服务器。但是,如前面的示例所示,服务器回退到其默认字符集和排序规则,而不是 Client 端请求的内容:

mysql> SHOW SESSION VARIABLES LIKE 'character\_set\_%';
+--------------------------+--------+
| Variable_name            | Value  |
+--------------------------+--------+
| character_set_client     | latin1 |
| character_set_connection | latin1 |
...
| character_set_results    | latin1 |
...
+--------------------------+--------+
mysql> SHOW SESSION VARIABLES LIKE 'collation_connection';
+----------------------+-------------------+
| Variable_name        | Value             |
+----------------------+-------------------+
| collation_connection | latin1_swedish_ci |
+----------------------+-------------------+

为什么会发生这种情况?毕竟 8.0Client 端和 5.7 服务器都知道utf8mb4,因此它们都可以识别它们。要了解此行为,必须了解,当 Client 端告诉服务器要使用哪个字符集时,它实际上告诉服务器该字符集的默认排序规则。因此,由于多种因素,导致发生上述行为:

  • utf8mb4的默认排序规则在 MySQL 5.7 和 8.0 之间有所不同(utf8mb4_general_citable 示 5.7,utf8mb4_0900_ai_citable 示 8.0)。

  • 当 8.0Client 端请求字符集utf8mb4时,它发送给服务器的是默认的 8.0 utf8mb4归类;即utf8mb4_0900_ai_ci

  • utf8mb4_0900_ai_ci仅从 MySQL 8.0 开始实现,因此 5.7 服务器无法识别它。

  • 因为 5.7 服务器无法识别utf8mb4_0900_ai_ci,所以它不能满足 Client 端字符集请求,并退回到其默认字符集和排序规则(latin1latin1_swedish_ci)。

在这种情况下,Client 端在连接后仍然可以通过发出SET NAMES 'utf8mb4'语句来使用utf8mb4。生成的整理是 5.7 默认utf8mb4整理;就是utf8mb4_general_ci。如果 Client 端另外想要utf8mb4_0900_ai_ci的排序规则,则它不能实现这一点,因为服务器无法识别该排序规则。Client 端必须要么愿意使用其他utf8mb4归类,要么从 MySQL 8.0 或更高版本连接到服务器。

运行时错误处理

在已构建的 Connecting,Client 端可以请求更改连接字符集并使用SET NAMES设置字符集进行排序规则。

某些字符集不能用作 Client 端字符集;某些字符集不能用作 Client 端字符集。参见不允许的 Client 字符集。如果您指定了有效但不能作为 Client 端字符集的字符集,则服务器将返回错误:

mysql> SET NAMES 'ucs2';
ERROR 1231 (42000): Variable 'character_set_client' can't be set to
the value of 'ucs2'

如果服务器无法识别字符集(或排序规则),则会产生错误:

mysql> SET NAMES 'bogus';
ERROR 1115 (42000): Unknown character set: 'bogus'

mysql> SET NAMES 'utf8mb4' COLLATE 'bogus';
ERROR 1273 (HY000): Unknown collation: 'bogus'

Tip

想要验证服务器是否遵循其请求的字符集的 Client 端,可以在连接并检查结果是否为预期的字符集后执行以下语句:

SELECT @@character_set_client;