根本原因是create procedure未显式声明character set utf8mb4,导致过程体中文被按默认字符集(如latin1)解析而乱码;mysql的utf8实为utf8mb3,仅支持3字节unicode,无法表示emoji及部分汉字,utf8mb4才是完整支持4字节unicode的官方推荐字符集。

MySQL存储过程中中文乱码,根本原因不是“过程写得不对”,而是字符集未显式声明为 utf8mb4 ——哪怕数据库、表、连接都设对了,CREATE PROCEDURE 本身不指定字符集,MySQL 就会按 character_set_database 的默认值(常是 latin1 或旧版 utf8)解析过程体里的字符串字面量,导致中文直接被截断或转成问号。
为什么 utf8mb4 是硬性要求,而不是 utf8
MySQL 的 utf8 实际是阉割版:最多只支持 3 字节编码,无法表示 Emoji、部分生僻汉字(如「?」「?」)、以及很多扩展 Unicode 字符。真正兼容全量 Unicode 的是 utf8mb4。如果你的存储过程里有中文注释、中文提示文本、或拼接中文字段,且目标字段/变量用了 utf8mb4,而过程体却用 utf8 解析,就会在编译阶段就丢字节。
-
utf8在 MySQL 中等价于utf8mb3,已标记为 deprecated -
utf8mb4才是官方推荐且唯一能完整支持 4 字节 Unicode 的字符集 - 即使你用
SET NAMES utf8,也只影响客户端连接层,不影响存储过程定义时的字符集推导
CREATE PROCEDURE 必须显式加 CHARACTER SET utf8mb4
不加这句,MySQL 默认按数据库级字符集(character_set_database)解析过程体——而这个值常常是错的,尤其在老库迁移后没重设的情况下。
MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。
正确写法示例:
CREATE DEFINER=`root`@`%` PROCEDURE `sp_log_user_action`(IN p_user_id INT, IN p_action VARCHAR(100))
COMMENT '记录用户操作日志'
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
CHARACTER SET utf8mb4 -- ← 这行不能省!
COLLATE utf8mb4_unicode_ci -- ← 建议同步指定校对规则
BEGIN
INSERT INTO user_log (user_id, action_desc, created_at)
VALUES (p_user_id, CONCAT('用户执行了:', p_action), NOW());
END
- 必须放在
COMMENT和LANGUAGE SQL之间(MySQL 语法顺序敏感) -
COLLATE虽非强制,但建议与表字段一致,避免隐式转换警告 - 如果过程里用到临时表或局部变量,其字符集也继承自该
CHARACTER SET声明
验证过程体是否真按 utf8mb4 加载
光看 SHOW CREATE PROCEDURE 不够,它只显示定义语句,不反映实际解析结果。要确认过程体内的中文是否被正确加载,需检查其内部字符串常量的编码行为:
- 执行
SELECT body FROM mysql.proc WHERE name = 'sp_log_user_action',观察返回值中中文是否正常(若显示为问号或乱码,说明创建时未生效) - 在过程内加一句
SELECT LENGTH('中文') AS len_utf8mb4;并调用,正确应返回6(utf8mb4下每个中文占 3 字节);若返回2,说明被当成了latin1 - 若发现异常,删掉重建,确保
CHARACTER SET utf8mb4写在正确位置且无空格/换行干扰
最易被忽略的一点:即使你把整个 MySQL 实例、所有库、所有表、所有连接都设成 utf8mb4,只要存储过程定义缺了那一行 CHARACTER SET utf8mb4,它的函数体就仍可能在解析阶段就把中文砍掉——这不是运行时问题,是定义即错误。










