1. MySQL查询慢是什么体验?
大多数互联网应用场景都是读多写少,业务逻辑更多分布在写上。对读的要求大概就是要快。那么都有什么原因会导致我们完成一次出色的慢查询呢?
1.1 索引
在数据量不是很大时,大多慢查询可以用索引解决,大多慢查询也因为索引不合理而产生。
MySQL 索引基于 B+ 树,这句话相信面试都背烂了,接着就可以问最左前缀索引、 B+ 树和各种树了。
说到最左前缀,实际就是组合索引的使用规则,使用合理组合索引可以有效的提高查询速度,为什么呢?
因为索引下推。如果查询条件包含在了组合索引中,比如存在组合索引(a,b),查询到满足 a 的记录后会直接在索引内部判断 b 是否满足,减少回表次数。同时,如果查询的列恰好包含在组合索引中,即为覆盖索引,无需回表。索引规则估计都知道,实际开发中也会创建和使用。问题可能更多的是:为什么建了索引还慢?
1.1.1 什么原因导致索引失效
建了索引还慢,多半是索引失效(未使用),可用 explain 分析。索引失效常见原因有 :
-
where 中使用 != 或 <> 或 or 或表达式或函数(左侧)
-
like 语句 % 开头
-
字符串未加’’
-
索引字段区分度过低,如性别
-
未匹配最左前缀
(一张嘴就知道老面试题了) 为什么这些做法会导致失效,成熟的 MySQL 也有自己的想法。
1.1.2 这些原因为什么导致索引失效
如果要 MySQL 给一个理由,还是那棵 B+ 树。
函数操作
当在 查询 where = 左侧使用表达式或函数时,如字段 A 为字符串型且有索引, 有 where length(a) = 6
查询,这时传递一个 6 到 A 的索引树,不难想象在树的第一层就迷路了。
隐式转换
隐式类型转换和隐式字符编码转换也会导致这个问题。
-
隐式类型转换对于 JOOQ 这种框架来说一般倒不会出现。
-
隐式字符编码转换在连表查询时倒可能出现,即连表字段的类型相同但字符编码不同。
破坏了有序性
至于 Like 语句 % 开头、字符串未加 ’’ 原因基本一致,MySQL 认为对索引字段的操作可能会破坏索引有序性就机智的优化掉了。
不过,对于如性别这种区分度过低的字段,索引失效就不是因为这个原因。
1.1.3 性别字段为什么不要加索引
为什么索引区分度低的字段不要加索引。盲猜效率低,效率的确低,有时甚至会等于没加。
对于非聚簇索引,是要回表的。假如有 100 条数据,在 sex 字段建立索引,扫描到 51 个 male,需要再回表扫描 51 行。还不如直接来一次全表扫描呢。
所以,InnoDB 引擎对于这种场景就会放弃使用索引,至于区分度多低多少会放弃,大致是某类型的数据占到总的 30% 左右时,就会放弃使用该字段的索引,有兴趣可以试一下。
1.1.4 有什么好用且简单的索引方法
前面说到大多慢查询都源于索引,怎么建立并用好索引。这里有一些简单的规则。
-
索引下推:性别字段不适合建索引,但确实存在查询场景怎么办?如果是多条件查询,可以建立联合索引利用该特性优化。
-
覆盖索引:也是联合索引,查询需要的信息在索引里已经包含了,就不会再回表了。
-
前缀索引:对于字符串,可以只在前 N 位添加索引,避免不必要的开支。假如的确需要如关键字查询,那交给更合适的如 ES 或许更好。
-
不要对索引字段做函数操作
-
对于确定的、写多读少的表或者频繁更新的字段都应该考虑索引的维护成本。
1.1.5 如何评价