我,【MySQL】高级篇,一个让你的数据管家单车变摩托的 “关系型数据库”

相关数据库sql文件
链接:https://pan.baidu.com/s/1RynVEnBL2nak5aJf0rXYmA
提取码:11gz

文章目录

  • 一、视图
        • 视图介绍
        • 视图的创建
        • 视图的修改
        • 视图的查看与删除
        • 视图的更新(还会更新原始表数据)
        • 视图与表的对比
  • 二、存储过程和函数
        • 变量
        • 存储过程和函数(类似java中的方法)
          • 存储过程
          • 函数
  • 三、流程控制结构
        • 顺序结构(程序从上往下依次执行)
        • 分支结构(程序从两条或多条路径中选择一条去执行)
          • if函数
          • case结构
          • if结构
        • 循环结构(程序在满足一定条件的基础上,重复执行一段代码)
          • 循环控制
          • 分类
            • while
            • loop(可以用来模拟简单的死循环)
            • repeat
  • 四、MySQL架构介绍
        • MySQL存储引擎
          • MyISAM和InnoDB
  • 五、索引优化查询★
        • 性能下降SQL查询慢
          • 原因
        • 常见的通用join查询
          • SQL执行顺序
          • Join图
        • 索引简介
          • 是什么
          • 优势
          • 劣势
          • mysql索引分类
          • mysql索引结构
          • 哪些情况需要创建索引
          • 哪些情况不需要创建索引
        • 性能分析
          • MySQLQuery Optimizer
          • MySQL常见瓶颈
          • Explain
        • 索引优化
          • 索引分析
          • 索引失效(应该避免)
          • 建表SQL
          • 案例(索引失效)
        • 一般性建议
  • 六、查询截取分析
        • 查询优化
          • 永远小表驱动大表(类似嵌套循环NestedLoop)
          • order by 关键字优化
          • group by 关键字优化
        • 慢查询日志
        • 批量数据脚本
          • 建表
          • 设置参数log_bin_trust_function_creators
          • 创建函数,保证每条数据都不同
          • 创建存储过程
          • 调用存储过程
        • Show Profile
          • 是什么
          • 分析步骤
        • 全局查询日志(仅限测试环境)
          • 配置启用
          • 编码启用
  • 七、MySQL锁机制
        • 概述
          • 定义
          • 锁的分类
        • 三锁(开销、加锁速度、死锁、粒度、并发性能只能就具体应用的特点来说哪种锁更合适)
          • 表锁(偏读)
          • 行锁(偏写)
          • 页锁(了解即可)
  • 八、主从复制
        • 复制的基本原理
          • slave会从master读取binlog来进行数据同步
          • 三步骤+原理图
        • 复制的基本原则
        • 复制的最大问题:延时
        • 一主一从常见配置

一、视图

  • 视图介绍

    • 含义:虚拟表,和普通表一样使用,mysql5.1版本出现的新特性,是通过表动态生成的数据
    • 应用场景:
      • 多个地方同时用到同样的查询结果
      • 该查询结果使用的SQL语句较复杂
  • 视图的创建

    • 语法

      create view 视图名
      as
      查询语句
      
      
      #例题
      #创建视图
      #查询邮箱中包含a字符的员工名、部门名和工种信息
      CREATE VIEW view1
      AS
      SELECT e.last_name,e.`email`,d.department_name,j.* 
      FROM employees e,departments d,jobs j
      WHERE e.`department_id` = d.`department_id`
      AND e.`job_id` = j.`job_id`;
      
      
      #使用视图
      SELECT * FROM view1 WHERE last_name LIKE '%a%';
      
    • 优点:

      • 重用sql语句,即创建好的视图可以当做一张表来反复使用
      • 简化复杂的sql操作,不必知道它的查询细节
      • 保护数据,提高安全性
  • 视图的修改

    • 两种方式

      #方式一(若视图存在则修改,若不存在则创建)
      create or replace view 视图名
      as
      查询语句
      
      #例一
      CREATE OR REPLACE VIEW view2
      AS
      SELECT * FROM jobs;
      
      #方式二
       alter view 视图名
       as
       查询语句
      
      #例二
      ALTER VIEW view2
      AS
      SELECT * FROM jobs;
      
      
  • 视图的查看与删除

    • 查看视图

      DESC 视图名;
      
    • 删除视图

      DROP VIEW 视图名,视图名,....;
      
  • 视图的更新(还会更新原始表数据)

    • 插入

      INSERT INTO 视图名 VALUES(字段值,字段值...);
      
      
    • 修改

      UPDATE 视图名 SET 属性 =,... WHERE 条件
      
      
    • 删除

      DELETE FROM  视图名 WHERE 条件;
      
      
    • 注意:视图的可更新性和视图中查询的定义有关,以下类型的视图是不能更新的

      • 包含以下关键字的SQL语句:分组函数、distinct、group、having、union或union all
      • select中包含子查询
      • join
      • from 一个不能更新的视图
      • where 子句的子查询引用了from子句中的表
  • 视图与表的对比

视图
创建语法的关键字 create view create table
是否实际占用物理空间 否(只保存了SQL逻辑) 是(保存了数据)
使用 一般不能增删改 可以增删改

二、存储过程和函数

  • 变量

    • 系统变量

      • 概念:变量由系统提供,不是用户自定义,属于服务器层面

      • 分类

        • 全局变量
          • 作用域:服务器每次启动将为所有的全局变量赋初始值,针对于所有的会话(连接)有效,但是不能跨重启
        • 会话变量
          • 作用域:仅针对于当前会话(连接)有效
      • 语法

        1.查看所有的系统变量
        SHOW GLOBAL VARIABLES; #查看全局变量
        SHOW SESSION VARIABLES;  #查看会话变量
        SHOW GLOBAL|SESSION 】 VARIABLES;
        
        2.查看满足条件的部分系统变量
        SHOW GLOBAL|SESSION 】 VARIABLES LIKE '%char%';
        
        3.查看指定的某个系统变量的值
        SELECT @@GLOBAL|SESSION.变量名
        
        4.为某个系统变量赋值
        	方式一:
        	SET GLOBAL|SESSION 】系统变量 名 =;
        	方式二:
        	SET @@GLOBAL|SESSION.系统变量 名 =;
        
      • 注意:如果是全局变量,则需要加global,如果是会话级别,则需要加session,如果不写,则默认session;

    • 自定义变量

      • 用户变量

        • 作用域:针对于当前会话(连接)有效,同于会话变量。可以应用在任何地方(begin end里外皆可)

        • 语法

          1.声明并初始化
          	SET @用户变量名 = 值;
          	SET @用户变量名: = 值;
          	SELECT @用户变量名: = 值;
          
          2.赋值(更新用户变量的值)
          	方式一:通过setselect
          		SET @用户变量名 = 值;
          		SET @用户变量名: = 值;
          		SELECT @用户变量名: = 值;
          	方式二:通过 select into
          		select 字段名 into @变量名
          		from 表
          		例:SELECT COUNT(*) INTO @count
          			FROM employees;
          3.使用(查看用户变量的值)
          	select @用户变量名;
          
      • 局部变量

        • 作用域:仅仅在定义他的begin end中有效(应用在begin end 中的第一句话!!!)

        • 语法

          1.声明
          	declare 变量名 类型;
          	declare 变量名 类型 default; #附上初始化值
          2.赋值
          	方式一:通过setselect
          		SET 局部变量名 = 值;
          		SET 局部变量名: = 值;
          		SELECT @局部变量名: = 值;
          	方式二:通过 select into
          		select 字段名 into 变量名
          		from 表
          		例:SELECT COUNT(*) INTO count
          			FROM employees;
          3.使用
          	select 局部变量名;
          
      • 用户变量与局部变量对比

用户变量 局部变量
作用域 当前会话 begin end 中
定义和使用的位置 会话中的任何地方 只能在begin end 中,且为第一句话
语法 必须加@符号,不用限定类型 一般不用加@符号,需要限定类型
  • 存储过程和函数(类似java中的方法)

    • 存储过程
      • 含义:一组预先编译好的SQL语句的集合,理解成批处理语句

      • 优点:

        • 提高代码重用性
        • 简化操作
        • 减少了编译次数并且减少了和数据库服务器的连接次数,提高了效率
      • 语法

        1.创建语法
        	create procedure 存储过程名(参数列表)
        	begin
        		存储过程体(一组合法的SQL语句)
        	end
        2.调用语法
        	call 存储过程名(实参列表);
        
        
        案例:
        #创建
        	DELIMITER $
        	CREATE PROCEDURE myp1()
        	BEGIN
        		INSERT INTO admin(username,PASSWORD) 
        		VALUES('tom','7896'),('jack','7896'),('rose','7896'),('laoying','7896');
        	END $
        #调用
        	CALL myp1();
        
        ****************************IN模式的存储过程*****************************
        案例1
        #创建
        	#根据女生名,查询相应男生信息
        	CREATE PROCEDURE myp2(IN bname VARCHAR(20))
        	BEGIN
        		SELECT bo.* FROM boys bo
        		RIGHT JOIN beauty b ON bo.id = b.boyfriend_id
        		WHERE b.name = bname;
        	END$				
        #调用
        	CALL myp2('赵敏');
        
        #案例2
        #创建存储过程实现,用户是否登陆成功
        	DELIMITER $
        	CREATE PROCEDURE myp3(IN username VARCHAR(20),IN PASSWORD VARCHAR(20))
        	BEGIN
        		DECLARE result INT DEFAULT 0;#声明变量并初始化
        		
        		SELECT COUNT(*) INTO result #赋值
        		FROM admin 
        		WHERE admin.`username` = username
        		AND admin.`password` = PASSWORD;
        		
        		SELECT IF(result>0,'成功','失败');
        	END$
        	CALL myp3('tom','7896');
        
        
        ****************************out模式的存储过程*****************************
        案例1
        	#根据女生名返回对应的男生名
        	DELIMITER $
        	CREATE PROCEDURE myp4(IN bename VARCHAR(20),OUT boname VARCHAR(20))
        	BEGIN
        		SELECT bo.boyName INTO boname 
        		FROM boys bo
        		INNER JOIN beauty b ON  bo.id = b.boyfriend_id
        		WHERE b.name = bename;
        	END$
        	
        	CALL myp4('小昭',@bnames);
        	SELECT @bnames;
        案例二
        	#根据女生名返回对应的男生名和魅力值
        	DELIMITER $
        	CREATE PROCEDURE myp5(IN bename VARCHAR(20),OUT boname VARCHAR(20),OUT usercp INT)
        	BEGIN
        		SELECT bo.boyName,bo.userCP INTO boname,usercp
        		FROM boys bo
        		INNER JOIN beauty be ON bo.id = be.boyfriend_id
        		WHERE be.name = bename;
        	END$
        	
        	CALL myp5('赵敏',@boname,@usercp);
        	SELECT @boname,@usercp;
        	
        ****************************inout模式的存储过程*****************************
        DELIMITER $
        CREATE PROCEDURE myp6(INOUT a INT,INOUT b INT)
        BEGIN
        	SET a = a*2;
        	SET b = b*2;
        END$
        
        SET @m = 2;
        SET @n = 3;
        CALL myp6(@m,@n);
        SELECT @m,@n;
        
        • 注意:
          • 参数列表包含三部分:参数模式、参数名、参数类型
            • 举例:in stuname varchar(20)
            • 参数模式:
              • in(该参数可以作为输入,也就是该参数需要调用方法传入值)
              • out(该参数可以作为输出,也就是该参数可以作为返回值)
              • inout(该参数输出输出皆可)
          • 如果存储过程体仅仅只有一句话,begin end可省略
          • 存储过程体中的每条SQL语句的结尾要求必须加分号。
          • 存储过程的结尾可以用delimiter重新设置
            • 语法

              delimiter 结束标记
              
      • 存储过程的删除

        • 语法

          drop procedure 存储过程名; #只能一个一个删除
          
      • 存储过程的查看

        • 语法

          show create procedure 存储过程名;
          
    • 函数
      • 含义:一组预先编译好的SQL语句的集合,理解成批处理语句

      • 优点:

        • 提高代码重用性
        • 简化操作
        • 减少了编译次数并且减少了和数据库服务器的连接次数,提高了效率
      • 语法

        1.创建语法
        	create function 函数名(参数列表) returns 返回类型
        	begin
        		函数体
        	end
        	*注意:
        		1.参数列表包括两部分:	参数名、参数类型
        		2.函数体:肯定会有return语句,如果没有会报错		
        			如果return语句没有放在函数体的最后也不会报错,但不建议
        		3.函数体中仅有一句话,则可以省略 begin  end
        		4.使用delimiter语句设置结束标记
        2.调用语法
        	select 函数名(参数列表)
        
        	案例:
        	#返回公司的员工个数(无参)
        		DELIMITER $
        		CREATE FUNCTION myf1() RETURNS INT
        		BEGIN
        			DECLARE a INT DEFAULT 0;#定义变量
        			SELECT COUNT(*) INTO a#赋值 
        			FROM employees; 
        			RETURN a;
        		END$
        		SELECT myf1();
        	#根据部门名返回平均工资(有参)
        		DELIMITER $
        		CREATE FUNCTION myf3(dename VARCHAR(20)) RETURNS DOUBLE
        		BEGIN
        			DECLARE sal DOUBLE;
        			SELECT AVG(salary) INTO sal
        			FROM employees e
        			JOIN departments d ON e.department_id = d.department_id 
        			WHERE d.department_name = dename;
        			
        			RETURN sal;
        		END$
        		SELECT myf3('Exe');
        3.查看函数
        	show create function 函数名;
        4.删除函数
        	drop function 函数名;
        
      • 函数与存储过程的区别

存储过程 函数
返回值 可以有0个返回,也可以有多个返回 有且仅有1个返回
适用途径 批量插入、更新 处理数据后返回一个结果

三、流程控制结构

  • 顺序结构(程序从上往下依次执行)

  • 分支结构(程序从两条或多条路径中选择一条去执行)

    • if函数
      • 功能:能够实现简单的双分支

      • 语法:

        if(表达式1,表达式2,表达式3
      • 执行顺序:如果表达式1成立,则返回表达式2的值,不成立则返回表达式3的值

      • 应用:任何地方

    • case结构
      • 情况一:类似于java中的switch语句,一般用于实现的等值判断
        • 语法:

          case 变量|表达式|字段
          when 要判断的值 then 返回的值1或语句1when 要判断的值 then 返回的值2或语句2...
          else 要返回的值n或语句n;
          end case
      • 情况二:类似于java中的多重if语句,一般用于实现区间判断
        • 语法:

          case
          when 要判断的条件1 then 返回的值1或语句1when 要判断的条件2 then 返回的值2或语句2...
          else 要返回的值n或语句n;
          end case#案例
          	#创建存储过程,根据传入的成绩,来显示等级,比如传入的成绩:
          	#90-100显示A,80-90显示B,60-80显示C,否则显示D
          	
          	DELIMITER /
          	CREATE PROCEDURE test_case(IN score INT)
          	BEGIN
          		CASE
          		WHEN score>=90 AND score<=100 THEN SELECT 'A';
          		WHEN score>=80 THEN SELECT 'B';
          		WHEN score>=60 THEN SELECT 'C';
          		ELSE SELECT 'D';
          		END CASE;
          	END/
          	CALL test_case(87);
          
      • 特点:
        • 可以作为表达式,嵌套在其他语句中使用,可以放在任何地方,begin end中或begin end外面
        • 可以作为独立的语句去使用,只能放在begin end中
        • 如果when中的值满足或条件成立,则执行对应的then后面的语句,并且结束case。如果都不满足,则执行else中的语句或值
        • else可以省略,如果省略else,且所有when条件都不满足,则返回null
    • if结构
      • 功能:实现多重分支

      • 语法

        if 条件1 then 语句1else if 条件2 then 语句2...else 语句n;】
        end if;
        
        应用在 begin end#案例
        	#创建存储过程,根据传入的成绩,来显示等级,比如传入的成绩:
        	#90-100返回A,80-90返回B,60-80返回C,否则返回D
        	DELIMITER $
        	CREATE FUNCTION test_if(score INT) RETURNS CHAR
        	BEGIN
        		IF score >= 90 AND score <= 100 THEN RETURN 'A';
        		ELSEIF score >= 80 THEN RETURN 'B';
        		ELSEIF score >= 60 THEN RETURN 'C';
        		ELSE RETURN 'D';
        		END IF;
        	END$
        	
        	SELECT test_if(88);
        
  • 循环结构(程序在满足一定条件的基础上,重复执行一段代码)

    • 循环控制
      • iterate:类似于continue,继续,结束本此循环,继续下一次
      • leave:类似于break,跳出,结束当前循环
    • 分类
      • while
        • 语法:

          【标签:】while 循环条件 do
          	循环体;
          end while 【标签】;
          
          #案例1
          	#批量插入十条语句
          	DELIMITER $
          	CREATE PROCEDURE pro_while(IN insertcount INT)
          	BEGIN
          		DECLARE i INT DEFAULT 1;
          		WHILE i<=insertcount DO
          			INSERT INTO admin(username,PASSWORD) VALUES(CONCAT('Rose',i),'146');
          			SET i = i+1;
          		END WHILE;
          	END$
          	CALL pro_while(10);
          	
          #案例2:添加leave语句
          	#批量插入,根据次数插入到admin表中多条记录,如果次数>20则停止
          	DELIMITER $
          	CREATE PROCEDURE test_while(IN insertcount INT)
          	BEGIN
          		DECLARE i INT DEFAULT 1;
          		a:WHILE i<insertcount DO
          			INSERT INTO admin(username,PASSWORD) VALUES(CONCAT('Jack',i),'666');
          			IF i>=20 THEN LEAVE a;
          			END IF;
          			SET i = i+1;
          		END WHILE a;
          	END$
          	CALL test_while(50);
          #案例3:添加iterate语句
          	#
          
      • loop(可以用来模拟简单的死循环)
        • 语法:

          【标签:】loop
          	循环体;
          end loop【标签】;
          
      • repeat
        • 语法:

          【标签:】repeat
          	循环体;
          until 结束循环的条件
          end repeat【标签】;
          
      • 三者比较
while loop repeat
特点 先判断后执行 先执行后判断 没有条件的死循环
位置 begin end 中 begin end 中 begin end 中

四、MySQL架构介绍

  • MySQL存储引擎

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

五、索引优化查询★

  • 性能下降SQL查询慢

    • 原因
    1. 查询语句写的烂
    2. 索引失效
    3. 关联太多join
    4. 服务器调优及各个参数设置(缓冲、线程数等)
  • 常见的通用join查询

    • SQL执行顺序
      • 手写

           select 查询列表
           from1 别名 【连接类型】
           join2 别名
           on 连接条件
           【where 筛选条件】
           【group by分组】
           【having 筛选条件】
           【order by 排序列表】
           【limit 行数】
        
      • 机读

           from1 别名 【连接类型】
           on 连接条件
           join2 别名
           【where 筛选条件】
           【group by分组】
           【having 筛选条件】
           select 查询列表
           【order by 排序列表】
           【limit 行数】
        
      • 总结
        我,【MySQL】高级篇,一个让你的数据管家单车变摩托的 “关系型数据库”_第1张图片

    • Join图
      我,【MySQL】高级篇,一个让你的数据管家单车变摩托的 “关系型数据库”_第2张图片
      注意:MySQL不支持 full outer 语法,可改为联合查询(union联合加去重),Oracle支持
  • 索引简介

    • 是什么
      • MySQL对缩印的官方定义为:索引是帮助MySQL高效获取数据的数据结构(可以理解为排好序的快速查找数据结构),目的是为了提高查找效率。
        数据本身之外,数据库还维护着一个满足特定查找算法的数据结构,这些数据结构以某种方式指向数据,这样就可以在这些数据结构的基础上实现高级查找算法,这种数据结构就是索引 (b树索引)
      • 一般来说索引本身也很大,不可能全部储存在内存中,因此索引往往以索引文件形式存储在磁盘上
      • 我们平常所说的索引,如果没有特别指明,都是指B树(多路搜索树,并不一定是二叉的)结构组织的索引。其中聚集索引、次要索引、复合索引、前缀索引、唯一索引默认都是B+树索引,统称索引。当然,除了B+树这种类型的索引之外,还有哈希索引等
    • 优势
      • 提高数据检索的效率,降低了数据库的IO成本
      • 通过索引对数据进行排序,降低数据排序的成本,降低了CPU的消耗
    • 劣势
      • 实际上索引也是一张表,该表保存了主键和索引字段,并指向实体表的记录,所以索引也是要占空间的
      • 虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行insert、update和delete。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新带来的键值变化后的索引信息
    • mysql索引分类
      • 单值索引:即一个索引包含单个列,一个表可以有多个单列索引

      • 唯一索引:索引列的值必须唯一,但允许有空值

      • 复合索引:即一个索引包含多个列

      • 基本语法 :

        #创建
        createuniqueindex index_name on table_name(column_name(length));
        alter table table_name adduniqueindex 【index_name】 on (column_name(length));
        
        #删除
        drop index 【index_name】 on table_name;
        
        #查看
        show index from table_name\G
        
        #使用alert命令,有四种方式来添加数据表的索引
        	alert table table_name add primary key(column_list);#该语句添加一个主键,这意味着索引值必须是唯一的,且不能是null
        	alter table table_name add unique index_name(column_list);#该语句创建的索引值是唯一的(除了null以外,null可能会出现多次)
        	alter table table_name add index index_name(column_list);#添加普通索引,索引值可出现多次
        	alter table table_name add fulltext index_name(column_list);#该语句指定了索引为fulltext,用于全文索引
        
    • mysql索引结构
      • BTree索引★
      • Hash索引
      • Full-Text索引
      • R-Tree索引
    • 哪些情况需要创建索引
      • 主键自动建立唯一索引
      • 频繁作为查询条件的字段应该创建索引
      • 查询中与其他表关联的字段,外键关系建立索引
      • 频繁更新的字段不合适创建索引(因为每次更新不单单是更新了记录还会更新索引)
      • where条件里用不到的字段不创建索引
      • 单键/组合索引的选择问题,who?(在高并发下倾向创建组合索引)
      • 查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度
      • 查询中统计或分组字段
    • 哪些情况不需要创建索引
      • 表记录太少
      • 经常增删改的表
      • 数据重复且分布平均的字段,因此应该只为最经常查询和最经常排序的数据列建立索引
      • 注意:如果某个数据列包含许多重复内容,为他建立索引就没有太大实际效果
  • 性能分析

    • MySQLQuery Optimizer
    • MySQL常见瓶颈
      • CPU:CPU在饱和的时候一般发生在数据装入内存或从磁盘上读取数据的时候
      • IO:磁盘I/O瓶颈发生在装入数据远大于内存容量的时候
      • 服务器硬件的性能瓶颈:top、free、iostat和vmstat来查看系统的性能状态
    • Explain
      • 是什么(查看执行计划)
        使用Explain关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的。分析你的查询语句或是表结构的性能瓶颈
      • 能干嘛
        • 表的读取顺序
        • 数据读取操作的操作类型
        • 那些索引可以使用
        • 那些索引被实际使用
        • 表之间的引用
        • 每张表有多少行为被优化器查询
      • 怎么玩
        • Explain+SQL语句
        • 执行计划包含的信息
          在这里插入图片描述
      • 各字段解释
        • id★:select查询的序列号,包含一组数字,表示查询中select子句或操作表的顺序
          • 三种情况:
            • id相同,执行顺序由上至下
            • id不同,如果是子查询,id的序列号会递增,id值越大优先级越高,越先被执行
            • id相同和不同同时存在,id如果相同,可以认为是一组,从上往下顺序执行,在所有组中,id值越大,优先级越高,越先执行
            • 关键词:衍生 = derived
        • select_type:查询的类型,主要是用于区别普通查询、联合查询、子查询等的复杂查询
          • 分类:
            • simple:简单的select查询,查询中不包含子查询或union
            • primary:查询中若包含任何复杂的子部分,最外层查询则被标记为
            • subquery:在select或where列表中包含了子查询
            • derived:在from列表中包含的子查询被标记为derived(衍生),MySQL会递归执行这些子查询,把结果放在临时表里
            • union:若第二个select出现在union之后,则被标记为union;若union包含在from子句的子查询中,外层select将被标记为:derived
            • union result:从union表获取结果的select
        • table:显示这一行的数据是关于哪张表的
        • type★:显示查询使用了何种类型;从好到次依次是:system > const > eq_ref > ref > range > index > All
          • 访问类型排列(了解即可):system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > All
          • 常用类型:
            • system:表只有一行记录(等于系统表),这是const类型的特例,平时不会出现,这个也可以忽略不计
            • const:表示通过索引一次就找到了,const用于比较 primary key 或者unique索引。因为只匹配一行数据,所以很快;如将主键置于where表中,MySQL就能将该查询转换为一个常量
            • eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描
            • ref:非唯一性索引扫描,返回匹配某个单独值得所有行;本质上也是一种索引访问,他返回所有匹配某个单独值得行,然而,他可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体
            • range:只检索给定范围的行,使用一个索引来选择行,key列显示使用了哪个索引;一般就是在你的where语句中出现了 between、<、>、in 的查询;这种范围扫描索引扫描比全表扫描要好,因为它只需要开始于索引的某一点,而结束语另一点,不用扫描全部索引
            • index:Full Index Scan,index与All区别为index类型只遍历索引树。这通常比All快,因为索引文件通常比数据文件小。(也就是说虽然all和index都是读全表,但 index 是从索引中读取的,而 all 是从硬盘中读的)
            • All:Full Table Scan,将遍历全表以找到匹配的行
            • 备注:一般来说,得保证查询至少达到range级别,最好能达到ref;
        • possible_keys:显示可能应用在这张表中的索引,一个或多个;查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用
        • key★:实际使用的索引。如果为null,则没有使用索引;查询中若使用了覆盖索引,则该索引仅出现在key列表中
        • key_len:表示索引中使用的字节数,可以通过该列计算查询中使用的索引的长度。在不损失精确性的情况下,长度越短越好;key_len 显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的
        • ref:显示索引的哪一列被使用了,如果可能的话,是一个常数。哪些列或常量被用于查找索引列上的值
        • rows★:根据表统计信息及索引选用情况,大致估算出找到所需的记录所要读取的行数
        • Extra★:包含不适合在其他列中显示但十分重要的额外信息
          • Using filesort★:说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取;MySQL中无法利用索引完成的排序操作称为“文件内排序”;
          • Using temporary★:使用了临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于排序order by和分组查询group by;
          • Using index★:表示相应的select操作中使用了覆盖索引(Covering Index),避免访问了表的数据行,效率不错!如果同时出现using where,表明索引被用来执行索引键值的查找;如果没有同时出现using where,表明索引用来读取数据而非执行查找动作
          • Using where:表明使用了where过滤
          • using join buffer:使用了连接缓存
          • impossible where:where子句的值总是false,不能用来获取任何元组
          • select tables optimized away:在没有group 子句的情况下,基于索引优化 min/max操作或者对于MyISAM存储引擎优化Count(*)操作,不必等到执行阶段在进行计算,查询执行计划生成的阶段即完成优化
          • distinct:优化distinct操作,在找到第一匹配的元组后即停止找同样值得动作
        • partitions:版本5.7以前,该项是explain partitions显示的选项,5.7以后成为了默认选项。该列显示的为分区表命中的分区情况。非分区表该字段为空(null)。
        • filtered:使用explain extended时会出现这个列,5.7之后的版本默认就有这个字段,不需要使用explain extended了。这个字段表示存储引擎返回的数据在server层过滤后,剩下多少满足查询的记录数量的比例,注意是百分比,不是具体记录数。
  • 索引优化

    • 索引分析
      • 单表

        	* 建表SQLCREATE 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 VARBINARY(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'),(3,3,3,3,'3','3');
        		
        		#索引
        		CREATE INDEX idx_article_ccv ON article(category_id,comments,views);
        
        • 案例:

          #查询category_id为1且comments大于1的情况下,views最多的artice_id
          EXPLAIN 
          	SELECT id,author_id 
          	FROM article 
          	WHERE category_id=1 
          	AND comments>1 
          	ORDER BY views 
          	DESC LIMIT 1; 
          

          结果(未建索引之前)
          在这里插入图片描述
          结果(建索引之后)
          在这里插入图片描述
          很明显创建索引后,虽然解决了全表扫描的问题,却又多了文件内排序的问题;这是因为 comments>1 范围查询和order by 和group by会中断联合索引,导致索引失效;
          解决办法:删除ccv,创建cv

          DROP INDEX idx_article_ccv ON article;
          
          CREATE INDEX idx_article_cv ON article(category_id,views);
          

          再次查询,完美解决
          在这里插入图片描述

      • 两表

        	* 建表SQLCREATE 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 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));
        
        • 案例

          #左连接
          EXPLAIN 
          	SELECT * 
          	FROM class 
          	LEFT JOIN book ON class.card = book.card;
          

          结果:出现全表扫描(All)
          在这里插入图片描述
          解决:添加索引优化

          #加在右表上
          ALTER TABLE book ADD INDEX Y(card);
          #加在左表上
          ALTER TABLE class ADD INDEX Y(card);
          

          结果:

          • 右表在这里插入图片描述
          • 左表
            在这里插入图片描述
          • 结论:可以看到第一次在右表加索引的type变成了ref,rows也变成了优化比较明显;这是由左连接特性决定的。LEFT JOIN 条件用于确定如何从右表搜索行,左边一定有;所以右边是关键点,一定要建索引同理,右连接用于确定如何从左表搜索行,右边一定有,所以左边是我们的关键点,一定要建立索引
      • 三表

        	* 建表SQLCREATE 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));
        
        • 案例

          EXPLAIN 
          	SELECT * 
          	FROM class 
          	LEFT JOIN book ON class.card = book.card
          	LEFT JOIN phone ON class.card = phone.card;
          

          结果:(出现了连接缓存(using join buffer))
          在这里插入图片描述
          索引优化

          ALTER TABLE book ADD INDEX Y(card);
          
          ALTER TABLE phone ADD INDEX Z(card);
          

          结果
          在这里插入图片描述
          结论:很明显,后两行的type都是ref且总rows优化效果很好。因此索引最好经常设置在需要查询的字段中

      • Join语句的优化

        • 尽可能减少join语句中的NestedLoop的循环总次数;永远用小结果集驱动大结果集
        • 优先优化NestedLoop的内层循环
        • 保证Join语句中被驱动表上的Join条件字段已经被索引
        • 当无法保证被驱动表的Join条件字段被索引且内存资源充足的前提下,不要太吝啬JoinBuffer的设置
    • 索引失效(应该避免)
      • 建表SQL
        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,'manage',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());
        SELECT * FROM staffs
        
        ALTER TABLE staffs ADD INDEX idx_nameAgePos(NAME,age,pos);
        
      • 案例(索引失效)
        • 全值匹配

          #查询一
          EXPLAIN SELECT * FROM staffs WHERE NAME = 'July';
          #查询二
          EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND age = 23;
          #查询三
          EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND age = 23 AND pos = 'dev';
          #查询四
          EXPLAIN SELECT * FROM staffs WHERE age = 25 AND pos = 'dev';
          #查询五
          EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND pos = 'dev';
          

          结果
          在这里插入图片描述
          在这里插入图片描述
          在这里插入图片描述
          在这里插入图片描述
          在这里插入图片描述

        • ★最佳左前缀法则(若索引多列,要遵守最左前缀法则 ):

          • 开头大哥不能死,即从最左边开始创建的第一个索引字段不能丢失(查询四)
          • 中间兄弟不能断,即中间的索引字段也不能丢失(查询五)
        • 不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描

          EXPLAIN SELECT * FROM staffs WHERE LEFT(NAME,4) = 'July';
          

          在这里插入图片描述

        • 存储引擎不能使用索引中范围条件右边的列

          EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND age > 25 AND pos = 'dev';
          

          在这里插入图片描述

        • 尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select *;

          EXPLAIN SELECT NAME,age,pos FROM staffs WHERE NAME = 'July' AND age = 25 AND pos = 'dev';
          EXPLAIN SELECT NAME,age,pos FROM staffs WHERE NAME = 'July' AND age > 25 AND pos = 'dev';
          

          在这里插入图片描述在这里插入图片描述

        • mysql在使用不等于(!= 或者 <>)的时候无法使用索引会导致全表扫描

          EXPLAIN SELECT * FROM staffs WHERE NAME != 'July';
          

          在这里插入图片描述

        • is null,is not null也无法使用索引

          EXPLAIN SELECT * FROM staffs WHERE NAME IS NULL;
          EXPLAIN SELECT * FROM staffs WHERE NAME IS NOT NULL;
          

          在这里插入图片描述
          在这里插入图片描述

        • like以通配符开头(‘%abc…’)mysql索引失效会变成全表扫描的操作

          EXPLAIN SELECT * FROM staffs WHERE NAME LIKE '%July%';
          EXPLAIN SELECT * FROM staffs WHERE NAME LIKE '%July';
          EXPLAIN SELECT * FROM staffs WHERE NAME LIKE 'July%';
          

          在这里插入图片描述
          在这里插入图片描述
          在这里插入图片描述
          很明显,当%在右边时才能够用上索引,但是这也存在这一个问题就是只能查询以July开头的数据,局限性太大

          • 问题:解决like ‘%字符串%’ 时索引不被使用的方法(覆盖索引)

            #建表SQL
            CREATE TABLE tbl_user(
            	id INT(11) NOT NULL AUTO_INCREMENT,
            	NAME VARCHAR(24) 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('2aa2',22,'[email protected]');
            INSERT INTO tbl_user(NAME,age,email) VALUES('3aa3',23,'[email protected]');
            INSERT INTO tbl_user(NAME,age,email) VALUES('4aa4',24,'[email protected]');
            #建索引
            CREATE INDEX idx_user_nameAge ON tbl_user(NAME,age);
            
            EXPLAIN SELECT NAME,age 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 FROM tbl_user WHERE NAME LIKE '%aa%';
            EXPLAIN SELECT id,NAME,age FROM tbl_user WHERE NAME LIKE '%aa%';
            EXPLAIN SELECT id,age FROM tbl_user WHERE NAME LIKE '%aa%';
            EXPLAIN SELECT id,NAME FROM tbl_user WHERE NAME LIKE '%aa%';
            

            建索引之前在这里插入图片描述
            建索引之后
            在这里插入图片描述

        • 字符串不加单引号索引失效(会引起类型转换)

          EXPLAIN SELECT * FROM staffs WHERE NAME = '2000'
          

          在这里插入图片描述

          EXPLAIN SELECT * FROM staffs WHERE NAME = 2000
          

          在这里插入图片描述

        • 少用or,用它来连接时会索引失效(8.0显示type是range)

          EXPLAIN SELECT * FROM staffs WHERE NAME = '2000' OR NAME = 'July'
          

          在这里插入图片描述

        • 总结

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 a = 3 and c = 5 使用到a,但是c不可以,b中间断了
where a = 3 and b > 4 and c = 5 能使用到a、b,c 在范围之后不能使用
where a = 3 and b like ‘kk%’ and c = 4 是, a、b、c都能用
  • 一般性建议

    • 对于单键索引,尽量选择针对当前query过滤性更好的索引。

    • 在选择组合索引时,当前Query中过滤性最好的字段在索引字段顺序中,位置越靠前越好。

    • 在选择组合索引的时候,尽量选择可以能够包含当前query中的where子句中更多字段的索引

    • 尽可能通过分析统计信息和调整query的写法来达到选择合适索引的目的

    • 总结口诀:

      全职匹配我最爱,最左前缀要遵守
      带头大哥不能死,中间兄弟不能断
      索引列上少计算,范围之后全失效
      Like百分写最右,覆盖索引不写星
      不等空值还有or,索引失效要少用

六、查询截取分析

  • 查询优化

    • 永远小表驱动大表(类似嵌套循环NestedLoop)
    • order by 关键字优化
      • order by子句,尽量使用index方式排序,避免使用FileSort方式进行
        • 建表SQL

          CREATE TABLE tblA(
          	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());
          
          CREATE INDEX idx_A_age_birth ON tblA(age,birth);
          
        • case

          • case1:

            EXPLAIN SELECT * FROM tblA WHERE age > 20 ORDER BY age;
            EXPLAIN SELECT * FROM tblA WHERE age > 20 ORDER BY age,birth;
            

            在这里插入图片描述

            EXPLAIN SELECT * FROM tblA WHERE age > 20 ORDER BY birth;
            EXPLAIN SELECT * FROM tblA WHERE age > 20 ORDER BY birth,age;
            

            在这里插入图片描述

          • case2

            EXPLAIN SELECT * FROM tblA ORDER BY birth;
            EXPLAIN SELECT * FROM tblA ORDER BY age ASC,birth DESC;
            

            在这里插入图片描述

        • MySQL支持两种方式排序,filesort和index,index效率高,它指MySQL扫描索引本身完成排序。filesort方式效率低

        • order by满足两种情况,会使用index排序:

          • order by语句使用索引最左前列
          • 使用where子句与order by子句条件列组合满足索引最左前列
      • 尽可能在索引列上完成排序操作,遵照索引建的最佳左前缀
      • 如果不在索引列上,filesort有两种算法;MySQL就要启动双路排序和单路排序
        • 双路排序:
          • mysql4.1之前是使用双路排序,字面意思是两次扫描磁盘,最终取得数据;读取行指针和order by列,对他们进行排序,然后扫描已经排好序的列表,按照列表中的值重新从列表中读取对应的数据输出
          • 从磁盘取排序字段,在buffer进行排序,再从磁盘取其他字段
          • 缺点:取一批数据,要对磁盘进行两次扫描,众所周知,I/O是很好时的,所以在mysql4.1之后,拥有了单路排序
        • 单路排序:
          • 从磁盘读取查询需要的所有列,按照order by列在buffer对他们进行排序,然后扫描排序后的列表进行输出,他的效率更快一些,避免了第二次读取数据。并且把随机I/O变成了顺序I/O,但是它会使用更多的空间,因为它把每一行都保存在内存之中了
        • 结论及引申出的问题:
          • 由于单路是后出的,总体而言好过双路
          • 但是用单路有问题
            • 在sort_buffer中,方法B比方法A要多占很多空间,因为方法B是把所有字段都取出,所有可能取出的数据的总大小超过了sort_buffer的容量,导致每次只能取sort_buffer容量大小的数据,进行排序(创建tmp文件,多路合并),排完再取sort_buffer容量大小的数据…… 从而多次I/O
      • 优化策略
        • 增大sort_buffer_size参数的设置
        • 增大max_length_for_sort_data参数的设置
      • 小总结
        • mysql两种排序方式:文件排序或扫描有序索引排序

        • mysql能为排序和查询使用相同的索引

        • order by能使用索引最左前缀

        • 如果where使用索引的最左前缀定义为常量,则order by能使用索引(where a = 1 order by b,c)

        • 不能使用索引进行排序的情况

          index:key a_b_c(a,b,c)
          
          #排序不一致
          order by a asc,b desc,c desc
          #丢失a索引
          order by b,c
          #丢失b索引
          order by a,c
          #d不是索引的一部分
          order by a,d
          #对于排序来说,多个相等条件也是范围查询
          where a in(...) order by b,c
          
    • group by 关键字优化
      • group by 实质是先排序后进行分组,遵照索引建的最佳左前缀
      • 当无法使用索引列,增大max_length_for_sort_data参数的设置+增大sort_buffer_size参数的设置
      • where高于having,能写在where限定的条件就不要去having限定了
      • 其他与order by趋同
  • 慢查询日志

    • 是什么?
      • MySQL的慢查询日志是MySQL提供的一种日志记录,他用来记录在MySQL中响应时间超过阈值的语句,具体指运行时间超过long_query_time值得SQL,则会被记录查询到慢查询日志中
      • 具体指运行时间超过long_query_time值得SQL,则会被记录到慢查询日志中去。long_query_time的默认值为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 = 1
        slow_query_log_file=/usr/local/mysql/data/localhost-slow.log

      • 查看low_query_time 参数:show variables like 'long_query_time%';
        在这里插入图片描述

        • 注意:在mysql源码中是判断大于low_query_time ,而非大于等于
      • 设置阈值时间:set global low_query_time = 3;
        在这里插入图片描述

        • 注意:修改时候再次查看,依然是10;这是因为需要重新连接或新开一个会话才能看到修改值;
          在这里插入图片描述
      • 查询当前系统有多少条慢查询记录:show global status like '%slow_queries%';
        在这里插入图片描述

  • 批量数据脚本

    • 建表
      #建表dept
      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;
      
      #建表emp
      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;
      
    • 设置参数log_bin_trust_function_creators
      • 由于开启过慢查询日志,开启了 bin-log,我们需要为我们的function制定一个参数

        show variables like 'log_bin_trust_function_creators';
        set global log_bin_trust_function_creators = 1;
        
        
        #永久性开启
        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 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
      	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$
      
      #随机产生部门编号
      DELIMITER $
      CREATE FUNCTION rand_num() RETURNS INT(5)
      BEGIN
      	DECLARE i INT DEFAULT 0;
      	SET i = FLOOR(100+RAND()*10);
      	RETURN i;	
      END$
      
    • 创建存储过程
      #创建往emp表中插入数据的存储过程
      DELIMITER $
      CREATE PROCEDURE insert_emp(IN START INT(10),IN max_num INT(10))
      BEGIN
      	DECLARE i INT DEFAULT 0;
      	SET autocommit = 0;#自动提交设置为0
      	
      	REPEAT
      	SET i = i + 1;
      	INSERT INTO emp(empno,ename,job,mgr,hiredate,sal,comm,deptno)
      	VALUES((START+1),rand_string(6),'salesman',0001,CURDATE(),2000,400,rand_num());
      	UNTIL i = max_num
      	END REPEAT;
      	COMMIT;
      END $
      
      
      
      #创建往dept表中插入数据的存储过程
      DELIMITER $
      CREATE PROCEDURE insert_dept(IN START INT(10),IN max_num INT(10))
      BEGIN
      	DECLARE i INT DEFAULT 0;
      	SET autocommit = 0;#自动提交设置为0
      	
      	REPEAT
      	SET i = i + 1;
      	INSERT INTO dept(deptno,dname,loc)
      	VALUES((START+1),rand_string(10),rand_string(8));
      	UNTIL i = max_num
      	END REPEAT;
      	COMMIT;
      END $
      
    • 调用存储过程
      CALL insert_dept(100,10);
      CALL insert_emp(100,10);
      
  • Show Profile

    • 是什么
      • 是mysql提供可以用来分析当前会话中语句执行的资源消耗情况。可以用于SQL的调优的测量
      • 默认情况下参数属于关闭状态,并保存最近15次的运行结果
    • 分析步骤
      • 是否支持,查看当前mysql版本:show variables like 'profiling';
      • 开启功能,默认是关闭:set profiling = on;
      • 运行SQL
      • 查看结果:show profiles
      • 诊断SQL,show profile cpu,block io for query + 上一步前面的问题SQL数字号码
      • 相对较重要状态
        • converting HEAP to MyISAM 查询结果太大,内存都不够用了往磁盘上搬了
        • createing tmp table 创建临时表
          • 拷贝数据到临时表
          • 用完再删除
        • copying to tmp table on disk:把内存中临时表复制到磁盘,危险!!
        • locked
  • 全局查询日志(仅限测试环境)

    • 配置启用
      • 在 my.cnf中,设置如下:

        #开启
        general_log = 1
        #记录日志文件的路径
        general_log_file = /path/logfile
        #输出格式
        log_output = file
        
    • 编码启用
      • 命令

        set global general_log = 1;
        set global log_output = 'table';
        
        #此后,你所编写的SQL语句,将会记录到mysql库里的general_log表,可以用下列的命令查看
        select * from mysql.general_log;
        

七、MySQL锁机制

  • 概述

    • 定义
      锁是计算机协调多个进程或线程并发访问某一资源的机制
    • 锁的分类
      • 从对数据库操作的类型(读/写)分
        • 读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会相互影响
        • 写锁(排它锁):当前写操作没有完成前,它会阻断其他写锁和读锁
      • 从对数据操作的粒度分
        • 表锁
        • 行锁
  • 三锁(开销、加锁速度、死锁、粒度、并发性能只能就具体应用的特点来说哪种锁更合适)

    • 表锁(偏读)
      • 特点
        • 偏向MyISAM存储引擎,开销小,加锁快;无死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低
      • 案例分析
        • 建表SQL

          CREATE TABLE mylock(
          	id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
          	NAME VARCHAR(20)
          )ENGINE MYISAM;
          
          
          INSERT INTO mylock(NAME) VALUES('a');
          INSERT INTO mylock(NAME) VALUES('b');
          INSERT INTO mylock(NAME) VALUES('c');
          INSERT INTO mylock(NAME) VALUES('d');
          INSERT INTO mylock(NAME) VALUES('e');
          
          SELECT * FROM mylock;
          
          SHOW OPEN TABLES;#查看表上加过的锁
          
          格式:LOCK TABLE 表名1 readwrite),表名2 readwrite),...;
          LOCK TABLE mylock READ,book WRITE;
          
          unlock tables;#解除所有锁
          

          在这里插入图片描述
          在这里插入图片描述

        • 加读锁

          LOCK TABLE mylock READ;
          
          #session1表示终端、session2代表连接终端的客户端
          session1                      			session2
          获得表的read锁定							连接终端
          当前session可以查询该表记录				其他session也可以查询该表的记录
          当前session不能查询其他没有锁定的表			其他session可以查询或更新未锁定的表
          当前session插入或者更新锁定的表都会提示错误	其他session插入或者更新锁定表会一直等待获得锁
          释放锁									获得锁,插入操作完成
          
        • 加写锁

          LOCK TABLE mylock WRITE;
          
          session1				session2
          可以读当前表				不可以查询当前表,阻塞
          可以修改当前表			不可以修改当前表,阻塞
          不能查询其他表,报错
          
      • 案例结论:总之,就是读锁会阻塞写,但不会阻塞读;而写锁会把读写都阻塞
      • 表锁分析:SHOW STATUS LIKE 'table%';
        在这里插入图片描述
        Table_locks_immediate:产生表级锁定的次数,表示可以立即获取锁的查询次数,每立即获取锁值加1;
        Table_locks_waited:出现表级锁定争用而发生等待的次数(不能立即获取锁的次数,每等待一次锁值加1),此值高则说明存在着较严重的表级锁争用情况
        此外,MyISAM的读写锁调度是写优先,这也是MyISAM不适合做写为主表的引擎。因为写锁后,其他线程不能做任何操作,大量的更新会使查询很难得到锁,从而造成永久阻塞
    • 行锁(偏写)
      • 特点:

        • 偏向InnoDB存储引擎,开销大,加锁慢;会出现死锁;锁定粒度小,发生锁冲突的概率最低,并发度也高
        • InnoDB与MyISAM的最大不同之处有两点:一是支持事务;二是采用了行级锁
      • 案例分析

        #行锁定基本演示
        session1						session2
        set autocommit = 0				set autocommit = 0
        更新但是不提交,没有手写commit		被阻塞,只能等
        提交更新							解除阻塞,更新正常进行
        								commit提交	
        
      • 结论

        • InnoDB存储引擎由于实现了行级锁定,虽然在锁定机制的实现方面所带来的性能损耗可能比表级锁定会要更高一些,但是在整体并发处理能力方面要远远优于MyISAM的表级锁定的。当系统并发量较高的时候,InnoDB的整体性能相比MyISAM就会有比较明显的优势
        • 但是,InnoDB的行级锁定同样也有其脆弱的一面,当我们使用不当时,可能会让InnoDB的整体性能表现不仅不能比MyISAM高,甚至可能会更差
      • 优化建议

        • 尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁
        • 合理设计索引,尽量缩小锁的范围
        • 尽可能较少检索条件,避免间隙锁
        • 尽量控制事务大小,减少锁定资源量和时间长度
        • 尽可能低级别事务隔离
    • 页锁(了解即可)
      开销和加锁时间介于表锁和行锁之间;会出现死锁;锁定粒度介于表锁和行锁之间,并发度一般

八、主从复制

  • 复制的基本原理

    • slave会从master读取binlog来进行数据同步
    • 三步骤+原理图
      • master将改变记录到二进制日志(binary log)。这些记录过程叫做二进制事件,binary log events
      • salve将master的binary log events拷贝到它的中继日志(relay log);
      • slave 重做中继日志中的事件,将改变应用到自己的数据库中。mysql复制是异步的且串行化的
        我,【MySQL】高级篇,一个让你的数据管家单车变摩托的 “关系型数据库”_第3张图片
  • 复制的基本原则

    • 每个slave只有一个master
    • 每个slave只能有一个唯一的服务器ID
    • 每个master可以有多个salve
  • 复制的最大问题:延时

  • 一主一从常见配置

    • mysql版本一致且后台以服务运行

    • 主从都配置在【mysqld】结点下,都是小写

    • 主机修改my.ini配置文件(Windows)

      • 【必须】主服务器唯一ID:server-id = 1
      • 【必须】启动二进制日志
        • log-bin = mysql本地的路径/mysqlbin
      • 【可选】启动错误日志
        • log-err = mysql本地的路径/mysqlerr
      • 【可选】根目录
        • basedir = "mysql本地的路径/"
      • 【可选】临时目录
        • tmpdir = "mysql本地的路径/"
      • 【可选】数据目录
        • datadir = "mysql本地的路径/Data/"
      • read-only = 0:主机,读写都可以
      • 【可选】设置不要复制的数据库
        • binlog-ignore-db = 库名
      • 【可选】设置可以复制的数据库
        • binlog-do-db = 需要复制的主数据库的名字
    • 从机修改my.cnf配置文件(Linux)

      • 【必须】从服务器唯一ID:server-id = 2
      • 【可选】启用二进制文件
    • 因修改过配置文件,mysql需重启服务

    • 主从机皆关闭防火墙

    • 在Windows主机上建立账户并授权salve

      • grant replication slave on *.* to 'zhangsan'@'从机数据库IP' identified by '123456';
      • flush privileges;
      • 查询master的状态
        • show master status;
        • 记录下File和Position的值
      • 执行完此步骤后不要再操作主服务器MySQL,防止主服务器状态值变化
    • 在Linux从机上配置需要复制的主机

      change master to master_host = ‘主机IP’,
      master_user = ‘zhangsan’,
      master_password = ‘123456’,
      master_log_file = ‘mysqlbin.具体数字’,
      master_log_pos = Position数字;

      • 启动从服务器复制功能:start slave;
      • show slave status\G
        • 下面两个参数都是yes,这说明主从配置成功
        • Slave_IO_Running:Yes
        • Slave_SQL_Running:Yes
    • 主机新建库、新建表、insert记录、从机复制

    • 如何停止从服务复制功能:stop slave;

你可能感兴趣的:(Java,数据库,mysql,索引)