PS:图片来源 尚硅谷 Mysql高级篇
架构
-
mysql配置文件
-
mysql分层(插件式,可拔插):插件式存储引擎架构将查询处理和其他任务以及数据的存储提取相分离
- 连接层
- 最上一层是客户端和连接服务(JDBC之类),包含本地sock通信和大多数基于客户端/服务器的工具实现类似于tcp/ip的通信,主要完成一些类似于连接处理,授权认证,及相关的安全方案。+线程池,+SSL安全连接
- 服务层 业务逻辑层
- 第二层架构主要完成大多数的核心服务功能,如SQL接口,并完成缓存的查询,SQL的分析和优化即部分内部函数的执行,所有跨存储引擎的功能也在这一层实现,如过程、函数等,在该层,服务器会解析查询并创建相应的内部解析树,并对其完成相应的优化如确定查询表的顺序,是否利用索引等,最后生成相应的执行操作,如果是select语句,服务器还会查询内部的缓存,如果缓存空间足够大,这样在解决大量读操作的环境中能够很好的提升系统性能。
- 引擎层 数据存储引擎层
- 存储引擎真正的负责了Mysql中数据的存储和提取,服务器通过API与存储引擎进行通信,不同的存储引擎具有的功能不同
- 存储层 数据存储层
- 将数据存储在运行于裸设备的文件系统之上,并完成与存储引擎的交互
- 连接层
-
MyISAM和InnoDB
-
SQL性能下降的原因
- 查询语句写的烂
- 索引失效(建了没用上)
- 单值:只对一张表一个一段建立索引 create index idx_user_name on user(name)
- 复合索引:create index idx_user_nameEmail on user(name,email)
- 关联查询太多join(设计缺陷或不得已的需求)
- 服务器调优及各个参数的设置(缓冲,线程数等)
-
SQL执行顺序
-
手写顺序
-
机读顺序:首先保证数据的完整性,连接所有数据,然后再从所有数据中where/group/having筛选,筛选后再选择字段,排序limit输出
-
解析过程
-
-
Join
- 中间:select * frrom A inner join B on A.KEY=B.KEY 两表共有部分
- 左上:SELECT *FROM A LEFT JOIN B A.KEY=B.KEY A表所有,B表补NULL
- 右上:SELECT *FROM A RIGHT JOIN B A.KEY=B.KEY B表所有,A表补null
- 左下:SELECT *FROM A LEFT JOIN B A.KEY=B.KEY WHERE B.KEY=NULL A独有,所以B必须等于NULL
- 右下:SELECT *FROM A right JOIN B A.KEY=B.KEY WHERE A.KEY=NULL B独有,A必须为NULL
- 左:SELECT * FROM A FULL JOIN B ON A.KEY=B.KEY mysql不支持全连接
- select * from A left join B on A.KEY = B.KEY UNION SELECT * FROM A RIGHT JOIN B ON A.KEY=B.KEY (union 联合并去重 unionall会保留重复)
- 右:SELECT * FROM A FULL JOIN B ON A.KEY=B.KEY WHERE A.KEY IS NULL OR B.KEY IS NULL
- SELECT *FROM A LEFT JOIN B A.KEY=B.KEY WHERE B.KEY=NULL union SELECT *FROM A right JOIN B A.KEY=B.KEY WHERE A.KEY=NULL
索引
-
索引
-
index是帮助mysql高效获取数据的数据结构
-
目的是提高查找效率,可以简单理解为“排好序的快速数据结构”
-
影响where 和 order by
-
在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据
-
这样就可以在这些数据结构上实现高级查找算法,这些数据结构,就是索引
-
一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件形式存储在磁盘上。
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sYQcddiM-1585762387283)(MySQL高级.assets/image-20200316225150761.png)]
-
优势:
- 提高数据检索效率,降低数据库的io成本
- 通过索引列对数据进行排序,降低数据排序成本,降低CPU消耗
-
劣势
- 索引也是表,会占用空间
- 会降低表的更新速度
- 需要花时间去建立最优秀的索引
-
索引分类
- 单值索引:一个索引只包含单个列,一个表可以有多个单列索引
- 唯一索引:索引列的值必须唯一,但允许有空值
- 复合索引:一个索引包含多个列
-
create [unique] index indexName On mytable(columnname(length)) //length 表示前缀,BLOB和TEXT类型必须指定
-
或者 alter table add [unique] index indexname on (columnname(length))
-
drop index [indexname] from tabel
-
show index from table
-
哪些是需要建立索引的
-
哪些是不需要建立索引的
- 表记录太少 三百万以内
- 经常增删改
- 数据重复且分布平均的表字段
-
-
性能分析
- MySQL查询优化器
- MySQL常见瓶颈
- CPU
- IO
- 服务器硬件的性能瓶颈:top,free,lostat vmstat查看系统性能状态
- MySQL查询优化器
Explain
-
Explain
- 使用EXPLAIN关键字可以模拟优化器执行SQL查询语句,从而指导MySQL是如何处理你的SQL语句的,分析你的查询语句或是表结构的性能瓶颈。
- 能做什么
- 表的读取顺序
- 数据读取操作的操作类型
- 哪些索引可以使用
- 哪些索引被实际使用
- 表之间的引用
- 每张表有多少行被优化器查询
- EXPLAN+SQL语句
- id select_type table type possible_keys key key_len ref row extra
- idD
- id相同,执行顺序由上到下
- 数值越大优先级越高,越先被执行(例如如果是子查询,id序号会递增)
- 建议:小表驱动大表
- select_type
- simple 简单的select查询,查询中不包含子查询或者UNION
- primary 查询中如果包含任何复杂的子部分,最外层查询被标记为…
- SUBQUERY 在select或者where列表中包含了的子查询
- derived 在from表中包含的子查询被标记为DERIVED(衍生)MySQL会递归执行这些子查询,把结果放在临时表中
- UNION 若第二个SELECT出现在UNION之后,则被标记为UNION,若UNION包含在FROM子句的查询外,外层SELECT被标记为DERIVED
- UNION RESULT 两种UNION结果的合并
- table 哪张表
- type 访问类型排序,显示查询使用了何种类型
- 从最好到最差:system>const>eq_ref>ref>range>index>All 一般来说,保证查询至少到range级别,最好能达到ref
- system 表只有一行记录(等于系统表),这是const类型的特列,基本不会出现
- const 表示通过索引一次就找到了,常用于主键或者唯一索引
- eq_ref 多表JOIN操作时,主键或唯一键作为ON连接条件
- ref 多表JOIN操作时,如果有非主键或唯一键作为连接条件,就是ref;(用到索引,查出多个记录)
- 这种类型表示mysql会根据特定的算法快速查找到某个符合条件的索引,而不是会对索引中每一个数据都进行一 一的扫描判断,也就是所谓你平常理解的使用索引查询会更快的取出数据。而要想实现这种查找,索引却是有要求的,要实现这种能快速查找的算法,索引就要满足特定的数据结构。简单说,也就是索引字段的数据必须是有序的,才能实现这种类型的查找,才能利用到索引。 (或全部在索引上取数据)
- range 只检索给定范围的行,使用一个一个索引来选择行。一般是where语句中出现between,<,>,in
- index 只读取索引树 ,进行索引扫描
- ALL全表扫描
- Null
- possible_keys 显示可能应用在这张表上的索引,一个或多个。查询涉及到这个字段上存在索引,则被列出,但不一定查询中使用(possible key的搜索原理是根据where中涉及到的字段去查找index)
- key 实际使用的索引,如果为null,则没有使用索引。
- 补充:覆盖索引,查询的多个字段和复合字段一致 possible_keys 不显示
- key_len :表示索引中使用的字节数,可通过该列计算查询的 中使用索引的长度,同样精度下,越小越好
- ref:显示索引的哪一列被使用/参考,如果可能的话,是一个常数,哪些列或常量被用于查找索引列上的值
- rows:根据表的统计信息和索引选用情况,大致估算找到所需的记录所需要读取的行数
- Extra:
- Using filesort :说明mysql会对数据使用一个外部索引的排序,而不是按照表内的索引顺序进行读取,Mysql中无法利用索引完成的排序操作称为“文件索引” →需要优化
- Using tmporary :使用临时表保存中间结果,Mysql在对查询结果排序时使用临时表,常见于排序order by和分组查询group by(一定要和索引顺序一致(最左前缀原则)) 一定要优化
- Using index
- 表示相应的select操作中使用了覆盖索引(Covering index),避免访问了表的数据行,效率不错!
- 如果同时出现 using where , 表明索引被用来执行索引键值的查找
- 如果没有出现using where,表明索引用来读取数据而非执行查找动作
- 覆盖索引;就是select的数据列只从索引中就可以取到,不必读取数据列,Mysql可以利用说因返回select列表中的字段,而不必根据索引再次读取数据文件,换句话说,查询列要被所建的索引覆盖。
- Using where
- Using join buffer 可适当调大一点buffer
- Impossible where where冲突
- select table optimized table 优化min/max或MyISAM优化conut
- distinc 优化distinc
- idD
- id select_type table type possible_keys key key_len ref row extra
-
优化
-
单表建立覆盖索引,range之后的索引不参与,可以隔开range 建立where和order的联合索引
CREATE INDEX idx_article_cv ON article(`category_id`,`views`); EXPLAIN SELECT id,author_id FROM article WHERE category_id=1 AND comments>1 ORDER BY views DESC LIMIT 1;
-
多表连接 join 索引建立在从表中
ALTER TABLE book ADD INDEX Y(card); EXPLAIN SELECT * FROM class LEFT JOIN book ON book.`card`=class.card;
-
其他
- 尽可能减少join语句中的NestedLoop(嵌套循环)的循环总次数,永远用小结果集驱动大的结果集
- 优先优化NestedLoop的内层循环
- 保证Join语句被驱动表上的Join条件字段已经被索引
- 当无法保证被驱动表Join字段被索引且内存资源充足的前提下,不要太吝啬JoinBuffer的设置
-
-
避免索引失效
- 最佳左前缀法则:如果索引了多列,要遵循最左前缀法则,指的是查询从索引的最左前列开始并且不跳过索引的列。
- 不在索引列上做任何操作(计算、函数、(手动或自动)类型转换)
- 存储引擎不能使用索引中范围条件右边的列
- 尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select*
- mysql在使用(!=或者<>)的时候无法使用索引,会导致全表扫描
- is null或者 is not null 无法使用索引
- %like 会导致索引失效,一般%放在右边
- 问题:解决like ‘%***%’
- 覆盖索引
- 问题:解决like ‘%***%’
- 字符串不加单引号索引失效
- 少用or 用union 或者 in
- MySQL8.0的版本, 左前缀+覆盖索引: ref ==> range 左前缀+不覆盖索引: ref ==> range 不左前缀+覆盖索引: ref ==> index 都不: all
- 经验
- 定值、范围还是排序,一般order by是给个范围
- group by 基本上都需要进行排序,可能会有临时表产生
- 建议
-
处理慢SQL流程
-
分析
-
观察,至少跑1天,看看生成的慢SQL情况
-
开启慢查询日志,设置阈值,比如超过5秒的就是慢SQL,并取出
-
explain + 慢SQL分析
-
show profile
-
进行SQL数据库服务器的参数调优
-
-
总结
- 慢查询的开启与捕获
- explain+慢SQL分析
- show profile 查询SQL在Mysql服务器里的执行细节和生命周期
- SQL数据库服务器的参数调优
-
-
Exists 和 in
- Exists 是先查A表,然后拿去子查询中做TF判断决定主查询数据是否保留
- Exists: 先查询一次外SELECT,后去子查询里做验证。
- IN:先查询子SELECT,然后用外表的一个记录做验证。
-
order by 优化
-
排序算法
- 双路排序:4.1之前 先读行指针和orderby列排序放在buffer,然后扫描拍好的列表,按照列表的值重新读取数据输出
- 单路排序:读取所有列放在buffer
-
尽量不要用*
-
sort buffer size增加
-
max_length_for_sort_data 增加
-
-
group by
- group 实质是先排序后进行分组,遵照索引建的最佳左前缀
- 当无法使用索引列,增大sort buffer size 、max_length_for_sort_data
- where高于having,能在where限定的条件不要再having限定
慢查询
-
Mysql的慢查询日志是Mysql提供的一种日志记录,它用来记录在Mysql中相应时间超过阈值的语句,具体指的是运行时间超过long_query_time的SQL,则会被记录到慢查询日志中。
-
具体指运行时间超过long_query_time值的SQL,则会被记录到慢查询日志中,long_query_time的默认值为10,意思是运行10秒以上的语句
-
由他来查看哪些SQL超出了我们的最大忍耐值,结合explain进行全面分析
-
开启
sql SHOW VARIABLES LIKE '%slow_query_log%'; set global slow_query_log=1 // 当前的
sql set global long_query_time=3; //需要重新连接或者新开会话才能看到修改值
-
mysqldumpslow
批量插入数据
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()*26),1));
SET i=i+1;
END WHILE;
RETURN return_str;
END $$
show profile
-
mysql提供可以用来分析当前会话中语句执行的资源消耗情况,可以用为sql的调优测量
-
默认关闭,保存15次结果
-
set profiling=on show profile; show profile cpu,block io for query id;
全局查询日志
- 配置
-
读锁(共享锁)针对同一份数据,多个读操作可以同时进行而不会相互影响
写锁(排他锁):当前写操作没有完成前,会阻断其他写锁和读锁
表锁
- 表锁:偏向MyISAM(不适合写,因为写的时候会阻塞其他线程的任何操作),开销小,加锁快,无死锁,粒度大,发生锁冲突的概率最高,并发度最低
SHOW OPEN TABLES;
lock table tablename read/write;
unlock tables;
-
加读锁后可以读但是不能写,当前session不可以读写其他的表,其他session读都不影响,写被锁表会被阻塞;
-
写锁
- 自己可以读写被锁表,不能操作其他表
- 其他session读写当前表会被阻塞
-
读锁会阻塞写,但不会堵塞读,写锁都会阻塞
-
分表表锁定 SHOW STATUS LIKE ‘table%’
- MyISAM的读写调度是写优先的,因此不适合做写为主表的引擎,因为写锁后,其他线程不能做任何操作,大量的更新会使得查询很难拿到锁,而造成永远的阻塞
行锁
-
偏向InnoDB存储引擎,开销大,加锁慢,会出现死锁,锁定粒度最小,发生锁冲突的概率最低,并发度也最高
-
InnoDB与MyISAM的最大不同有两点,一是支持事务(ACID),二是采用了行级锁
-
索引失效(类型转换)会导致行锁变成表锁
-
在mysql 的 InnoDB引擎支持行锁,与Oracle不同,mysql的行锁是通过索引加载的,即是行锁是加在索引响应的行上的,要是对应的SQL语句没有走索引,则会全表扫描,
-
间隙锁 Next-key
- 当我们用范围条件进行检索数据是,并请求共享和排他锁时,InnoDB会给符合条件的的已有数据记录的索引项加锁,对于键值在条件范围内但不存在的记录,叫做间隙(GAP),InnoDB也会给间隙进行加锁
-
如何锁定一行
- select for update
-
Innodb存储引擎由于实现了行锁,虽然在锁定机制的实现方面所带来的性能损耗可能比表级锁定会高一些,但是整体并发处理能力要远远优于MyISAM的表级锁定,当系统并发量较高,Innodb的整体性能和MyISAM相比就会有比较明显的优势了。但是Innodb的行级锁定同样也有脆弱的一面,当我们使用不当的时候,可能会让Innodb的整体性能表现不仅不能比MyISAM高,甚至可能会更差。
-
行锁分析
show status like ‘innodb_row_lock%’
- 优化建议
- 尽可能让所有的数据检索都通过索引来完成,避免索引失效行锁升级表锁
- 合理设计索引,尽量缩小锁的范围
- 尽量较少的检索条件,避免间隙锁
- 尽量控制事务大小,减少锁定资源量和时间长度
- 尽可能低级别事务隔离
主从复制
- slave会从master读取二进制日志进行数据同步
- 详细步骤
- master将改变记录到二进制日志,这些日志记录过程叫做二进制日志事件
- salve将master的binary log events 拷贝到它的中继日志(relay log)
- slave重做中继日志中的事件,将改变应用到自己的数据库中,Mysql复制时异步的且串行化的
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E69Fosub-1585762387297)(MySQL高级.assets/image-20200326194107425.png)]
- 复制的最大问题 : 延迟
优化建议
- 尽可能让所有的数据检索都通过索引来完成,避免索引失效行锁升级表锁
- 合理设计索引,尽量缩小锁的范围
- 尽量较少的检索条件,避免间隙锁
- 尽量控制事务大小,减少锁定资源量和时间长度
- 尽可能低级别事务隔离
主从复制
- slave会从master读取二进制日志进行数据同步
- 详细步骤
- master将改变记录到二进制日志,这些日志记录过程叫做二进制日志事件
- salve将master的binary log events 拷贝到它的中继日志(relay log)
- slave重做中继日志中的事件,将改变应用到自己的数据库中,Mysql复制时异步的且串行化的
- 复制的最大问题 : 延迟