应通过连接时指定init_command执行SET NAMES统一设置字符集,而非依赖全局配置;不同语言驱动实现方式各异,需注意兼容性与执行时机,并验证实际生效的三个核心变量值。
MySQL 客户端连接时如何设置默认字符集
直接改 my.cnf 全局配置对多服务器不适用——你连 a 服务器要 utf8mb4,连 b 服务器要 gbk,全局配死就废了。真正可控的方式是让每次连接自己带初始化命令。
核心方法:在连接字符串或客户端配置里加 init_command,让它一上来就执行 SET NAMES utf8mb4 这类语句。
-
mysql命令行工具支持--init-command="SET NAMES utf8mb4"参数 - Python 的
pymysql或mysql-connector-python都有init_command连接参数 - PHP 的
mysqli可在mysqli_real_connect()后立刻调用mysqli_query($conn, "SET NAMES gbk"),但更稳妥是设mysqli_options($conn, MYSQLI_INIT_COMMAND, "SET NAMES gbk") - 注意:
SET NAMES等价于同时设character_set_client、character_set_results、character_set_connection,比只设其中一个更安全
不同语言里 init_command 的写法差异
不是所有驱动都叫这个名字,也不是所有都支持 SQL 字符串;名字和行为容易混淆。
- Go 的
github.com/go-sql-driver/mysql不用 init_command,而是靠 DSN 里的charset=utf8mb4参数(它会自动发SET NAMES) - Java 的
mysql-connector-j用connectionInitSql配置项,值为字符串,如SET NAMES gbk - Ruby 的
mysql2gem 支持:init_command => "SET NAMES utf8mb4",但必须确保服务端已启用对应字符集,否则连接直接失败 - Node.js 的
mysql2包也支持initCommand选项,注意它是数组形式:initCommand: ["SET NAMES utf8mb4", "SET time_zone = '+08:00'"]
为什么 SET NAMES 不总管用?常见失效场景
你以为发了 SET NAMES 就万事大吉,其实很多地方它根本没机会执行,或者执行了也白执行。
- 连接池复用旧连接时,如果上一次连接被别人改过字符集,你的
init_command可能被跳过(比如某些 Go 驱动的连接池默认不重跑 init) - MySQL 8.0+ 默认启用
caching_sha2_password认证插件,部分老驱动在握手阶段就失败,根本到不了执行init_command的环节 -
SET NAMES gbk要求服务端已加载gbk字符集,否则报错Unknown character set: 'gbk'—— 查看可用集用SHOW CHARACTER SET - 如果应用层用了 ORM(比如 Django、Rails),它们可能在连接后又自己执行一遍
SET,覆盖掉你设的值
验证当前连接实际生效的字符集
别信配置文件,也别信日志,直接查 MySQL 内部变量最准。
- 连上去后立即执行:
SELECT @@character_set_client, @@character_set_connection, @@character_set_results; - 想看整个会话的字符集链路,用:
SHOW VARIABLES LIKE 'character\_set%';(注意下划线要转义) - 如果发现三个值不一致,说明
SET NAMES没生效,或者被中间某层覆盖了 - 特别注意:
character_set_database是当前库的默认字符集,但它不影响连接通信,只影响新创建表的字段默认值
多服务器环境里,字符集问题往往不是“设不设”,而是“什么时候设、谁先设、有没有被覆盖”。每次换服务器,先连上去跑一遍 SHOW VARIABLES,比翻十页文档更快。










