「这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战」
1. 关联查询语句
项目开发中的业务需要对多张表进行关联查询,并返回查询结果,其中使用到了客户表和认证记录表,客户表数据大概有十几万、认证记录有几万条,具体的查询语句如下:
SELECT
cr.*,
cc.*,
cu.*
FROM
retailer r
LEFT JOIN customer c1 ON r.CustomerCode = c1.Code
LEFT JOIN customer c2 ON r.parenCustomertCode = c2.EnCode
WHERE r.DeleteMark = 0
实际执行中,却发现数据量并不太大的关联查询持续了几十秒。
2. 问题分析
2.1 检查分析流程
- 针对查询语句过慢的问题,首先使用explain关键字对sql的执行计划进行分析,发现整个查询过程中均没有使用索引,每个表的数据不大,但是三张表联合,数据量直接乘积量级了;
- 既然定位到索引问题,就去数据库查看表的索引信息,却发现相关字段已经建立了索引,但是查询过程中却未使用到索引;
- 使用左外连接时,数据库会以左表为驱动,右表被驱动,考虑使用inner join替换left join来观察数据库查询是否会进行优化,替换后使用explain发现retailer表作为了被驱动表,且使用了索引,但是customer两个表仍然未走索引,且耗时过长;
- 经过对关联表的结构进行深入对比,且对字段类型、长度、编码等信息对比后发现数据表的编码方式存在差异,retailer表中字段字符集格式为utf8mb4,而customer表中字符集格式为utf8;
- 将关联字段字符集统一后,使用left join左外连接仍然可以很好的命中索引,查询时间直接0.1s。
2.2 问题总结
- 对多张表进行关联查询时,如果被驱动表的连接条件字段添加了索引,且满足使用索引的条件,但是sql语句却没有使用索引,仍然进行了全表查询,则有可能是:
- 两个表的关联字段类型、长度不一致
- 两个表的关联字段字符集类型不一致
- 对于业务逻辑过于复杂的查询,建议查询关联表数量不要超过3个,可以在代码逻辑层面进行处理,减少数据量过大对数据库服务器造成的压力。
2.3 官方说明
MySQL官方针对关联查询也进行了说明,即在进行表关联时,关联字段需要使用相同的类型、长度以及字符集,否则无法使用索引。
To retrieve rows from other tables when performing joins. MySQL can use indexes on columns more efficiently if they are declared as the same type and size.
For comparisons between nonbinary string columns, both columns should use the same character set. For example, comparing a utf8 column with a latin1 column precludes use of an index.
3. MySQL的utf8和utf8mb4
既然碰到了字符集不一致的问题,那么就顺便学习一下这俩字符集都是什么。
3.1 UTF是什么
由于对可以用ASCII表示的字符使用UNICODE并不高效,因为UNICODE比ASCII占用大一倍的空间,而对ASCII来说高字节的0对他毫无用处。为了解决这个问题,就出现了一些中间格式的字符集,他们被称为通用转换格式,英文叫做Unicode Transformation Format,即UTF。
- UTF-8其中的8表示最小单位是1字节(即8 bits),因此使用时有1、2、3字节等进行编码;
- 如果是UTF-16则说明字符集最小单位是2个字节,因此只能出现2、4、6字节的编码。
3.2 utf8和utf8mb4
utf8mb4是MySQL5.5.3版本之后增加的字符编码
标准的UTF-8字符集编码是可以使用1-4个字节去编码21位字符,MySQL里面实现的utf8最长使用3个字符,包含了大多数字符,但是像一些不常用汉字或者表情字符就无法满足使用;
utf8mb4,mb4 即 most bytes 4,是utf8的超集并完全兼容utf8,能够用四个字节存储更多的字符,如emoji表情和一些不常用的汉字需要使用四个字符编码,使用utf8mb4字符集可以更好的存储,性能更高。