Mysql慢优化
在mysql中,long_query_time的值为10,当sql语句执行的时间超过这个数值时,则会被记录到慢查询日志中。
Mysql语句查询流程
1、客户端发送sql语句到服务端;
2、服务端查看是否打开了缓存,若缓存打开,则查询缓存,若缓存命中,则直接返回从缓存中查询到的数据(在Msyql8.0中,已没有缓存的概念);
3、使用分析器对sql语句进行语法分析,判断其是否有语法错误;
4、通过优化器生成后续执行计划;
5、通过执行器调用存储引擎的接口,执行sql语句。
定位慢sql的方法
日志查询:开启慢查询日志,并使用mysqldumpslow等命令分析慢查询日志,查找到慢sql语句。
服务监控:从业务的根基监控慢sql,主要通过字节码插桩、连接层扩展、使用ORM框架等方式,对服务中的慢sql进行监控和警示。
定位到慢sql后,通过explain命令对sql语句进行分析,查看慢sql的如何执行。
优化sql的方式
避免过多的列查询
进行查询时,尽量避免使用select *,使用select 列名的方式进行查询,只查询需要的列。
对于分页优化,通过延迟关联、书签两种方式。
延迟关联
对于延迟关联,在偏移量很大时,如limit 10000,10需要查询10010条数据然后舍去前10000数据,这样会造成不必要的查询资源浪费,因此可使用延迟关联,如下列sql:
select * from a where tid = "1" limit 10000,10
该语句可被优化为先查询所需要的id,这些必须要满足tid="1"的条件,且limit同条件同为10000,10,因为未优化前,要先满足tid = "1"才能查询对应的偏移量,优化后的sql语句如下:
select * from a where id in (select id from a where tid = "1" limit 10000,10)
in后面仅查询了id一个属性,因此浪费的查询开销更小,最后通过in只需要查询10条需要的数据。
该方法的主要思路是查询出需要的主键,然后主键表关联原表即可。
书签
对于书签,通过记录上一次查询返回的最后一行数据,下一次查询时从这个数据开始,避免了重复行的查询,一般通过属性>last_max_id作为条件,查询时不需要offset,只需要查询的数据即可。
索引优化
索引覆盖
使用非主键索引查询时需要回表,但如果索引的叶子节点中已包含了要查询的字段,则不需要进行回表查询,这种方法就是索引覆盖。将需要查询的字段与主键一起建立联合索引。
避免使用<>、!=等操作符
使用上述操作符会导致索引失效,可考虑使用>、<、=、between等代替上述操作符。
适当使用前缀索引
前缀索引适用于前几位区分度较大、字段长度很大的字段查询,如查询邮箱时,由于格式一般为@xxx.com,因此比较适合使用前缀索引,添加的方式:
alter table a add index index2(email(6))
对于前缀索引,无法进行
避免在索引列上使用函数
在索引列上使用函数会导致索引失效,因为首先需要计算出函数值后再进行比较等操作,这样无法利用索引。
正确使用联合索引
对于联合索引的使用,需要满足最左前缀原则(也称最左匹配原则),指的是使用联合索引时查询条件从索引的最左侧的列开始,不跳过中间的列。
join优化
优化子查询
对于where、select列表中的子查询,往往会导致性能问题,因为可能会为每一行的外层查询执行一次子查询,而使用join可以对其进行优化。如下列sql语句,使用子查询的方式如下:
select * from a where id in (select id from b);
那么查询a的每一行时,会执行子查询select id from b,导致性能下降,此时可使用join来进行优化:
select * from a ioin b on a.id = b.id
此时连接后表中只有a、b数据表中id相等的数据行,和上述sql语句是等效的,同时减少了循环查询的次数。
小表驱动大表
在执行 join 操作时,尽量让行数较少的表驱动行数较多的表,这样可以减少查询过程中需要处理的数据量,同时连接得到的数据表的额外数据量也更少,减少了空间浪费,如下列sql语句:
select * from A left join B on A.id = B.id
其中A数据表数据量较小,B数据表数据量较大。
适当增加冗余字段
对于一些查询频率较高的字段,可考虑使用增加冗余字段,在查询时不用使用join关联其他数据表,直接查询即可。
避免使用join关联过多的表
一般使用join关联不超过3个数据表。
union优化
union是用于关联两个或多个select查询的结果,对于使用union查询的语句,可将where、limit等条件查询语句,下推到各select中,每个分支仅处理满足条件的数据,减少了不必要的数据合并和过滤,如下列语句,在未实现条件下推时的查询如下:
select * from(select * from aunionselect * from b
)as c
where c.id = 1;
条件下推后,sql语句如下:
select * from a where id = 1
union
select * from b where id = 1;
除上述方法外,还可利用索引的有序性,按照索引顺序扫描得出的自然有序结果,从而避免了排序操作。