SQL优化详细总结

一.Sql执行顺序:

sql语句:select distinct … from … where … group by …having… order by… limit…
执行顺序:from…on…where…order by…having…select distinct…order by…limit…

二.explain字段(分析SQL的执行计划):

SQL性能问题:
a.分析SQL执行计划(explain + SQL语句),可以模拟SQL优化器执行SQL语句,从而知道编写的SQL状况
b.MySQL查询优化会干扰我们的优化

1. id:

select识别符,是select查询序列号,序号为sql执行顺序;
id值越大先执行
id相同:从上往下顺序执行,数据个数越少越先执行(原因:笛卡尔积),数据量小的表优先执行

2. select_type(select的类型):

simple:简单的select,没有union和子查询
subquery:包含子查询SQL的子查询(非最外层)
primary:最外面的select,在有子查询的语句中,最外面的select查询就是primary
derived:衍生查询(在查询时候用到了临时表)
在from 子查询中,只有一张表;
在from 子查询中,如果有table1 union table2,则table1就是derived
union:union语句的第二个或者说是后面那一个
dependent union: union中的第二个或后面的select语句,取决于外面的查询
union result:union的结果,哪些表存在union

3.table:

输出的行所用表

4. type(连接类型从最佳类型到最差类型):

system:表仅有一行,是const的特例
const: 表最多有一个匹配行,const用于比较primary或者unique索引;必须用到primary key或者unique索引
eq_ref: 用在一个索引的所有部分被联接使用并且索引是primary key或者unique索引;可以用于使用=比较带索引的列
ref: 如果联接只使用键的最左边的前缀,或者键不是primary key或者unique索引,则使用ref
ref_or_null: 与ref相比,添加了MYSQL能专门搜索包含null值的行,经常使用在子查询中
index_merge: 使用索引合并优化方法
unique_subquery:
index_subquery:
range: 给定范围内的检索,where后面事范围(between或者in、>、<),使用一个索引来检查行;in有时会失效,转为无索引
index: 查询全部索引中的数据,与ALL基本相同,除了只有索引树被扫描
ALL: 查询全部表中的数据,对每个行组合,都进行完整行扫描

type小结:system/const:结果只有一条数据
ef-ref:结果多条;但每条数据是唯一的
ref:结果多条;但是每条数据是0或者多条

5. possible_keys:

可能用到的索引,是一种索引,不准
如果是null,则说明没有索引

6. keys:

实际使用到的索引

7. key_len:

索引的长度,用于判断复合索引是否被完全使用
在utf-8中:1哥字符占3个字节(所有索引长度会乘以3)
如果索引字段可以为null,则会使用1个字节用于标识

8. ref:

指明当前表所参照的字段,表之间的引用
(1)const:常量
(2)用到的字段

9. rows:

被索引优化查询的数据个数(实际通过索引查询到的数据个数)
数值越小越好

10. Extra:

using filesort:性能消耗大;需要额外的一次排序,经常出现在order by中
对于单索引,如果排序和查找收同一个字段,不会出现using filesort;
如果不是同一个字段,则会出现using filesort
Where那些字段,就order by那些字段
复合索引:不能跨列(最佳左前缀)
避免where和order by按照复合索引的顺序使用 不要跨列或无序使用

using temporary:性能损耗大,用到了临时表,已经有表了,但不适用,必须再来一张表,一般出现在group by中
避免:查询哪些列就跟

using index:性能提升;索引覆盖,不读取原文件,只从索引中查询数据
只要使用到的列都在索引中,则称为索引覆盖
如果用到了索引覆盖Using index时,会对possible_keys和keys产生影响
如果没有where,则索引只出现在key中;
如果有where,则索引出现在key和possible_keys中

using where:需要回表查询

impossible where:where子句永远为false

三.单表优化:

  1. 如果(a,b,c,d)复合索引和使用的顺序全部一致(且不跨列使用),则复合索引全部使用
    如果部分一致,则使用部分索引
  2. 索引不能跨列使用(最佳做前缀),保持索引的定义和使用顺序一样
  3. 索引需要逐步优化
  4. 将含In的范围查询放在where条件最后,防止失效

四.多表优化:

左连接:
索引往哪里加:(1)小表驱动大表;(2)索引建立在经常使用的字段上
(3)左外连接给左表加索引,右外连接给右表加索引
where 小表.x = 大表.y;
编写 …on t.cid=c.cid时,将数据量小的表放左边

Using join buffer: extra中的一个选项,作用:mysql引擎使用了连接缓存

五.避免索引失效的一些原则:

1.复合索引:
(1)不要跨列或无序使用(最佳左前缀)
(a,b,c)
(2)尽量使用全索引匹配

2.不要再索引上进行任何操作(计算、函数、类型转换),否则做索引失效

3.复合索引不能使用不等于(!= <>)或者 is not null,否则自身以及右侧索引全部失效;
复合索引中如果有 > ,则自身和右侧索引全部失效
Sql优化,是一种概率层面的优化,至于是否实
际使用我们的优化,要用explain进行推测(服务器中有sql优化器优化影响我们的优化)

4.补救:尽量使用索引覆盖(using index)

5.like尽量以常量开头,不要以%开头
如果一定要使用%x%,则使用索引覆盖 using index

6.尽量不要使用显示或者隐式转换,否则索引失效

7.尽量不要使用or,否则索引失效

六.一些其他的优化方法:

1.exist和in:

select …from table where exist (子查询)
select tname from teacher where exist (select * from teacher);
select…from table where 字段 in (子查询)
如果主查询的数据集大,则使用In
如果子查询的数据集大,则使用exist;
exist语法:将主查询的结果,放到子查询结果中进行条件校验,如果检验成功(看子查询是否有数据),则保留数据

2.order by优化:

using filesort 有两种算法:双路排序、单路排序(根据IO的次数)
MYSQL4.1之前,默认使用 双路排序(扫描2 次磁盘IO)
MYSQL4.1之后,默认使用 单路排序

3.提高order by查询的策略:

选择使用单路、双路;调整buffer的容量大小
避免select*…
复合索引 不要跨列使用,避免使用using filesort
保证全部的排序字段 排序的一致性(都是升序或者降序)

七.SQL排查 – 慢查询日志:

1.MYSQL提供的一种日志记录,用于记录mysql中响应时间超过阈值的sql语句(long_querytime默认10s)

2.慢查询日志默认是关闭的,可以开发调优时打开,最后部署时关闭

3.检查是否开启了慢查询日志:
show variables like ‘%slow_query_log%’

4.开启慢查询日志:
(1)临时开启:
Set global slow_query_log=1;在内存中开启
Exit
Service mysql restart
(2)永久开启:
/etc/my.conf中(mysqld中)追加配置:
[mysqld]
slow_query_log=1
slow_query_log_file=/var/lib/mysql/localhost-slow.log

5.慢查询阀值:
show variables like ‘%long_query_time%’

临时设置阀值:
Set global long_query_time=5;需要重新登陆后生效

永久设置阀值:
在/etc/my.conf中(mysqld中)追加配置:
[mysqld]
long_query_time =5

6.查看超过阀值的SQL(看到慢查询的条数):
show global status like ‘%slow_queries%’ ;

7.慢查询的sql被记录在了日志中:
(1)可以通过日志查看具体慢sql(即存放的位置:/var/lib/mysql/localhost-slow.log)
cat /var/lib/mysql/localhost-slow.log;
(2)通过mysqldumpslow工具查看慢sql

八.分析海量数据:profiles

1.show profiles;

默认关闭
show variables like ‘%profiling%’;
set profiling = on;
show profiles: 会记录所有profling打开之后的全部SQL查询语句
缺点:不够精确,只能看到总共消费的时间,不能看到各个硬件消费的时间

2.精确分析:

sql诊断
Show profile all for query +需要查询的Query_Id

3.全局查询日志:

记录开启之后的全部sql语句(在开发调优时开启,部署后关闭)
show variables like ‘%general_log%’;
(1)执行的所有记录sql记录在表中:
set global general_log=1; 开启全局日志
set global log_output=’table’; 设置将全部的sql记录在表中
(2)执行的所有sql记录在文件中:
set global general_log=on;
set global log_output=’file’;
set global general_log_file = ‘/tmp/general.log’;

可以通过 select * from mysql.general_log; 查看日志

九.锁机制:解决因资源共享而造成的并发问题

1.分类:

2.按操作类型分为:

a.读锁(共享锁):
对同一数据,多个操作可以同时进行,互不干扰
b.写锁(互斥锁):
如果当前写操作没有完毕,则无法进行其他的读操作和写操作

3.按操作范围分:

a.表锁:
一次性对一张表整体加锁,如MyISAM储存引擎,开销小,加锁块,无死锁;但锁范围大,容易发送锁冲突,并发度低
b.行锁:
一次性对一条数据加锁,如InnoDB存储引擎;开销大,加锁慢,容易出现死锁,锁范围小,不易发生锁冲突,并发度高(小概率发生高并发问题)
c.页锁:

4.锁语法:

加锁语法:lock table 表1 read/write,表2 read/write… ;
查看加锁的表:show open tables;
释放锁:unlock tables;

5. 加读锁:

a) 在会话0中加读锁:
lock table tablelock read;
select * from tablelock; 查可以
delete from tablelock where id=1; 增删改不行

select * from teacher2; 查其他表,不可以
delete from tablelock where id=1;其他表增删改不可以
如果一个会话对A表加了read锁,则该会话可以对A进行读操作不能进行写操作;而且当前会话不能对其他表进行读和写操作
总结:如果给A表加了read锁,则当前会话只能对A进行读操作
b) 在会话1中:
select * from tablelock; 查可以
delete from tablelock where id=1; 增删改不行,会等待会话0释放锁

select * from teacher2; 查其他表,可以
delete from tablelock where id=1;其他表增删改也可以
总结:会话0给A表加了读锁;其他会话可以对A表以外的其他表进行读写操作;
对A表可以读,写需要等待释放锁

6. 加写锁:

会话0:
lock table tablelock write;
当前会话可以对加了写锁的表进行任何操作,不能操作其他表

会话1:
对会话0中加写锁的表可以进行增删改查的前提是会话0释放锁;

7. MYSQL表级锁的锁模式:

MyISAM在执行查询语句select前,会自动给涉及的所有表加读锁;
在执行增删改(DML)时,会自动给涉及的表加写锁;
因此在对MyISAM表进行读操作时,不会阻塞其他进程对同一表的读请求,会阻塞对同一表的写请求,只有当读锁释放后才能进行写操作;
在对MyISAM表的写操作时,会阻塞其他进程对同一表的读和写操作,只有当写锁释放后,才能执行其他进程的读写操作

8. 分析表的锁定:

查看哪些表加了锁:show open tables; 1代表被加了锁
分析表锁定的严重程度:show status like ‘table%’;有下面两个参数
Table_locks_immediate:即可能获取到的锁数
Table_locks_waited: 需要等待的表锁数(值越大,存在更大的锁竞争)
一般建议:
Table_locks_immediate/ Table_locks_waited >5000,建议采用InnoDB,否则使用MyISAM引擎

9. 行锁(InnoDB):

Mysql默认自动commit;oracle默认不会自动commit
研究行锁,将自动commit关闭:
set autocommit = 0;
start transaction;
begin;三种方式均可
以后需要通过commit

会话0:写操作
insert into linelock values(‘a6’);
会话1:写操作 同样的数据
Update linelock set name=’ax’ where id=6;
对行锁情况(关闭自动commit):
a.如果会话x对某条数据a进行DML操作,则其他会话必须等待会话x结束事务(commit或者roolback)后,才能对数据进行操作;
b.表锁是通过unlock tables,也可以通过事务解锁;行锁是通过事务解锁(commit或者roolback)

行锁如果操作不同数据,不同会话则互不干扰

10. 行锁的注意事项:

a.如果没有索引,则行锁会转为表锁;
b.行锁的一种特殊情况:间隙锁;值在范围内,但却不存在
mysql间隙会自动给间隙加锁-间隙锁
行锁:如果有where,则实际加锁的范围就是where后面的范围

11. 行锁:

InnoDB默认采用行锁;高并发使用InnoDB,否则使用MyISAM
缺点:比表锁性能损耗大;
优点:并发能力强,效率高

行锁分析:
Show status like ‘%innodb_row_lock%’;
参数值:
Innodb_row_lock_current_waits:当前正在等待锁的数量
Innodb_row_lock_time:等待总时长,从系统启动到现在一共等待的时间
Innodb_row_lock_time_avg:平均等待时长
Innodb_row_lock_time_max:最大等待时长
Innodb_row_lock_waits:等待次数

仅仅查询数据,可以使用for update加锁
select * from linelock where id =2 for update;

十.主从复制(集群在数据库中的一种实现):

1.集群:

(1)负载均衡
(2)失败迁移

2.主从复制:

(mysql图形化客户端:SQLyog,Navicat)

如果要远程连接数据库,则需要授权远程访问
授权远程访问:(A->B,则在B计算机的Mysql中执行以下命令)
GRANT ALL PRIVILEGES ON . TO ‘root’ @’%’ IDENTIFIED BY ‘root’ WITH GRANT OPTION;
FLUSH PRIVILEGES;

如果仍然报错,可能是防火墙没关闭,在B关闭防火墙 service iptables stop;

3.主从复制原理:

SQL优化详细总结_第1张图片

(1) master将改变的数记录在本地的二进制日志中(binary log);该过程称之为二进制日志事件
(2) slave将master的binary log拷贝到自己的relay log(中继日志文件)
(3) 中继日志事件,将数据读取到自己的数据库中
Mysql主从复制是异步的,串行化的,有延迟
Master:slaver=1:n

十二.MYSQL分层:

1.mysql逻辑分层:

连接层、服务层、引擎层、存储层

SQL优化详细总结_第2张图片

2.InnoDB和MyISAM的区别:

InnoDB(默认):事务优先(适合高并发操作;行锁)
MyISAM:性能优先(表锁)
查看当前使用引擎:show variables like ‘%storage_engine%’;
指定数据库对象的引擎:
ENGINE=MyISAM

十三.SQL优化:

1.主要就是优化索引(index);

索引是帮助mysql高效获取数据的数据结构(树:B树、二叉树、Hash树)
Mysql索引使用的是B树

2.索引的弊端:

索引本身很大,可以存放内存/硬盘(通常为硬盘)
索引不是所有情况都适用:
少量数据
频繁更新的字段
很少使用的列

3.索引会提高查询效率,会降低增删改的效率

4.优势:

提高查询效率(降低IO使用率)
降低CPU使用率(B树索引本身已经排好序,可以直接使用)

5.Btree树:

一般指B+树,数据放在叶子节点中
B+树中查询任意的数据次数等于B+树的高度

6.索引:

(1)分类:
主键索引:不能重复,不能是null
单值索引:单列,一个表可以有多个单值索引
唯一索引:不能重复,可以为null
复合索引:多个列构成的索引(相当于书的二级目录)

(2)创建索引:
第一种:create 索引类型 索引名 on 表(字段)
单值索引:create index dept_index on table(name);
唯一索引:create unique index dept_index on table(name);
复合索引:create index dept_name_index on tb(dept,name);

第二种:
alter table 表名 add 索引类型 索引名(索引字段)

如果一个字段事primary key,则该字段默认是 主键索引

(3)删除索引:
Drop index 索引名 on 表名;
(4)查询索引:show index from 表名;

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