mMySQL学习笔记(4)——查询优化(笔记)

分析:
1、至少跑一天,看看生产慢SQL情况
2、开启慢查询日志,设置阈值,比如超过5秒的就是慢SQL,并将它抓取出来。
3、explain+慢SQL分析
4、show profile(分析效果与explain差不多)
5、运维经理 or DBA 进行SQL数据库服务器的参数调优。


小表驱动大表
即小的数据集驱动大的数据集


in:

select * from A where id in (select id from B)

这个语句,等价于先查询B表,再查询A表。小表应该是B,大表应该是A。B驱动A


exists:

select * from A where exists(select 1 from B where B.id = A.id)

现从A做一个查询,之后在查询B表。
意思是查看A表记录是否与B表满足 B.id = A.id,如果是,则返回一条记录;如果否,则不返回。
这个语句,先查询A,再查询B。因此,A应该是小表。B应该是大表。 A驱动B

select ... from table where exists(subquery)

该语句理解为:将主查询的数据,放到子查询中做条件验证,根据验证结果(true 或者Flase)来决定主查询的数据结果是否予以保留。


hint:
1、exists(subquery)只返回true 或者Flase,因此子查询中select 1 或者select 没有区别。
2、exists子查询实际上经过了优化而不是我们理解的逐条对比。
3、exists子查询往往也可以用条件表达式、其他子查询后者join来代替,何种最优需要具问具分。

order by(讨论会不会产生filesort):
order by尽量使用index而不是filesort排序。



建表:
age ,birth两个字段。 建立索引age,birth


1、

select * from tbla where age > 20 order by age;

以前关注用没用索引,现在关注order by后面会不会产生filesort。这句话不会差生filesort。

2、

select * from tbla where age > 20 order by age,birth;order by age,birth

用到了索引排序,因此不会产生filesort

3、

select * from tbla where age > 20 order by birth;

产生filesort,order by后面缺少带头大哥,因为age所引用到了但是是一个范围,因此不能“从一楼到二楼”。

4、

select * from tbla where age > 20 order by birth,age;

产生filesort,这就不像where后面的and,顺序反了可以优化,order by后面顺序反了不会优化。

5、

select * from tbla  order by birth;

产生filesort,带头大哥没有

6、

select * from tbla where birth > '2020-04-23 00:00:00' order by birth;

产生filesort,带头大哥没有

7、

select * from tbla where birth > '2020-04-23 00:00:00' order by age;

不会产生filesort,order by后面用到了索引排序。

8、

select * from tbla order by age ASC,birth DESC;

带头大哥在,顺序和索引相同。但仍会产生filesort,要么同升,要么同降。


总结:
MySQL支持两种方式排序:index和filesort。index效率要高。

order by满足下列两个情况会用到索引排序:
1、order by后面的语句使用索引最左前缀匹配原则。
2、使用where子句与order by子句条件列组合满足最左前缀匹配原则

如果用不到index排序,filesort排序会有两种算法:
双路排序:MySQL4.1之前使用双路排序,两次扫描磁盘得到数据。
单路排序:从磁盘读取所有列,按照order by列在buffer对他们进行排序,然后扫描排序后的列表进行输出,它的效率更快些,避免了第二次读取数据。并且把随机IO变成顺序IO。

单路总体而言好于双路。

提高order by速度:
1、order by时候尽量不要用select *。
2、尝试提高sort_buffer_size
3、尝试提高max_length_for_sort_data

group by:
group by实质是线排序后分组,遵循索引建立的最左前缀原则
where 优先于HAVING。能写where就不要用HAVING。

慢查询日志:
MySQL慢查询日志是MySQL提供的一种日志记录,它用来记录在MySQL中响应时间超过阈值的语句,具体指运行时间超过long_query_time值的SQL,则会被记录到慢查询日志中。

long_query_time默认10秒。

默认MySQL没有开启慢查询日志。没事不要开启,开启会影响性能。

查看开启:SHOW VARIABLES LIKE ‘%slow_query_log%’
开启:
set global slow_query_log = 1 (仅对当前数据库有效,重启失效)
查看阈值:
SHOW global VARIABLES LIKE ‘%long_query_time%’
设置阈值:
set global long_query_time = 10(表示10秒)
可使用select sleep(6)验证。

mysqldumpslow:官方提供的日志分析工具
得到返回记录最多的10个SQL:
mysqldumpslow -s r -t 10 /var/lib/mysql/XXXXX-slow.log

得到访问次数最多的10个SQL:mysqldumpslow -s c -t 10 /var/lib/mysql/XXXXX-slow.log

得到按时间排序前10条里面含有左连接的查询语句:mysqldumpslow -s t -t 10 -g “left join” /var/lib/mysql/XXXXX-slow.log

存储过程:

DELIMITER $$

意思是将结束符改为$$。



show profile:

MySQL调优思路:
step1:察觉到系统变慢
step2:给SQL跑一圈(重现故障)利用慢查询日志
step3:采取explain分析具体SQL
step4:第三部没摆平,使用show profile

show profile是MySQL提供可以用来分析当前会话中语句执行的资源消耗情况。可以用于SQL调优的测量。

默认情况下是关闭状态。

查看show profile是否开启:
show variables like ‘profiling’;

开启show profile:
set profiling=on;
开启之后,show profile会在后台自动抓取查询信息。

查看所有执行过的SQL。
show profiles;

诊断SQL:
show profile cpu,block io for query Query_ID(show profiles里面的id);
这句话诊断出一条SQL生命周期里发生的所有事件以及发生的时长。
上述SQL中,cpu和block IO是可替换项:可添加一个或者多个,中间用逗号隔开。

type:
| ALL --显示所有的开销信息
| BLOCK IO --显示块IO相关开销
| CONTEXT SWITCHES --上下文切换相关开销
| CPU --显示CPU相关开销信息
| IPC --显示发送和接收相关开销信息
| MEMORY --显示内存相关开销信息
| PAGE FAULTS --显示页面错误相关开销信息
| SOURCE --显示和Source_function,Source_file,Source_line相关的开销信息
| SWAPS --显示交换次数相关开销的信息

在status状态下出现以下item说明有问题:
1、converting HEAP to MyISAM 查询结果太大了,内存都不够用往磁盘上搬了
2、creating tmp table 创建临时表(拷贝数据到临时表,用完再删除)
3、copying to tmp table on disk 把内存中临时表复制到磁盘,危险!!
4、locked




MySQL锁:
对数据操作类型分:(读锁/写锁)
读锁:针对同一份数据,多个读操作可以同时进行而不会互相影响。
写锁(排它锁):当前写操作没有完成前,它会阻断其他写锁和读锁。

对锁的粒度分:
表锁:
开销小、加锁快,无死锁,锁的粒度大,发生锁冲突的概率最高,并发度最低。

建表:

create table mylock(
     id int primary key auto_increment,
     name varchar(20),
      ) engine myisam;

插入数据:

insert into mylock(name) values ('a'),('b'),('c'),('d'),('e');

开两个终端(链接数据库):
手动增加表锁:
lock table 表名字 read/write , 表名字2 read/write , 其他;

解锁:
unlock tables;(所有表全解锁了)

查看表是否加过锁:
show open tables;
database tables in_use name_locked
是否加锁0/1

如何分析表锁定:
show status like ‘table%’;
table_locks_immediate:产生表级锁定次数,表示可以立即获取锁的查询次数,每立即获得锁值加1。
table_locks_waited:出现表级锁定争用而发生的等待次数,此值高说明存在着较为严重的表级锁争用情况。

MyISAM读写锁调度是写锁优先(即读锁写锁竞争时,写锁能抢到),由于写锁特性,写锁优先会导致其他进程读写永远阻塞。

读锁:
session1对表mylock加读锁。
session1可以读mylock,不能写mylock,不能读写其他未锁定的表。
session2可以读mylock,写mylock时会进入阻塞状态,直到session释放锁才执行写操作。可以读写其他未锁定的表。

写锁:
session1对表mylock加写锁。
session1可以读写mylock,不能读写其他未加锁的表
session2可以读写其他未加锁的表,读写mylock会阻塞,等待session释放锁。

简而言之(针对session2):读锁会阻塞写,但是不会阻塞读。而写锁则会把读和写都阻塞。

行锁(innodb默认加锁?):
开销大、加锁慢、会出现死锁;锁粒度最小,发生锁冲突概率最低,并发度也高。InnoDB和MyISAM区别:1、InnoDB支持事务。2、InnoDB采用了行级锁。

课堂实验:
建表:

create table test_innodb_lock(a int(11) , b varchar(16))engine = innodb;

insert into test_innodb_lock values(1,'b2'),(3,'3'),(4,'4000'),(5,'5000'),(6,'6000'),(7,'7000'),(8,'8000'),(9,'9000'),(1,'b1');

建索引:

create index test_innodb_a_ind on test_innodb_lock(a);
create index test_innodb_lock_b_ind on test_innodb_lock(b);

设置自动提交为0;

set autocommit = 0;

在session1中更改数据(未提交),session1自己看到的数据更改了,但是session2看得的数据是原来的数据。
两个session更改同一行数据:
在这里插入图片描述两个session更改不同行数据:

可自由更改无影响。

索引建立之后使用不当导致行锁升级为表锁

case:
在session1中执行下列语句:

update test_innodb_lock set a = 10 where b = 4000;

由于b是varchar而where后面没有加单引号,因此导致索引失效。此时,行锁变表锁。seesion2中再去写会被阻塞。

间隙锁:
当我们用范围条件而不是相等条件检索数据时,会给这个范围内的所有键值加锁。
例如:
表如图所示:
在这里插入图片描述
session1执行:
update test_innodb_lock set b = ‘0987’ where a >1 and a<6;
此时,未提交,MySQL会将a>1 ,<6的键值全上锁。(即使没有a=2的记录)。

seesion2执行:
insert into test_innodb_lock values(2,‘2000’);
commit;
由于seesion1没有commit,所以seesion2执行此句后会阻塞,直到seesion1 commit之后释放锁。

如何锁定一行:
seesion1执行:
select * from test_innodb_lock where a = 8 for update;
手工为a=8这条记录上锁。

session2更改a=8这行数据会阻塞,直到session1执行commit。

行锁问题排查:
show status like ‘innodb_row_lock%’;
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值