mysql高级优化(全)-尚硅谷

目录

  • 前言
  • 1. 简介
    • 1.1 安装
    • 1.2 MySQL逻辑架构
      • 1.2.1 存储引擎
  • 2. 索引优化分析
    • 2.1 原因
    • 2.2 常见通用的join查询
    • 2.3 索引
      • 2.3.1 索引分类
      • 2.3.2 索引结构
      • 2.3.3 索引情况
    • 2.4 性能分析
      • 2.4.1 id(查询序列号)
      • 2.4.2 select_type(查询类型)
      • 2.4.3 type(访问类型)
      • 2.4.4 possible_keys 、key 和 key_len(可能用到索引、实际用到索引、长度)
      • 2.4.5 ref(条件查询)
      • 2.4.6 rows(行数)
      • 2.4.7 explain例子分析
    • 2.5 优化分析
      • 2.5.1 索引单表优化
      • 2.5.2 索引两表优化
      • 2.5.3 索引三表优化
    • 2.6 索引失效
    • 2.7 面试常考
    • 2.8 总结
  • 3. 查询截取分析
    • 3.1 in和exists
    • 3.2 OrderBy优化
    • 3.3 GroupBy优化
    • 3.4 慢查询日志
    • 3.5 批量插入数据脚本
    • 3.6 Show Profile
    • 3.7 全局查询日志
  • 4. 锁机制
  • 5. 主从复制

前言

本篇文章主要涉及mysql的高级篇,主要是mysql的架构介绍、索引优化分析、查询截取分析、mysql锁机制以及主从复制等

在这之前的学习可参考我之前的文章进行学习

数据库知识 链接
数据库查询常用语句语法 博客链接
数据库中增删改常用语法语句(全) 博客链接
数据库中事务(Transaction)的详解 博客链接
数据库之DQL、DML、DDL、DCL详细分析 博客链接
SQLZOO(中文版)习题答案(全) 博客链接

还有其他细节以及数据库的算法题 可看我的专栏
数据库细节以及数据库算法专栏

本篇文章的笔记主要通过该视频进行学习:
尚硅谷MySQL数据库高级,mysql优化,数据库优化

1. 简介

1.1 安装

可通过安装包或者我之前的文章进行学习

linux版本可通过rpm命令或者apt-get命令或者tar进行解压等

1.2 MySQL逻辑架构

连接服务引擎存储
架构可以在多种不同场景中应用并发挥良好作用。主要体现在存储引擎的架构上,插件式的存储引擎架构将查询处理和其它的系统任务以及数据的存储提取相分离
在这里插入图片描述

  1. 连接层
    最上层是一些客户端和连接服务,包含本地sock通信和大多数基于客户端/服务端工具实现的类似于tcplip的通信。主要完成一些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程池的概念,为通过认证安全接入的客户端提供线程。同样在该层上可以实现基于SSL的安全链接。服务器也会为安全接入的每个客户端验证它所具有的操作权限。

  2. 服务层
    第二层架构主要完成大多少的核心服务功能,如SQL接口,并完成缓存的查询,SQL的分析和优化及部分内置函数的执行。所有跨存储引擎的功能也在这一层实现,如过程、函数等。在该层,服务器会解析查询并创建相应的内部解析树,并对其完成相应的优化如确定查询表的顺序是否利用索引等,最后生成相应的执行操作。如果是select语句,服务器还会查询内部的缓存。如果缓存空间足够大,这样在解决大量读操作的环境中能够很好的提升系统的性能。

  3. 引擎层
    存储引擎层,存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API与存储引擎进行通信。

  4. 存储层
    数据存储层,文件系统之上,完成与存储引擎的交互。

1.2.1 存储引擎

  • 查看mysql以提供什么存储引擎 show engines;
    在这里插入图片描述

  • 查看mysql当前默认的存储引擎 show variables like '%storage_engine%';

关于mysql的存储引擎具体可看我之前的文章
对于这部分比较详细
Mysql的两种存储引擎详细分析及区别(全)

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

myIsam只关注性能,是因为他只能查询,查询后不用提交事务
而innodb关注事务,比如增删改,都要手动提交事务
不缓存真实数据,所以要查询的时候比较慢,而缓存了真实数据,查询就比较快

2. 索引优化分析

2.1 原因

性能下降sql、执行时间长、等待时间长

  1. 查询语句写的差
  2. 索引失效 (索引建立了,没用上索引)

补充:索引分为单值和复合

  • 单值:单个表中的某个字段建一个索引
  • 复合:单个表中的某个字段建多个索引

可以通过频繁使用给他建立索引,所以查询的比较快

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

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

2.2 常见通用的join查询

  • inner join 全连接
  • full outer join 外连接
    在这里插入图片描述

上图中第6个的实现 可以通过如下:

由于有些mysql不能使用full join,不过可以换种方法表示
A 的独有 + AB 共有 + B的独有
union本身就可以去重
所以可以这样使用

select * from tbl_emp a left join tbl_dept b on a.deptId = b.id
union
select * from tbl_emp a right join tbl_dept b on a.deptId = b.id;

 
 
   
   
   
   
  • 1
  • 2
  • 3

上图中第7个的实现可以通过如下:
也就是A的独有+ B的独有
之后通过union进行合并

select * from tbl_emp a left join tbl_dept b on a.deptId = b.id where b.id is null 
union 
select * from tbl_emp a right join tbl_dept b on a.deptId = b.id where a.deptId is null;

 
 
   
   
   
   
  • 1
  • 2
  • 3

视频中的sql如何实践,具体这里就不给出,只需要知道其逻辑即可

具体其sql代码如下:

CREATE TABLE `tbl_emp` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
`deptId` int(11) DEFAULT NULL,
PRIMARY KEY (`id`) ,
KEY `fk_dept_id`(`deptId`)
)ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8;

CREATE TABLE tbl_dept (
id int(11) NOT NULL AUTO_INCREMENT,
deptName varchar(30) DEFAULT NULL,
locAdd varchar(40) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8;

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

2.3 索引

提高效率,类比资源
排好序的、快速查找(影响order by)数据结构

在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引

官方解释:

数据本身之外,数据库还维护着一个满足特定查找算法的数据结构,这些数据结构以某种方式指向数据,这样就可以在这些数据结构的基础上实现高级查找算法,这种数据结构就是索引。

如下方二叉树的数据结构所示
索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录
在这里插入图片描述

左边是数据表,一共有两列七条记录,最左边的是数据记录的物理地址。
为了加快Col2的查找,可以维护一个右边所示的二叉查找树,每个节点分别包含索引键值和一个指向对应数据记录物理地址的指针,这样就可以运用二叉查找在一定的复杂度内获取到相应数据,从而快速的检索出符合条件的记录。

一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。

  • 平常所说的索引,如果没有特别指明,都是指B树(多路搜索树,并不一定是二叉的)结构组织的索引。其中聚集索引,次要索引,覆盖索引,复合索引,前缀索引
  • 唯一索引默认都是使用B+树索引,统称索引。

当然,除了B+树这种类型的索引之外,还有哈稀索引(hash index)等。

优势:

  • 提高数据检索的效率,降低数据库的IO成本(不用一直通过磁盘查找)
  • 通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗

劣势

  • 索引列也是要占用空间的(占空间)
  • 更新表时,MySQL不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息

MysQL有大数据量的表,需要花时间研究建立最优秀的索引,或优化查询

2.3.1 索引分类

  • 单值索引:即一个索引只包含单个列,一个表可以有多个单列索引。
  • 唯一索引:索引列的值必须唯一,但允许有空值。
  • 复合索引:即一个索引包含多个列

基本语法:

  • 创建
CREATE [UNIQUE] INDEX indexName ON mytable(columnName(length));
//或者
ALTER mytable ADD [UNIQUE] INDEX [indexName] ON (columnName(length));

 
 
   
   
   
   
    • 删除 DROP INDEX [indexName] ON mytable;
    • 查看 SHOW INDEX FROM tableName;

    添加具体有四种方式:

    1. ALTER TABLE tbl_name ADD PRIMARY KEY (column_list);:该语句添加一个主键,这意味着索引值必须是唯一的,且不能为NULL。
    2. ALTER TABLE tbl name ADD UNIQUE index_name (column_list);:这条语句创建索引的值必须是唯一的(除了NULL外,NULL可能会出现多次)。
    3. ALTER TABLE tbl_name ADD INDEX index_name (column_list);:添加普通索引,索引值可出现多次。
    4. ALTER TABLE tbl_name ADD FULLTEXT index_name (column_list);:该语句指定了索引为FULLTEXT,用于全文索引。

    2.3.2 索引结构

    • BTree索引
    • Hash索引
    • full-text全文索引
    • R-Tree索引

    BTree索引:
    在这里插入图片描述
    初始化介绍

    浅蓝色的称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示)
    如磁盘块1包含数据项17和35,包含指针P1、P2、P3,
    P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。

    • 真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。

    • 非叶子节点只不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中。

    查找过程

    如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO。
    在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,内存时间因为非常短(相比磁盘的IO)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,
    29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找找到29,结束查询,总计三次IO。

    真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常非常高。

    2.3.3 索引情况

    分为有索引和无索引
    索引(查找,排序)

    需要建立索引的情况有:

    • 主键自动建立唯一索引
    • 频繁作为查询条件的字段应该创建索引
    • 查询中与其它表关联的字段,外键关系建立索引
    • 单键/组合索引的选择问题,who?(在高并发下倾向创建组合索引)
    • 查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度
    • 查询中统计或者分组字段(分组都是需要排序的)

    不需要简历索引的情况有:

    • 表记录太少
    • 经常增删改的表(提高了查询速度,但是会同时江低更新表的速度,对表进行更新的时候,mysql还要保存数据,还要保存一下索引文件)
    • 数据重复且分布平均的表字段,因此应该只为最经常查询和最经常排序的数据列建立索引。注意,如果某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果。
    • 频繁更新的字段不适合创建索引,因为每次更新不单单是更新了记录还会更新索引
    • Where条件里用不到的字段不创建索引

    一个索引的选择性越接近于1,这个索引的效率就越高

    2.4 性能分析

    MySQL Query Optimizer:Mysql中有专门负责优化SELECT语句的优化器模块,主要功能:通过计算分析系统中收集到的统计信息,为客户端请求的Query提供他认为最优的执行计划,但不见得最优

    MySQL常见瓶颈(通过cpu 、io 、 服务器的硬件进行分析)

    • CPU:CPU在饱和的时候一般发生在数据装入内存或从磁盘上读取数据时候
    • IO:磁盘I/O瓶颈发生在装入数据远大于内存容量的时候
    • 服务器硬件的性能瓶颈:top,free,iostat和vmstat来查看系统的性能状态

    通过explain的关键字进行分析

    explain的使用:explain + sql语句

    在这里插入图片描述

    关于explain的功能可以

    • id:表的读取顺序。
    • select_type:数据读取操作的操作类型。
    • possible_keys:哪些索引可以使用。
    • key:哪些索引被实际使用。
    • ref:表之间的引用。
    • rows:每张表有多少行被优化器查询。

    2.4.1 id(查询序列号)

    select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序

    三种情况:

    • id相同,执行顺序由上至下
      (id都是1,执行顺序从上到下)
      在这里插入图片描述

    • id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
      (在内层的子查询序列,其等级越高)
      在这里插入图片描述

    • (以上两种情况同时存在的时候)
      注意框框是一个临时表
      按照上面的规则进行排序
      id如果相同,可以认为是一组,从上往下顺序执行;在所有组中,id值越大,优先级越高,越先执行,衍生=DERIVED
      在这里插入图片描述

    2.4.2 select_type(查询类型)

    select_type:查询的类型,主要是用于区别普通查询、联合查询、子查询等的复杂查询。

    具体的类型有:

    • SIMPLE - 简单的select查询,查询中不包含子查询或者UNION。
    • PRIMARY - 查询中若包含任何复杂的子部分,最外层查询则被标记为。(最外层)
    • SUBQUERY - 在SELECT或WHERE列表中包含了子查询。(内层)越内层等级越高,越先执行
      在这里插入图片描述
    • DERIUED - 在FROM列表中包含的子查询被标记为DERIVED(衍生)MySQL会递归执行这些子查询,把结果放在临时表里。
    • UNION - 若第二个SELECT出现在UNION之后,则被标记为UNION;若UNION包含在FROM子句的子查询中外层SELECT将被标记为:DERIVED。
    • UNION RESULT - 从UNION表获取结果的SELECT。(合并之后的查询就是这个选项)

    2.4.3 type(访问类型)

    访问类型排列

    type显示的是访问类型,是较为重要的一个指标,结果值从最好到最坏依次是:

    system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index >ALL

    常见的指标主要有:
    system>const>eq_ref>ref>range>index>ALL

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

    • system:表只有一行记录(等于系统表),这是const类型的特列,平时不会出现,这个也可以忽略不计。

    • const:表示通过索引一次就找到了,const用于比较primary key或者unique索引。因为只匹配一行数据,所以很快如将主键置于where列表中,MySQL就能将该查询转换为一个常量。(单表中的主键id,一张表一个条件
      在这里插入图片描述

    • eq_ref唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫(联表唯一,和上面的区别在于索引数量不同
      在这里插入图片描述

    • ref:非唯一性索引扫描,返回匹配某个单独值的所有行,本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而,它可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体。(上面的条件是一对一,这个条件是一对多

    • range:只检索给定范围的行,使用一个索引来选择行。key列显示使用了哪个索引一般就是在你的where语句中出现了between、<、>、in等的查询。这种范围扫描索引扫描比全表扫描要好,因为它只需要开始于索引的某一点,而结束语另一点,不用扫描全部索引。
      在这里插入图片描述

    • index:Full Index Scan,index与ALL区别为index类型只遍历索引树。这通常比ALL快,因为索引文件通常比数据文件小(也就是说虽然all和Index都是读全表,但index是从索引中读取的,而all是从硬盘中读的)。
      在这里插入图片描述

    • all:Full Table Scan,将遍历全表以找到匹配的行。
      (也就是通过 select * 全部数据读取)

    2.4.4 possible_keys 、key 和 key_len(可能用到索引、实际用到索引、长度)

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

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

    所谓的覆盖索引:查询时未发生回表。查询的字段只能建立在索引的字段中

    • key_len (估计用到的长度)
      表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。在不损失精确性的情况下,长度越短越好
      key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的

    2.4.5 ref(条件查询)

    (显示使用到的条件查询,如果是常量就为const)
    显示索引的哪一列被使用了,如果可能的话,是一个常数。哪些列或常量被用于查找索引列上的值。

    在这里插入图片描述

    由key_len可知t1表的idx_col1_col2被充分使用,col1匹配t2表的col1,col2匹配了一个常量,即 ‘ac’。
    查询中与其它表关联的字段,外键关系建立索引。

    2.4.6 rows(行数)

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

    每张表被优化器查询
    把不合适的索引删除,慢慢优化

    在这里插入图片描述

    2.4.7 explain例子分析

    在这里插入图片描述

    第一行(执行顺序4):id列为1,表示是union里的第一个select,select_type列的primary表示该查询为外层查询,table列被标记为,表示查询结果来自一个衍生表,其中derived3中3代表该查询衍生自第三个select查询,即id为3的select。【select d1.name… 】

    第二行(执行顺序2):id为3,是整个查询中第三个select的一部分。因查询包含在from中,所以为derived。【select id,namefrom t1 where other_column=’’】

    第三行(执行顺序3):select列表中的子查询select_type为subquery,为整个查询中的第二个select。【select id from t3】

    第四行(执行顺序1):select_type为union,说明第四个select是union里的第二个select,最先执行【select name,id from t2】

    第五行(执行顺序5):代表从union的临时表中读取行的阶段,table列的表示用第一个和第四个select的结果进行union操作。【两个结果union操作】

    2.5 优化分析

    关于索引的优化也可看我之前的文章进行学习
    Mysql中索引的最左前缀原则图文剖析(全)

    2.5.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 VARCHAR(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’),
    (1,1,3,3,‘3’,‘3’);

      经过如下查询:

      explain SELECT id, author_id FROM article WHERE category_id = 1 AND comments > 1 ORDER BY views DESC LIMIT 1;
      
       
       
         
         
         
         
      • 1

      在这里插入图片描述

      发现其上面的单表查询,不是索引的话,他是进行了全表查询,而且在extra还出现了Using filesort等问题

      所以思路可以有建立其复合索引
      具体建立复合索引有两种方式:

      1. create index idx_article_ccv on article(category_id,comments,views);
      2. ALTER TABLE 'article' ADD INDEX idx_article_ccv ( 'category_id , 'comments', 'views' );

      在这里插入图片描述
      但这只是去除了它的范围,如果要去除Using filesort问题的话,还要将其中间的条件范围改为等于号才可满足

      发现其思路不行,所以删除其索引 DROP INDEX idx_article_ccv ON article;
      在这里插入图片描述
      主要的原因是:

      这是因为按照BTree索引的工作原理,先排序category_id,如果遇到相同的category_id则再排序comments,如果遇到相同的comments 则再排序views。

      当comments字段在联合索引里处于中间位置时,因comments > 1条件是一个范围值(所谓range),MySQL无法利用索引再对后面的views部分进行检索,即range类型查询字段后面的索引无效。

      所以建立复合索引是对的
      但是其思路要避开中间那个范围的索引进去
      只加入另外两个索引即可create index idx_article_cv on article(category_id, views);

      在这里插入图片描述

      2.5.2 索引两表优化

      建立两张表

      CREATE TABLE IF NOT EXISTS class(
      	id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
      	card INT(10) UNSIGNED NOT NULL,
      	PRIMARY KEY(id)
      );
      

      CREATE TABLE IF NOT EXISTS book(
      bookid INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
      card INT(10) UNSIGNED NOT NULL,
      PRIMARY KEY(bookid)
      );

      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 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)));

      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 通过分析该数据EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card;
      • 再加入索引 ALTER TABLE book ADD INDEX Y(card);
      • 在分析EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card;
        在这里插入图片描述

      之所以只加入一个表的索引,索引就好了
      那如果加入另外一个表的索引,结果还是没变化
      这是因为
      可以看到第二行的type变为了ref,rows也变少了,优化比较明显。这是由左连接特性决定的。LEFT JOIN条件用于确定如何从右表搜索行,左边一定都有,所以右边是我们的关键点,一定需要在右表建立索引

      如果建立左索引,其实没变化
      在这里插入图片描述

      如果使用右连接RIGHT JOIN查询

      换成左边是我们的关键点,要想优化需要在左表建立索引。

      在这里插入图片描述

      所以:索引两表优化,左连接右表建索引,右连接左表建索引

      2.5.3 索引三表优化

      建立三张表:
      (在前面中有两张表,现在多一个表即可)

      CREATE TABLE IF NOT EXISTS phone(
      	phoneid INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
      	card INT(10) UNSIGNED NOT NULL,
      	PRIMARY KEY(phoneid)
      )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
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26

      因为有用到前面的索引,所以把不必要的索引删除干净
      在这里插入图片描述

      添加必要的索引
      通过后两张表
      分别是alter tablephoneadd index z(card);,以及alter tablebookadd index y(card);
      在这里插入图片描述
      因此索引最好设置在需要经常查询的字段中

      Join语句的优化

      • 尽可能减少Join语句中的NestedLoop的循环总次数:“永远用小结果集驱动大的结果集”。

      • 优先优化NestedLoop的内层循环

      • 保证Join语句中被驱动表上Join条件字段已经被索引。

      • 当无法保证被驱动表的Join条件字段被索引且内存资源充足的前提下,不要太吝惜JoinBuffer的设置。

      2.6 索引失效

      为了更好的演示效果,采用实战方式

      建立一张表

      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’,23,‘dev’,NOW());
      INSERT INTO staffs(name,age,pos,add_time) VALUES(‘2000’,23,‘dev’,NOW());

      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11

      建立索引 ALTER TABLE staffs ADD INDEX index_staffs_nameAgePos(name,age,pos);

      全值匹配,且从左到右,只不过长度变长了
      在这里插入图片描述
      索引失效(应该避免)

      从左开始且不跳列才不会失效

      • 最佳左前缀法则 - 如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过复合索引中间列。

      在这里插入图片描述
      即使跳过了中间的索引,但是其长度没变化,索引已经失效了
      在这里插入图片描述

      索引列上不做额外操作才不会失效

      • 不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描。
        在这里插入图片描述

      少用>,<,between…and等结构

      • 存储引擎不能使用索引中范围条件右边的列。(就是>,<,between…and)
        在这里插入图片描述

      减少select *

      • 尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致))
        区别在于extra,索引的不同,速度不一样
        在这里插入图片描述

      不用!=或者<>

      • mysql在使用不等于(!=或者<>)的时候无法使用索引会导致全表扫描。
        在这里插入图片描述

      不用 is null, is not null

      • is null, is not null 也无法使用索引。
        在这里插入图片描述
        模糊查询
      • like以通配符开头(’%abc…’),mysql索引失效会变成全表扫描的操作。

      在这里插入图片描述
      注意看上面模糊查询的细节,只有xx%前缀查询才不会失效

      那如何改变%xx% 让其不失效呢
      引入一张表

      CREATE TABLE `tbl_user`(
      	`id` INT(11) NOT NULL AUTO_INCREMENT,
      	`name` VARCHAR(20) DEFAULT NULL,
      	`age`INT(11) DEFAULT NULL,
      	`email` VARCHAR(20) DEFAULT NULL,
      	PRIMARY KEY(`id`)
      )ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
      

      INSERT INTO tbl_user(name,age,email)VALUES(‘1aa1’,21,[email protected]);
      INSERT INTO tbl_user(name,age,email)VALUES(‘2bb2’,23,[email protected]);
      INSERT INTO tbl_user(name,age,email)VALUES(‘3cc3’,24,[email protected]);
      INSERT INTO tbl_user(name,age,email)VALUES(‘4dd4’,26,[email protected]);

      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12

      在没有索引的时候,怎么查询都是全表查询
      增加一个索引CREATE INDEX idx_user_nameAge ON tbl_user(NAME,age);
      之后的索引查询为字段值
      在这里插入图片描述
      如果使用下面这些,都是使用的覆盖索引,结果都是一样的

      EXPLAIN SELECT id FROM tbl_user WHERE NAME LIKE '%aa%';
      EXPLAIN SELECT NAME FROM tbl_user WHERE NAME LIKE '%aa%';
      EXPLAIN SELECT age FROM tbl_user WHERE NAME LIKE '%aa%';
      

      EXPLAIN SELECT id,NAME FROM tbl_user WHERE NAME LIKE ‘%aa%’;
      EXPLAIN SELECT id,NAME,age FROM tbl_user WHERE NAME LIKE ‘%aa%’;
      EXPLAIN SELECT NAME,age FROM tbl_user WHERE NAME LIKE ‘%aa%’;

      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

      id之所以没加索引,但是可以加入使用不会失效,是因为他是主键

      但是如果加入了没有主键又不是索引的东西,%xx%就会失效
      在这里插入图片描述

      类型要正确
      即使类型不正确也可以查询,但是底层会帮你转换类型,在判断,但会浪费时间,索引直接失效,变成了全表查询

      • 字符串不加单引号索引失效。
        在这里插入图片描述
        不用or关键字就不会失效

      • 少用or,用它来连接时会索引失效。
        在这里插入图片描述

      2.7 面试常考

      为了更加贴切的展示,先建立一张表

      create table test03(
          id int primary key not null auto_increment,
          c1 char(10),
          c2 char(10),
          c3 char(10),
          c4 char(10),
          c5 char(10)
      );
      

      insert into test03(c1,c2,c3,c4,c5) values (‘a1’,‘a2’,‘a3’,‘a4’,‘a5’);
      insert into test03(c1,c2,c3,c4,c5) values (‘b1’,‘b2’,‘b3’,‘b4’,‘b5’);
      insert into test03(c1,c2,c3,c4,c5) values (‘c1’,‘c2’,‘c3’,‘c4’,‘c5’);
      insert into test03(c1,c2,c3,c4,c5) values (‘d1’,‘d2’,‘d3’,‘d4’,‘d5’);
      insert into test03(c1,c2,c3,c4,c5) values (‘e1’,‘e2’,‘e3’,‘e4’,‘e5’);

      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14

      建立一索引 create index idx_test03_c1234 on test03(c1,c2,c3,c4);

      以下都符合最左前缀原则,都会用到索引,只是估计用到的长度在变化而已

      正常顺序

      explain select * from test03 where c1='a1';
      explain select * from test03 where c1='a1' and c2='a2';
      explain select * from test03 where c1='a1' and c2='a2' and c3='a3';
      explain select * from test03 where c1='a1' and c2='a2' and c3='a3' and c4='a4';
      
       
       
         
         
         
         
      • 1
      • 2
      • 3
      • 4

      乱序
      即使更改一下顺序,mysql的最左前缀原则还是符合的,因为mysql有优化器会帮你查询是否匹配

      explain select * from test03 where c1='a1' and c2='a2' and c3='a3' and c4='a4';
      explain select * from test03 where c1='a1' and c2='a2' and c4='a4' and c3='a3';
      
       
       
         
         
         
         
      • 1
      • 2

      限定范围
      如果开始限定范围
      第一条会用到3个索引,前两个用来查找,第三个用来排序
      第二条用到了4个索引,前3个用来查找,第四个用来排序

      explain select * from test03 where c1='a1' and c2='a2' and c3>'a3' and c4='a4';
      explain select * from test03 where c1='a1' and c2='a2' and c4>'a4' and c3='a3';
      
       
       
         
         
         
         
      • 1
      • 2

      通过看其长度也可知道
      在这里插入图片描述
      使用order by
      下面那个例子都是一样的
      前两个都是在查找,第三个只是在排序,到了这里就已经断了,所以第四个可有可无

      explain select * from test03 where c1='a1' and c2='a2' and c4='a4' order by c3;
      explain select * from test03 where c1='a1' and c2='a2' order by c3;
      
       
       
         
         
         
         
      • 1
      • 2

      在这里插入图片描述
      如果把3换成4,会出现Using filesort。,因为优化器会给你文件排序(因为中间跳了一个)
      在这里插入图片描述
      如果使用这个不会出现Using filesort,因为是正常的索引顺序在这里插入图片描述
      而这个就会出现Using filesort,因为顺序颠倒了

       explain select * from test03 where c1='a1' and c5='a5' order by c3,c2;
      
       
       
         
         
         
         
      • 1

      下面这两个一样的功能
      前两个是查找,第二三是排序

       explain select * from test03 where c1='a1' and c2='a2' order by c2,c3;
       explain select * from test03 where c1='a1' and c2='a2' and c5='a5' order by c2,c3;
      
       
       
         
         
         
         
      • 1
      • 2

      这个就不一样,如果确定了一个查找的顺序之后,即使排序乱序也不会出现filesort
      但是如果不用的话就会出现

      explain select * from test03 where c1='a1' and c2='a2' and c5='a5' order by c3,c2;
      explain select * from test03 where c1='a1' and c5='a5' order by c3,c2;
      
       
       
         
         
         
         
      • 1
      • 2

      在这里插入图片描述
      group by

      如果乱序,还会多出现一个临时表

      定值、范围还是排序,一般order by是给个范围

      group by基本上都需要进行排序,会有临时表产生

      explain select * from test03 where c1='a1' and c4='a4' group by c2,c3;
      explain select * from test03 where c1='a1' and c4='a4' group by c3,c2;
      

      explain select * from test03 where c1=‘a1’ and c4=‘a4’ order by c2,c3;

      • 1
      • 2
      • 3
      • 4

      在这里插入图片描述
      模糊查询

      explain select * from test03 where c1='a1' and c2 like 'kk%' and c3='a3';
      explain select * from test03 where c1='a1' and c2 like '%kk' and c3='a3';
      explain select * from test03 where c1='a1' and c2 like '%kk%' and c3='a3';
      explain select * from test03 where c1='a1' and c2 like 'k%kk%' and c3='a3';
      
       
       
         
         
         
         
      • 1
      • 2
      • 3
      • 4

      具体其执行结果如下
      在这里插入图片描述

      2.8 总结

      • 对于单键索引,尽量选择针对当前query过滤性更好的索引。
      • 在选择组合索引的时候,当前Query中过滤性最好的字段在索引字段顺序中,位置越靠前越好。
      • 在选择组合索引的时候,尽量选择可以能够包含当前query中的where字句中更多字段的索引。
      • 尽可能通过分析统计信息和调整query的写法来达到选择合适索引的目的。

      假设index(a, b, c)

      where语句 索引是否被使用
      where a = 3 可以,使用到a
      where a = 3 and b = 5 可以,使用到a,b
      where a = 3 and b = 5 and c = 4 可以,使用到a,b,c
      where b = 3 或者 where b = 3 and c = 4 或者 where c = 4 不可
      where a = 3 and c = 5 使用到a,但是c不可以,b中间断了
      where a = 3 and b > 4 and c = 5 使用到a和b,c不能用在范围之后,b断了
      where a = 3 and b like ‘kk%’ and c = 4 可以,使用到a,b,c。
      where a = 3 and b like ‘%kk’ and c = 4 Y,使用到a
      where a = 3 and b like ‘%kk%’ and c = 4 Y,使用到a
      where a= 3 and b like ‘k%kk%’ and c = 4 Y,使用到a,b,c

      优化的口诀如下:

      全值匹配我最爱, 最左前缀要遵守;

      带头大哥不能死, 中间兄弟不能断;

      索引列上少计算, 范围之后全失效;

      LIKE 百分写最右, 覆盖索引不写 *

      不等空值还有 OR, 索引影响要注意;

      VAR 引号不可丢, SQL 优化有诀窍。

      3. 查询截取分析

      SQL调优过程:

      1. 观察,至少跑1天,看看生产的慢SQL情况。
      2. 开启慢查询日志,设置阙值,比如超过5秒钟的就是慢SQL,并将它抓取出来。
      3. explain + 慢SQL分析。
      4. show profile。
      5. 运维经理 or DBA,进行SQL数据库服务器的参数调优。

      总结:

      • 慢查询的开启并捕获
      • explain + 慢SQL分析
      • show profile查询SQL在Mysql服务器里面的执行细节和生命周期情况
      • SQL数据库服务器的参数调优。

      3.1 in和exists

      小表驱动大表
      RBO原理:

      • 当B表的数据集必须小于A表的数据集时,用in优于exists。
      select * from A where id in (select id from B)
      等价于:
      for select id from B
      for select * from A where A.id = B.id
      
       
       
         
         
         
         
      • 1
      • 2
      • 3
      • 4
      • 当A表的数据集系小于B表的数据集时,用exists优于in
      select * from A where exists (select 1 from B where B.id = A.id)
      等价于:
      for select * from A
      for select * from B where B.id = A.id
      
       
       
         
         
         
         
      • 1
      • 2
      • 3
      • 4

      关于exists的关键字

      该语法可以理解为:将主查询的数据,放到子查询中做条件验证,根据验证结果(TRUE或FALSE)来决定主查询的数据结果是否得以保留。

      提示

      • EXSTS(subquey)只返回TRUE或FALSE,因此子查询中的SELECT * 也可以是 SELECT 1 或select ‘X’,官方说法是实际执行时会忽略SELECT清单,因此没有区别。
      • EXISTS子查询的实际执行过程可能经过了优化而不是我们理解上的逐条对比,如果担忧效率问题,可进行实际检验以确定是否有效率问题。
      • EXISTS子查询往往也可以用条件表达式,其他子查询或者JOIN来替代,何种最优需要具体问题具体分析

      3.2 OrderBy优化

      ORDER BY子句,尽量使用Index方式排序,避免使用FileSort方式排序

      建立一张表以及一个索引

      create table tblA(
          #id int primary key not null auto_increment,
          age int,
          birth timestamp not null
      );
      

      insert into tblA(age, birth) values(22, now());
      insert into tblA(age, birth) values(23, now());
      insert into tblA(age, birth) values(24, now());

      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9

      索引 create index idx_A_ageBirth on tblA(age, birth);

      如果索引的时候也是按照顺序,两种情况都是一样的
      在这里插入图片描述
      但是索引的时候不按顺序
      就会出现这样的情况
      在这里插入图片描述

      MySQL支持二种方式的排序:FileSort和lIndex

      • Index效率高,它指MySQL扫描索引本身完成排序
      • FileSort方式效率较低。

      ORDER BY满足两情况,会使用Index方式排序:

      • ORDER BY语句使用索引最左前列。
      • 使用where子句与Order BY子句条件列组合满足索引最左前列。

      如果不在索引列上,mysql的filesort有两种算法:双路排序、单路排序

      • 双路排序:MySQL4.1之前是使用双路排序,字面意思就是两次扫描磁盘,最终得到数据,读取行指针和OrderBy列,对他们进行排序,然后扫描已经排序好的列表,按照列表中的值重新从列表中读对应的数据输出。

      从磁盘取排序字段,在buffer进行排序,再从磁盘取其他字段。取一批数据,要对磁盘进行了两次扫描,众所周知,I\O是很耗时的,所以在mysql4.1之后,出现了第二种改进的算法,就是单路排序。

      • 单路排序:从磁盘读取查询需要的所有列,按照order by列在buffer对它们进行排序,然后扫描排序压的列表进行输出,它的效率更快一些,避免了第二次读取数据。并且把随机IO变成了顺序IO,但是它会使用更多的空间,因为它把每一行都保存在内存中了。

      但是用单路有问题

      在sort_buffer中,单路排序比双路排序要多占用很多空间,因为单路排序是把所有字段都取出,所以有可能取出的数据的总大小超出了sort_buffer的容量,导致每次只能取sort_buffer容量大小的数据,进行排序(创建tmp文件,多路合并),排完再取取sort_buffer容量大小,再排……从而多次I/O。
      本来想省一次I/O操作,反而导致了大量的I/O操作,反而得不偿失。

      优化策略

      • 增大sort_buffer_size参数的设置
      • 增大max_length_for_sort_data参数的设置

      为什么设置sort_buffer_size、max_length_for_sort_data参数能优化排序?

      主要是因为可以提高Order By的速度

      具体原因如下:

      Order by时select * 是一个Query需要的字段,这点非常重要。在这里的影响是;

      • 当Query的字段大小总和小于max_length_for_sort_data而且排序字段不是TEXT|BLOB类型时,会用改进后的算法——单路排序,否则用老算法——多路排序。
      • 两种算法的数据都有可能超出sort_buffer的容量,超出之后,会创建tmp文件进行合并排序,导致多次IO,但是用单路排序算法的风险会更大一些,所以要提高sort_buffer__size。
      • 尝试提高sort_buffer_size,不管用哪种算法,提高这个参数都会提高效率,当然,要根据系统的能力去提高,因为这个参数是针对每个进程的。
      • 尝试提高max_length_for_sort_data,提高这个参数,会增加用改进算法的概率。但是如果设的太高,数据总容量超出sort_buffer_size的概率就增大,明显症状是高的磁盘I/O活动和低的处理器使用率

      小结
      为排序使用索引

      MySql两种排序方式∶文件排序 或 扫描有序索引排序

      MySql能为 排序 与 查询 使用相同的索引

      创建复合索引 a_b_c (a, b, c)

      order by能使用索引最左前缀

      • ORDER BY a
      • ORDER BY a, b
      • ORDER BY a, b, c
      • ORDER BY a DESC, b DESC, c DESC

      如果WHERE使用素引的最左前缀定义为常量,则order by能使用索引

      • WHERE a = const ORDER BY b,c
      • WHERE a = const AND b = const ORDER BY c
      • WHERE a = const ORDER BY b, c
      • WHERE a = const AND b > const ORDER BY b, c

      不能使用索引进行排序

      • ORDER BY a ASC, b DESC, c DESC //排序不—致
      • WHERE g = const ORDER BY b, c //产丢失a索引
      • WHERE a = const ORDER BY c //产丢失b索引
      • WHERE a = const ORDER BY a, d //d不是素引的一部分
      • WHERE a in (…) ORDER BY b, c //对于排序来说,多个相等条件也是范围查询

      3.3 GroupBy优化

      GroupBy优化(和order by差不多)

      • group by实质是先排序后进行分组,遵照索引建的最佳左前缀。
      • 当无法使用索引列,增大max_length_for_sort_data参数的设置 + 增大sort_buffer_size参数的设置。
      • where高于having,能写在where限定的条件就不要去having限定了。

      3.4 慢查询日志

      • MySQL的慢查询日志是MySQL提供的一种日志记录,它用来记录在MySQL中响应时间超过阀值的语句,具体指运行时间超过long_query_time值的SQL,则会被记录到慢查询日志中。long_query_time的默认值为10,意思是运行10秒以上的语句。
      • 由他来查看哪些SQL超出了我们的最大忍耐时间值,比如一条sql执行超过5秒钟,我们就算慢SQL,希望能收集超过5秒的sql,结合之前explain进行全面分析

      默认情况下,MySQL数据库没有开启慢查询日速,需要我们手动来设置这个参数。

      一般不建议启动该参数,因为开启慢查询日志会或多或少带来一定的性能影响。慢查询日志支持将日志记录写入文件。

      通过如下命令进行操作

      • 查看日志是否开启SHOW VARIABLES LIKE '%slow_query_log%';
      • 开启 set global slow_query_log=1,只对当前数据库生效,默认关闭的

      结果如下
      在这里插入图片描述
      永久生效
      修改配置文件my.cnf
      [mysqld]下增加或修改参数slow_query_log和slow_query_log_file后,然后重启MySQL服务器。也即将如下两行配置进my.cnf文件

      slow_query_log =1
      slow_query_log_file=/var/lib/manongyanjiuseng-slow.log
      
       
       
         
         
         
         
      • 1
      • 2

      关于慢查询的参数slow_query_log_file,它指定慢查询日志文件的存放路径,系统默认会给一个缺省的文件host_name-slow.log(如果没有指定参数slow_query_log_file的话)

      什么样的数据会放到慢查询日志
      需要设置一个多长的时间段就会放到日志中
      这个是由参数long_query_time控制,默认情况下long_query_time的值为10秒

      • 命令:SHOW VARIABLES LIKE 'long_query_time%';
        运行时间正好等于long_query_time的情况,并不会被记录下来,需大于
        在这里插入图片描述

      • 设置阈值时间后还需要重启才可以生效 set global long_query_time=3;

      • 查询当前系统中有多少条慢查询记录 show global status like '%Slow_queries%';

      如果在配置文件中设置阈值
      具体配置如下

      #[mysqld]下配置:
      slow_query_log=1;
      slow_query_log_file=/var/lib/mysql/atguigu-slow.log
      long_query_time=3;
      log_output=FILE;
      
       
       
         
         
         
         
      • 1
      • 2
      • 3
      • 4
      • 5

      ==结合工具进行分析mysqldumpslow ==
      在生产环境中,如果要手工分析日志,查找、分析SQL,显然是个体力活,MySQL提供了日志分析工具mysqldumpslow。

      查看mysqldumpslow的帮助信息,mysqldumpslow --help

      工作常用参考

      • 得到返回记录集最多的10个SQL ,mysqldumpslow -s r -t 10 /var/lib/mysql/atguigu-slow.log
      • 得到访问次数最多的10个SQL,mysqldumpslow -s c -t 10 /var/lib/mysql/atguigu-slow.log
      • 得到按照时间排序的前10条里面含有左连接的查询语句,mysqldumpslow -s t -t 10 -g "left join" /var/lib/mysql/atguigu-slow.log
      • 另外建议在使用这些命令时结合│和more 使用,否则有可能出现爆屏情况,mysqldumpslow -s r-t 10 /ar/lib/mysql/atguigu-slow.log | more

      3.5 批量插入数据脚本

      为了更好的展示
      先建立一张表

      create database bigData;
      use bigData;
      

      CREATE TABLE dept(
      id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
      deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,
      dname VARCHAR(20)NOT NULL DEFAULT “”,
      loc VARCHAR(13) NOT NULL DEFAULT “”
      )ENGINE=INNODB DEFAULT CHARSET=utf8;

      CREATE TABLE emp(
      id int unsigned primary key auto_increment,
      empno mediumint unsigned not null default 0,
      ename varchar(20) not null default “”,
      job varchar(9) not null default “”,
      mgr mediumint unsigned not null default 0,
      hiredate date not null,
      sal decimal(7,2) not null,
      comm decimal(7,2) not null,
      deptno mediumint unsigned not null default 0
      )ENGINE=INNODB DEFAULT CHARSET=utf8;

      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21

      开启了可以创建存储函数的权限,主要是这个功能

      设置参数log_bin_trust_function_creators

      show variables like 'log_bin_trust_function_creators';
      set global log_bin_trust_function_creators=1;
      
       
       
         
         
         
         
      • 1
      • 2

      在这里插入图片描述
      这是在终端上输入的
      但是重启后会失效

      如果要配置永久的,需要在配置文件上配置

      • windows下my.ini[mysqld]加上log_bin_trust_function_creators=1

      • linux下/etc/my.cnf 下my.cnf[mysqld]加上log_bin_trust_function_creators=1

      创建函数,保证每条数据都不同

      随机产生字符串

      delimiter $$ # 两个 $$ 表示结束
      create function rand_string(n int) returns varchar(255)
      begin
          declare chars_str varchar(100) default 'abcdefghijklmnopqrstuvwxyz';
          declare return_str varchar(255) default '';
          declare i int default 0;
          while i < n do
              set return_str = concat(return_str,substring(chars_str,floor(1+rand()*52),1));
              set i=i+1;
          end while;
          return return_str;
      end $$
      
       
       
         
         
         
         
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12

      执行完语句后,在终端上输入select rand_string(2)$$;,要以$$结尾

      随机产生部门编号

      delimiter $$
      create function rand_num() returns int(5)
      begin
          declare i int default 0;
          set i=floor(100+rand()*10);
          return i;
      end $$
      
       
       
         
         
         
         
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

      执行完语句后,在终端上输入select rand_num()$$,要以$$结尾

      创建存储过程,创建往emp表中插入数据的存储过程

      delimiter $$
      create procedure insert_emp(in start int(10),in max_num int(10))
      begin
          declare i int default 0;
          set autocommit = 0;
          repeat
              set i = i+1;
              insert into emp(empno,ename,job,mgr,hiredate,sal,comm,deptno) values((start+i),rand_string(6),'salesman',0001,curdate(),2000,400,rand_num());
              until i=max_num
              end repeat;
          commit;
      end $$
      
       
       
         
         
         
         
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12

      创建往dept表中插入数据的存储过程

      delimiter $$
      create procedure insert_dept(in start int(10),in max_num int(10))
      begin
          declare i int default 0;
          set autocommit = 0;
          repeat
              set i = i+1;
              insert into dept(deptno,dname,loc) values((start+i),rand_string(10),rand_string(8));
              until i=max_num
              end repeat;
          commit;
      end $$
      
       
       
         
         
         
         
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12

      调用存储过程
      往dept表中插入数据

      DELIMITER ;
      CALL insert_dept(100, 10);
      
       
       
         
         
         
         
      • 1
      • 2

      往emp表中插入50万数据

      DELIMITER ;
      CALL insert_emp(100001, 500000);
      
       
       
         
         
         
         
      • 1
      • 2

      3.6 Show Profile

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

      默认情况下,参数处于关闭状态,并保存最近15次的运行结果。

      • 查看当前状态是否开启了, show variables like 'profiling';
        在这里插入图片描述
      • 开启 ,set profiling=on;

      通过执行show profiles;
      在这里插入图片描述

      - 诊断SQL,show profile cpu,block io for query 上一步前面的问题SQL数字号码;
      例如show profile cpu,block io for query 3;

      具体的参数介绍

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

      在查询该文件的时候如果出现了这些一定要特别注意

      • converting HEAP to MyISAM 查询结果太大,内存都不够用了往磁盘上搬了。
      • Creating tmp table 创建临时表,拷贝数据到临时表,用完再删除
      • Copying to tmp table on disk 把内存中临时表复制到磁盘,危险!
      • locked

      3.7 全局查询日志

      General log默认不开启的原因有两个:

      日志将会非常大,对磁盘是一个很大的压力。因为所有的操作都会被记录下来。
      对MySQL数据的性能有一定的影响。

      不要在生产环境开启这个功能。

      #开启
      general_log=1
      #记录日志文件的路径
      general_log_file=/path/logfile
      #输出格式
      log_output=FILE
      
       
       
         
         
         
         
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 查看状态是否开启,show variables like 'general_log';
      • set global general_log=1;
      • set global log_output='TABLE';

      log_output=‘FILE‘表示将日志存入文件,默认值是‘FILE‘

      log_output=‘TABLE‘表示将日志存入数据库,这样日志信息就会被写入到mysql.slow_log表中.

      所编写的sql语句,将会记录到mysql库里的geneial_log表,可以用下面的命令查看:select * from mysql.general_log;

      查出来会有输出语句
      在这里插入图片描述

      4. 锁机制

      具体详情可看我这篇文章
      Mysql中各类锁的机制详细解析(全)

      关于mysql的锁也可类似对比一下java的锁
      详情可看我这篇文章
      java中各类锁的机制详细解析(全)

      5. 主从复制

      在这里插入图片描述

      1. master将改变记录到二进制日志(binary log)。这些记录过程叫做二进制日志事件,binary log events;
      2. slave将master的binary log events拷贝到它的中继日志(relay log) ;
      3. slave重做中继日志中的事件,将改变应用到自己的数据库中。MySQL复制是异步的且串行化的

      复制的原则主要有:

      • 每个slave只有一个master
      • 每个slave只能有一个唯一的服务器ID
      • 每个master可以有多个salve

      主从都配置在[mysqld]结点下,都是小写
      具体的配置文件在window上是这么修改,主机修改my.ini

      • server-id=1 [必须]主服务器唯一ID

      • log-bin=自己本地的路径/mysqlbin[必须]启用二进制日志

      • log-err=自己本地的路径/mysqlerr[可选]启用错误日志

      • basedir=“自己本地路径”[可选]根目录

      • tmpdir=“自己本地路径”[可选]临时目录

      • datadir=“自己本地路径/Data/”[可选]数据目录

      • binlog-ignore-db=mysql[可选]设置不要复制的数据库

      • binlog-do-db=需要复制的主数据库名字[可选]设置需要复制的数据库

      linux上作为从机,从机修改my.cnf配置文件

      • [必须]从服务器唯一ID
      • [可选]启用二进制日志

      关闭防火墙以及重启设备
      具体可看我之前的文章进行学习补充
      ubuntu:防火墙配置详细讲解(全)
      linux之防火墙命令firewall、iptable以及端口号等详解诠释(全)

      在windows上授权 linux,只需要给予服务器ip等

      GRANT REPLICATION SLAVE ON *.* TO ‘zhangsan’@‘从机器数据库IP’ IDENTIFIED BY123456;
      
       
       
         
         
         
         
      • 1
      • show master status;
      • 记录下File和Position的值

      在linux中授权window的IP地址

      CHANGE MASTER TO MASTER_HOST=’主机
      IP’, MASTER_USER=‘zhangsan’, MASTER_PASSWORD=123456, MASTER_LOG_FILE='File名字’, MASTER_LOG_POS=Position数字;
      
       
       
         
         
         
         
      • 1
      • 2
      • start slave; 启动服务器的slave
      • show slave status\G
      • stop slave;停止

      关于主从复制更加详细的内容可看我之前的文章
      关于主从复制的超详细解析(全)

      对于数据库想增强了解可看我的栏目
      数据库栏目

      你可能感兴趣的:(Mysql高级优化,mysql)