表字符集与存储过程字符集不一致导致的存储过程执行缓慢
昨天遇到个线上问题,某个存储过程执行异常缓慢,在数据量
不到1K条的情况下竟然需要4min才能全部完成。
查看了存储过程的具体操作,全部是删除逻辑,涉及大概十几张表
删除操作。
大致如下(项目代码不便透露,这里意思一下即可)
1 CREATE PROCEDURE deleteinfo(IN `aaax` varchar(50),IN `bbbx` varchar(50)) 2 BEGIN 3 DECLARE aaa varchar(255); 4 DECLARE bbb varchar(255); 5 6 SET aaa = aaax; 7 SET bbb = bbbx; 8 9 Delete from table1 10 where aaa = aaax 11 and bbb = bbbx; 12 13 Delete from table2 14 where aaa = aaax 15 and bbb = bbbx; 16 17 Delete from Ptable3 18 where aaa = aaax 19 and bbb = bbbx; 20 21 END
经过确认,这几张表内的全部数据不过1K左右,但是执行时间却异常缓慢,
于是我决定手动执行验证下时间,发现不过10s内就全部完毕,那么我猜测
存储过程中一定发生了什么。
经过进一步的排查,我们将目光放到了索引上,这几张表的索引都是没问题的,
会不会存储过程中没有利用到索引呢?
抱着这个疑问我查找了下索引失效的可能,最终发现,与存储过程和表的字符集有关。
我们执行存储过程时,定义的参数没有指定字符集,则默认取character_set_server这个全局变量的值
1 DECLARE aaa varchar(255); 2 DECLARE bbb varchar(255);
我们可以通过下面语句来查看
show variables like "%char%";
接下来我们再来看下表的字符集,通过information_schema.columns可以查出
1 SELECT DISTINCT 2 TABLE_SCHEMA,TABLE_NAME,CHARACTER_SET_NAME 3 FROM 4 information_schema.`COLUMNS` 5 WHERE 6 TABLE_SCHEMA = (select databases()) 7 AND CHARACTER_SET_NAME != 'utf8mb4'
我们发现很多表的字符集是utf-8,而存储过程内部参数确是utf-8mb4的,在这种情况下,
mysql会将表转换为utf-8mb4(mylsq字符集转换遵循由小到大的规则,因为utf8mb4是utf8的超集,所以把
utf8转换为utf8mb4),转换后,由于原来表上的索引依然是utf8的,这就导致了这些索引的失效。
于是,我们终于找出了这个问题的罪魁祸首,接下来,我们看下怎么处理这个问题,这里有2个方案
(1)修改character_set_server=utf8,然后重建存储过程(存储过程的字符集是在新建时读取的character_set_server因此不重建则无效)
(2)将表的字符集改为utf8mb4
经过考量后决定修改表的字符集(character_set_server修改需重启数据库服务,线上项目改动风险过大),可以通过以下语句实现
alter table table_name convert to charset utf8mb4;
但项目上表较多的时候一张张表修改显然是不现实的,因此这里提供下批量生成语句,
可以直接将非utf8mb4的表查询出来并生成修改语句(修改前建议先备份)
1 SELECT 2 CONCAT( 3 "ALTER TABLE `", 4 tb.TABLE_SCHEMA, 5 "`.`", 6 tb.TABLE_NAME, 7 "` CONVERT TO CHARACTER SET utf8mb4;" 8 ) 9 FROM 10 ( 11 SELECT DISTINCT 12 TABLE_SCHEMA,TABLE_NAME 13 FROM 14 information_schema.`COLUMNS` 15 WHERE 16 TABLE_SCHEMA = (select database()) 17 AND CHARACTER_SET_NAME != 'utf8mb4' 18 and TABLE_NAME 19 not in (select TABLE_NAME from information_schema.VIEWS where TABLE_SCHEMA = (select database())) 20 ) as tb;
最后,这个问题时由于存储过程字符集utf8mb4,表字符集utf8引起的,但是反过来则不会有这个问题,因为utf8mb4是兼容utf8的。