主从复制乱码主因是字符集不一致:主库用utf8mb4写入,从库character_set_server为latin1,导致字节被错误解析为乱码(如“æ–‡”或???);须统一主从character_set_server为utf8mb4并持久化配置,仅SET NAMES无法根治。
主从复制出现乱码,不是表结构或数据本身坏了,而是主库写入时用的编码,和从库解析 binlog 时用的编码对不上——最常踩的坑是和在从库上不一致。
为什么从库查出来是 ??? 或 æ–‡ 而不是中文
MySQL 主从复制不校验字符集。只要 SQL 能执行,就照搬。如果主库
,但从库
,那么主库写入的 utf8mb4 字节流,会被从库当成 latin1 解析,一个中文被拆成两三个字节分别当拉丁字符处理,结果就是 mojibake(如“æ–‡”)或一堆 ?。
典型现象包括:
主库
正常,从库查同一行显示
或乱码字节序列
显示表是
,但
中
、
、
是
插入 emoji 报错
,说明底层存储层已拒绝四字节字符
必须统一
,且要持久化
这个变量决定新数据库、新表的默认字符集,也影响 IO 线程连接主库时的上下文初始化。仅靠
或改表结构,救不了根本。
操作要点:
MySQL(Linux)
MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。
下载
主从两端都检查:
,必须完全一致,推荐设为
修改配置文件
的
段落,添加:
重启 MySQL 生效;
只对新连接有效,且不持久
注意:
不会自动转换已有库/表,需单独执行
和
该在哪用、不该在哪用
等价于同时设置
、
、
。它只作用于当前连接,不能替代服务端配置,但能快速验证问题是否出在连接层。
使用边界很明确:
✅ 应用连接池初始化时必须执行(如 JDBC URL 加
,PHP PDO 加
)
✅ 用
客户端手动连从库调试时,第一句就跑:
❌ 不要用在从库的 SQL 线程上下文中去“修复”历史乱码数据——它只影响后续语句的解析,不影响已同步进来的错误字节
❌ 不要客户端发的是 latin1 编码字节,却执行
,这会导致 double-encoded(双编码),乱得更彻底
容易被忽略的链路断点:binlog 格式与 init_connect
即使 server 和 connection 都设对了,如果 binlog 记录方式或连接初始化没兜住,照样出问题。
binlog_format 推荐用
:语句级(
)模式下,
会被记录并重放,但重放时只影响从库 SQL 线程的会话变量,不改变数据本体;而
模式直接同步行变更,绕过字符集解析歧义
在从库
中加
,确保所有新连接(包括复制线程使用的连接)一建立就带对编码
加
可强制忽略客户端声明的字符集,防止应用层传错值覆盖服务端配置
验证是否生效:在从库执行
,看
和
是否为
,再查
确认三连项是否全为
真正难的不是改哪一行配置,而是确认「主库写入时用什么编码」和「从库重放时按什么编码解」这两件事,在每一个环节(server、connection、binlog、client)都对齐。漏掉任意一层,乱码就会在某个时间点突然冒出来。
character_set_servercharacter_set_clientcharacter_set_server = utf8mb4character_set_server = latin1SELECT???SHOW CREATE TABLEutf8mb4SHOW VARIABLES LIKE 'character_set%'character_set_clientcharacter_set_connectioncharacter_set_resultslatin1Incorrect string value: '\xF0\x9F\x92\xA9'character_set_serverSET NAMES utf8mb4SHOW VARIABLES LIKE 'character_set_server';utf8mb4my.cnf[mysqld]character-set-server = utf8mb4
collation-server = utf8mb4_0900_ai_ciSET GLOBAL character_set_server = 'utf8mb4'character_set_serverALTER DATABASE db_name CHARACTER SET = utf8mb4;ALTER TABLE t CONVERT TO CHARACTER SET utf8mb4;SET NAMES utf8mb4SET NAMES utf8mb4character_set_clientcharacter_set_connectioncharacter_set_results?useUnicode=true&characterEncoding=utf8mb4PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4"mysqlSET NAMES utf8mb4;SET NAMES utf8mb4ROWSTATEMENTSET NAMESROWmy.cnfinit_connect='SET NAMES utf8mb4'skip-character-set-client-handshakeSHOW SLAVE STATUS\GSlave_IO_RunningSlave_SQL_RunningYesSHOW VARIABLES LIKE 'character_set%'utf8mb4