MySql 高级
Show profile
1 | mysql> set profiling=1; |
show processlist
1 | mysql> show processlist; |
performance schema
逻辑架构
和其他数据库相比,Mysql有点与众不同,他的架构在多种不同场景中应用并发挥良好作用。主要体现在存储引擎的架构上。插入式的存储引擎架构将查询处理和其他的系统任务以及数据的存储提取相分离。这种架构可以根据业务的需求和实际需要选择合适的存储引擎。
连接层
最上层是一些客户端和连接服务,包含本地sock通信和大多数基于客户端/服务端工具实现的类似于tcp/ip的通信。主要完成一些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程池的概念,为通过认证安全接入的客户端提供线程。同样在该层上可以实现基于SSL的安全链接。服务器也会为安全接入的每个客户端验证它所具有的操作权限。
服务层
第二层架构主要完成大多少的核心服务功能,如SQL接口,并完成缓存的查询,SQL的分析和优化及部分内置函数的执行,所有跨存储引擎的功能也在这一层实现,如过程、函数等。在该层,服务器会解析查询并创建相应的内部解析树,并对其完成相应的优化,如确定查询表的顺序、是否利用索引等,最后生成相应的执行操作。
如果是select语句,服务器还会查询内部的缓存。这样在解决大量读操作的环境中能够很好的提升系统的性能。
引擎层
存储引擎层,存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API与存储引擎进行通信,不同的存储引擎具有的功能不通,这样我们可以根据自己的实际需要进行选取。
存储层
数据存储层,主要是将数据存储在运行于裸设备的文件系统之上,并完成与存储引擎的交互。
首先,我们对该图中的各个模块做一简单介绍:
1、Connectors
指的是不同语言中与SQL的交互。
2、Connection Pool
管理缓冲用户连接,线程处理等需要缓存的需求。负责监听对 MySQL Server 的各种请求,接收连接请求,转发所有连接请求到线程管理模块。每一个连接上 MySQL Server 的客户端请求都会被分配(或创建)一个连接线程为其单独服务。而连接线程的主要工作就是负责 MySQL Server 与客户端的通信,接受客户端的命令请求,传递 Server 端的结果信息等。线程管理模块则负责管理维护这些连接线程。包括线程的创建,线程的 cache 等。
3、 Management Serveices & Utilities
系统管理和控制工具。
4、 SQL Interface
接受用户的SQL命令,并且返回用户需要查询的结果。
5、 Parser
SQL命令传递到解析器的时候会被解析器验证和解析。解析器是由Lex和YACC实现的,是一个很长的脚本。在 MySQL中我们习惯将所有 Client 端发送给 Server 端的命令都称为 query ,在 MySQL Server 里面,连接线程接收到客户端的一个 Query 后,会直接将该 query 传递给专门负责将各种 Query 进行分类然后转发给各个对应的处理模块。
主要功能:
a 、 将SQL语句进行语义和语法的分析,分解成数据结构,然后按照不同的操作类型进行分类,然后做出针对性的转发到后续步骤,以后SQL语句的传递和处理就是基于这个结构的;
b、 如果在分解构成中遇到错误,那么就说明这个sql语句是不合理的。
6、 Optimizer
查询优化器:SQL语句在查询之前会使用查询优化器对查询进行优化。就是优化客户端请求query,根据客户端请求的 query 语句,和数据库中的一些统计信息,在一系列算法的基础上进行分析,得出一个最优的策略,告诉后面的程序如何取得这个 query 语句的结果。
使用的是“选取-投影-联接”策略进行查询:
用一个例子就可以理解: select uid,name from user where gender = 1;
这个select 查询先根据where 语句进行选取,而不是先将表全部查询出来以后再进行gender过滤;然后根据uid和name进行属性投影,而不是将属性全部取出以后再进行过滤。最后将这两个查询条件联接起来生成最终查询结果。
7 、Cache和Buffer
查询缓存:主要功能是将客户端提交 给MySQL 的 Select 类 query 请求的返回结果集 cache 到内存中,与该 query 的一个 hash 值 做一个对应。该 query 所取数据的基表发生任何数据的变化之后, MySQL 会自动使该 query 的Cache 失效。在读写比例非常高的应用系统中, Query Cache 对性能的提高是非常显著的。当然它对内存的消耗也是非常大的。
如果查询缓存有命中的查询结果,查询语句就可以直接去查询缓存中取数据。这个缓存机制是由一系列小缓存组成的。比如表缓存,记录缓存,key缓存,权限缓存等。
8 、存储引擎接口
MySQL区别于其他数据库的最重要的特点就是其插件式的表存储引擎。MySQL插件式的存储引擎架构提供了一系列标准的管理和服务支持,这些标准与存储引擎本身无关,可能是每个数据库系统本身都必需的,如SQL分析器和优化器等,而存储引擎是底层物理结构的实现,每个存储引擎开发者都可以按照自己的意愿来进行开发。
注意:存储引擎是基于表的,而不是数据库。
查看命令
1 | #查看mysql提供什么存储引擎 |
MyISAM和InnoDB对比
索引优化分析
SQL执行顺序
手写顺序:
机器顺序:
Join查询
索引简介
MySQL官方对索引的定义为:索引(Index)是帮助MySQL高校获取数据的数据结构。
可以得到索引的本质:索引是数据结构。索引的目的在提高查询效率,可以类比字典。可以简单理解为”排好序的快速查找数据结构”。
数据本身之外,数据库还维护着一个满足特定查找算法的数据结构,这些数据结构以某种方式指向数据,这样就可以在这些数据结构的基础上实现高级查找算法,这种数据结构就是索引。
一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往以文件形式存储在硬盘上。
我们平时所说的索引,如果没有特别指明,都是指B树(多路搜索树,并不一定是二叉树)结构组织的索引。其中聚集索引,次要索引,覆盖索引,复合索引,前缀索引,唯一索引默认都是使用B+树索引,统称索引。当然,除了B+树这种类型的索引之外,还有哈希索引(hash index)等。
优势:
1.类似大学图书馆建书目索引,提高数据检索效率,降低数据库的IO成本
2.通过索引列对数据进行排序,降低数据排序成本,降低了CPU的消耗
劣势:
1.实际上索引也是一张表,该表保存了主键和索引字段,并指向实体表的记录,所以索引列也是要占用空间的
2.虽然索引大大提高了查询速度,同时却会降低更新表的速度,如果对表INSERT,UPDATE和DELETE。因为更新表时,MySQL不仅要不存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息。
3.索引只是提高效率的一个因素,如果你的MySQL有大数据量的表,就需要花时间研究建立优秀的索引,或优化查询语句。
索引分类
单值索引:即一个索引只包含单个列,一个表可以有多个单列索引
唯一索引:索引列的值必须唯一,但允许有空值
复合索引:即一个索引包含多个列
索引结构
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,显然成本非常非常高。
Btree索引(或Balanced Tree),是一种很普遍的数据库索引结构,oracle默认的索引类型(本文也主要依据oracle来讲)。其特点是定位高效、利用率高、自我平衡,特别适用于高基数字段,定位单条或小范围数据非常高效。理论上,使用Btree在亿条数据与100条数据中定位记录的花销相同。
mysql支持其他索引结构,如Hash
索引、full-text
全文索引,R-Tree
索引。
哪些情况需要创建索引?
1 | 1.主键自动建立唯一索引 |
哪些情况不需要创建索引?
1 | 1.表记录太少 |
性能分析
MySQL Query Optimizer
mysql中有专门负责优化 select 语句的优化器模块,主要功能:通过计算分析系统中收集到的统计信息,为窗户端请求的query提供它认为最优的执行计划(不一定是DBA认为最优的,这部分最耗费时间)。
当客户端向mysql发送一条query,命令解析器模块完成请求分类,区别出是select并转发给 mysql query optimizer时,mysql query optimizer首先会对整条query进行优化,处理掉一些常量表达式的预算,直接换算成常量值。并对query中的查询条件进行简化和调整,如去掉一些无用或显而易见的条件、结构调整等。
然后分析query中的hint信息(如果有),看显示hint信息是否可以完全确定该query的执行计划。如果没有hint或hint信息还不足以完全确定执行计划,则会读取所涉及对象的统计信息,根据query进行写相应的计算分析,然后再得出最后的执行计划。
常见瓶颈
- CPU : CPU在饱和的时候一般发生在数据装入内存或从磁盘上读取数据的时候
- IO : 磁盘I/O瓶颈发生在装入数据远大于内存容量的时候
- 服务器硬件的性能瓶颈 : top,free,iostat和vmstat来查看系统的性能状态
Explain
使用EXPLAIN关键字可以模拟优化器执行SQL语句,从而知道MySQL是如何处理你的SQL语句的。分析你的查询语句或是结构的性能瓶颈。
它能够看到表的读取顺序、数据读取操作的操作类型、哪些索引可以使用、哪些索引被实际使用、表之间的引用、每张表有多少行被优化器查询。
id
select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序
三种情况:
第一种:id相同,执行顺序由上至下
第二种:id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
第三种:id相同不同,同时存在
select_type
查询的类型,主要用于区别,普通查询、联合查询、子查询等的复杂查询
1 | 1.SIMPLE : 简单的select查询,查询中不包含子查询或者UNION |
table
显示这一行的数据是关于哪张表的
type
显示查询使用了何种类型,从最好到最差依次是:system>const>eq_ref>ref>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:FullTable Scan,将遍历全表以找到匹配的行
一般来说,得保证查询只是达到range级别,最好达到ref
possible_keys
显示可能应用在这张表中的索引,一个或多个。查询涉及的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用。
key
实际使用的索引。如果为null则没有使用索引,查询中若使用了覆盖索引,则索引和查询的select字段重叠。
key_len
表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。在不损失精确性的情况下,长度越短越好。
key_len显示的值为索引最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的。
ref
显示索引那一列被使用了,如果可能的话,是一个常数。那些列或常量被用于查找索引列上的值
rows
根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数
Extra
包含不适合在其他列中显示但十分重要的额外信息
Using filesort:
说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。
MySQL中无法利用索引完成排序操作成为“文件排序”
Using temporary:
使用了临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于排序order by 和分组查询 group by
Using Index:
表示相应的select操作中使用了覆盖索引(Coveing Index),避免访问了表的数据行,效率不错!
如果同时出现using where,表明索引被用来执行索引键值的查找;
如果没有同时出现using where,表面索引用来读取数据而非执行查找动作。
impossible where:
where子句的值总是false,不能用来获取任何元组
Using where:表示使用了 where 过滤
using join buffer:使用了连接缓存
distinct:优化distinct,在找到第一匹配的元组后即停止找同样值的工作
select tables optimized away:
在没有GROUPBY子句的情况下,基于索引优化MIN/MAX操作或者
对于MyISAM存储引擎优化COUNT(*)操作,不必等到执行阶段再进行计算,
查询执行计划生成的阶段即完成优化。
案例
索引优化
1.最佳全值匹配
2.最佳左前缀法则
如果索引了多例,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列。
3.不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描
4.存储引擎不能使用索引中范围条件右边的列
5.尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select*
6.mysql在使用不等于(!=或者<>)的时候无法使用索引会导致全表扫描
7.is null,is not null 也无法使用索引
8.like以通配符开头(’$abc…’)mysql索引失效会变成全表扫描操作
问题:解决like’%字符串%’索引不被使用的方法??
1、可以使用主键索引
2、使用覆盖索引,查询字段必须是建立覆盖索引字段
3、当覆盖索引指向的字段是varchar(380)及380以上的字段时,覆盖索引会失效!
9.字符串不加单引号索引失效
10.少用or,用它连接时会索引失效
小总结
like KK%
相当于=常量 %KK
和%KK%
相当于范围
一般性建议:
对于单键索引,尽量选择针对当前query过滤性更好的索引
在选择组合索引的时候,当前Query中过滤性最好的字段在索引字段顺序中,位置越靠前越好
在选择组合索引的时候,尽量选择可以能包含当前query中的where子句中更多字段的索引
尽可能通过分析统计信息和调整query的写法来达到选择合适索引的目的
查询截取分析
永远小表驱动大表,类似嵌套循环Nested Loop
相关:
尽可能减少Join语句中的NestedLoop的循环总次数:“永远用小结果集驱动大的结果集”
优先优化NestedLoop的内层循环:
保证Join语句中被驱动表上Join条件字段已经被索引
当无法保证被驱动表的Join条件字段被索引且内存资源充足的前提下,不要太吝啬
JoinBuffer
的设置;
Order by优化
MySQL支持二种方式的排序,FileSort和Index,Index效率高。它指MySQL扫描索引本身完成排序。FileSort方式效率较低。
ORDER BY子句,尽量使用Index方式排序,避免使用FileSort方式排序。
ORDER BY满足两情况,会使用Index方式排序:
ORDER BY语句使用索引最左前列
使用where子句与OrderBy子句条件列组合满足索引最左前列
尽可能在索引列上完成排序操作,遵照索引建的最佳左前缀,如果不在索引列上,会进行filesort排序。
filesort有两种算法:双路排序和单路排序。
双路排序:
MySQL4.1之前是使用双路排序,字面意思是两次扫描磁盘,最终得到数据。
读取行指针和orderby列(省空间),对他们进行排序,然后扫描已经排序好的列表,按照列表中的值重新从列表中读取对应的数据传输。
从磁盘取排序字段,在buffer进行排序,再从磁盘取其他字段。
取一批数据,要对磁盘进行两次扫描,众所周知,I\O是很耗时的,所以在mysql4.1之后,出现了第二张改进的算法,就是单路排序。
单路排序:
一次取出所有的查询字段和排序字段,按照orderby列在buffer对它们进行排序,然后扫描排序后的列表进行输出,它的效率更快一些,避免了第二次读取数据,并且把随机IO变成顺序IO,但是它会使用更多的空间,因为它把每一行都保存在内存中了。
两种排序在缓冲区内都是采用快速排序算法,若缓冲区满了之后,会放到临时文件中,最后利用归并算法排序。
针对order limit M,N,使用堆排序算法。
总体而言,单路排序好过双路排序,但是单路排序也有一定的问题:
可以通过增大sort_buffer_size参数的设置、增大max_length_for_sort_data参数的设置来进行优化。
总结
Group by优化
1.group by 实质是先排序后进行分组,遵照索引建的最佳左前缀
2.当无法使用索引列,增大max_length_for_sort_data参数的设置+增大sort_buffer_size参数的设置
3.where高于having,能写在where限定的条件就不要去having限定了。
主从复制
原理
每个slave只有一个master,每个slave只能有一个唯一的服务器ID,每个master可以有多个salve。
锁机制
锁的分类
从数据操作的类型分:
读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响
写锁(排它锁):当前写操作没有完成前,它会阻断其他写锁和读锁。
从对数据操作的颗粒度:
表锁
行锁
表锁(偏读)
特点:偏向MyISAM存储引擎,开销小,加锁快,无死锁,锁定粒度大,发生锁冲突的概率最高,并发最低。
加读锁:
加写锁:
结论:
简而言之,就是读锁会阻塞写,但是不会阻塞读,而写锁则会把读和写都堵塞。
表锁分析:
此外,Myisam的读写锁调度是写优先,这也是myisam不适合做写为主表的引擎。因为写锁后,其他线程不能做任何操作,大量更新回事查询很难得到锁,从而造成永远阻塞。
行锁(偏写)
特点:偏向InnoDB存储引擎,开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。InnoDB与MyISAM的最大不同有两点:一是支持事务(TRANSACTION);二是采用了行级锁。
行锁基本演示:
无索引行锁升级为表锁:varchar 不用 ‘ ‘ 导致系统自动转换类型, 行锁变表锁
间隙锁的危害:
间隙锁一个比较致命的弱点,就是当锁定一个范围键值之后,即使某些不存在的键值也会被无辜的锁定,而造成在锁定的时候无法插入锁定键值范围内的任何数据。在某些场景下这可能会对性能造成很大的危害。
面试题:常考如何锁定一行
结论:
行锁分析:
优化建议:
1 | 1.尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁 |
最后更新: 2021年03月03日 23:16