【Mysql】 Mysql 进阶

一、配置文件

二进制日志 log-bin 用于主从复制
错误日志 log-error 默认是关闭的,记录验证的警告和错误信息,每次启动和关闭的详细信息
慢查询日志 log 默认关闭,记录查询的sql语句,如果开启会降低mysql的效率

数据文件: 
frm => 存放表结构
myd => 存放表数据
myi => 存放表索引
 

二、MyISAM 与 InnoDB 的对比

 MyISAMInnoDB
外键不支持支持
事务不支持支持
行表锁表锁,即使操作一条记录也会锁住整个表,不适合高并发的操作行锁,操作时只锁住某一行,不对其他行有影响,适合高并发操作
缓存只缓存索引,不缓存真实数据不仅缓存索引还要缓存真实数据,对内存要求较高,而且内存大小对性能有决定性影响
表空间
关注点性能事务
默认安装YY

  

 

三、SQL 性能下降的原因

SQL慢、执行时间长、等待时间长 => {

查询语句写的烂

索引失效

关联查询太多join(设计缺陷或不得已的需求)

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

 }

 

3.2 SQL 执行加载顺序

手写顺序

机读顺序

 

四、Join关系

4.1 内连接 Inner Join

只获取两者的共有部分

select <select_list> from tableA a inner join tableB b on a.key=b.key

  

4.2 左连接 Left Join

得到左表的全部,右表不足补 null

select <select_list> from tableA a left join tableB b on a.key=b.key

  

4.3 右连接 Right Join

右表的全部,A表不足补null

select <select_list> from tableA a right join tableB b on a.key=b.key

  

4.4 获取一个表的独有

select <select_list> from tableA a left join tableB b on a.key=b.key where b.key is null;

  

4.5 获取一个表的独有2

select <select_list> from tableA a right join tableB b on a.key=b.key where a.key is null;

  

4.6 全连接

select <select_list> from tableA a full outer join tableB b on a.key=b.key;

  

4.7 去除重合

select <select_list> from tableA a full outer join tableB b on a.key=b.key where a.key is null or b.key is null;

  

 

五、索引

5.1 索引的概念

索引是已经排好序以便快速查找的数据结构

在数据之外,数据库系统还维护者满足特定查找算法的数据结构,这些数据结构以某种方式指向数据。
这种数据结构,就是索引。

 

5.2 索引的优劣势

5.2.1 索引的优势

  • 实现目录的功能,提高检索效率,降低数据库的IO成本
  • 通过索引对数据进行排序,降低数据排序成本,降低 CPU 消耗

5.2.2 索引的劣势

  • 索引文件需要消耗磁盘空间。索引起始就是一张表,以文件形式存储在磁盘上,表内保存了主键和索引字段,指向实体表的记录,
  • 额外的更新和保存开销。索引增加了查询速度,但降低了更新速度,由于更新表时不仅要保存数据,还要更新和保存索引文件,修改索引列的字段以及指向位置
  • 需要更新和优化索引

 

5.3 索引操作

 

5.4 索引实现方式

5.4.1 概述

常见的索引类型有:

  • BTree 索引
  • hash索引
  • full-text 全文索引
  • RTree索引

一般来说索引也很大,不可能全部存储在内存中,因为索引往往以索引文件的形式存储在磁盘上
我们平常所说的索引,如果没有特别指明,都是指B+树(多路搜索树,不一定的二叉树)结构阻止的索引。其中聚集索引、次要索引、符合索引、前缀索引、唯一索引默认都是用B+树索引。
除了B+树外,还有哈希索引等

 

5.4.2 BTree 索引

 

5.5 索引的适用

5.5.1 适合建立索引

 

5.5.2 不适合建立索引

 

六、性能优化

6.1 MySql Query Optimizer

6.2 其他

 

七、Explain 使用

7.1 什么是 Explain

7.2 Explain 能做什么

7.3 对应表头

id 决定了表的读取和加载顺序

select_type 主要是查询的类型

table 表示这一行数据是哪张表的

type 表示查询的类型,能体现出查询效率

possible_keys 显示可能应用在这张表中的索引,一个或多个。查询涉及到的字段上若存在索引则会被列出,但不一定被查询实际使用

key 实际使用的索引,如果为 NULL,则没有使用索引。查询中若使用了覆盖索引,则该索引仅出现在 key 列表中

key_len 表示索引中使用的字节数,可通过该列计算查询中使用的索引长度,在不损失精确性的情况下,长度越短越好

               其显示的值为索引字段的最大可能长度,并非实际使用长度,是根据表定义计算得到的,而不是通过表内检索得到

ref 显示索引的哪一列被使用了,如果可能的话,是一个常数。哪些列或常量被用于查找索引列上的值

rows 根据表统计信息及索引选用情况,大致估算出找到所需记录所需要读取的行数

Extra 包含不在列中显示,但十分重要的信息

 

7.3.1 id

id 决定了表的读取和加载顺序

id相同:执行顺序从上到下

id不同:

id相同和不同同时存在

 

7.3.2 select_type

select_type 主要记录了查询的类型

 

7.3.3 type

一般来说,需要保证查询至少达到 range 级别,最好能达到 ref

 描述
system单表,且表只有一行记录,是 const 类型的特例,等于系统表
const通过索引一次就找到了,ocnst 用于比较 primary key 或者 unique 索引,由于只匹配一行数据,速度很快。
eq_ref唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配,常见于主键或唯一索引扫描,与const不同的是可能用在多表操作中
ref非唯一性索引扫描,即通过索引扫描,可能找出多个符合条件的行
range

只检索给定范围的行,使用一个索引来所选择,key 列显示使用了哪个索引

一般是在 where 语句中使用了 between、<、>、in 等的查询

这种范围索引扫描优于全表扫描,它只需要开始于索引的某一点,结束与另一个点,不需要扫描全部索引

indexFull Index Scan,索引表的全表扫描,由于 index 只遍历索引树,且索引文件一般比数据文件小,通常比全表扫描快。且 index 是从索引中读取的,all 从硬盘中读取的,index 有更少的 io
allFull Table Scan,全表扫描

 

7.3.4 Extra

 描述严重程度
using filesort

说明 mysql 会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。

Mysql 中将无法利用索引完成的排序操作称为 “文件排序”

严重,必须优化
using temporary

使用了临时表保存中间结果,MySQL 在对查询结果排序时使用临时表,常见于排序

order by 和分组查询 group by 等需要统计的模块

非常严重,必须优化

由于临时表的创建,数据搬运以及删除临时表,都十分消耗数据库性能

using index

表示对应的 select 操作中使用了覆盖索引(Covering Index),直接从索引中找出数据,而没有查询数据库表,效率较高

如果同时出现 using where,表明索引被用来执行索引键值的查找

若没有出现 using where,表明索引用来读取数据,而非执行查找动作

即建的索引为 q1,q2,查询的内容小于等于q1,q2,不能超出范围

效率较高
using where表示使用了 where 查询,无需回表查询数据 
using index condition表示查找中使用了索引,但需要回表查询数据。即索引下推技术 
Backward index scan 方向扫描,MySQL 8.0 存在,避免 filesort 
using join buffer使用了连接缓存 
impossible where

where 自居的值总是false,不能用来获取任何元组。

如查性别又是男性又是女性的

select * from test where sex=1 and sex=2

等于sql语句错误,需要优化。

 

7.3.5 覆盖索引

 

7.3.6 索引下推技术(Using index condition)

https://www.jianshu.com/p/bdc9e57ccf8b

 

八、索引优化demo

8.1 单表优化demo

# 准备动作

创建表:

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,
	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,3,3,'3','3')

select * from article;

最开始的查询语句

EXPLAIN SELECT id,author_id from article where category_id=1 and comments>1 order by views desc limit 1;

# 添加索引优化

添加索引

create index idx_article_ccv on article(category_id,comments,views);

查询

EXPLAIN SELECT id,author_id from article where category_id=1 and comments>1 order by views desc limit 1;

问题

 

# 索引再次优化

删除索引

alter table article drop index idx_article_ccv;

重建索引

create index idx_cv on article(category_id,views);

结论

可以看到 type 变为了 ref,Extra 中的 using filesort 也消失了,达到了预期目标

 

8.2 两表优化demo

# 准备工作

create table if not exists class(
	id int(10) UNSIGNED not null auto_increment primary key,
	card int(10) UNSIGNED not null
);
create table if not exists book(
	bookid int(10) UNSIGNED not null auto_increment primary key,
	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 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)));

# 加索引

记住:左连接,右表加索引。右连接,左表加索引。

这是由左连接的特性决定的,left join 条件用于确定如何从右表开始搜素行,左表数据一定都有

alter table book add index idx_card(card);

查询

explain select * from class left join book on class.card=book.card

结果:

 

8.3 三表优化demo

# 准备工作

create table if not exists phone(
	phoneid int(10) unsigned not null auto_increment primary key,
	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)));

select * from phone;

# 增加索引

alter table book add index idx_card(card);
alter table phone add index idx_card(card);
alter table class add index idx_card(card);

# 查询

explain select * from class left join book on class.card=book.card left join phone on book.card=phone.card;

# 结果

 

8.4 总结

  • 小表驱动大表,尽可能减少 join 语句中的 NeedLoop(嵌套) 的循环总次数。即以小表作为最左边的表
  • 优先优化 NeedLoop(嵌套) 的内层循环
  • 保证 join 语句中被驱动表上 join 的条件字段已经被索引
  • 当无法保证被驱动表上的 join 条件字段被索引且内存资源充足的前提下,不要太吝啬 JoinBuffer 的设置

 

九、索引失效场景

9.1 准备工作

建表,插入数据,建立索引

create table staffs(
	id int primary key auto_increment,
	name varchar(24) not null default '' comment '姓名',
	age int not null default 0 comment '年龄',
	pos varchar(20) not null default '' comment '职位',
	add_time timestamp not null default current_timestamp comment '入职时间'
)charset utf8 comment '员工记录表';

insert into staffs(name, age, pos, add_time) values('z3',22,'manager',NOW());
insert into staffs(name, age, pos, add_time) values('July',22,'dev',NOW());
insert into staffs(name, age, pos, add_time) values('tom',22,'dev',NOW());

select * from staffs;


alter table staffs add index idx_staffs_name_age_pos(name,age,pos);
show index from staffs;

9.2 索引失效场景总结

9.2.1 违背最左前缀

最左前缀即在复合索引中,如(a,b,c),搜索条件必须匹配为:(a)、(ab)、(abc),否则会部分失效。

例子:

有复合索引(name,age,pos),此时查询条件为:(name, age, pos)

explain select * from staffs where name='tom' and age=22 and pos='dev';

若查询条件改成(age, pos)

explain select * from staffs where age=22 and pos='dev';

 

9.2.2 在索引列上使用了mysql内置函数,导致索引失效

explain select * from staffs where left(name, 4) = 'July';

 

9.2.3 使用 sql 语句中范围右边的列,索引失效

explain select * from staffs where name='tom' and age>21 and pos='manager';

 

9.2.4 like 后面以通配符 % 开头,索引失效

explain select * from staffs where name like '%tom%';

如果左边必须有 % 如何处理:

使用覆盖索引进行全表扫描。即查询出的字段必须在建立的索引范围内

explain select name from staffs where name like '%tom%';

 

十、常见面试题

10.1 优化sql

最左原则

若有范围,由于>号后索引失效,需要调整范围顺序,使范围顺序顺从索引顺序,能够增加使用的索引字段

Order By 注意事项

使用 order by 时,尽量按最左原则排序,若无法按照最左原则,可以尝试将某个内容变为变量如:

Group By 注意事项

分组之前必排序,若顺序错乱后,就会出现临时表

使用 group by 时,尽量按最左原则排序,若无法按照最左原则,可以尝试将某个内容变为变量如:

 

10.2 索引建议

  

 

10.3 优化

10.3.1 in 和 exist

https://www.jianshu.com/p/f212527d76ff

 

10.3.2 order by

 

## 双路排序

mysql 4.1 之前是使用双路排序,即两次扫描磁盘,才最终得到数据。

读取行指针和 orderby 列,对他们进行排序,然后扫描已经排序好的列表,按照列表中的值重新从列表中读取对应的数据输出

从磁盘取排序字段,在 buffer 上进行排序,再从磁盘上读取其他字段

缺点:

取一批数据需要对磁盘进行两次扫描,由于 I/O 是很耗时的,故在 mysql 4.1 之后,出现了第二种改进的算法

 

## 单路排序

 

## 对比

若单路取出的数据总大小超出了 sort_buffer 的容量,会导致多次 I/O 并多次排序

 

10.3.3 group by

分组先查询,故大部分与 order by 一致

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值