Mysql优化学习

mysql优化学习


Mysql 优化


1. 基本优化步骤

优化流程:
1.定义慢sql,开启慢sql日志,把慢sql查询出来
2.查看当前mysql的存储引擎,查看存储引擎表,对不同类型的sql有必要的情况下尝试对表更换更适合的存储引擎,通常因为安全性要求都设置为InnoDB存储引擎
3.执行慢sql,explain查看sql执行计划或者sql执行完的剖析(show profile)
4.根据执行计划和原有的索引,先修改sql
5.仅修改sql效果不显著则根据执行计划重新新建索引,删除无效的索引或效果不理想的索引(数量大的索引也可能会很大,浪费磁盘空间)
6.效果仍然不显著下,进行对数据表分区,水平分表,无效果下进行垂直分表,重构表结构,减少表的数据量
7.全部操作都无法满足,查询服务器的cpu和内存的是否还未到峰值,若还存在则可尝试修改my.ini/my.cnf中的缓存相关参数,如table_cache和key_buffer_size,innodb_buffer_pool_size、innodb_log_file_size,tmp_table_size,max_heap_table_size 有些配置需要重启mysql,需要格外注意
8.提升服务器的硬件

2. 优化操作

1.查询慢sql

  • 开启mysql慢日志查询
show variables like '%slow_query_log';#查看慢sql日志状态,默认关闭OFF 

Mysql优化学习_第1张图片

set global slow_query_log = 1;#开始慢sql日志,开启后再查看慢日志状态为ON

注意:这种开启慢日志为临时开启,当mysql服务重启后状态会置为默认状态OFF。若需要设置默认状态为ON则需求修改mysql配置文件my.cnf。
不建议一直开启慢日志,也不建议在数据并发量高时开启,应为慢日志的监控和记录也是吃资源,慢sql量大时,会卡住,甚至卡死,导致mysql无法正常继续正常工作。查询慢sql时尽量避免访问高峰期进行,自行调用接口排查慢sql,查询完后关闭慢日志,性能好的机器随意。

Mysql优化学习_第2张图片

  • 查找定位慢sql
  1. 定义慢sql
show global variables like '%long_query_time%';;#查看慢sql,慢sql的默认值为10秒
set global long_query_time = 3;#定义自己的慢sql,通常超过3秒测试都会BB,自行定义

show variables like ‘%slow_query_log_file%’;#查看慢日志路径
show variables like ‘%quer%’;#查看所有相关信息
set global slow_query_log_file=‘/var/lib/mysql/test.log’;#自定义慢日志存放的路径(不需要的用默认的就行)

  1. 跑接口,查找慢sql
show global status like '%slow_queries%';查询慢sql条数

在这里插入图片描述

show variables like '%slow_query_log_file%';#查看慢日志的存放路径

根据日志的时间查询到自己需要的慢sql

Mysql优化学习_第3张图片
Mysql优化学习_第4张图片
Mysql优化学习_第5张图片

2.选择合适的存储引擎

查看存储引擎表,根据实际情况选择合适的存储引擎(通常因为安全性要求都设置为InnoDB存储引擎)

Mysql优化学习_第6张图片

3.学会查看sql执行计划,根据执行计划需改sql和表的索引(重点)

  • 使用EXPLAIN查看sql的执行信息
EXPLAIN  select * from dual

在这里插入图片描述

  1. id
    SELECT识别符。这是SELECT查询序列号,查询序号即为sql语句执行的顺序,倒序,从大的ID开始执行,同ID的从上往下执行
  2. select_type
    1 simple #它表示简单的select,没有union和子查询
    2 primary #最外面的select,在有子查询的语句中,最外面的select查询就是primary
    3 union #union语句的第二个或者说是后面那一个
    4 dependent union #UNION中的第二个或后面的SELECT语句,取决于外面的查询
    5 union result #UNION的结果
  3. table
    输出的行所用的数据表,存在临时表
  4. partitions
    官方定义为The matching partitions(匹配的分区),该字段看table所在的分区, 值为NULL表示表未被分区
  5. type
    对表访问方式, 表示MySQL在表中找到所需行的方式, 又称“访问类型”. 常用的类型有: all、index、range、 ref、eq_ref、const、system(从左到右, 性能从差到好), 一般来说, 需要保证查询至少达到range级别, 最好能达到ref级别.
    all: Full Table Scan, 全表扫描.
    index: Full Index Scan, 全索引扫描. index与all区别为index只遍历索引树, 通常比all快, 因为索引文件通常比数据文件小.
    range: 只检索给定范围的行, 使用一个索引来检索行, 可以在key列中查看使用的索引, 一般出现在where条件中, 比如使用between, <, >, in等查询. 这种索引的范围扫描比全表扫描要好, 因为索引的开始点和结束点都固定, 不用扫描全索引.
    ref: 非唯一性索引扫描, 返回匹配某个单独值的所有行. 本质上也是一种索引访问, 返回匹配某值(某条件)的多行数据, 属于查找和扫描的混合体.
    eq_ref: 类似ref, 区别在于使用的索引是唯一索引, 对于每个索引键值, 表中只有一条记录匹配, 常见于主键或唯一索引扫描.
    const、system: 表示通过一次索引就找到了结果, 常见于primary key或unique索引, 因为只匹配一行数据, 所以查询非常快. 如将主键置于where列表中, MySQL就能将该查询转换为一个常量. system是const类型的特例, 当查询的表只有一行的情况下, 使用system.
  6. possible_keys
    显示可能应用在表中的索引, 可能一个或多个. 查询涉及到的字段若存在索引, 则该索引将被列出, 但不一定被查询实际使用.
  7. key
    实际中使用的索引, 如为NULL, 则表示未使用索引. 要想强制MySQL使用或忽视possible_keys列中的索引, 在查询中使用FORCE INDEX、USE INDEX或者IGNORE INDEX.
  8. key_len
    表示索引中所使用的字节数, 可通过该列计算查询中使用的索引长度. 在不损失精确性的情况下, 长度越短越好. key_len显示的值为索引字段的最大可能长度, 并非实际使用长度, 即key_len是根据表定义计算而得, 并不是通过表内检索出的.
  9. ref
    显示关联的字段. 如果使用常数等值查询, 则显示const, 如果是连接查询, 则会显示关联的字段.
  10. rows
    根据表统计信息及索引选用情况大致估算出找到所需记录所要读取的行数. 当然该值越小越好.
  11. filtered
    百分比值, 表示存储引擎返回的数据经过滤后, 剩下多少满足查询条件记录数量的比例,通常情况下值越大越好,但是不一定,比如为了避免出现filesort(使用可以满足order by的索引),即使filtered的值比较低也没问题.
  12. extra(重要)
    using filesort: 表明mysql会对数据使用一个外部的索引排序, 而不是按照表内的索引顺序进行读取. 在mysql中, 无法利用索引完成的排序操作称为"文件排序". 当出现using filesort时就非常危险了, 在数据量非常大的时候几乎"九死一生". 出现using filesort尽快优化sql语句.
    using temporary: 使用了临时表保存中间结果, 常见于排序order by和分组查询group by. 非常危险, “十死无生”, 急需优化.
    using index: 表明相应的select操作中使用了覆盖索引, 避免访问表的额外数据行, 效率不错. 如果同时出现了using where, 表明索引被用来执行索引键值的查找. 如果没有同时出现using where, 表明索引用来读取数据而非执行查找动作.
    using where: 不用读取表中所有信息, 仅通过索引就可以获取所需数据, 这发生在对表的全部的请求列都是同一个索引的部分的时候, 表示mysql服务器将在存储引擎检索行后再进行过滤.
    range checked for each record: 没有找到合适的索引

对于using temporary的详细列子:
Mysql优化学习_第7张图片
Mysql优化学习_第8张图片
从图中看出只是排序使用了不同的表造成了不同的结果。
这是为什么呢?
他俩之间只是一个order by不同,MySQL 表关联的算法是 Nest Loop Join,是通过驱动表的结果集作为循环基础数据,然后一条一条地通过该结果集中的数据作为过滤条件到下一个表中查询数据,然后合并结果。EXPLAIN 结果中,第一行出现的表就是驱动表(Important!)以上两个查询语句,驱动表都是a,如上面的执行计划所示!
对驱动表可以直接排序,对非驱动表(的字段排序)需要对循环查询的合并结果(临时表)进行排序 (Important!)
所以使用b表时因为不是驱动表就会出现临时表,using temporary。
驱动表的定义
当进行多表连接查询时, [驱动表] 的定义为:
1)指定了联接条件时,满足查询条件的记录行数少的表为[驱动表];
2)未指定联接条件时,行数少的表为[驱动表](Important!)。
总结就是一句话小结果集驱动大结果集!
总结就是一句话小结果集驱动大结果集!
总结就是一句话小结果集驱动大结果集!


根据执行计划查看sql需要在哪里地方修改

  • 没有索引的字段加索引,匹配最左规则(组合索引包含从左到右的字段使用索引,不包含左边的字段索引失效)
    Mysql优化学习_第9张图片

  • 索引失效

  1. 注意where/join的类型,列如 date = int 会进行隐式装换导致索引失效
  2. 模糊查询like使用“name%”索引可用,“%name”索引失效
  3. 组合索引范围搜索,范围搜索后的字段不使用索引
    假设建立了一个sex索引
    (select * from date where id=x and sex=x and age>x;#sex能执行对应索引)
    (select * from date where id=x and age>x and sex=x;#sex不能执行对应索引)
  4. 一些函数和计算的时候也会导致索引失效,尽量避免在where/join时使用 , is null 和 is not null 在查询设置了not null列中也会导致索引失效
  5. 不等于导致索引失效,不等于的情况包括(!= 、<、>、not in)
  6. or前后条件都包含索引则走索引,or前后有一个不包含索引索引失效

Mysql优化学习_第10张图片

  1. 使用not in会导致索引失效?
    explain select * from data where age not in (18,19.20);
    range 类型索引的,并没失效。
    ps:需要特别说明的是mysql5.7和5.8以后不同的版本效果不一样,5.7中这种情况sql执行结果是全表扫描,而5.8以后中使用了 range 类型索引。
  2. 使用不等于号会导致索引失效 ?
    explain select * from data where age!=18;
    explain select * from data where age<>18;
    range 类型索引的,并没失效。
    mysql5.7和5.8以后不同的版本效果不一样,和上面的一样,可以使用大于小于分开写代替 select * from data where age>18 and age <18;
  3. order by索引字段顺序不当导致索引失效?
    假设建立了一组索引(age,sex)
    EXPLAIN select * from data where age=‘18’ order by age,sex;#执行ref 类型的索引,并且没有额外信息。
    但是如果把 order by 后面的条件改成如下两种排序:
    EXPLAIN select * from data where age=‘18’ order by name;
    EXPLAIN select * from data where age=‘18’ order by name,age;
    #执行ref 类型的索引,但是额外信息中提示: Using filesort ,即按文件重排序。
    上面两个例子能够看出有没有使用索引跟 where 后面的条件有关,而跟 order by 后面的字段没关系。而需不需要按文件重排序,则跟 order by 后面的字段有直接关系。这种情况一般是联合索引中索引字段的顺序,跟 sql 中 where 条件及 order by 不一致导致的,只要顺序调整一致就不会出现这个问题。
    Mysql优化学习_第11张图片ps:部分内容和图片来自超链接过去看看
  • 小表驱动大表(永远用小结果集驱动大结果集),在数据量很大的表中如果能先进行where过滤到很多数据,只要保留索引字段,临时表中还是能使用索引,并不一定用临时表就是坏结果。
    列如:SELECT xx form a JOIN (SELECT xx from c where c.xx = xx)b on a.xx = b.xx (c表是一个上亿的数据量,但是where后只剩几万数据量)
  • LIMIT避免偏移量过大的查询,列如:select * from data limit 100000,20,这样只查20条数据,但偏移量大效率低下,改成select * form data where id(或者row_number) > 100000 limit 20,使用索引先过滤后再查询,效率更高

4.数据表分区、水平分表、垂直分表

  1. 分区
    分区就是把表的数据按区域一块块地分开,变成多个独立开的小文件,逻辑上还是一张数据表,实际物理机上已经是多个小文件,查询的时候只需要查找对应的区域不需要查全表。
    优势:
    可以存储更多的数据,突破系统单个文件最大限制。
    提升性能,提高每个分区的读写速度,提高分区范围查询的速度。
    并行处理,对于SUM()、COUNT()这样聚合函数的查询,能更快。
    可以通过删除相关分区来快速删除数据。
    更大的数据吞吐量,通过跨多个磁盘来分散数据查询,从而提高磁盘I/O性能。
    如何实现分区网上一堆,这里随便给一个

  2. 水平分表
    水平分表就是数据表行的拆分,通俗点就是把数据按照某些规则拆分成多张表或者多个库来存放。分为库内分表和分库。
    比如一个表有400万数据,查询很慢,可以分到四个表,每个表有400万数据
    Mysql优化学习_第12张图片

  3. 垂直分表
    垂直分表就是列的拆分,根据表之间的相关性进行拆分。常见的就是一个表把不常用的字段和常用的字段就行拆分,然后利用主键关联。或者一个数据库里面有订单表和用户表,数据量都很大,进行垂直拆分,用户库存用户表的数据,订单库存订单表的数据。
    Mysql优化学习_第13张图片
    图来自这个分库分表方案
    最好就在数据库建立的初期想好的数据结构,避免在数据量多大业务过多时再去实现分库分表,这样代码的sql对应的都要改,工作量不少而且还容易出错。

5.修改mysql配置文件参数(有些配置需重启mysql)
业务需求无法避免或者已经改不动了,那就索性改TM的配置,缓存直接拉大,还要什么自行车。列如临时表避免不了使用而且临时表数据很大,那就加大mysql的临时表的大小,缺什么加大什么,只要机器能顶住。
table_cache、key_buffer_size、innodb_buffer_pool_size、innodb_log_file_size、tmp_table_size、max_heap_table_size
不止这些还有很多配置,如何修改配置网上一堆,不过注意的问题也需要看一下,不然会整个mysql不正常那就死翘翘了。

参考文章:
https://blog.csdn.net/atwdy/article/details/125181469
https://blog.csdn.net/qq_36770474/article/details/119537186
https://blog.csdn.net/weixin_42039228/article/details/123255722

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