MySQL字符集乱码主因是服务端、客户端、连接层三者未对齐;需在连接后执行SET NAMES utf8mb4或在连接串中指定charset=utf8mb4,各语言驱动须显式设置,代理层需透传字符集协商包。
mysql 客户端连接时的字符集不匹配,90% 是服务端、客户端、连接层三者没对齐,不是改一个配置就能解决。
MySQL 连接时 character_set_client / character_set_connection / character_set_results 怎么设才真正生效
这三个变量控制的是「连接会话」层面的编码行为,不是全局设置。即使 my.cnf 里写了 collation-server = utf8mb4_unicode_ci,只要连接时没显式指定,客户端发来的 SQL 仍可能被错误转码。
- 必须在建立连接后立刻执行
SET NAMES utf8mb4(等价于同时设三个变量),或在连接字符串中加?charset=utf8mb4 - PHP 的
mysqli要调用mysqli_set_charset($conn, 'utf8mb4'),不能只靠mysqli_init()后的options() - Java 的 JDBC URL 必须带
useUnicode=true&characterEncoding=utf8mb4,光写characterEncoding=utf8会 fallback 到utf8(即 utf8mb3) - Python 的
pymysql需显式传参charset='utf8mb4',mysql-connector-python同理;漏掉就默认用服务器character_set_server,但很多老库是latin1
PostgreSQL 中 client_encoding 和 lc_ctype 的区别与实操陷阱
client_encoding 控制字节流到文本的解码方式,lc_ctype 影响大小写、排序等 locale 行为——两者常被混为一谈,但改错一个就会出问题。
- 连接时用
SET client_encoding TO 'UTF8'只影响当前会话;若应用复用连接池,必须在每次获取连接后重置,或在pg_hba.conf中配client_encoding=utf8(需 PostgreSQL ≥ 12) -
lc_ctype是数据库级属性,建库时指定(CREATE DATABASE ... LC_CTYPE 'en_US.UTF-8'),运行时不可改;如果业务要支持中文排序,不能只改client_encoding,否则ORDER BY仍按 C locale 排 - Docker 环境下,容器启动时未挂载宿主机 locale 文件或未设
ENV LANG=C.UTF-8,会导致lc_ctype初始化失败,日志报invalid locale name: "en_US.utf8"
跨数据库代理层(如 ProxySQL、MaxScale)转发连接时字符集丢失的典型表现
代理层若未透传或重写客户端的字符集协商包,后端真实 MySQL 实例看到的永远是默认值(通常是 latin1),导致插入乱码、查询无结果。
- ProxySQL 的
mysql_servers表中必须开启use_ssl=0(除非真用 SSL)且确认max_connections不限制连接初始化阶段的 handshake 包 - MaxScale 的
[MySQLClient]监听段需显式配置auth_all_servers=true和strip_db_prefix=false,否则部分客户端驱动(如旧版 Node.jsmysql2)的 initial handshake 会被截断 - 最简验证法:在代理后连上真实 MySQL,执行
SHOW VARIABLES LIKE 'character\_set%',对比直连结果;若character_set_client是latin1,基本可判定代理未透传
异构环境里最麻烦的不是某个 DB 改不了编码,而是同一套中间件连接不同字符集的库时,连接池无法自动感知切换——比如一个连接刚查完 latin1 库,下次被分配去连 utf8mb4 库,就直接乱码。这种场景必须靠连接字符串隔离,或者强制连接池按 charset 参数分桶,而不是指望“统一配置”。

















