900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > mysql修改字符集utf8mb4引发的惨案

mysql修改字符集utf8mb4引发的惨案

时间:2022-03-09 12:49:24

相关推荐

mysql修改字符集utf8mb4引发的惨案

mysql修改字符集utf8mb4引发的惨案

环境配置: Linux CentOS 7 mysql5.7字符编码是utf8;惨案原因:数据库表需要支持表情很符号,表情符号一般是4个字符,utf8最多支持3个字符,如果插入表情符号4个字符的字段就会报错,因此我们修改了此表的字符集为utf8mb4,在这说明一下utf8mb4是utf8的超集。问题来了:MySQL环境上有两张表做left join时使用的字段都索引,但是执行计划里面显示有一张表使用了全表扫描,扫描全表近百万行记录,导致sql执行很慢。诊断结果:是mysql表修改字符集utf8mb4引发的惨案;问题诊断重现:首先,表结构和表记录如下:

CREATE TABLE `t1` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(20) DEFAULT NULL,`code` varchar(50) DEFAULT NULL,PRIMARY KEY (`id`),KEY `idx_code` (`code`),KEY `idx_name` (`name`)) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8

t1 插入一些数据

CREATE TABLE `t2` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(20) DEFAULT NULL,`code` varchar(50) DEFAULT NULL,PRIMARY KEY (`id`),KEY `idx_code` (`code`),KEY `idx_name` (`name`)) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4

t2插入一些数据

2张表left join的执行计划如下:

desc select * from t2 join t1 on t1.code = t2.code where t2.name = 'dddd'\G;

可以明显地看到,t2.name ='dddd’使用了索引,而t1.code = t2.code这个关联条件没有使用到t1.code上面的索引,一开 始也百思不得其解,但是机器不会骗人。显示警告查看

查询执行计划的告警信息:

show warnings;

发现问题:在发现了转换(使用utf8mb4的testdb.t1.code)之后,Scott发现2个表的字符集不一样。t1为utf8,t2为utf8mb4。但是为什么表表字符集不一样(实际是我修改了字符集 字符串字符集不一样了)就会导致T1全表扫描呢?下面来做分析。

(1)首先t2左联接t1决定了t2是驱动表,此步相当于执行了选择* from t2 where t2.name =‘dddd’,取码范围的值,这里为’8a77a32a7e0825f7c8634226105c42e5’;

(2)然后拿起t2查到的代码的值根据联接条件去t1里面查找,这一步就相当于执行了select * from t1 where t1.code =‘8a77a32a7e0825f7c8634226105c42e5’;

(3)但是由于第(1)步里面T2表取出的代码字段是utf8mb4字符集,而T1表里面的代码是UTF8字符集,这里需要做字符集转换,字符集转换遵循由小到大的原则,因为utf8mb4是UTF8的超集,所以这里把UTF8转换成utf8mb4,即把t1.code转换成utf8mb4字符集,转换了之后,由于t1.code上面的索引仍然是UTF8字符集,所以这个索引就被执行计划忽略了,然后T1表只能选择全表扫描。更糟糕的是,如果T2筛选出来的记录不止1条,那么T1就会被全表扫描多次,性能之差可想而知。

解决问题:

既然原因已经清楚了,如何解决呢?当然是改字符集了,把T1改成和T2一样或者把T2改成T1都可以,这里选择把T1转成utf8mb4。那怎么转字符集呢?

有一个同学会说用alter table t1 charset utf8mb4;而是错的,这只是改了表的替代字符集,即新的数据才会使用utf8mb4,已经存在的一部分仍然是utf8。

用只有alter table t1 convert to charset utf8mb4;才是正确的。

但是还要注意一点,更改表改字符集的操作是双重写的(用lock = node会报错)所以业务高峰时请不要操作,即使在业务低峰时期,大表的操作仍建议使用pt-online -schema改变在线修改字符集。

测试环境:使用 alter table t1 convert to charset utf8mb4, lock=shared;

现在再来查看执行计划,可以看到已经没问题了。

复盘总结:

1、 表字符集不同时,可能导致加入的SQL使用不到索引,引起严重的性能问题;

2、 改字符集的alter table操作会多个写,业务mysql建议使用pt-online-schema-change;

3、 如果要大批量修改表的字符集,同样做好SQL的审议工作,关联的表的字符集一起做修改。

4、 效仿一下使用show warnings【千万不能忘】

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。