sql调优之:字符集不一致导致的索引失效案例

本文涉及的产品
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
RDS AI 助手,专业版
简介: 生产上有一段sql,大概几百行,做的是inser into …select ;的操作,但是执行了一个多小时都出不来,正常情况下不会这么久,通过一步步定位,最后发现是其中某一段的join 特别慢导致的,后面对这段sql重点分析发现,这里的表竟然没有走索引,对于几十万以上的表之间的连接,若是没有索引那就是灾难性的,加不加索引就好比一个人力三轮与超跑的区别。

一、问题复盘与解决



1.问题描述


生产上有一段sql,大概几百行,做的是inser into …select ;的操作,但是执行了一个多小时都出不来,正常情况下不会这么久,通过一步步定位,最后发现是其中某一段的join 特别慢导致的,后面对这段sql重点分析发现,这里的表竟然没有走索引,对于几十万以上的表之间的连接,若是没有索引那就是灾难性的,加不加索引就好比一个人力三轮与超跑的区别。至于为什么没走索引最后定位到了是因为字符集与排序规则与其他表不一致,所以导致了索引失效。下面是犯罪现场:

..........
left join 
 (
  select
   tmvrr.project_code,
   COUNT(1) as project_abnormal_sum, 
   COUNT(DISTINCT tmvrr.customer_id, tmvrr.room_relation_id) as customer_room_car_sum, 
   sum(case when tmvrr.rule_type = '1' then 1 else 0 end) as project_accuracy, 
   sum(case when tmvrr.rule_type = '2' then 1 else 0 end) as project_integrity, 
   sum(case when tmvrr.rule_type = '3' then 1 else 0 end) as project_timeliness, 
   case when tmvrr.estate_type in (1, 3) then '住宅' else '非住宅' end as estate_type  
  FROM
   sunacwy_mdm.tb_mdm_verification_rules_result tmvrr
  left join sunacwy_mdm.tb_unqualify_sc_pool tusp on 
   tmvrr.project_code = tusp.project_code
   and tmvrr.customer_id = tusp.mdm_data_id
   and tmvrr.rule_id = tusp.rule_codes
   and tmvrr.room_relation_id = tusp.mdm_data_id2
  where
   tmvrr.group_code <> ''
   and (tusp.project_code is null or tmvrr.commit_content <> tusp.commit_content)  
  group by
   tmvrr.project_code,
   case when tmvrr.estate_type in (1, 3) then '住宅' else '非住宅' end 
 ) tmvrr on 
  base_org.project_code  = tmvrr.project_code and base_org.estate_type   = tmvrr.estate_type
 .......


2.问题解决


问题解决其实很简单,就是将所有字段的字符集和排序规则更改成了mysql5.7的默认值(笔者生产库是mysql5.7),字符集是utf8mb4,排序规则是utf8_general_ci,然后问题就解决了,更改完以后只能说是天差地别。之前几个小时出不来,更改后几分钟就ok了。

-- 更改全表的字符集与排序规则,除了字符串其他字段其实无需指定排序规则,指定了也不生效
ALTER TABLE tb_mdm_verification_rules_result CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci ;
-- 单个字段的更改排序规则
alter table tb_mdm_verification_rules_result change group_name  group_name varchar(50) character set utf8mb4 collate utf8mb4_general_ci COMMENT '集团名称';


3.总结


其实产生原因很简单,但是找起来确实费了一番劲,这与explain使用不是这么熟练也是有关系的(当然最坑的是建表的人干得事)下面一节笔者会复现这个问题,并分析怎么排查这种问题,方便下次碰到一眼就可以解决。


二、mysql5.7与mysql8.0.3中问题复现



无论mysql5.7还是mysql8.0.3都具有相同的以下环境,我们就使用两个表的连接来复现该问题。

### 环境 预设###
表:tb_mdm_unit ,数据量:54280,索引字段:id
表:tb_mdm_floor,数据量:441849,索引字段:unit_id


1.mysql5.7环境问题复现


①相同字符集和排序规则时count


这里字符集都是utf8mb4,排序规则都是utf8mb4_general_ci

select count(1) from tb_mdm_unit un 
left join tb_mdm_floor fl on un.id = fl.unit_id ;


bf0cc80e73934da8ab618a8468562bf2.png


可以看到此时非常的快,只需要0.5s就出来结果了。


②看下相同字符集和排序规则的执行计划


07c0e8b64bbb41e3a6c11185a4168514.png


执行计划key列里是关联条件使用的索引,rows是索引关联到的行数(这个是效率高低的直观体现),Extra是非常重要的一列,sql真正底层在怎么运行是需要看这列的,Using index表示关联完全走了索引,是很高效的行为了。


③不相同字符集和排序规则count


alter table tb_mdm_floor change unit_id unit_id char(20) character set utf8 collate utf8_bin DEFAULT NULL COMMENT '单元id';
select count(1) from tb_mdm_unit un 
left join tb_mdm_floor fl on un.id = fl.unit_id ;


2315462b489e4ef5931de16021053421.png


可以看到此时是巨慢的,到达92s仍是没有出来结果,只能终止这个进程了。由此可见在更改了字符集和排序规则以后对sql的影响是灾难性的。


④看下不相同字符集和排序规则的执行计划


0e200161bf2240a88e4da068476b6e6e.png


对比这个执行计划与上面那个,可以发现 rows列和Extra列有很大区别,rows列的第二行,由6直接变成了435420,那这个数字意味着什么呢?这个问题先放下,可以先看下Extra列,这一列由Using index变成了Using where; Using index; Using join buffer (Block Nested Loop)。出现Using where 其实是因为mysql执行优化器对于这条sql进行了优化,优化后的sql 会在末尾拼接 “where 1”,在不走索引的查询中好像都会在末尾拼接这个。所以这里有个Using where(有兴趣的话,使用show warnings,即可看到这个执行优化器优化后的sql)。出现Using index是因为,确实走到了索引,但是因为字符集不一致即使走了也相当于没走,其实此时索引是不生效的。最后一个要说的也是最重要的是:Using join buffer (Block Nested Loop);当sql中出现外连时若是连接条件不是索引,Extra中就会提示这个提示(想要详细了解的建议看下这个博主的这篇文章写得还是不错的:MySQL Join原理),其实mysql5.5之前若是没有索引就是会出现笛卡尔积,mysql5.5之后8.0之前就是使用的Block Nested Loop来解决join时没有索引的问题,这种算法虽然比笛卡尔积效率高很多,但是对于大数据量的表来说还是无法承受的,所以上面的查询结果还是出不来,mysql8.0.19之后则采用了hash join 来解决连表时不走索引的问题。这种方式也很高效,效率其实是接近索引的。


2.mysql8.0环境问题复现


①相同字符集和排序规则时count


这里字符集都是utf8mb4,排序规则都是utf8mb4_general_ci(mysql8.0以后默认排序规则不是他了,这里为了验证保持统一)

select count(1) from tb_mdm_unit un 
left join tb_mdm_floor fl on un.id = fl.unit_id ;


下面是执行结果,可以看到0.49s很快就出来了


fc6b255be77c40958699338cc5c7727a.png


②看下相同字符集和排序规则的执行计划


7143c1ada8974e128c4da63b6709d001.png


Extra列都是Using index,说明连接完全走的索引,key列也没有区别,有点区别的是rows,可以发现第二行只有1,在5.7里这个数是6,不过这个区别和今天验证的问题没有关系,这个区别能说明,mysql8的索引比mysql5.7的索引更高效(所以建议能将mysql5.7升级到mysql8的就升级吧)。


③不相同字符集和排序规则count


alter table tb_mdm_floor change unit_id unit_id char(20) character set utf8 collate utf8_bin DEFAULT NULL COMMENT '单元id';
select count(1) from tb_mdm_unit un 
left join tb_mdm_floor fl on un.id = fl.unit_id ;

73076fdf0919411e928af5b4c72d37a5.png


在mysql8.0中将字符集更改过以后好像还是很快,只是比以前慢了一点,并没有像5.7那样根本出不来结果,这是为什么呢?其实mysql8以后(准确说是8.0.19以后)对不走索引的join进行了优化,如果么有索引那么就使用hash算法将等值连接的两个值都使用hash算法进行计算,然后使用计算后的结果进行连接(有点像是给你建了两个hash索引一样),这样也会提升很多的速度。具体看下执行计划。


④看下不相同字符集和排序规则的执行计划


c3b1e4d6223a41b582bdd9c478ae00ad.png


对比这个执行计划与mysql5.7同场景的执行计划可以发现,唯一的不同点就是 Using join buffer (Block Nested Loop) 变成了 Using join buffer (hash join)。上面也简单说了其实是mysql8对不走索引的等值连接进行的优化,等值连接的优化算法由Block Nested Loop变成了hash join。hash join 相当于是额外建立了两个hash索引,所以速度还是很快的,但是这个速度肯定不如走原来的索引快。


3.总结


left join 和right join 在 mysql5.7中若是连接条件不是索引,或者索引失效了,则会导致数据量的剧增,此时使用的连接算法BNL相当于是进行局部的笛卡尔积,效率也是很低的,到了mysql8.0以后,若是索引实现会使用hash算法对连接条件进行关联,这个效率是很高的,基本接近走索引的效率了。所以说升级到mysql8.0好处还是显而易见的(什么?msyql8.0还有什么优化?可以看看这一篇:关联子查询的前世今生),实在升级不了,那么我们就需要根据执行计划,来看连接表是否出现了BNL算法出现的情况,一旦出现请关注索引的建立和失效情况,不然sql的性能会非常低下。


4.延展问题:相同字符集不同排序规则会影响索引吗?


只调整排序规则,sql如下:

alter table tb_mdm_floor change unit_id unit_id char(20) character set utf8mb4 collate utf8mb4_polish_ci DEFAULT NULL COMMENT '单元id';
select count(1) from tb_mdm_unit un 
left join tb_mdm_floor fl on un.id = fl.unit_id ;


执行结果如下,直接报错,事实上笔者换了多个排序规则,都是会报这个错误,所以相同字符集不同排序规则会不会导致索引失效,直接就不用看了,因为sql执行不了。7f598810c838483fb532ab67ce0e038f.png


4.延展问题:字符集为什么导致索引失效?


其实很简单,两个字段不是使用字符编码,就没办法判断相等,建立的索引其实也是一个列,这个列的字符集合别的列不同,那就无法比较是否相等,自然就会造成索引失效了。

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
7月前
|
存储 SQL 关系型数据库
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
|
10月前
|
SQL 存储 关系型数据库
SQL优化策略与实践:组合索引与最左前缀原则详解
本文介绍了SQL优化的多种方式,包括优化查询语句(避免使用SELECT *、减少数据处理量)、使用索引(创建合适索引类型)、查询缓存、优化表结构、使用存储过程和触发器、批量处理以及分析和监控数据库性能。同时,文章详细讲解了组合索引的概念及其最左前缀原则,即MySQL从索引的最左列开始匹配条件,若跳过最左列,则索引失效。通过示例代码,展示了如何在实际场景中应用这些优化策略,以提高数据库查询效率和系统响应速度。
430 10
|
10月前
|
SQL 数据库 数据安全/隐私保护
数据库数据恢复——sql server数据库被加密的数据恢复案例
SQL server数据库数据故障: SQL server数据库被加密,无法使用。 数据库MDF、LDF、log日志文件名字被篡改。 数据库备份被加密,文件名字被篡改。
|
11月前
|
SQL 运维 监控
SQL查询太慢?实战讲解YashanDB SQL调优思路
本文是Meetup第十期“调优实战专场”的第二篇技术文章,上一篇《高效查询秘诀,解码YashanDB优化器分组查询优化手段》中,我们揭秘了YashanDB分组查询优化秘诀,本文将通过一个案例,助你快速上手YashanDB慢日志功能,精准定位“慢SQL”后进行优化。
|
11月前
|
SQL 索引
【YashanDB知识库】字段加上索引后,SQL查询不到结果
【YashanDB知识库】字段加上索引后,SQL查询不到结果
|
10月前
|
SQL 关系型数据库 MySQL
大数据新视界--大数据大厂之MySQL数据库课程设计:MySQL 数据库 SQL 语句调优方法详解(2-1)
本文深入介绍 MySQL 数据库 SQL 语句调优方法。涵盖分析查询执行计划,如使用 EXPLAIN 命令及理解关键指标;优化查询语句结构,包括避免子查询、减少函数使用、合理用索引列及避免 “OR”。还介绍了索引类型知识,如 B 树索引、哈希索引等。结合与 MySQL 数据库课程设计相关文章,强调 SQL 语句调优重要性。为提升数据库性能提供实用方法,适合数据库管理员和开发人员。
|
10月前
|
关系型数据库 MySQL 大数据
大数据新视界--大数据大厂之MySQL 数据库课程设计:MySQL 数据库 SQL 语句调优的进阶策略与实际案例(2-2)
本文延续前篇,深入探讨 MySQL 数据库 SQL 语句调优进阶策略。包括优化索引使用,介绍多种索引类型及避免索引失效等;调整数据库参数,如缓冲池、连接数和日志参数;还有分区表、垂直拆分等其他优化方法。通过实际案例分析展示调优效果。回顾与数据库课程设计相关文章,强调全面认识 MySQL 数据库重要性。为读者提供综合调优指导,确保数据库高效运行。
|
12月前
|
SQL 数据库
数据库数据恢复—SQL Server报错“错误 823”的数据恢复案例
SQL Server数据库附加数据库过程中比较常见的报错是“错误 823”,附加数据库失败。 如果数据库有备份则只需还原备份即可。但是如果没有备份,备份时间太久,或者其他原因导致备份不可用,那么就需要通过专业手段对数据库进行数据恢复。
|
12月前
|
SQL 关系型数据库 OLAP
云原生数据仓库AnalyticDB PostgreSQL同一个SQL可以实现向量索引、全文索引GIN、普通索引BTREE混合查询,简化业务实现逻辑、提升查询性能
本文档介绍了如何在AnalyticDB for PostgreSQL中创建表、向量索引及混合检索的实现步骤。主要内容包括:创建`articles`表并设置向量存储格式,创建ANN向量索引,为表增加`username`和`time`列,建立BTREE索引和GIN全文检索索引,并展示了查询结果。参考文档提供了详细的SQL语句和配置说明。
437 2
|
SQL 数据库
gbase 8a 数据库 SQL优化案例-关联顺序优化
gbase 8a 数据库 SQL优化案例-关联顺序优化