MySQL高级——数据库优化(下)

文章目录

  • 三、查询截取分析
    • 查询优化
      • 永远小表驱动大表
      • order by 关键字优化
        • order by子句,尽量使用Index方式排列,避免使用FileSort方式排列
          • 建表SQL
          • Case
        • 如果不在索引列上,mysql进行的filesort有两种算法:双路排序和单路排序
          • 双路排序
          • 单路排序
          • 结论及引申出的问题
        • 单路排序的优化策略
        • 小总结
      • group by 关键字优化
    • 慢查询日志
      • 是什么
      • 怎么玩
        • 说明
        • 查看是否开启及如何开启
        • 开启后什么样的SQL会被记录到慢查询日志里
        • Case
        • 配置版
      • 日志分析工具mysqldumpslow
    • 批量数据脚本
    • Show Profile
      • 分析步骤
    • 全局查询日志
  • 四、MySQL锁机制
    • 概述
      • 定义
      • 生活购物库存问题
      • 锁的分类
    • 三锁
      • 表锁(偏读)
        • 特点
        • 案例分析
        • 案例结论
        • 表锁分析
      • 行锁(偏写)
        • 特点
        • 复习老知识——事务
          • 事务及其ACID属性
          • 并发事务处理带来的问题
          • 事务隔离级别
        • 案例分析
        • 间隙锁的危害
        • 面试题(常考):如何锁定一行
        • 案例结论
        • 行锁分析
        • 优化建议
      • 页锁
  • 五、主从复制
    • 复制的基本原理
      • 三步骤+原理图
    • 复制的基本原则
    • 复制的最大问题——延时
    • 一主一从常见配置
      • 主机配置修改
      • 从机配置修改
      • 主机从机都重启mysql
      • 主机从机关闭防火墙
      • 授权配置

三、查询截取分析

MySQL高级——数据库优化(下)_第1张图片

查询优化

永远小表驱动大表

MySQL高级——数据库优化(下)_第2张图片
MySQL高级——数据库优化(下)_第3张图片
括号里的结果集小的时候用in,反之用exists。
in返回的是数据,而exists返回true或false。
MySQL高级——数据库优化(下)_第4张图片
MySQL高级——数据库优化(下)_第5张图片

order by 关键字优化

order by子句,尽量使用Index方式排列,避免使用FileSort方式排列
建表SQL
CREATE TABLE `tblA`(
	# 只有age和birth字段
	#`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());

# 创建索引
CREATE INDEX idx_A_ageBirth ON tblA(age,birth);

SELECT * FROM tblA;
Case

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里因为age为范围查找,所以复合索引到不了birth,如果age等于固定值查找,birth就可以用到复合索引。
在这里插入图片描述
这里排序的字段没按照顺序,所以排序的两个字段都用不到这个复合索引。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
倒序排序使得用不上前一个字段的排序,从而进行文件内排序。(都是正序或者都是倒序的即可使用)
在这里插入图片描述
在这里插入图片描述

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

MySQL41之前是使用双路排序,字面意思就是两次扫描磁盘,最终得到数据,读取行指针和order by列,对他们进行排序,然后扫描已经排序好的列表,按照列表中的值重新从列表中读取对应的数据输出。
从磁盘取排序字段,在buffer进行排序,再从磁盘取其他字段。
取一批数据,要对磁盘进行了两次扫描,众所周知,I/O是很耗时的,以在mysq4.1之后,出现了第二种改进的算法,就是单路排序。

单路排序

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

结论及引申出的问题

MySQL高级——数据库优化(下)_第6张图片
在这里插入图片描述

单路排序的优化策略

MySQL高级——数据库优化(下)_第7张图片

小总结

MySQL高级——数据库优化(下)_第8张图片

group by 关键字优化

group by 的优化与 order by 的优化类似。
在这里插入图片描述

慢查询日志

是什么

MySQL高级——数据库优化(下)_第9张图片

怎么玩

说明

在这里插入图片描述

查看是否开启及如何开启

查看:SHOW VARIABLES LIKE '%slow_query_log%';(默认关闭)
MySQL高级——数据库优化(下)_第10张图片
开启:SET GLOBAL slow_query_log=1;(重启mysql就会失效)
在这里插入图片描述
MySQL高级——数据库优化(下)_第11张图片

开启后什么样的SQL会被记录到慢查询日志里

慢于指定时间的sql就会被记录到慢查询日志中,阈值是参数long_query_time,默认为超过10秒算慢查询。
MySQL高级——数据库优化(下)_第12张图片

Case

查看慢查询日志的阈值:SHOW VARIABLES LIKE 'long_query_time%';(默认10秒)
MySQL高级——数据库优化(下)_第13张图片
设置阈值时间:SET GLOBAL long_query_time=3;
MySQL高级——数据库优化(下)_第14张图片
为什么设置后还是显示10秒?
MySQL高级——数据库优化(下)_第15张图片
其实已经生效了,只是还没显示出来,加上GLOBAL关键字再去查询即可。

查看:SHOW GLOBAL VARIABLES LIKE 'long_query_time%';
MySQL高级——数据库优化(下)_第16张图片
记录慢SQL并后续分析
MySQL高级——数据库优化(下)_第17张图片
MySQL高级——数据库优化(下)_第18张图片
MySQL高级——数据库优化(下)_第19张图片
查看当前系统中有慢查询记录数:SHOW GLOBAL STATUS LIKE '%Slow_queries%';
MySQL高级——数据库优化(下)_第20张图片

配置版

MySQL高级——数据库优化(下)_第21张图片

日志分析工具mysqldumpslow

查看mysqldumpslow的帮助信息:mysqldumpslow --help
MySQL高级——数据库优化(下)_第22张图片
MySQL高级——数据库优化(下)_第23张图片
工作常用参考
MySQL高级——数据库优化(下)_第24张图片
mysqldumpslow -s r -t 10 /var/lib/mysql/54e3aba6888e-slow.log
MySQL高级——数据库优化(下)_第25张图片

批量数据脚本

往表里插入1000万条数据

  1. 建表

    	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=GBK;
    
    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=GBK;
    
  2. 设置参数log_bin_trust_function_creators
    MySQL高级——数据库优化(下)_第26张图片
    查看:SHOW VARIABLES LIKE 'log_bin_trust_function_creators';
    MySQL高级——数据库优化(下)_第27张图片
    修改参数:SET GLOBAL log_bin_trust_function_creators=1;
    MySQL高级——数据库优化(下)_第28张图片

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

    1. 随机产生字符串
      # DELIMITER:修改sql语句的结束符号,这里把 ; 改为 $$
      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 ;	#把结束符号修改回 ;
      DELIMITER ;
      
      # 删除语句
      #drop function 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 $$
      DELIMITER ;
      
      # 删除语句
      drop function rand_num;
      
  4. 创建存储过程

    1. 创建往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 $$
      DELIMITER ;
      
      # 删除语句
      drop procedure insert_emp;
      
    2. 创建往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 $$
      DELIMITER ;
      
      # 删除语句
      drop procedure insert_dept;
      
  5. 调用存储过程

    1. 调用存储过程 insert_dept:CALL insert_dept(100,10);
      往emp表中插入10条数据,deptno从100开始。
      MySQL高级——数据库优化(下)_第29张图片
    2. 调用存储过程 insert_emp:CALL insert_emp(100001,500000);
      往emp表中插入50万条数据,empno从100001开始。
      在这里插入图片描述
      MySQL高级——数据库优化(下)_第30张图片

Show Profile

在这里插入图片描述

分析步骤

  1. 是否支持,看看当前的mysql版本是否支持
    查看:SHOW VARIABLES LIKE 'profiling';
    MySQL高级——数据库优化(下)_第31张图片

  2. 开启功能,默认关闭,使用前需要开启
    开启:SET PROFILing=on;
    MySQL高级——数据库优化(下)_第32张图片

  3. 运行SQL
    select * from emp group by id%10 limit 150000;
    MySQL高级——数据库优化(下)_第33张图片
    select * from emp group by id%20 order by 5;
    MySQL高级——数据库优化(下)_第34张图片

  4. 查看结果:show profiles;
    在这里插入图片描述

  5. 诊断SQL,show profile cpu,block io for query 【使用show profiles查出的Query_Id】;
    MySQL高级——数据库优化(下)_第35张图片
    这里显示的和下图老师显示的不同,并没有出现Copying to tmp table,而是executing
    MySQL高级——数据库优化(下)_第36张图片
    可用参数:
    MySQL高级——数据库优化(下)_第37张图片上面的命令用到了CPU和BLOCK IO两个参数。

  6. 日常开发需要注意的结论
    MySQL高级——数据库优化(下)_第38张图片
    MySQL高级——数据库优化(下)_第39张图片

全局查询日志

全局查询日志:开启后保存所有输入SQL语句,保存到mysql库里的general_log表中(安装mysql就有的)。

  1. 配置启用:
    MySQL高级——数据库优化(下)_第40张图片
  2. 编码启用:
    MySQL高级——数据库优化(下)_第41张图片
    set global general_log=1;
    set global log_output='TABLE';
    select * from mysql.general_log;
    MySQL高级——数据库优化(下)_第42张图片

只能在测试环境下使用,永远不要在生产环境开启这个功能(全局查询日志),因为开启后会把所有输入的sql语句保存到表里,这样会降低性能。

四、MySQL锁机制

概述

定义

在这里插入图片描述

生活购物库存问题

MySQL高级——数据库优化(下)_第43张图片

锁的分类

  1. 从对数据操作的类型(读\写)分:读锁、写锁
    在这里插入图片描述
  2. 从对数据操作的粒度分:表锁、行锁

三锁

MySQL高级——数据库优化(下)_第44张图片

表锁(偏读)

特点

在这里插入图片描述

案例分析
  1. 建表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 表名 read(write), 表名2 read(write), 其它;
    
    # 释放表锁
    unlock tables;
    

    MySQL高级——数据库优化(下)_第45张图片
    在这里插入图片描述

  2. 加读锁
    读锁可以共享读操作,即当前用户和其他用户都可以进行读操作,但是当前用户不能修改当前表,并且此时不能读其他表,而其他用户对当前表进行写操作时会进入阻塞状态,等待加了读锁的用户释放锁后才能进行写操作。
    MySQL高级——数据库优化(下)_第46张图片
    MySQL高级——数据库优化(下)_第47张图片
    MySQL高级——数据库优化(下)_第48张图片
    窗口1给mylock表加了表锁,可读,但不可写,并且不可读其他表。
    窗口2可以读加了表锁的mylock表,并且可以读其他表,但是想写mylock表时,会进入阻塞状态,等待窗口1释放锁后进行写操作。
    在这里插入图片描述
    窗口1解锁:unlock tables;
    窗口1解锁后,窗口2写操作成功。
    MySQL高级——数据库优化(下)_第49张图片

  3. 加写锁
    写锁为独享锁,加了写锁的表只能由加了锁的用户进行读写,但此用户不能对其他表进行操作,其他用户对其进行读写操作时,都会进入阻塞状态。
    MySQL高级——数据库优化(下)_第50张图片
    MySQL高级——数据库优化(下)_第51张图片
    MySQL高级——数据库优化(下)_第52张图片
    窗口1解锁后,窗口2完成读操作。
    MySQL高级——数据库优化(下)_第53张图片

案例结论

MySQL高级——数据库优化(下)_第54张图片
MySQL高级——数据库优化(下)_第55张图片

表锁分析

MySQL高级——数据库优化(下)_第56张图片
show status like 'table%';
MySQL高级——数据库优化(下)_第57张图片
MySQL高级——数据库优化(下)_第58张图片

行锁(偏写)

特点

在这里插入图片描述

复习老知识——事务

MySQL高级——数据库优化(下)_第59张图片

事务及其ACID属性

MySQL高级——数据库优化(下)_第60张图片

个人的记忆小技巧:原子一致,才能隔离持久。(对应ACID的顺序)

并发事务处理带来的问题
  1. 更新丢失
    MySQL高级——数据库优化(下)_第61张图片
    事务A和事务B同时进行修改,读取的数据都一样,事务A先一步完成修改提交后,事务B的提交的数据覆盖事务A已经修改了的数据,造成事务A更新的丢失。

  2. 脏读
    MySQL高级——数据库优化(下)_第62张图片
    事务A修改了数据但还未提交时,事务B读取到事务A修改了但未提交的数据,并进行了数据库上的操作,此时事务A出现问题,进行了回滚,造成事务B脏读。

  3. 不可重复读
    在这里插入图片描述
    事务A读取了某数据后,事务B进行了修改或者删除,之后事务A再次读取此数据时,发现此数据发生了改变或已被删除,此为不可重复读。

  4. 幻读
    MySQL高级——数据库优化(下)_第63张图片
    事务A按照某查询条件读取数据后,事务B插入了满足此查询条件的数据,之后事务A再次按照此查询条件读取数据,发现多了或少了某些数据,此为幻读。

事务隔离级别

MySQL高级——数据库优化(下)_第64张图片

查看隔离级别:
mysql5:show variables like 'tx_isolation';
mysql8:select @@transaction_isolation;
MySQL高级——数据库优化(下)_第65张图片
默认为可重复读(rr)。

案例分析
  1. 建表SQL

    create table test_innodb_lock(a int(11),b varchar(16))engine=innodb;
    
    insert into test_innodb_lock values(1,'b2');
    insert into test_innodb_lock values(3,'3');
    insert into test_innodb_lock values(4,'4000');
    insert into test_innodb_lock values(5,'5000');
    insert into test_innodb_lock values(6,'6000');
    insert into test_innodb_lock values(7,'7000');
    insert into test_innodb_lock values(8,'8000');
    insert into test_innodb_lock values(9,'9000');
    insert into test_innodb_lock values(1,'b1');
    
    # 创建索引,读写时自动加上行锁
    create index test_innodb_a_ind on test_innodb_lock(a);
    create index test_innodb_lock_b_ind on test_innodb_lock(b);
    
    select * from test_innodb_lock;
    
  2. 行锁定基本演示
    MySQL高级——数据库优化(下)_第66张图片
    关闭窗口1和窗口2的自动提交:set autocommit=0;
    在这里插入图片描述
    当关闭自动提交后,窗口1修改了数据,但未提交,此时窗口2也对此数据进行修改,就会进入阻塞状态,等待窗口1提交后,再对数据进行修改,如果窗口2等待的时间超过阈值,就会报错error。
    在这里插入图片描述
    MySQL高级——数据库优化(下)_第67张图片
    当窗口1提交后,窗口2完成对数据的修改,但由于都关闭了自动提交,此时窗口2去查看数据已经修改为4003,而窗口1去查看,由于窗口2还未提交,所以看到的还是4002。(这是由于隔离级别为可重复读的缘故)
    MySQL高级——数据库优化(下)_第68张图片
    因为两个窗口都关闭了自动提交,所以当一个提交后,另一个也需要提交才能看到修改后的数据。
    MySQL高级——数据库优化(下)_第69张图片
    由于两个窗口修改的不是同一行,所以并不会被另一方影响到。

  3. 无索引行锁升级为表锁
    在这里插入图片描述
    由于字段b为varchar类型的,而窗口1查询时没加引号导致索引失效,从而导致行锁变成表锁,此时,其他用户对此表的其他行进行修改也被迫进入阻塞状态,直到窗口1提交后,其他用户才能修改。
    在这里插入图片描述

间隙锁的危害

在这里插入图片描述
MySQL高级——数据库优化(下)_第70张图片
在这里插入图片描述
由于操作的表中字段a没有2,而窗口1操作的范围包括到2,而此时,窗口2插入a为2的数据,窗口1还修改后还未提交,会造成窗口2进入阻塞状态,等待窗口1提交后才可插入数据,这是因为窗口1操作的范围内,会对每一行加上行锁,不管有没有数据。
在这里插入图片描述
MySQL高级——数据库优化(下)_第71张图片
虽然窗口2插入的数据在窗口1修改的数据范围里,但是并没有被修改。
在这里插入图片描述

面试题(常考):如何锁定一行

MySQL高级——数据库优化(下)_第72张图片

begin;

# 为a=8行上锁
select * from test_innodb_lock where a=8 for update;

commit;

update test_innodb_lock set b='xxxx' where a=8;

在这里插入图片描述
在这里插入图片描述
为某行上锁:select * from 【表名】 where 【字段】=【xx】 for update;

如何锁定一行:在查询要上锁的数据的sql语句后加上for update即可。

案例结论

在这里插入图片描述

行锁分析

MySQL高级——数据库优化(下)_第73张图片
show status like 'innodb_row_lock%';
MySQL高级——数据库优化(下)_第74张图片
MySQL高级——数据库优化(下)_第75张图片

优化建议

MySQL高级——数据库优化(下)_第76张图片

页锁

在这里插入图片描述

五、主从复制

复制的基本原理

slave会从master读取 binlog来进行数据同步。

三步骤+原理图

MySQL高级——数据库优化(下)_第77张图片

复制的基本原则

MySQL高级——数据库优化(下)_第78张图片

复制的最大问题——延时

一主一从常见配置

之前docker上两台mysql8配置的主从复制:docker mysql8.0.18 一主一从 实现 主从复制
分库分表中间件Sharding-Sphere学习笔记:Sharding-Sphere 学习笔记
Sharding-Sphere也是看尚硅谷的视频学的,感觉还不错。

MySQL高级——数据库优化(下)_第79张图片

主机配置修改

MySQL高级——数据库优化(下)_第80张图片

从机配置修改

MySQL高级——数据库优化(下)_第81张图片

主机从机都重启mysql

主机从机关闭防火墙

CentOS7 关闭和开启防火墙

授权配置

在这里插入图片描述
在这里插入图片描述
MySQL高级——数据库优化(下)_第82张图片

学习视频(p46-p63):https://www.bilibili.com/video/BV1KW411u7vy?p=46

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