MySQL进阶部分
一、MySQL的架构介绍
1.MySql简介
概述:
2.MySQL逻辑架构介绍
1.Connectors
指的是不同语言中与SQL的交互
2 Management Serveices & Utilities:
系统管理和控制工具
3 Connection Pool: 连接池
管理缓冲用户连接,线程处理等需要缓存的需求。
负责监听对 MySQL Server 的各种请求,接收连接请求,转发所有连接请求到线程管理模块。每一个连接上 MySQL Server 的客户端请求都会被分配(或创建)一个连接线程为其单独服务。而连接线程的主要工作就是负责 MySQL Server 与客户端的通信,
接受客户端的命令请求,传递 Server 端的结果信息等。线程管理模块则负责管理维护这些连接线程。包括线程的创建,线程的 cache 等。
4 SQL Interface: SQL接口。
接受用户的SQL命令,并且返回用户需要查询的结果。比如select from就是调用SQL Interface
5 Parser: 解析器。
SQL命令传递到解析器的时候会被解析器验证和解析。解析器是由Lex和YACC实现的,是一个很长的脚本。
在 MySQL中我们习惯将所有 Client 端发送给 Server 端的命令都称为 query ,在 MySQL Server 里面,连接线程接收到客户端的一个 Query 后,会直接将该 query 传递给专门负责将各种 Query 进行分类然后转发给各个对应的处理模块。
主要功能:
a . 将SQL语句进行语义和语法的分析,分解成数据结构,然后按照不同的操作类型进行分类,然后做出针对性的转发到后续步骤,以后SQL语句的传递和处理就是基于这个结构的。
b. 如果在分解构成中遇到错误,那么就说明这个sql语句是不合理的
6 Optimizer: 查询优化器。
SQL语句在查询之前会使用查询优化器对查询进行优化。就是优化客户端请求的 query(sql语句) ,根据客户端请求的 query 语句,和数据库中的一些统计信息,在一系列算法的基础上进行分析,得出一个最优的策略,告诉后面的程序如何取得这个 query 语句的结果
他使用的是“选取-投影-联接”策略进行查询。
用一个例子就可以理解: select uid,name from user where gender = 1;
这个select 查询先根据where 语句进行选取,而不是先将表全部查询出来以后再进行gender过滤
这个select查询先根据uid和name进行属性投影,而不是将属性全部取出以后再进行过滤
将这两个查询条件联接起来生成最终查询结果
7 Cache和Buffer: 查询缓存。
他的主要功能是将客户端提交 给MySQL 的 Select 类 query 请求的返回结果集 cache 到内存中,与该 query 的一个 hash 值 做一个对应。该 Query 所取数据的基表发生任何数据的变化之后, MySQL 会自动使该 query 的Cache 失效。在读写比例非常高的应用系统中, Query Cache 对性能的提高是非常显著的。当然它对内存的消耗也是非常大的。
如果查询缓存有命中的查询结果,查询语句就可以直接去查询缓存中取数据。这个缓存机制是由一系列小缓存组成的。比如表缓存,记录缓存,key缓存,权限缓存等
8 、存储引擎接口
存储引擎接口模块可以说是 MySQL 数据库中最有特色的一点了。目前各种数据库产品中,基本上只有 MySQL 可以实现其底层数据存储引擎的插件式管理。这个模块实际上只是 一个抽象类,但正是因为它成功地将各种数据处理高度抽象化,才成就了今天 MySQL 可插拔存储引擎的特色。
从图2还可以看出,MySQL区别于其他数据库的最重要的特点就是其插件式的表存储引擎。MySQL插件式的存储引擎架构提供了一系列标准的管理和服务支持,这些标准与存储引擎本身无关,可能是每个数据库系统本身都必需的,如SQL分析器和优化器等,而存储引擎是底层物理结构的实现,每个存储引擎开发者都可以按照自己的意愿来进行开发。
注意:存储引擎是基于表的,而不是数据库。
3.MySQL存储引擎
查看命令:
MyISAM和InnoDB区别:
InnoDB四大特性
1、插入缓冲(insert buffer)
插入缓冲(Insert Buffer/Change Buffer):提升插入性能,change buffering是insert buffer的加强,insert buffer只针对insert有效,change buffering对insert、delete、update(delete+insert)、purge都有效
使用插入缓冲的条件:
非聚集索引(辅助索引)
非唯一索引
Change buffer是作为buffer pool中的一部分存在。Innodb_change_buffering
参数缓存所对应的操作(update会被认为是delete+insert):
all
: 默认值,缓存insert, delete, purges操作none
: 不缓存inserts
: 缓存insert操作deletes
: 缓存delete操作changes
: 缓存insert和delete操作purges
: 缓存后台执行的物理删除操作
innodb_change_buffer_max_size
参数:控制使用的大小,默认25%,最大可设置50%,如果mysql实例中有大量的修改操作,可考虑增大该参数;
对满足插入缓存条件的插入,每一次的插入不是写到索引页中,而是:
会先判断插入的非聚集索引页是否在缓冲池中,如果在直接插入;
如果不在,则先放到insert buffer中,再按照一定的频率进行合并操作,再写会磁盘;
通常可以将多个插入合并到一个操作中,目的是为了减少随机IO带来的性能损耗;
这样通常能将多个插入合并到一个操作中,目的还是为了减少随机IO带来性能损耗。
上面提过在一定频率下进行合并,那所谓的频率是什么条件?
辅助索引页被读取到缓冲池中。正常的select先检查Insert Buffer是否有该非聚集索引页存在,若有则合并插入。
辅助索引页没有可用空间。空间小于1/32页的大小,则会强制合并操作。
Master Thread 每秒和每10秒的合并操作。
insert buffer的数据结构是一颗B+树;
全局只有一颗insert buffer B+树,负责对所有表的辅助索引进行insert buffer;
这颗B+树放在共享表空间中,试图通过独立表空间ibd文件恢复表中数据时,往往会导致check table失败,因为表中的辅助索引中的数据可能还在insert buffer中,也就是共享表空间中,所以ibd文件恢复后,还需要repair table操作来重建表上所有的辅助索引;
2、二次写(double write)
doublewrite缓存位于系统表空间的存储区域,用来缓存innodb的数据页从innodb buffer pool中flush之后并写入到数据文件之前;
当操作系统或数据库进程在数据页写入磁盘的过程中崩溃,可以在doublewrite缓存中找到数据页的备份,用来执行crash恢复;
数据页写入到doublewrite缓存的动作所需要的io消耗要小于写入到数据文件的消耗,因为此写入操作会以一次大的连续块的方式写入;
从上图可知:
内存中doublewrite buffer大小2M;物理磁盘上共享表空间中连续的128个页,也就是2个区(extent)大小同样为2M
对缓冲池脏页进行刷新时,不是直接写磁盘。流程:
通过memcpy()函数将脏页先复制到内存中的doublewrite buffer
通过doublewrite分两次,每次1M顺序的写入共享表空间的物理磁盘上。这个过程中,doublewrite页是连续的,因此这个过程是顺序的,所以开销并不大;
完成doublewrite页的写入后,再将doublewrite buffer中的页写入各个表空间文件中,此时写入是离散的,可能会较慢;
如果操作系统在第三步的过程中发生了崩溃,在恢复过程中,可以从共享表空间中的doublewrite中找到该页的一个副本,将其复制到表空间文件中,再应用重做日志;
3、自适应hash索引(ahi)
innodb存储引擎会监控对表上二级索引的查找,如果发现某二级索引被频繁访问,此索引成为热数据,建立hash索引以提升查询速度,此建立是自动建立哈希索引,故称为自适应哈希索引(adaptive hash index)。
该属性通过innodb_adapitve_hash_index
开启,也可以通过—skip-innodb_adaptive_hash_index
参数关闭
注意事项:
自适应哈希索引会占用innodb buffer pool
只适合搜索等值(=)的查询,对于范围查找等操作,是不能使用的
极端情况下,自适应hash索引才有比较大的意义,可以降低逻辑读
4、预读(read ahead)
extent 定义:表空间(tablespace 中的一组 page)
InnoDB使用两种预读算法来提高I/O性能:线性预读(linear read-ahead)和随机预读(randomread-ahead)。
线性预读:以extent为单位,将下一个extent提前读取到buffer pool中;
随机预读:以extent中的page为单位,将当前extent中的剩余的page提前读取到buffer pool中;
线性预读一个重要参数:innodb_read_ahead_threshold,控制什么时间(访问extent中多少页的阈值)触发预读;
默认:56,范围:0~64,值越高,访问模式检查越严格;
没有该变量之前,当访问到extent最后一个page时,innodb会决定是否将下一个extent放入到buffer pool中;
随机预读说明:
当同一个extent的一些page在buffer pool中发现时,innodb会将extent中剩余page一并读取到buffer pool中;
随机预读给innodb code带来一些不必要的复杂性,性能上也不稳定,在5.5版本已经废弃,如果启用,需要修改变量:innodb_random_read_ahead为ON;
二、索引优化分析
1.索引简介
是什么?
MySQL官方对索引的定义为:索引(Index)是帮助MySQL高校获取数据的数据结构。可以得到索引的本质:索引是数据结构。可以简单理解为”排好序的快速查找数据结构”。
结论:数据本身之外,数据库还维护着一个满足特定查找算法的数据结构,这些数据结构以某种方式指向数据,
这样就可以在这些数据结构的基础上实现高级查找算法,这种数据结构就是索引。
一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往以文件形式存储在硬盘上。
优势:
通过索引列对数据进行排序,降低数据排序成本,降低了CPU的消耗
劣势:
1.实际上索引也是一张表,该表保存了主键和索引字段,并指向实体表的记录,所以索引列也是要占用空间的。
2.虽然索引大大提高了查询速度,同时却会降低更新表的速度,如果对表INSERT,UPDATE和DELETE。因为更新表时,MySQL不仅要不存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息。
3.索引只是提高效率的一个因素,如果你的MySQL有大数据量的表,就需要花时间研究建立优秀的索引,或优化查询语句。
mysql索引分类
1 | 单值索引:即一个索引只包含单个列,一个表可以有多个单列索引 |
基本语法:
1 | 创建: |
使用Alter命令:
mysql索引结构:
BTree:
B树(Balance Tree)是一种多路平衡查找树,他的每一个节点最多包含M个孩子,M就是B树的阶。M的大小取决于磁盘页的大小。
https://www.bilibili.com/video/BV1UJ411J7CU?p=2&spm_id_from=pageDriver
https://www.bilibili.com/video/BV1BK4y1X7Gp?from=search&seid=1872408051532032494
https://blog.csdn.net/whoamiyang/article/details/51926985
B-树就是B树,中间的横线不是减号,所以不要读成B减树。
B+Tree
一个m阶的B+树具有如下几个特征:
1 | 1.有k个子树的中间节点包含有k个元素(B树中是k-1个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子节点。 |
B树索引:
B+树索引:
其中MyISAM和InnoDB存储引擎都采用B+树索引,但底层实现方式不同
InnoDB实现方式:其中叶子节点存储数据和键值即是原表中数据(聚集索引)
MyISAM实现方式:其中叶子节点存储键值和数据表中对应数据的物理地址(非聚集索引)
Hash索引:
1 | 1.只能使用=或<=>操作符的等式比较 |
hash index是基于哈希表实现的,只有精确匹配索引所有列的查询才会生效。对于每一行数据,存储引擎都会对所有的索引列计算一个hash code,并将的有的hash code存储在索引中,同时在哈希表中保存指向每个数据行的指针。
哪些情况需要创建索引
1 | 1.主键自动建立唯一索引 |
哪些情况不要创建索引
1 | 1.表记录太少 |
2.性能分析
MySQL Query Optimizer
MySQL常见瓶颈:
1 | CPU:CPU在饱和的时候一般发生在数据装入在内存或从磁盘上读取数据时候 |
Explain
是什么(查看执行计划)
1 | 使用EXPLAIN关键字可以模拟优化器执行SQL语句,从而知道MySQL是如何处理你的SQL语句的。分析你的查询语句或是结构的性能瓶颈 |
怎么玩
1 | Explain+SQL语句 |
执行计划包含的信息
各个字段解释
id
select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序
三种情况
id相同,执行顺序由上至下
id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
id相同不同,同时存在
select_type
有哪些:
查询的类型,主要用于区别普通查询、联合查询、子查询等的复杂查询
1 | 1.SIMPLE:简单的select查询,查询中不包含子查询或者UNION |
table
显示这一行的数据是关于哪张表的
type
显示查询使用了何种类型
从最好到最差依次是:
1 | system>con st>eq_ref>ref>range>index>ALL |
1 | system:表只有一行记录(等于系统表),这是const类型的特例,平时不会出现,这个也可以忽略不计 |
possible_keys
显示可能应用在这张表中的索引,一个或多个。查询涉及的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用
key
实际使用的索引。如果为null则没有使用索引
key_len
表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。在不损失精确性的情况下,长度越短越好key_len显示的值为索引最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的
ref
显示索引那一列被使用了,如果可能的话,是一个常数。那些列或常量被用于查找索引列上的值
rows
根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数
Extra
包含不适合在其他列中显示但十分重要的额外信息
1 | 1.Using filesort:说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。 |
覆盖索引(Covering Index):
1 | 4.Using where:表面使用了where过滤 |
3.索引优化
索引失效(应该避免)
1.全值匹配我最爱
2.最佳左前缀法则
3.不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描,这个意思是不能在where中做操作,但是可以在select中
4.存储引擎不能使用索引中范围条件右边的列
5.尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select*
6.mysql在使用不等于(!=或者<>)的时候无法使用索引会导致全表扫描
7.is null,is not null 也无法使用索引
8.like以通配符开头(’$abc…’)mysql索引失效会变成全表扫描操作
建立全文索引:https://blog.csdn.net/Samdy_Chan/article/details/78138420
如果要使用全值匹配,,但是又不想要索引失效,则对要匹配的值和要筛选的值建立索引,要符合索引覆盖。
9.字符串不加单引号索引失效
10.少用or,用它连接时会索引失效
11.小总结
一般性建议
1 | 对于单键索引,尽量选择针对当前query过滤性更好的索引 |
4.索引下推
https://blog.csdn.net/sinat_29774479/article/details/103470244
为什么范围查找索引回失效,后续索引回失效
作者:Limit
链接:https://www.zhihu.com/question/304037770/answer/1287557228
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
假设 表T1有字段a,b,c,d,e,其中a是主键,除e为varchar其余为int类型,并创建了一个联合索引idx_t1_bcd(b,c,d),然后b、c、d三列作为联合索引。Tip:基于InnoDB存储引擎。
T1表
联合索引:联合索引的所有索引列都出现在索引数上,并依次比较三列的大小。
联合索引B+树的结构
先看T1表,他的主键暂且我们将它设为整型自增的(PS:为什么是整型自增),InnoDB会使用主键索引在B+树维护索引和数据文件,然后我们创建了一个联合索引(b,c,d)也会生成一个索引树,同样是B+树的结构,只不过它的data部分存储的是联合索引所在行的主键值(上图叶子节点紫色背景部分)
对于联合索引来说只不过比单值索引多了几列,而这些索引列全都出现在索引树上。对于联合索引,存储引擎会首先根据第一个索引列排序,如上图我们可以单看第一个索引列,如,1 1 5 12 13…他是单调递增的;如果第一列相等则再根据第二列排序,依次类推就构成了上图的索引树,上图中的1 1 4 ,1 1 5以及13 12 4,13 16 1,13 16 5就可以说明这种情况。
联合索引的查找方式
当我们的SQL语言可以应用到索引的时候,比如
1 | select * from T1 where b = 12 and c = 14 and d = 3; |
也就是T1表中a列为4的这条记录。存储引擎首先从根节点(一般常驻内存)开始查找,第一个索引的第一个索引列为1,12大于1,第二个索引的第一个索引列为56,12小于56,于是从这俩索引的中间读到下一个节点的磁盘文件地址,从磁盘上Load这个节点,通常伴随一次磁盘IO,然后在内存里去查找。当Load叶子节点的第二个节点时又是一次磁盘IO,比较第一个元素,b=12,c=14,d=3完全符合,于是找到该索引下的data元素即ID值,再从主键索引树上找到最终数据。
三、查询截取分析
查询优化
永远小表驱动大表,类似嵌套循环Nested Loop
order by关键字优化
ORDER BY子句,尽量使用Index方式排序,避免使用FileSort方式排序
尽可能在索引列上完成排序操作,遵照索引建的最佳左前缀
如果不在索引列上,filesort有两种算法:
mysql就要启动双路排序和单路排序
1 | 双路排序 |
优化策略
增大sort_buffer_size参数的设置
增大max_length_for_sort_data参数的设置
小总结:
GROUP BY关键字优化
1 | groupby实质是先排序后进行分组,遵照索引建的最佳左前缀 |
慢查询日志
是什么
日志分析工具mysqldumpslow
查看mysqldumpshow的帮助信息
批量数据脚本
往表里插入1000W数据
1.建表
2.设置参数log_trust_function_createors
3创建函数保证每条数据都不同
随机产生字符串
随机产生部门编号
4.创建存储过程
创建往emp表中插入数据的存储过程
创建往dept表中插入数据的存储过程
5.调用存储过程
Show profiles
是什么:
1 | 是mysql提供可以用来分析当前会话中语句执行的资源消耗情况。可以用于SQL的调优测量 |
默认情况下,参数处于关闭状态,并保存最近15次的运行结果
分析步骤:
1.是否支持,看看当前的SQL版本是否支持
2.开启功能,默认是关闭,使用前需要开启
3.运行SQL
1 | select * from emp group by id%10 limit 150000 |
4.查看结果,show profiles;
5.诊断SQL,show profile cpu,block io for query 上一步前面的问题SQL 数字号码;
6.日常开发需要注意的结论
1 | converting HEAP to MyISAM 查询结果太大,内存都不够用了往磁盘上搬了。 |
全局查询日志
配置启用
编码启用
永远不要在生产环境开启这个功能。
四、MySQL锁机制
概述
https://blog.csdn.net/qq_44766883/article/details/105879308
锁的分类
从数据操作的类型(读、写)分:
1 | 读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响 |
从对数据操作的颗粒度:
1 | 表锁 |
从程度上划分:
1 | 悲观锁 |
表锁(偏读)
特点
1 | 偏向MyISAM存储引擎,开销小,加锁快,无死锁,锁定粒度大,发生锁冲突的概率最高,并发最低 |
案例分析
建表SQL
加读锁
加写锁
案例结论
表锁分析
行锁(偏写)
特点
1 | 偏向InnoDB存储引擎,开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。 |
由于行锁支持事务,复习老知识
事务(Transation)及其ACID属性
并发事务处理带来的问题
1 | 脏读:(Dirty Reads):事务A读到了事务B已修改但尚未提交的数据,还在这个数据基础上做了操作,此时,如果 B事务回滚,A读取的数据无效,不符合一致性要求。 |
事务隔离级别
案例分析
建表SQL
行锁定基本演示
无索引行锁升级为表锁
1 | varchar 不用 ' ' 导致系统自动转换类型, 行锁变表锁 |
间隙锁危害
面试题:常考如何锁定一行
案例结论
行锁分析
优化建议
1 | 尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁 |
五、主从复制
复制的基本原理
1 | slave会从master读取binlog来进行数据同步 |
三大步骤:
1 | master将改变记录到二进制日志(binary log)。这些记录过程叫做二进制日志时间,binary log events |
复制的基本原则
1 | 每个slave只有一个master |
复制最大问题
延时
六、参考资料
哔哩哔哩尚硅谷讲堂
哔哩哔哩图灵学院
github:java2020