sql优化的具体方法

使用索引

索引是数据库优化最常用也是最重要的手段之一, 通过索引通常可以帮助用户解决大多数的MySQL的性能优化问题。

create table `tb_seller` (
	`sellerid` varchar (100),
	`name` varchar (100),
	`nickname` varchar (50),
	`password` varchar (60),
	`status` varchar (1),
	`address` varchar (100),
	`createtime` datetime,
    primary key(`sellerid`)
)engine=innodb default charset=utf8mb4; 

# 创建复合索引
create index idx_seller_name_sta_addr on tb_seller(name,status,address);

避免索引失效

1). 全值匹配 ,对索引中所有列都指定具体值。该情况下,索引生效,执行效率高。

explain select * from tb_seller where name='小米科技' and status='1' and address='北京市'\G;

在这里插入图片描述
2). 最左前缀法则
如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始,并且不跳过索引中的列。匹配最左前缀法则,走索引:
在这里插入图片描述
如果符合最左法则,但是出现跳跃某一列,只有最左列索引生效:(违背最左前缀法则导致索引失效原因:因为建立一个复合索引时,其实是以第一个索引字段进行排序后,再在其基础上对第二个索引字段排序,再在其基础上对后面的索引字段排序,…… 所以在N个索引列组合为条件查询时,必须严格按照复合索引创建的顺序来进行sql语句的编写,不能跳跃索引,否则索引失效。
在这里插入图片描述
3). 范围查询右边的列,不能使用索引 。
在这里插入图片描述
4). 不要在索引列上进行运算操作, 索引将失效。
在这里插入图片描述
5). 字符串不加单引号,造成索引失效。由于,在查询时,没有对字符串加单引号,MySQL的查询优化器,会自动的进行类型转换,也就是说他进行了运算操作,造成索引失效。
在这里插入图片描述
6). 尽量使用覆盖索引,避免select *
尽量使用覆盖索引(只访问索引的查询(索引列完全包含查询列)),减少select * 。
在这里插入图片描述
所以类似于select * 这样的查询列,超出索引列,就会降低性能,因为虽然走了索引查询,但是查询到数据后还需要进行回表查询其他非索引字段的数据,降低了性能。
在这里插入图片描述

using index :使用覆盖索引的时候就会出现
using where:在查找使用索引的情况下,需要回表去查询所需的数据
using index condition:查找使用了索引,但是需要回表查询数据
using index ; using where:查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询数据

7). 用or分割开的条件, 如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到。

示例,name字段是索引列 , 而createtime不是索引列,中间是or进行连接是不走索引的 :

explain select * from tb_seller where name='黑马程序员' or createtime = '2088-01-01 12:00:00'\G;	

在这里插入图片描述
8). 以%开头的Like模糊查询,索引失效。
如果仅仅是尾部模糊匹配,索引不会失效。如果是头部模糊匹配,索引失效。
在这里插入图片描述
解决方案 : 通过覆盖索引来解决
在这里插入图片描述
9). 如果MySQL评估(如果评估全表扫描快,那么就不会走索引)使用索引比全表更慢,则不使用索引。
在这里插入图片描述
10). is NULL , is NOT NULL 有时索引失效。其实也就是mysql自身评估(如果评估全表扫描快,那么就不会走索引)的原因。
在这里插入图片描述
11). in 走索引, not in 索引失效。
在这里插入图片描述
12). 尽量使用复合索引,而少使用单列索引 。

create index idx_name_sta_address on tb_seller(name, status, address);
就相当于创建了三个索引 : 
	name
	name + status
	name + status + address

创建单列索引

create index idx_seller_name on tb_seller(name);
create index idx_seller_status on tb_seller(status);
create index idx_seller_address on tb_seller(address);

此时当3个索引字段全部被作为条件查询时,数据库会选择一个最优的索引(辨识度最高索引)来使用,并不会使用全部索引 。

查看索引使用情况

show status like 'Handler_read%';	
show global status like 'Handler_read%';	

在这里插入图片描述

  • Handler_read_first:索引中第一条被读的次数。如果较高,表示服务器正执行大量全索引扫描(这个值越低越好)。
  • Handler_read_key:如果索引正在工作,这个值代表一个行被索引值读的次数,如果值越低,表示索引得到的性能改善不高,因为索引不经常使用(这个值越高越好)。
  • Handler_read_next :按照键顺序读下一行的请求数。如果你用范围约束或如果执行索引扫描来查询索引列,该值增加。
  • Handler_read_prev:按照键顺序读前一行的请求数。该读方法主要用于优化ORDER BY … DESC。
  • Handler_read_rnd :根据固定位置读一行的请求数。如果你正执行大量查询并需要对结果进行排序该值较高。你可能使用了大量需要MySQL扫描整个表的查询或你的连接没有正确使用键。这个值较高,意味着运行效率低,应该建立索引来补救。
  • Handler_read_rnd_next:在数据文件中读下一行的请求数。如果你正进行大量的表扫描,该值较高。通常说明你的表索引不正确或写入的查询没有利用索引。

优化insert语句

当进行数据的insert操作的时候,可以考虑采用以下几种优化方案。

  • 如果需要同时对一张表插入很多行数据时,应该尽量使用多个值表的insert语句,这种方式将大大的缩减客户端与数据库之间的连接、关闭等消耗。使得效率比分开执行的单个insert语句快。

    示例, 原始方式为:

insert into tb_test values(1,'Tom');
insert into tb_test values(2,'Cat');
insert into tb_test values(3,'Jerry');
# 优化后的方案为
insert into tb_test values(1,'Tom'),(2,'Cat')(3,'Jerry');
  • 修改mysql为手动提交事务,在事务中进行批量数据插入。
start transaction;
insert into tb_test values(1,'Tom');
insert into tb_test values(2,'Cat');
insert into tb_test values(3,'Jerry');
commit;
  • -数据有序插入
insert into tb_test values(4,'Tim');
insert into tb_test values(1,'Tom');
insert into tb_test values(3,'Jerry');
insert into tb_test values(5,'Rose');
insert into tb_test values(2,'Cat');
#  优化后
insert into tb_test values(1,'Tom');
insert into tb_test values(2,'Cat');
insert into tb_test values(3,'Jerry');
insert into tb_test values(4,'Tim');
insert into tb_test values(5,'Rose');

优化order by语句

1). 通过对返回数据进行排序,也就是通常说的 filesort 排序,所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序。
在这里插入图片描述
2). 通过有序索引顺序扫描直接返回有序数据,这种情况即为 using index,不需要额外排序,操作效率高
在这里插入图片描述
多字段排序
在这里插入图片描述了解了MySQL的排序方式,优化目标就清晰了:尽量减少额外的排序,通过索引直接返回有序数据where 条件和Order by 使用相同的索引,并且Order By 的顺序和索引顺序相同, 并且Order by 的字段都是升序,或者都是降序。否则肯定需要额外的操作,这样就会出现FileSort。
在这里插入图片描述

优化group by语句

由于GROUP BY 实际上也同样会进行排序操作,而且与ORDER BY 相比,GROUP BY 主要只是多了排序之后的分组操作。当然,如果在分组的时候还使用了其他的一些聚合函数,那么还需要一些聚合函数的计算。所以,在GROUP BY 的实现过程中,与 ORDER BY 一样也可以利用到索引。

如果查询包含 group by 但是用户想要避免排序结果的消耗, 则可以执行order by null 禁止排序。如下 :

drop index idx_emp_age_salary on emp;
explain select age,count(*) from emp group by age;

在这里插入图片描述

explain select age,count(*) from emp group by age order by null;

在这里插入图片描述
从上面的例子可以看出,第一个SQL语句需要进行"filesort",而第二个SQL由于order by null 不需要进行 “filesort”, 而上文提过Filesort往往非常耗费时间。针对分组字段创建索引就会提高效率。
在这里插入图片描述

优化嵌套查询

Mysql4.1版本之后,开始支持SQL的子查询。这个技术可以使用SELECT语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在另一个查询中。使用子查询可以一次性的完成很多逻辑上需要多个步骤才能完成的SQL操作,同时也可以避免事务或者表锁死,并且写起来也很容易。但是,有些情况下,子查询是可以被更高效的连接(JOIN)替代

示例 ,查找有角色的所有的用户信息 :
在这里插入图片描述

优化or语句

对于包含OR的查询子句,如果要利用索引,则OR之间的每个条件列都必须用到索引而且不能使用到复合索引; 如果没有索引,则应该考虑增加索引。
在这里插入图片描述
建议使用 union 替换 or :
在这里插入图片描述
我们来比较下重要指标,发现主要差别是 type 和 ref 这两项。
type 显示的是访问类型,是较为重要的一个指标,结果值从好到坏依次是:

system > const > eq_ref > ref > fulltext > ref_or_null  > index_merge > unique_subquery > index_subquery > range > index > ALL

UNION 语句的 type 值为 ref,OR 语句的 type 值为 range,可以看到这是一个很明显的差距。UNION 语句的 ref 值为 const,OR 语句的 type 值为 null,const 表示是常量值引用,非常快。这两项的差距就说明UNION 要优于 OR 。

优化分页查询

一般分页查询时,通过创建覆盖索引能够比较好地提高性能。一个常见又非常头疼的问题就是 limit 2000000,10 ,此时需要MySQL排序前2000010 记录,仅仅返回2000000 - 2000010 的记录,其他记录丢弃,查询排序的代价非常大 。
在这里插入图片描述

方式1

在索引上完成排序分页操作,最后根据主键关联回原表查询所需要的其他列内容。
在这里插入图片描述

方式2

该方案适用于主键自增,而且主键自增不能出现断层 的表,可以把Limit 查询转换成某个位置的查询 。
在这里插入图片描述

使用SQL提示

SQL提示,是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些人为的提示来达到优化操作的目的。

USE INDEX

在查询语句中表名的后面,添加 use index 来提供希望MySQL去参考的索引列表,就可以让MySQL不再考虑其他可用的索引。
在这里插入图片描述

IGNORE INDEX

如果用户只是单纯的想让MySQL忽略一个或者多个索引,则可以使用 ignore index 作为 hint 。
在这里插入图片描述

FORCE INDEX

为强制MySQL使用一个特定的索引,可在查询中使用 force index 作为hint 。

create index idx_seller_address on tb_seller(address);

在这里插入图片描述


本文参考www.itheima.com学习资料,只用于本人学习笔记总结,不可转载,如有侵权请谅解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值