MySQL乱码根本原因是客户端、连接、服务端、表字段四层字符集不一致;必须统一为utf8mb4,尤其注意连接层character_set_client/connection/results三参数及字段级显式声明。

根本原因不是Python编码错了,而是MySQL的字符集配置和Python实际发送的数据不匹配——尤其是当数据里含 emoji、生僻汉字或某些四字节 UTF-8 字符时,utf8(MySQL 的伪 utf-8)直接拒绝写入。
为什么 MySQL 的 utf8 不等于真正的 UTF-8?
MySQL 的 utf8 是个历史包袱:它只支持最多 3 字节的 UTF-8 编码,而 emoji(如 \xF0\x9F\x98\x9D)、部分 Unicode 扩展区汉字、甚至某些标点符号是 4 字节。只要 Python 发送了这类字符,MySQL 就会报 Incorrect string value: '\xF0\x9F\x98\x9D' for column。
-
utf8mb4才是 MySQL 中真正兼容标准 UTF-8 的字符集(mb4= maximum bytes 4) - MySQL 版本必须 ≥ 5.5.3 才支持
utf8mb4 -
utf8和utf8mb4的 collation 也不同,不能混用(比如utf8mb4_unicode_ci≠utf8_general_ci)
连接参数里没指定 charset=utf8mb4 就白配
即使数据库、表、字段全设成 utf8mb4,如果 Python 连接时没显式声明字符集,pymysql 或 SQLAlchemy 仍可能按默认(latin1 或空)协商,导致服务端误判编码。
- pymysql 连接字符串末尾加
?charset=utf8mb4:mysql://user:pass@host:3306/db?charset=utf8mb4 - SQLAlchemy 引擎创建时传
connect_args={'charset': 'utf8mb4'} - 别信“改了 my.cnf 就一劳永逸”——客户端连接参数优先级高于服务端全局配置
字段级字符集才是最后一道防线
数据库和表设成 utf8mb4 后,字段仍可能继承旧的 latin1 或 utf8。必须逐个确认并修正:
安全 Python 代码沙箱:在隔离的沙箱中执行任意 Python 代码,预装 requests、NumPy、pandas 库。返回 stdout、stderr。
立即学习“Python免费学习笔记(深入)”;
- 查字段编码:
SHOW FULL COLUMNS FROM your_table;看Collation列 - 改单个字段:
ALTER TABLE your_table MODIFY content TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - 批量改所有
VARCHAR/TEXT字段需脚本辅助,不能只靠CONVERT TO(它不改列定义里的显式 charset)
Python 层还要防 SQL 注入和引号逃逸
编码正确 ≠ 能入库。含单引号、反斜杠、控制字符的字符串,若拼进 SQL 语句又没转义,会触发语法错误或截断:
- 永远用参数化查询(
cursor.execute("INSERT ... VALUES (%s)", (val,))),别自己拼"'%s'" - 如果非得手动拼接(极不推荐),必须用
pymysql.escape_string(val)处理每个值 - 注意:
None、float('nan')、datetime类型也要提前转换,否则可能触发Unknown column 'nan'类报错
最容易被忽略的是:改完服务端配置后没重启 MySQL,或改完连接字符串后没清掉旧连接池缓存——哪怕所有配置都对,残留的旧连接仍会沿用错的字符集协商结果。

















