MySQL迁移报错: Unknown collation utf8mb4_0900_ai_ci 终极解决
2025-03-15 11:57:21
MySQL 数据库迁移后 "Unknown collation: 'utf8mb4_0900_ai_ci'" 报错,但数据库中并不存在该排序规则
遇到过这种问题?程序连不上 MySQL 数据库,报 “1273 (HY000): Unknown collation: 'utf8mb4_0900_ai_ci'”,说是不认识这个叫 'utf8mb4_0900_ai_ci' 的排序规则,可数据库里压根就没用它啊! 这就很让人头大了。别急,我们一起看看咋回事,再把它搞定。
一、 啥原因造成的?
一般这种情况,都是从高版本 MySQL 数据库(比如 8.0)往低版本(比如 5.7)迁移数据后出现的。因为 'utf8mb4_0900_ai_ci' 是 MySQL 8.0 引入的,5.7 版本根本不认。 虽然你仔细检查过,表结构里都是 'utf8mb4_general_ci',没问题啊,但问题可能出在别的地方:
- 连接字符串: 有些客户端(或驱动程序)会默认用 'utf8mb4_0900_ai_ci' 去连接数据库,即使数据库本身设置不是这个。
- 数据库或服务器的默认配置: MySQL 服务器、数据库,甚至是连接的客户端, 都可能有自己的默认字符集和排序规则设置。即使数据表明确指定了,也可能被全局的设置覆盖掉。
- 存储过程、函数、触发器等: 数据表的定义是干净的, 但数据库里的存储过程、函数或触发器,可能创建的时候用到了 'utf8mb4_0900_ai_ci', 它们躲在暗处,也可能导致报错。
- 旧的连接池: 有时候,程序用的数据库连接池还保留着老的连接信息。那些老连接可能带着错误的排序规则。
二、 怎么解决?逐个排查!
下面我们分几种情况, 提供不同的解决方法。 记得做任何修改前备份数据库 !
1. 检查并修改连接字符串
看看你的连接字符串,是不是漏掉了什么? Python SQLAlchemy 的连接字符串长这样:
engine = create_engine('mysql+mysqlconnector://user:**** **** ***@** **** **** :3306/amatdb?charset=utf8mb4')
重点看 charset=utf8mb4
这里,只指定了字符集,没指定排序规则! MySQL 会按自己的默认规则来。我们给它加上排序规则:
engine = create_engine('mysql+mysqlconnector://user:**** **** ***@** **** **** :3306/amatdb?charset=utf8mb4&collation=utf8mb4_general_ci')
加了个 &collation=utf8mb4_general_ci
,明确告诉它,用这个排序规则!
2. 修改 MySQL 服务器和数据库的默认配置
如果你能改动 MySQL 服务器配置,直接把默认的排序规则也改掉,就省事了。
-
查看当前配置:
SHOW VARIABLES LIKE 'character\_set\_%'; SHOW VARIABLES LIKE 'collation%';
看看
character_set_server
,collation_server
,character_set_database
,collation_database
这几个变量的值。 -
修改配置文件 (my.cnf 或 my.ini): 找到 MySQL 的配置文件 (位置可能不一样, 搜一下),在
[mysqld]
下面加上或修改:[mysqld] character_set_server = utf8mb4 collation_server = utf8mb4_general_ci
改完记得重启 MySQL 服务 !
-
在数据库启动时进行设置:
如果在Docker中运行MySQL,您可以使用环境变量进行设置:docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag -character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci
如果是通过systemd,可以通过在服务设置中设定
command_line_args
的方式来实现:[Service] #Command line arguments to be passed to mysqld_safe #Number of processes. Safe to set up to 2 * number of cores available. #The software takes advantage of SMP systems as appropriate. ExecStart= ExecStartPre= ExecStartPost= command_line_args=--character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci
-
只修改数据库的配置(不推荐): 也可以单独修改数据库的默认值,但通常不建议这样,因为新建的表还是会按服务器的默认设置走:
ALTER DATABASE amatdb CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
这样改完, 理论上对新建表, 设置会生效.
3. 揪出并修改存储过程、函数、触发器
把那些可能藏着 'utf8mb4_0900_ai_ci' 的家伙找出来,改掉!
-
查询存储过程和函数:
SELECT ROUTINE_SCHEMA, ROUTINE_NAME, ROUTINE_TYPE, COLLATION_CONNECTION FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = 'your_database_name'; -- 换成你的数据库名
看
COLLATION_CONNECTION
列,如果有 'utf8mb4_0900_ai_ci',记下来! -
查询触发器:
SELECT TRIGGER_SCHEMA, TRIGGER_NAME, COLLATION_CONNECTION FROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA = 'your_database_name'; -- 换成你的数据库名
同样看
COLLATION_CONNECTION
列。 -
修改有问题的对象: 找到后,可以用
ALTER PROCEDURE
,ALTER FUNCTION
,ALTER TRIGGER
语句修改它们的排序规则,或者直接删除重建。修改前,一定先用SHOW CREATE PROCEDURE/FUNCTION/TRIGGER
看清楚定义!举个例子, 假如有个存储过程叫
my_proc
,要修改它:-- 先查看定义,确认修改位置 SHOW CREATE PROCEDURE my_proc; -- 修改排序规则 ALTER PROCEDURE my_proc SQL SECURITY INVOKER COLLATION 'utf8mb4_general_ci';
进阶操作: 批量导出、修改、再导入
如果有很多存储过程、函数要改,手动太麻烦了。可以写个脚本,批量导出所有定义,用文本替换把 'utf8mb4_0900_ai_ci' 换成 'utf8mb4_general_ci',再批量导入回去。要注意 SQL 语句的分隔符可能也需要修改,免得导入出错。
或者使用如下查询语句一次性查看全部受影响对象,进行统一导出:
SELECT CONCAT('ALTER ', ROUTINE_TYPE, ' `', ROUTINE_SCHEMA, '`.`', ROUTINE_NAME, '` COLLATION ''utf8mb4_general_ci'';') FROM information_schema.ROUTINES WHERE COLLATION_CONNECTION = 'utf8mb4_0900_ai_ci' AND ROUTINE_SCHEMA = 'your_database_name' UNION ALL SELECT CONCAT('ALTER TRIGGER `', TRIGGER_SCHEMA, '`.`', TRIGGER_NAME, '` COLLATION ''utf8mb4_general_ci'';') FROM information_schema.TRIGGERS WHERE COLLATION_CONNECTION = 'utf8mb4_0900_ai_ci' AND TRIGGER_SCHEMA = 'your_database_name';
这个命令会将所有的alter命令组合生成出来,但是可能不包括修改触发器以及event等部分的内容。
4. 清理/重启连接池
如果你用了数据库连接池,试试重启连接池,或者清理一下旧连接,让它重新建立连接。不同连接池操作方式不一样,具体看你用的连接池的文档。
5.终极大招:重新导数据(如果上面的都不行)
上面几个方法都试过了, 还不行?那只能放大招了:
-
从 MySQL 8.0 的数据库,用
mysqldump
导出数据。 导出的时候,加上参数,强制指定字符集和排序规则:mysqldump -u your_user -p --default-character-set=utf8mb4 --set-gtid-purged=OFF your_database_name > your_dump_file.sql
将
set-gtid-purged=OFF
, 可以解决一些reset master
会发生的问题. -
修改导出的 .sql 文件, 替换
utf8mb4_0900_ai_ci
成utf8mb4_general_ci
。这一步可以用文本编辑器或者 sed 命令:sed -i 's/utf8mb4_0900_ai_ci/utf8mb4_general_ci/g' your_dump_file.sql
-
把修改后的 .sql 文件导入到 MySQL 5.7 的数据库。
特别提醒: 如果你的数据量很大,导出会很久。导入前最好把目标数据库的事务日志关掉(SET GLOBAL innodb_flush_log_at_trx_commit = 0;
),导入完再打开,能快很多。 但这样做有风险, 如果导入过程中断电, 可能会丢数据。请务必谨慎权衡。
这几个方法, 一个个排查下来,应该能解决你的问题。问题原因不一定是单一的, 多个原因混合导致的可能性也不是没有。所以多试试不同的组合拳!