MySQL高级—Sql优化

MySQL高级

sql查询慢的原因

执行时间或者等待时间长

  1. 查询语句写的烂

  2. 索引失效,(要么没有建索引或者建了索引没有用上)索引包括单值索引(一个索引只包含单个列)和复合索引(一个索引包含多个列)、唯一索(索引列必须唯一)

  3. 关联查询用太多的join

  4. 服务器调优及各个参数设置(缓冲、线程数等)

MySQL执行顺序

程序员写的sql语句
select
from
join on 
where
group by
having 
order by
limit
机器读取顺序
from
on join
where 
group by
having 
select
order by
limit

七种JOIN

七种JOIN.png
JOIN续.png

索引

是一种帮助sql高效获取数据的数据结构,简单的理解为“排好序的快速查找的数据结构”所以使用索引快,默认是B+树

优势:

​ 类似图书馆建书目索引,田炯数据检索效率,降低数据库IO成本;通过索引列对数据进行排序,减低数据排序的成本,降低了CPU消耗

劣势:

​ 索引列也是要占用空间的,虽然索引大大提高了查询效率,同时也降低更新表的速度,如对表进行增删改操作,因为更新表时不仅要保存数据,还要保存索引文件每次更新添加了的字段

创建索引

create index 索引名称(一般格式idx_+多个字段名的驼峰原则) on 表名(字段1、字段2)

删除索引

drop index 索引名称 on 表名

查看索引

show index from 表名

哪些情况需要创建索引

  1. 主键会自动建立唯一索引
  2. 频繁作为查询条件的字段应该创建索引
  3. 查询中与其它表关联的字段,外键关系建立索引
  4. 频繁更新的字段不适合创建索引
  5. where条件中用不到的字段不用建立索引
  6. 查询中排序的字段建立索引
  7. 查询中统计或者分组字段

哪些情况不需要创建索引

  1. 表记录太少
  2. 经常增删改的表——因为提高了查询效率,同时也降低更新表的速度,如对表进行增删改操作,因为更新表时不仅要保存数据,还要保存索引文件
  3. 数据重复且分布平均的表字段,

mysql常见瓶颈

CPU:cpu在饱和的时候,一般是发生在数据装入内存或从磁盘上读取数据时候

IO:磁盘I/O瓶颈发生在装入数据远大于内存容量的时候

服务器硬件性能的瓶颈

Explain

定义

​ 查看执行计划,使用explain可以模拟优化器执行SQL查询语句,从而知道Mysql是如何处理你的sql语句的,分析你的查询语句和表结构的性能瓶颈

作用:

  1. 知道表的读取顺序

  2. 数据读取操作的操作类型

  3. 哪些索引真实被使用

  4. 哪些索引被理论使用

  5. 表之间的引用

  6. 每张表有多少行别优化器查询

格式:

​ explain+SQL语句

包含的信息

expalin头部信息.png

各个字段的解释

explain信息1.png
explain信息2.png

explain信息3.png

优化案例

案例所用的库是window下的explain库

Explain执行计划单表 两表 三表 建表SQL语句
1. 单表

CREATE TABLE IF NOT EXISTS `article`(
`id` INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`author_id` INT (10) UNSIGNED NOT NULL,
`category_id` INT(10) UNSIGNED NOT NULL , 
`views` INT(10) UNSIGNED NOT NULL , 
`comments` INT(10) UNSIGNED NOT NULL,
`title` VARBINARY(255) NOT NULL,
`content` TEXT NOT NULL
);
 
 
INSERT INTO `article`(`author_id`,`category_id` ,`views` ,`comments` ,`title` ,`content` )VALUES
(1,1,1,1,'1','1'),
(2,2,2,2,'2','2'),
(3,3,1,1,'3','3');
 
SELECT * FROM ARTICLE;

2. 两表建表SQL语句

CREATE TABLE IF NOT EXISTS `class`(
`id` INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`card` INT (10) UNSIGNED NOT NULL
);
 
 
CREATE TABLE IF NOT EXISTS `book`(
`bookid` INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`card` INT (10) UNSIGNED NOT NULL
);
 
 
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card)VALUES(FLOOR(1+(RAND()*20)));
 
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card)VALUES(FLOOR(1+(RAND()*20)));

3. 三张表的语句在上放的基础上多加一张表

CREATE TABLE IF NOT EXISTS `phone`(
`phoneid` INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`card` INT (10) UNSIGNED NOT NULL
)ENGINE = INNODB;
 
 INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card)VALUES(FLOOR(1+(RAND()*20)));

1、单表优化 表article

#	1、查询article表中category_id为1,且comments大于1的情况下,views最多的author_id 
explain select id,author_id from article where category_id = 1 and comments > 1 order by views desc limit 1;
#	2、查看索引,只使用了一种主键索引
show index from article;
# 	3、创建索引(使用组合索引)来优化查询,因为使用explain发现type是All,而且没有用到索引,Extra还使用了Using filesort,查询效率低,使用索引之后type变成了range且使用了索引
CREATE INDEX idx_article_ccv on article(category_id,comments,views);
#	4、解决Extra中还使用了Using filesort,不要建三个字段的组合索引,因为comment>1是一个范围值,对后面的索引views就失效了,所以在这就建两个字段的索引,不要为comments建立索引
#	5、先删除索引,重新建立索引,这时再执行第二条语句查看,发现Extra没有使用Using filesort,此时优化成功
DROP INDEX idx_article_ccv ON article;
CREATE INDEX idx_article_cv on article(category_id,views);

2、两表优化 表class和表book

#	1、先查看join后的执行计划信息,看看哪里需要优化,type都为All,且没有用到索引,rows加起来是40,接下来进行优化,索引应该加在左表还是右表呢?经过测试左连接索引加在右表上,右连接加在左表上
EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card;
#	2、创建索引,查看第一条语句,type其中一个变成了ref,rows加起来才21,性能优化了
CREATE INDEX idx_book_card ON book(card);

3、三表优化 表book、表class、表phone

#	1、先删除三表中所有的索引,再进行测试
DROP INDEX idx_book_card ON book;
DROP INDEX idx_article_cv ON article;
#	2、使用左连接进行测试,开始查看执行计划信息时,type都为All,rows都为20,而且没有用到索引,所以可以进行性能优化
EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card=book.card LEFT JOIN phone ON book.card=phone.card
#	3、还是根据左连接索引加在右表上,右连接加在左表上,查看第一条语句,明显进行了性能优化了
CREATE INDEX idx_book_card ON book(card);
CREATE INDEX idx_phone_card ON phone(card);

索引优化

索引优化,防止索引失效

索引优化.png

**全值匹配我最爱:**指的是创建了几个索引列,就使用了几个。

**使用覆盖索引:**就是查询的字段(也可以是部分字段)正好就是索引列,避免使用select *,在Extra中就会出现一个using index,此时性能提升了

优化口诀

​ 例如为表user的字段name、age和password创建了索引 idx_user_nameAgePassword

第一句:最佳左前缀法则:简单理解为带头大哥必须有,中间兄弟不能断(在where后面name索引必须有,age不能断,否则会发生索引失效)

​ **例子:**第一条sql语句使用到了索引,其他的不行

SELECT * FROM user WHERE name='zs' AND age=12;
SELECT * FROM user WHERE age=12 AND password='123456';
SELECT * FROM user WHERE name='zs' AND password='123456';

**第二句:**索引列上无计算

​ **例子:**下面语句性能就不好了,创建的索引失效了

select * from user where left(name,3)='lzy';

**第三句:**范围之后全失效

​ **例子:**在age>4是一个范围,后面的password索引失效了,只用到了name和age索引

explain select * from user where name='zs' and age>4 and password='123456';

**第四句:**like百分号加右边,否则索引失效。如果非要用两个%,解决办法就是使用覆盖索引(不要使用select *),查询的字段(也可以是部分字段,但不能超出索引个数)与建立的索引个数和顺序最好要一样

例子:

#创建索引后,
create index idx_user_nameAge on user(name,age);
#下面的语句索引失效,因为使用了select *
explain select * from user where name like '%zs%';
#使用覆盖索引
explain select name from user where name like '%zs%';
explain select name,age from user where name like '%zs%';
#下面一条语句的字段数超出了索引个数,所以索引失效了
explain select name,age,password from user where name like '%zs%';

**第五句:**varchar类型的字段的引号不能丢

​ **例子:**name字段是varchar类型的,其中有个name为200

explain select * from user where name='200';
#由于mysql内部会进行自动类型转换,将数字当字符串查询,变成了全表扫描,索引失效
explain select * from user where name=200

索引是否被用到

案例索引是否被用到.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值