Mysql优化笔记

一.Mysql逻辑分层

1.链接层

  • 提供与客户端链接的服务

2.服务层

  • 提供各种用户使用的接口(select)
  • 提供sql优化器(对效率低sql进行自动优化)

3.引擎层

  • 提供各种存储数据的方式-引擎类型

4.存储层

  • 存储数据

二.Mysql数据库引擎

1.Mysql引擎分类

  • InnoDb:事务优先,适合高并发操作,使用行锁

  • Mysiam:性能优先,使用表锁

2.Mysql引擎命令

#获取当前数据库支持引擎:
show engines ; 
show variables like '%storage_engine%' 
#创建表指定引擎: 
CREATE TABLE `NewTable` (...) ENGINE=InnoDB ;

三.索引Index

1.索引原理BTree+

  1. 每个节点都可以有n个数据及n+1条指向下层节点的指针,n个数据作用为用来切分n+1条指针
  2. BTree+ 中查询任意数据次数为BTree树的高度,BTree+ 中所有数据存贮在叶节点中
  3. BTree查询过程: 根据顶层n条数据确定区间即定位索引,再根据索引检索到下层节点重复根据数据定位索引操作,直至叶节点查询到数据

2.索引分类:

  1. 单值索引 index:普通索引,索引某列,表中可存在多个单值索引

  2. 唯一索引 unique:同普通索引,但值不可重复

  3. 复合索引 index:多列构成的索引,命中索引头即可命中索引

  4. 主键索引 primary Key : 同唯一索引,但主键索引值不可为null

3.索引优缺

3.1.索引优点

  • 根据BTree+查询策略更快的命中数据,提供查询效率
  • 降低io使用率,降低cpu使用率(排序等-索引本身已排序)

3.2.索引缺点

  • 索引本身较大,过多索引会大量增加数据存储内存

  • 索引会降低增删改效率(需修改数据后修改索引)

    3.3.不适用场景

  • 少量数据,

  • 频繁更新数据
    较少使用查询字段

    4.索引命令:

  • 创建索引

    creat 索引类型 索引名 on 表名(字段…)

  • 修改索引

    alter table 表名 add 索引类型 索引名(字段…)

  • 查询索引

    show index from 表名

四.Explain结果解析

  • 模拟sql优化器执行sql语句,从而了解sql执行情况

1.id-执行编号

  1. id相同时,以自上而下执行,同优先级的执行顺序,因数据量改变,较小先执行:笛卡儿积策略;
  2. id不同时,以自大向小的优先级执行;
  3. id相同与不同同时存在,则先按照自大向小优先级查询,再按照自上而下执行

2.select_type-查询类型

  1. PRIMARY: 包含子查询sql中的主查询,一般为最外层

  2. SUBQUERY: 包含子查询sql中的子查询,一般为非最外层

  3. simple:简单查询(不包含子查询,union)

  4. derived: 衍生查询(使用了临时表)

    例:
    	a. form子查询中只有一张表
    	explain select  cr.cname 	from ( select * from course where tid in (1,2) ) cr ;
    	b. form子查询中,如存在table1 union table2 则 table1为derived,table2为union
    	explain select  cr.cname 	from ( select * from course where tid = 1  union select * from course where tid = 2 ) cr ;
    
  5. union: 子查询存在

    例:	table1 union table2此时table2为union
    
  6. union result :表之间存在union查询

3.table-表名

4.type-查询类型

  • system : 只有一条数据的系统表 ;或 衍生表只有一条数据的主查询

    const : 仅能查到一条数的sql:用于primary key 或者unique索引

  • eq_ref :唯一性索引:对于每个索引键的查询,返回 匹配唯一行数据(有且只有1个,不能多 、不能0

    ref : 非唯一性索引: 对于每个索引建的查询,返回匹配的所有行

  • range : 检索指定范围的行 :where后范围查询(between,> < >=, 特殊:in有时候会失效 ,从而转为无索引all)

    index: 查询全部索引中数据

    all: 查询全部表中的数据

注:

效率对比 : system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

5.possible_keys-可能命中索引

6.key-实际使用的索引

7.key_len-实际使用索引的长度,

  • 用于判断复合索引是否被完全使用(a,b,c)。

8.ref-表之间的引用

  • 指明当前表所参照的字段

9.rows-通过索引查询到的数据量

10.Extra-额外的信息

  1. using filesort :性能消耗大;需要“额外”的一次排序(查询),常见于 order by 语句中。

    对于单索引,如果排序和查找为同字段,则不会出现using filesort;否则,则会出现using filesort

    对于复合索引:不能跨列(最佳左前缀),where和order by 按照复合索引的顺序使用,不要跨列或无序使

  2. using temporary: 性能损耗大 ,用到了临时表。一般出现在group by 语句中。

    应查询那些列,就根据那些列 group by

  3. using index :性能提升; 索引覆盖(覆盖索引),不读取原文件,只从索引文件中获取数据,不需要回表查询

    如果用到了索引覆盖(using index时),会对 possible_keys和key造成影响:

    ​ a.如果没有where,则索引只出现在key中;

    ​ b.如果有where,则索引 出现在key和possible_keys中。

  4. using where (需要回表查询)

  5. impossible where : where子句永远为false

  6. Using join buffer: Mysql引擎使用了连接缓存。


五.慢sql排查

慢sql: 性能低,执行时间较长,等待时间过长,sql语句欠佳(链接查询),索引失效,服务器参数设置不合理(缓冲、线程数)

1.开启慢查询日志:

  • 慢查询日志:记录MySQL种响应时间超过阀值的SQL语句 (long_query_time,默认10秒),默认关闭

  • 查询慢查询阈值

    show variables like ‘%long_query_time%’ ;

  • 临时设置慢查询阈值(需重新登录)

    set global long_query_time = 5 ;

  • 查询是否开启慢查询日志

    show variables like ‘%slow_query_log%’ ;

  • 临时开启慢查询日志,需重启mysql

    set global slow_query_log = 1 ;

  • 永久开启慢查询日志,需重启mysql

    修改vi /etc/my.cnf, 加入:
    #–永久开启慢查询日志
    slow_query_log=1
    slow_query_log_file=/var/lib/mysql/localhost-slow.log
    #–永久设置慢查询阈值(不生效则重启)
    long_query_time=3

2.查看慢查询sql

  • 查看慢查询sql数目

    show global status like ‘%slow_queries%’

  • 查看慢查询日志记录的sql

    cat /var/lib/mysql/localhost-slow.log

  • mysqldumpslow工具查看慢SQL

    mysqldumpslow 【参数】 慢查询日志的文件
    实例:
    #获取返回记录最多的3个SQL
    mysqldumpslow -s r -t 3 /var/lib/mysql/localhost-slow.log
    #获取访问次数最多的3个SQL
    mysqldumpslow -s c -t 3 /var/lib/mysql/localhost-slow.log
    #按照时间排序,前10条包含left join查询语句的SQL
    mysqldumpslow -s t -t 10 -g “left join” /var/lib/mysql/localhost-slow.log
    注:
    s:排序方式
    r:逆序
    l:锁定时间
    g:正则匹配模式

六.sql解析过程

  • 编写过程:

    select dinstinct …from …join …on …where …group by …having …order by …limit …

  • 解析过程:

    from … on… join …where …group by …having …select dinstinct …order by limit …

  • order by 查询原理

    1.using filesort 据IO的次数区分为两种算法:
    双路排序:MySQL4.1之前默认,扫描两次磁盘:1.从磁盘中扫描排序字段,在buffer中排序,2.扫描其他排序字段
    单路排序:MySQL4.1之后默认,仅读取一次字段,在buffer中排序(并非绝对,如数据量过大,则会进行分片读取,多次读取)

      注: 
      	单路占用buffer>双路,如设置buffer过小(需要排序的列的总大小超过了max_length_for_sort_data定义的字节数),将自动单路转双路,设置buffer大小为 set max_length_for_sort_data = 1024 (byte)	
    

七.sql优化策略

1.避免索引使用乱序

alter table test03 add index idx_a1_a2_a3_4(a1,a2,a3,a4) ;
explain select a1,a2,a3,a4 from test03 where a1=1 and a2=2 and a3=3 and a4 =4 ; 
	--最佳写法,索引的使用顺序和复合索引一致
explain select a1,a2,a3,a4 from test03 where a4=1 and a3=2 and a2=3 and a1 =4 ; 
	--概率最佳写法,索引顺序使用虽不一致,但sql在真正执行前经过了SQL优化器的调整下与索引保持了一致。

2.避免索引使用跨列

alter table test03 add index idx_a1_a2_a3_4(a1,a2,a3,a4) ;
explain select a1,a2,a3,a4 from test03 where a1=1 and a2=2 and a4=4 order by a3; 
	--以上SQL用到了a1 a2两个索引,而a4跨列使用,索引失效,a3与a1,12连续,索引有效;

3.避免索引使用时进行排操作

alter table test03 add index idx_a1_a2_a3_4(a1,a2,a3,a4) ;
explain select * from test03 where a1= 1 and a2 = 2 ;
	--用到了a1,a2个索引
explain select * from test03 where a1 = 1 and a2*2 = 2 ;
	--用到了a1索引
explain select * from test03 where a1*2 = 1 and a2*2 = 2 ;
	--用到了0个索引
explain select * from test03 where a1*2 = 1 and a2 = 2 ;
	--用到了0个索引,对于复合索引,左边失效,右侧全部失效。
explain select * from test03 where a1 = '123' ; 
	--程序底层将 '123' -> 123,即进行了类型转换,因此索引失效

3.避免回表查询

alter table test03 add index idx_a1_a2_a3_4(a1,a2,a3,a4) ;
explain select a1,a2,a3,a4 from test03 where a1=1 and a2=2 and a4=4 order by a3; 
	--以上SQL用到了a1 a2两个索引,该两个字段using index索引覆盖 ;而a4跨列使用,该索引失效,回表查询,出现using where;
explain select a1,a2,a5 from test03 where a1=1 and a2=2; 
	--以上SQL用到了a1 a2两个索引,该两个字段using index索引覆盖 ;而a5需回表查询,出现using where;

4.避免额外排序

alter table test03 add index idx_a1_a2_a3_4(a1,a2,a3,a4) ;
explain select a1,a2,a3,a4 from test03 where a1=1  order by a3; 
	--以上SQL用到了a1索引,orderby条件中a3与a1跨列,故出现using filesort(“多了一次额外的查找/排序”) -	--不要跨列使用( where和order by 拼起来,不要跨列使用)
explain select a1,a2,a3,a4 from test03 where a1=1  order by a2 , a3; 
	--以上SQL用到了a1索引,oderby中a2,a3与a1连续,故不会出现出现using filesort :

5.避免其他问题索引失效

  • 复合索引不能使用不等于 in-概率失效,如必须使用in查询放入最后,避免in范围查询造成索引失效
  • 符合索引不能使用 != < > 或is null (is not null) ,否则自身以及右侧索引全部失效
  • like尽量以“常量”开头,不要以’%'开头,否则索引失效
  • 不要使用or,否则可能所有索引失效(包含左侧索引)

6.其他优化

  • 多表关联小表驱动大表原则,小表在左
  • 主查询的数据集大,使用in,子查询数据集较大,则使用exist
  • order by: 选择使用单路、双路 ;调整buffer的容量大小;
    保证全部的排序字段 排序的一致性(都是升序 或 降序)​

八.mysql锁机制分析

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

示例:
买最后一件衣服X
A: X 买:X加锁 ->试衣服…下单…付款…打包 ->X解锁
B: X 买:发现X已被加锁,等待X解锁,X已售空

1. 锁分类 :

  • 操作类型分类:
    • 读锁(共享锁): 对同一个数据(衣服),多个读操作可以同时进行,互不干扰
    • 写锁(互斥锁): 如果当前写操作没有完毕(买衣服的一系列操作),则无法进行其他的读操作、写操作
  • 操作范围分类
    • 表锁:一次性对一张表整体加锁,如MyISAM引擎
      • 特点:开销小、加锁快;无死锁
      • 缺点:锁的范围大,容易发生锁冲突、并发度低
    • 行锁:一次性对一条数据加锁,如InnoDB引擎
      • 特点:;锁的范围较小,不易发生锁冲突,并发度高(很小概率 发生高并发问题:脏读、幻读、不可重复度、丢失更新等问题)
      • 缺点:。开销大,加锁慢;容易出现死锁
    • 页锁

2. 锁互斥 :

2.1 表锁

是通过unlock tables,也可以通过事务解锁

  • 读锁:
当前表其他表
当前会话可读不可写不可读不可写
其他会话可读不可写可读可写
  • 写锁
当前表其他表
当前会话可读可写不可读不可写
其他会话不可读不可写不可读不可写

2.2.行锁

是通过unlock tables,也可以通过事务解锁

注: 如获取锁语句有where,则实际加索的范围 就是where后面的范围(不是实际的值,如间隙锁)

不同会话操作(DML)同条数据,其他会话必须等当前commit/rollback后才能操作;

会话1:
insert into linelock values(6,'a6') ;
	---插入数据id=6,未commit
会话2:
update linelock set name='ax' where id = 6;
	---修改数据id=6,阻塞

不同会话操作(DML)不同数据,互不干涉,各自commit后生效;

会话1:
insert into linelock values(8,'a8') ;
	---插入数据id=8,未commit
会话2:
update linelock set name='ax' where id = 5;
	---修改数据id=5,未阻塞

如表无索引,则行锁升级为表锁;

---------------无索引--------------------
会话1:
update linelock set name = 'ai' where name = '3' ;
	---修改数据name=3,name字段无索引,未commit
会话2:
update linelock set name = 'aiX' where name = '4' ;
	---修改数据name=4,name字段无索引,阻塞,待会话1commit后,会话2才能执行完毕,且需要会话2commit后生效
---------------有索引--------------------
alter table linelock add index idx_linelock_name(name);
会话1:
update linelock set name = 'aj' where name = '1' ;
	---修改数据name=1,name字段有索引,未commit
会话2:
update linelock set name = 'ajX' where name = '2' ;
	---修改数据name=2,name字段有索引,未阻塞,commit后生效

如索引发生类型转化,则行锁升级为表锁;

---------------无索引--------------------
会话1:
update linelock set name = 'ak' where name = 9 ;
	---修改数据name=9,类型发生转化,未commit
会话2:
update linelock set name = 'akX' where name = 10 ;
	---修改数据name=10,类型发生转化,阻塞,待会话1commit后,会话2才能执行完毕,且需要会话2commit后生效
---------------有索引--------------------
alter table linelock add index idx_linelock_name(name);
会话1:
update linelock set name = 'aj' where name = '1' ;
	---修改数据name=1,name字段有索引,未commit
会话2:
update linelock set name = 'ajX' where name = '2' ;
	---修改数据name=2,name字段有索引,未阻塞,commit后生效

注:测试表如下

create table linelock(
id int(5) primary key auto_increment,
name varchar(20)
)engine=innodb ;
insert into linelock(name) values('1')  ;
insert into linelock(name) values('2')  ;
insert into linelock(name) values('3')  ;
insert into linelock(name) values('4')  ;
insert into linelock(name) values('5')  ;


insert into linelock(name) values('9')  ;
insert into linelock(name) values('10')  ;
2.2.1.间隙锁

行锁的一种特殊情况,值在范围内,但却不存在,Mysql会自动给间隙 加索

update linelock set name ='x' where id >1 and id<9 ;   
	---即在此where范围中,没有id=7的数据,则id=7的数据成为间隙,mysql会对其加间隙锁

3.锁操作

3.1表锁

  • 增加/释放/查看表锁

    locak table 表1  read/write  ,表2  read/write   ,...;
    unlock tables;
    show open tables ;
    
  • 分析表锁定的严重程度

    show status like 'table%' ;
    	---Table_locks_immediate :即可能获取到的锁数
    	---Table_locks_waited:需要等待的表锁数(如果该值越大,说明存在越大的锁竞争)
    	---Table_locks_immediate/Table_locks_waited > 5000, 建议采用InnoDB引擎
    

3.2.行锁

  • 分析表锁定的严重程度

    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 :	等待次数。从系统启到现在一共等待的次数
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值