优化手段金字塔模型
优化手段从底层往上,成本越高,但效果却越低。加硬件是成本最高,但效果却最低。sql语句的优化、索引的正确使用以及优化,是成本最低的方式,但往往却是效果最高的一种方式。
联合索引优化
最左前缀原则
联合索引优化中有一个口诀:带头大哥不能死,中间兄弟不能断
。比方说现在有联合索引:name、age、sex。想要这个索引生效,name字段必须使用,如果没有使用到name,则此索引必不生效。原因是:想要一个索引生效,首先的前提是:保证B+树的有序
,但当联合索引中没有用到name时,后面的字段无法保证其顺序,因此得扫描全表,故而无法使用索引。而当使用到了name时,保证了其字段的数据是有序的了,但后面的字段的数据仍然无序,所以,想要完全发挥联合索引的最高性能时,应该是查:name/age/sex、name/age,假设只需要查询name和sex时,应该查:name/age/sex或者调整联合索引为:name、sex、age。
索引列上少计算数
在查询的时候,索引列上尽可能少进行计算,比如:select * from user where left(name,3) = '张三'
。此时索引是不生效的,可以考虑使用like进行模糊查询,改成:select * from user where name like '张三%'
。此时会使用范围索引。查询时间时使用函数:date(create_time) = '2023-07-01'
时也同理,B+树查找的时候每个叶子节点都需要进行运算,故而需要扫描全表。而使用范围时:create_time >= '2023-07-01 00:00:00' and create_time <= '2023-07-01 23:59:59'
时,可以使用到范围索引。因为B+树中可以寻找到最小的具体值,然后根据有序的原则,找到与之关联的范围数据,故而不需要全表扫描,即:
保证B+树的有序性,不要在索引列上做任何操作(计算、函数、类型转换),否则会导致索引失效而转向扫描全表
范围后面全失效
仍然是保证B+树的有序的问题,当使用范围查询的时候,是无法保证后续数据的有序的,因而后面的字段索引会失效,如果是最左的字段就使用范围查询时,会导致索引失效而转向全表扫描。
覆盖索引不使用*
select 后面不跟*而是跟着联合索引的字段时,不需要通过回表即可得到全部数据,效率自然会更高
不等于、空值、or
这三种情况很大几率是扫描全表的,但是可能能匹配上某些索引,但经过mysql底层评估,最终可能还是进行了全表扫描,此时可以使用force index (索引名)
来手动使用索引
like百分号写最右
在使用like模糊查询的时候,应该尽量把百分号写在最右边,因为B+树进行检索的时候,如果第一个字都不能确定,那将要进行全表的扫描。
varchar类型的引号不可以丢
当索引字段类型是varchar类型的时候,引号如果不写,也会导致索引失效而转向扫描全表
SQL优化
尽量避免使用select *
- 查询时需要将*转化为对应的字段,增加查询解析器的成本
- select * 查询一般不走覆盖所有会产生大量的回表查询
- 实际使用中,我们往往只需要几个字段的数据,将全部数据查出来空耗CPU、内存资源
小表驱动大表
通过数据量较小、索引比较完备的表,然后使用其索引和条件对大数量的表进行筛选,从而减少计算量,提高查询效率。
提升group by的效率
- 创建索引:如果使用的group by的列没有索引,那么查询可能会变慢。因此,可以创建一个或多个合适的索引来提速
- 调整查询:查询的写法也会影响group by的效率。可以尝试不适用子查询或临时表,或者使用join或exists来代替in子查询
批量操作
当需要批量新增或修改或删除的时候,不应该考虑遍历一条条进行处理,mysql中提供了批量操作的方式
使用limit
查询部分数据,可以通过limit来限制查询的结果,常见于分页查询。使用limit可以提高查询性能、减少需要处理的时间,并且只返回我们所关心的行 。
百万行级别数量的数据表limit越来越慢怎么办?
首先mysql是怎么处理limit的呢?比方说:select * from user limit 100000,10
这个时候mysql会从第一条开始找,一条条进行查找,直到找到第100000+10条数据
然后,抛弃掉前面的100000条
最后,返回剩下的10条数据
很显然,这种一条条寻找的办法是很损耗性能的,我们应该想办法帮它规避掉这部分的操作,优化方法:
- 利用自增id直接定位最开始的数据
select * from user where id >= 100000 limit 10
- 先找出所需数据的索引列,然后通过索引列找到对应的数据
select * from user where id in (select id from user where name = '张三' ) limit 100000,10
- 因为使用了in,范围太大可能会导致全表扫描,因此还可以进行二次优化,改用 inner join
select * from user inner join(select id from user where name = '张三' limit 0,10) b using (id)
使用union all 代替 union
union all:获取所有数据但不去重,包含重复数据
union:获取所有数据且去重,不包含重复数据
如果业务数据容许出现重复数据,则更推荐union all,因为union去重要遍历、排序、比较。它更耗时,且耗费CPU资源,但去重后的结果最精准。
join的表不宜过多
数据表设计的时候,应该尽量减少join操作的使用,简化表之间的关系,以提高查询效率和系统的性能。
总结
1.减少数据扫描
2.返回更少的数据
3.减少交互次数
4.减少服务器CPU和内存开销