记录一次MySQL联表查询优化过程

本文讲述了在工作中遇到的一个性能问题,由于两表关联字段的字符编码不一致(`utf8` vs `utf8mb4`),导致查询效率显著降低。通过explain分析,发现使用了BlockNestedLoop方法,解决方法是统一字符集并建立联合索引,最终优化了查询速度。
摘要由CSDN通过智能技术生成

工作中遇见一次两表联查时效率特别慢的情况,查了半天才找到真正的原因——关联字段的字符编码集不一致!!!一开始查的时候有看过联表的字段类型是否一致,却忽略了编码集的问题。

两表数据量都接近100w,关联字段均已创建索引,查询时要花费几秒以上的时间,而单表查询时花费不过几十毫秒。通过explain看到 第二条的extra

Using where; Using index; Using join buffer (Block Nested Loop) ,问题就出在这个Using join buffer (Block Nested Loop)中,正常关联字段都建立了索引的话extra应该是Using index

表结构

CREATE TABLE `employee` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `employee_no` varchar(20) DEFAULT NULL COMMENT '员工编号',
  `employee_name` varchar(20) DEFAULT NULL COMMENT '员工名字',
  `mobile` varchar(16) DEFAULT NULL COMMENT '手机号',
  `position` varchar(50) DEFAULT NULL COMMENT '职务信息',
  `sex` tinyint(4) DEFAULT NULL COMMENT '性别 1表示男性,2表示女性',
  `email` varchar(255) DEFAULT NULL COMMENT '邮箱',
  `address` varchar(255) DEFAULT NULL COMMENT '地址',
  `is_delete` tinyint(4) DEFAULT NULL COMMENT '是否删除 : 0=否,1=是',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `uk_employee_no` (`employee_no`) USING BTREE,
  KEY `idx_employee_name` (`employee_name`) USING BTREE,
  KEY `idx_mobile` (`mobile`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ;

CREATE TABLE `employee_dept_relation` (
  `id` bigint(21) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `employee_no` varchar(20) DEFAULT NULL COMMENT '员工编号',
  `dept_code` varchar(20) DEFAULT NULL COMMENT '员工所属机构编号',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`id`) USING BTREE,
  KEY `idx_dept_code` (`dept_code`) USING BTREE,
  KEY `idx_employee_no` (`employee_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='员工和部门关系表' ;

查询语句

select a.employee_no,a.employee_name,a.mobile,a.sex,b.dept_code from employee a, employee_dept_relation b where a.employee_no=b.employee_no and a.is_delete=0 and a.employee_name = "员工A"

表中员工A同名的有13个

explain 计划

idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfiltered                           Extra                           
1SIMPLEarefuk_employee_no,idx_employee_nameidx_employee_name259const1310Using where
1SIMPLEbindexidx_employee_no95977217100Using where; Using index; Using join buffer (Block Nested Loop)

 可以看出这里驱动表为a,并且a的typeref,说明走了二级索引,没有什么问题,但是b表type类型为index,说明进行了全索引扫描,仅次于全部扫描了,而且ExtraUsing join buffer (Block Nested Loop) ,说明联表的索引没有生效,参考这个篇文章《join表连接的三种算法思想》。

 于是开始查找索引无效的原因,在网上找了很久排除了索引列含有null值,索引列类型不一致,最终查看了一下字符集才发现两边一个表是utf8一个是utf8mb4,之前之所以没有注意到是因为在DDL语句中字符集的设置是在表上,没有对字段设置。这两个表中employee表是从远端拉取的,拉取过程中发现表中有多个字段带有表情符号,所以同事处理的时候直接在表上设置了字符集。另外一张表,是本地其他途径同步的,默认是库的编码,这就造成了两边字符不一致。

 将a表的empoyee_no字符集和排序规则改为和b表一致之后,再次执行,发现只需要0.063s即可。

更改字符集后explain计划

idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfiltered
Extra
1SIMPLEarefuk_employee_no,idx_employee_nameidx_employee_name259const1310Using where
1SIMPLEbrefidx_employee_noidx_employee_no62a.employee_no1100Using index condition

 可以看到两张表的type都是ref,走了索引。速度大大提升,到这里基本满足需求了,其实还可以继续优化,比如对employee_dept_relation表的employee_nodept_code建联合索引 ,再次执行只需要0.021s。

建立联合索引后 explain计划

idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfiltered
Extra
1SIMPLEarefuk_employee_no,idx_employee_nameidx_employee_name259const1310Using where
1SIMPLEbrefidx_employee_noidx_employee_no62a.employee_no1100Using where; Using index
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pzzhao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值