前面几篇文章和小伙伴们聊的基本上都是从索引的角度去优化 MySQL 查询,然而,索引创建的好,并不意味着查询就一定快,影响查询效率的因素特别多,今天我们就来聊一聊这些可能影响到查询的因素。
开始今天的内容之前,先来和小伙伴们大概捋一捋 MySQL 的查询流程。我们来看如下一张图:
这张图大家大概有个印象,在后续的 MySQL 查询和优化中,很多东西就容易理解了。
接下来我们就来看看什么情况下查询会变慢。
数据按需取用。有时候我们会忽略多拿数据对查询性能的影响,然而优化是一个锱铢必较的事情,需要多少数据就查询多少,要尽量避免数据库查询 100 条,结果前端只展示 10 条这种情况。如有需要,可以通过 limit 来限制数据库查询出来的数据总量。
如果在查询的时候使用了唯一性索引的话,那么查询到记录之后 MySQL 就停止扫描了;但是如果查询的时候使用的是非唯一性索引的话,那么扫描到第一条记录之后,还会继续向后扫描,直到扫描到第一条不满足条件的记录为止,对于这种情况,如果我们确定查询的结果只有一条,则可以通过 limit 进行限制,设置 limit 1,那么扫描到第一条满足条件的记录之后,就不会继续扫描了。
查询的时候尽量避免 select *
,这个问题在之前的文章中松哥其实和大家聊过了,因为很多时候我们在前端其实并不需要使用到那么多字段,可能只是为了查询简单,直接来一个 select *
,有时候列数和数据总量都比较少的时候,这么写也看不出来性能明显的差异,但是当列数和数据量大了,那么 select *
带来的影响就会比较大了。
特别是有的时候多表联合查询,如果用 select *
就会把多张表的查询结果拼接到一起,那么此时查询结果的列数就会成倍增加。
在前面的文章中,松哥也和大家提到过覆盖索引,如果索引设计得当,那么在查询的时候可以通过覆盖索引来提高查询的性能,但是如果使用了 select *
那么大概率是用不了覆盖索引了。
这里举一个 TienChin 项目的例子,用户登录成功之后,在后续的流程中,经常会用到当前登录用户的信息,如果每次都去数据库查询,每次查询返回结果都是一致的,没有必要,此时我们可以将用户信息存入到 Redis 缓存中,需要的时候从 Redis 中提取就可以了。
在项目中,对于这些需要多次频繁查询,且每次查询返回结果一样的数据,都可以选择将之存入到缓存中以提高查询性能。
在查询的时候,我们可以通过 explain 来查看执行计划,执行计划中有一个指标是扫描行数,如下图中的 rows,这个就表示查询优化器预估要扫描多少行记录,filtered 则表示预估满足条件的比例。
一般在单表查询时候我们并不会特别关注 filtered 字段,在多表联合查询的时候会比较关注该字段的值。
这一条实际上就是让大家关注前面查询计划中的 type 字段的值,type 字段的取值有很多种,例如常见的 index、ALL、range、const 以及 ref,还有一些不常见的如 system、eq_ref、fulltext、ref_or_null、index_merge、unique_subquery、index_subquery 等,每一种都代表了不同的查询计划,再结合查询计划中的 Extra 字段中的值,我们大致上可以将查询分为三种类型: