sql性能分析以及sql语句的优化

sql性能分析的几种方式

  • SQL执行频率

    show [session|global] status like ‘com__(七个下划线)_’;可以查看当前数据库的INSERT、UPDATE、DELETE、SELECT的访问频次

    通过这个指令我们可以查看当前数据库是以查询为主还是更新为主。如果是以查询为主,那么就要考虑对数据库的索引进行优化了。

  • 慢查询日志。定位需要对哪些查询语句进行优化。

    MySQL的配置文件(/etc/my.cnf)中配置如下信息

    # 开启MySQL慢日志查询开关
    slow_query_log=1
    # 设置慢日志的时间为2秒,SQL语句执行时间超过2秒,就会视为慢查询,记录慢查询日志
    long_query_time=2
    

    日志信息所在:/var/lib/mysql/localhost-slow.log

    这样在慢查询日志中就会记录那些查询效率较低的sql语句

  • profile :这个可以让我们知道sql语句运行时的时间都消耗在哪里了,用的不是很多

  • explain:获取sql的执行计划

explain

使用方式:直接在sql语句前面加explain,或者加desc,作用基本一致,使用expalin多一些

EXPLAIN SELECT 字段列表 FROM 表名 WHERE 条件 ;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-59U9tmMo-1664243430019)(/Users/wangkx/Desktop/wkx的md笔记/img/image-20220926195406219.png)]

各个字段的含义:

  • id:select查询的序列号。表示查询中执行select子句或者是操作表的顺序,当id相同的时候,执行的顺序是从上往下,当id不同的时候,id值越大,执行顺序越靠前
  • select_type:表示 SELECT 的类型,常见的取值有 SIMPLE(简单表,即不使用表连接或者子查询)、PRIMARY(主查询,即外层的查询)、UNION(UNION 中的第二个或者后面的查询语句)、SUBQUERY(SELECT/WHERE之后包含了子查询)等
  • type:表示连接类型,性能由好到差的连接类型为NULL、system、const、eq_ref、ref、range、 index、all
  • possible_key:显示在这张表上,此次查询可能会用到的索引。有一个或多个
  • key :此次查询实际用到的索引,如果是null值的话就说明没有使用索引
  • key_len:表示索引中使用的字节数, 该值为索引字段最大可能长度,并非实际使用长度,在不损失精确性的前提下, 长度越短越好
  • rows:MySQL认为必须要执行查询的行数,在innodb引擎的表中,是一个估计值,可能并不总是准确的
  • filtered :表示返回结果的行数占需读取行数的百分比, filtered 的值越大越好

sql语句优化

插入数据优化

对于insert

  • 批量插入:一般是500-1000条

  • 手动提交事务:执行一系列insert之前开启事务,执行完提交事务,避免事务的频繁开启和提交

  • 按照主键顺序插入

对于一次有几百万条数据

这个时候使用insert效率会很低,可以使用load指令把本地文件大批量加载到数据库中

-- 客户端连接服务端时,加上参数 -–local-infile
  mysql –-local-infile -u root -p
-- 设置全局参数local_infile为1,开启从本地加载文件导入数据的开关
  set global local_infile = 1;
-- 执行load指令将准备好的数据,加载到表结构中
	load data local infile '/root/sql1.log' into table tb_user fields terminated by ',' lines terminated by '\n' ;   //每个字段按照逗号隔开,行间按照换行隔开

主键优化

  • 尽量降低主键长度,因为二级索引会有很多,它的叶子结点存储的是主键值,主键长度过大会导致单个节点体积变大,占用过多磁盘空间
  • 插入数据时,尽量选择顺序插入,选择使用AUTO_INCREMENT自增主键。
  • 尽量不要使用UUID做主键或者是其他自然主键,如身份证号。uuid是乱序的会导致页分裂,身份证号太长了
  • 尽量避免对主键的修改

在InnoDB引擎中,数据行是记录在逻辑结构 page 页中的,每一个页的大小是固定的,默认16K,所以说一个页中所存储的行也是有限的。然后因为使用的是B+索引,表数据是按照主键顺序存储的,页与页之间的顺序是有序的。

页分裂

mysql规定每个页至少包含两个数据(只有一个数据就是链表了)。

当主键顺序插入时,第一个页满了就往第二个页写,页与页之间使用指针相连。

但是当主键乱序插入时,新数据不会直接插入到空白页(这样做数据会无序),而是会把对应页的后半部分数据移动到空白页,然后再把要插入的值插入到空白页。最后还得重新设置链表的指针。效率就很低了

页合并

删除数据时,把对应位置标记为无效,当页中删除的记录达到 一个阈值的时候(默认为页的50%),InnoDB会开始寻找最靠近的页(前或后)看看是否可以将两个页合并以优化空间使用。

order by优化

首先MySQL的排序有两种方式

  • Using filesort : 通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sortbuffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序
  • Using index : 通过扫描索引文件就可以直接返回有序数据,这种情况即为 using index,不需要额外排序,操作效率高

原则:

  • 我们可以根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀法则
  • 尽量使用覆盖索引,回表操作一定会Using filesort
  • 如果不可避免的出现filesort,大数据量排序时,可以适当增大排序缓冲区大小sort_buffer_size(默认256k),避免数据在磁盘中排序

group by优化

在分组操作时,给group by后的条件加索引,尽量加联合索引

分组操作时,索引的使用也是满足最左前缀法则的

limit优化

在数据量比较大时,如果进行limit分页查询,在查询时,越往后,分页查询效率越低,因为会先对前面的记录排序

优化思路: 一般分页查询时,通过创建 覆盖索引 能够比较好地提高性能,可以通过覆盖索引加子查询形式进行优化

举例:我们可以先使用limit只查出id(用到覆盖索引),然后把这张结果表和原表做一次连表查询

select * from tb_sku t , 
(select id from tb_sku order by id limit 2000000,10) a
where t.id = a.id;

count优化

不是很容易优化,非要优化的话可以使用redis自己计数。

  • count(字段):没有not null 约束 : InnoDB 引擎会遍历整张表把每一行的字段值都取出来,返回给服务层,服务层判断是否为null,不为null,计数累加。有not null 约束:InnoDB 引擎会遍历整张表把每一行的字段值都取出来,返回给服务层,直接按行进行累加。
  • count(主键):InnoDB 引擎会遍历整张表,把每一行的 主键id 值都取出来,返回给服务层。服务层拿到主键后,直接按行进行累加(主键不可能为null)
  • count(数字):InnoDB 引擎遍历整张表,但不取值。服务层对于返回的每一行,放一个数字“1”进去,直接按行进行累加
  • count(*):InnoDB引擎并不会把全部字段取出来,而是专门做了优化,不取值,服务层直接按行进行累加。

按照效率排序的话,count(字段) < count(主键 id) < count(1) ≈ count(),所以尽量使用 count()。

update优化

在InnoDB引擎中,执行一条update语句:update course set name = ‘SpringBoot’ where name = ‘PHP’

如果我们给name字段加了索引,此时InnoDB加的是行锁,其他事务可以操作别的记录,并发性能高

但是如果name字段没有索引,或者name字段索引失效,update时直接升级为表锁,其他事务无法操作数据库表。

所以要尽量使用索引字段为条件来更新数据,增加并发性。

你可能感兴趣的:(sql,数据库,mysql)