MYSQL优化 学习笔记
SQL性能下降原因
SQL语句被多次使用,(放在for循环中),代码逻辑或查询语句写的差
索引失效
关联查询太多join
服务器调优及各个参数设置
SQL执行顺序
随着 Mysql 版本的更新换代, 其优化器也在不断的升级, 优化器会分析不同执行顺序产生的性能消耗不同而动态调整执行顺序。
经常出现的以下:
索引优化
什么是索引
MySQL 官方对索引的定义为:索引(Index)是帮助 MySQL 高效获取数据的数据结构。可以得到索引的本质: 索引是数据结构。
可以简单理解为:排好序的快速查找数据结构(索引会影响排序及检索的效率)
索引的优缺点
优点
提高数据检索的效率,降低数据库的IO成本
通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗
缺点
虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息
实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占用空间的
适合索引的场景
主键自动建立唯一索引
频繁作为查询条件的字段应该创建索引
查询中与其它表关联的字段,外键关系建立索引
单键/组合索引的选择问题,组合索引性价比更高
查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度
查询中统计或者分组字段
不适合索引的场景
表记录太少(有无索引差别不大)
经常增删改的表或者字段
Where 条件里用不到的字段不创建索引
过滤性不好的不适合建索引(重复性较高,比如国籍、性别之类的字段)
性能分析
Explain 性能分析
Explain是什么(查看执行计划)
使用 EXPLAIN 关键字可以模拟优化器执行 SQL 查询语句,从而知道 MySQL 是如何处理你的 SQL 语句的。分析你的查询语句或是表结构的性能瓶颈
Explain能干嘛
Explain怎么玩
-- 用法:EXPLAIN + SQL语句
EXPLAIN SELECT * FROM TBL_EMP;
各个字段解释
id:select查询的序列号,是较为重要的一个指标
回答:表的读取顺序
id包含一组数字,表示查询中执行select子句或操作表的顺序
id相同:执行顺序为 从上至下执行
id不同:执行顺序为 id越大的,越先被执行
id有相同有不同:执行顺序为
id不同时,值较大的先执行
id相同时,从上至下执行
select_type:查询操作类型
回答:表的读取操作类型
select_type代表查询的类型,主要是用于区别普通查询、联合查询、子查询等的复杂查询
select_type 属性 | 含义 |
SIMPLE | 简单的 select 查询,查询中不包含子查询或者UNION |
PRIMARY | 查询中若包含任何复杂的子部分,最外层查询则被标记为 Primary |
DERIVED | 在 FROM 列表中包含的子查询被标记为 DERIVED(衍生) MySQL 会递归执行这些子查询, 把结果放在临时表里 |
SUBQUERY | 在SELECT或WHERE列表中包含了子查询 |
DEPEDENT SUBQUERY | 在SELECT或WHERE列表中包含了子查询,子查询基于外层 |
UNCACHEABLE SUBQUERY | 无法使用缓存的子查询 |
UNION | 若第二个SELECT出现在UNION之后,则被标记为UNION; 若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED |
UNION RESULT | 从UNION表获取结果的SELECT |
SUBQUERY 和 DEPEDENT SUBQUERY
都是 WHERE 后面的条件,SUBQUERY 是单个值(=),DEPEDENT SUBQUERY 是一组值(IN)
UNCACHEABLE SUBQUERY
当使用了**@@来引用系统变量**的时候,不会使用缓存
table:这个数据是基于哪张表的 derived = 衍生表
type:访问类型,是较为重要的一个指标
-- 值从最好到最坏依次是:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > all
-- 工作中常见的顺序为
system > const > eq_ref > ref > range > index > all
一般来说,得保证查询至少达到 range 级别,最好能达到 ref。
类型名 | 含义 |
SYSTEM | 表只有一行记录(等于系统表),这是 const(常量) 类型的特列,平时不会出现,这个也可以忽略不计 |
CONST | 表示通过索引一次就找到了,const 用于比较 primary key 或者 unique 索引。因为只匹配一行数据,所以很快。如将主键置于 where 列表中,MySQL 就能将该查询转换为一个常量 |
EQ_REF | 唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描 |
REF | 非唯一性索引扫描,返回匹配某个单独值的所有行。本质上也是一种索引访问,它返回所有匹配某个单独值的行, 然而,它可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体 |
RANGE | 只检索给定范围的行,使用一个索引来选择行。key 列显示使用了哪个索引一般就是在你的 where 语句中出现 了 between、<、>、in 等的查询这种范围扫描索引扫描比全表扫描要好,因为它只需要开始于索引的某一点,而 结束语另一点,不用扫描全部索引 |
INDEX | 出现index是sql使用了索引但是没用通过索引进行过滤,一般是使用了覆盖索引或者是利用索引进行了排序分组 |
ALL | Full Table Scan,将遍历全表以找到匹配的行 |
possible_key:可能使用的索引,一个或多个,但不一定被查询实际使用 回答:哪些索引可以被使用
key:实际使用的索引。如果为NULL,则没有使用索引 ,是较为重要的一个指标 回答:哪些索引被实际使用
查询中使用了覆盖索引,则该索引出现在key列表中
create index idx_users_name_phone on users(name, phone);
-- 以下语句possible_key、key均为null
explain select * from users;
-- 以下语句possible_key为null,但是key为idx_users_name_phone
explain select name, phone from users;
key_len:索引字段的最大可能长度,并非实际使用长度。
即:根据表定义计算而得,不是通过表内检索出的
ref:显示被使用的索引的具体信息 回答:表之间的引用
索引的哪一列被使用了,如果可能的话,可以是一个常数。哪些列或常量被用于查找索引列上的值
rows:被查询的行数,是较为重要的一个指标 回答:每张表有多少行数据被优化器查询
显示 MySQL 认为它执行查询时必须检查的行数。越少越好,当使用不同的索引的时候,这个行数可能会减少
Extra:额外重要信息
Using filesort:文件内排序
Using temporary:用临时表保存中间结果,排序时使用临时表(常见于ORDER BY 和GROUP BY)。
Using_index:走了覆盖索引,避免访问表的数据行
同时出现Using where,表明索引被用来执行索引键值的查找。
没有同时出现Using where,表明索引用来读取数据而非执行查找动作。
Using where:使用了where过滤
Using join buffer:使用了连接缓存
impossible where:where子句的值总是false,不能用来获取任何元组
explain select name, phone from users where sex = 0 and sex = 1;
SQL优化
避免索引失效的情况
单表查询优化
1、全局匹配最快
查询的字段按照顺序在索引中都可以匹配到这个时候是最快的。
2、最佳左前缀法则
过滤条件要使用索引必须按照索引建立时的顺序,依次满足,一旦跳过某个字段,索引后面的字段都无法被使用
多列索引是先按照第一列进行排序,然后在第一列排好序的基础上再对第二列排序,如果没有第一列的话,直接访问第二列,那第二列肯定是无序的,直接访问后面的列就用不到索引
3、索引列上不计算
不在索引列上做任何操作(计算、函数、(自动 or 手动)类型转换),会导致索引失效而转向全表扫描
4、范围之后全失效
使用范围查询后,如果范围内的记录过多,会导致索引失效,因为从自定义索引映射到主键索引需要耗费太多的时间,反而不如全表扫描来得快
建议:将可能做范围查询的字段的索引顺序放在最后
5、覆盖索引多使用
使用覆盖索引(Using index)会提高检索效率,只返回索引列,尽量减少select *的用法
6、使用不等会失效
在使用不等于(!= 或者<>)时,有时会无法使用索引会导致全表扫描
7、使用NULL值要小心
但是如果允许字段为空,则
IS NULL 不会导致索引失效
IS NOT NULL 会导致索引失效
8、模糊查询加右边
要使用模糊查询时,百分号最好加在右边,而且进行模糊查询的字段必须是单值索引
对索引使用模糊查询时,只有当百分号在右边,索引为单值索引且模糊查询语句在最右边时,索引才会生效
对索引进行模糊查询时,最好在右边加百分号。必须在左边或左右加百分号时,需要用到覆盖索引来提升查询效率
问题:如何解决like '%关键字%'时索引失效的方法?
答:用覆盖索引,即:建的索引,与返回值的字段个数及顺序需要完全一样
9、字符串加单引号
当字段为字符串时,查询时必须带上单引号。否则会发生自动的类型转换,从而发生全表扫描
10、尽量不用or查询
如果使用or,可能导致索引失效。所以要减少or的使用,可以使用 union all 或者 union 来替代
关联查询优化
1、INNER JOIN优化
inner join 时,mysql 会把i小结果集的表选为驱动表(小表驱动大表)
所以最好把索引建立在大表上
2、LEFT JOIN、RIGHT JOIN优化
在优化关联查询时,只有在被驱动表上建立索引才有效
left join 时,左侧的为驱动表,右侧为被驱动表
即左外连接查询索引建在右表,右外连接索引建在左表。
ORDER BY 与 GROUP BY优化
要想在排序/分组时使用索引,避免 Using filesort,首先需要发生索引覆盖,其次
ORDER BY /GROUP BY后面字段的顺序要和复合索引的顺序完全一致
ORDER BY /GROUP BY后面的索引必须按照顺序出现,排在后面的可以不出现
要进行升序或者降序时,字段的排序顺序必须一致。不能一部分升序,一部分降序,可以都升序或者都降序
如果复合索引前面的字段作为常量出现在过滤条件中,排序字段可以为紧跟其后的字段
慢查询日志(DBA及运维人员)
默认情况下,MySQL 数据库没有开启慢查询日志,需要我们手动来设置这个参数
如果不是调优需要的话,一般不建议启动该参数,因为开启慢查询日志会或多或少带来一定的性能影响。慢查询日志支持将日志记录写入文件。
SQL 语句 | 描述 |
SHOW VARIABLES LIKE ‘%slow_query_log%’ | 慢查询日志是否开启 |
set global slow_query_log=1 | 开启慢查询日志 |
SHOW VARIABLES LIKE ‘long_query_time%’ | 查看慢查询设定阈值 |
set long_query_time=1 | 设定慢查询阈值 |
mysql提供慢日志分析工具:mysqldumpslow
常用命令:
show profile
SQL的生命周期
备注:本文是基于bibi网站《尚硅谷MySQL数据库高级,mysql优化,数据库优化》的学习笔记,同时又参考网友笔记整合而成
B站 MySQL视频 传送门:https://www.bilibili.com/video/BV1KW411u7vy?p=1
网友笔记:http://jjssobk.gitee.io/my-web/2021/01/24/mysql%E4%BC%98%E5%8C%96