Mysql的索引、性能分析与SQL优化

文章目录

    • 存储引擎
      • 1.INnnoDB与MyISAM
    • 索引
      • 1.索引的定义
      • 2.索引的优势
      • 3.索引的劣势
      • 4.索引的分类
      • 5.哪些情况需要创建索引
      • 6.哪些情况不要创建索引
    • 性能分析
      • 1.执行计划信息
      • 2.索引失效
    • SQL优化
      • 1.join语句的优化
      • 2.like语句的优化
      • 3.group by关键字优化语句优化
      • 4.order by优化
      • 5.mysql 启动双路排序 和 单路排序
    • 优化策略
    • 慢查询日志
      • 1.查看是否开启及如何开启
    • 锁机制
      • 1.表锁
      • 2.行锁
    • B树
      • 1.查找过程
      • 2.Btree索引的特点
      • 3.B树和B+树的区别

存储引擎

1.INnnoDB与MyISAM

对比项 MyISAM INnnoDB
主外键 不支持 支持
事物 不支持 支持
行表锁 表锁,即使操作一条记录 行锁
缓存 只缓存索引,不缓存真实数据 索引与数据都会缓存,堆内存要求较高,
且内存大小对性能有决定性的影响
表空间
关注点 性能 事物
默认安装 Y Y

索引

1.索引的定义

MySQL官方对索引的定义为:索引(Index)是帮助MySQL高校获取数据的数据结构。
可以得到索引的本质:索引是数据结构。

数据本身之外,数据库还维护着一个满足特定查找算法的数据结构,这些数据结构以某种方式指向数据,
这样就可以在这些数据结构的基础上实现高级查找算法,这种数据结构就是索引。

我们平时所说的索引,如果没有特别指明,都是指B树(多路搜索树,并不一定是二叉树)结构组织的索引。其中聚集索引,次要索引,覆盖索引,复合索引,前缀索引,唯一索引默认都是使用B+树索引,统称索引。当然,除了B+树这种类型的索引之外,还有哈希索引(hash index)等。

2.索引的优势

  • 类似大学图书馆建书目索引,提高数据检索效率,降低数据库的IO成本
  • 通过索引列对数据进行排序,降低数据排序成本,降低了CPU的消耗

3.索引的劣势

  • 实际上索引也是一张表,该表保存了主键和索引字段,并指向实体表的记录,所以索引列也是要占用空间的
  • 虽然索引大大提高了查询速度,同时却会降低更新表的速度,如果对表INSERT,UPDATE和DELETE。因为更新表时,MySQL不仅要不存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息
  • 索引只是提高效率的一个因素,如果你的MySQL有大数据量的表,就需要花时间研究建立优秀的索引,或优化查询语句

4.索引的分类

  1. 单值索引
    • 即一个索引只包含单个列,一个表可以有多个单列索引
    • 建议一张表索引不要超过5个,优先考虑复合索引
  2. 唯一索引
    • 索引列的值必须唯一,但允许有空值
  3. 复合索引
    • 即一个索引包含多个列
  4. 基本语法
    • ALTER mytable ADD [UNIQUE] INDEX [indexName] ON(columnname(length));。
    • DROP INDEX [indexName] ON mytable;
    • SHOW INDEX FROM table_name
  5. 覆盖索引
    • SQL只需要通过索引就可以返回查询所需要的数据,而不必通过二级索引查到主键之后再去查询数据。
  6. 全文索引
    只有在MyISAM引擎上才能使用,而且只能在CHAR,VARCHAR,TEXT类型字段上使用全文索引。
  7. 聚集索引(MyISAM)
    B+树叶子节点只会存储数据行(数据文件)的指针,简单来说数据和索引不在一起,就是非聚集索引

5.哪些情况需要创建索引

  • 主键自动建立唯一索引
  • 频繁作为查询的条件的字段应该创建索引
  • 查询中与其他表关联的字段,外键关系建立索引
  • 频繁更新的字段不适合创建索引
  • Where条件里用不到的字段不创建索引
  • 单间/组合索引的选择问题,who?(在高并发下倾向创建组合索引)
  • 查询中排序的字段,排序字段若通过索引去访问将大大提高排序的速度
  • 查询中统计或者分组字段

6.哪些情况不要创建索引

  • 表记录太少
  • 经常增删改的表
  • 数据重复且分布平均的表字段,因此应该只为经常查询和经常排序的数据列建立索引。注意,如果某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果。

性能分析

1.执行计划信息

在这里插入图片描述

  1. id:select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序

    包括三种情况

    • id相同,执行顺序由上至下
    • id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
    • id相同不同,同时存在
  2. select_type:查询的类型,主要用于区别普通查询、联合查询、子查询等的复杂查询,包括六种

    • SIMPLE:简单的select查询,查询中不包含子查询或者UNION
    • PRIMARY:查询中若包含任何复杂的子部分,最外层查询则被标记为
    • SUBQUERY:在SELECT或者WHERE列表中包含了子查询
    • DERIVED:在FROM列表中包含的子查询被标记为DERIVED(衍生)MySQL会递归执行这些子查询,把结果放在临时表里。
    • UNION:若第二个SELECT出现在UNION之后,则被标记为UNION;若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED
    • UNION RESULT:从UNION表获取结果的SELECT
  3. table:显示这一行的数据是关于哪张表的

  4. 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,将遍历全表以找到匹配的行
  5. possible_keys:显示可能应用在这张表中的索引,一个或多个。查询涉及的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用

  6. key:实际使用的索引。如果为null则没有使用索引,查询中若使用了覆盖索引,则索引和查询的select字段重叠

  7. key_len:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。在不损失精确性的情况下,长度越短越好,key_len显示的值为索引最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的

  8. ref:显示索引那一列被使用了,如果可能的话,是一个常数。那些列或常量被用于查找索引列上的值

  9. rows:根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数

  10. Extra:包含不适合在其他列中显示但十分重要的额外信息

    • Using filesort(不好的):说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。
      MySQL中无法利用索引完成排序操作成为“文件排序”
    • Using temporary(不好的):使用了临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于排序order by 和分组查询 group by
    • Using index:表示相应的select操作中使用了覆盖索引(Coveing Index),避免访问了表的数据行,效率不错!如果同时出现using where,表明索引被用来执行索引键值的查找;如果没有同时出现using where,表面索引用来读取数据而非执行查找动作。
    • Using where:表面使用了where过滤
    • using join buffer:使用了连接缓存
    • impossible where:where子句的值总是false,不能用来获取任何元组
    • select tables optimized away:在没有GROUPBY子句的情况下,基于索引优化MIN/MAX操作或者
      对于MyISAM存储引擎优化COUNT(*)操作,不必等到执行阶段再进行计算,查询执行计划生成的阶段即完成优化。
    • distinct:优化distinct,在找到第一匹配的元组后即停止找同样值的工作

2.索引失效

  1. 全值匹配我最爱
  2. 最佳左前缀法则-如果索引了多例,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列。(例如复合索引中,第一个索引字段没被使用,则后面的也不会被使用)
  3. 不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描
  4. 存储引擎不能使用索引中范围条件右边的列
  5. 尽量使用覆盖索引(只访问索引列的查询(索引列和查询列一致)),减少select*
  6. mysql在使用不等于(!=或者<>)的时候无法使用索引会导致全表扫描
  7. is null,is not null 也无法使用索引
  8. like以通配符开头(’$abc…’)mysql索引失效会变成全表扫描操作
  9. 字符串不加单引号索引失效
  10. 少用or,用它连接时会索引失效

SQL优化

1.join语句的优化

  • 尽可能减少join语句中嵌套循环的总次数,以小表驱动大表,也就是小表join大表
  • 保证join语句中被驱动的表的join字段已经被索引
  • 无法保证被驱动的表的join字段被索引且内存资源充足的前提下,适当加大 joinBuffer的缓冲池设置

2.like语句的优化

  • 可以使用主键索引
  • 使用覆盖索引,查询字段必须是建立覆盖索引字段
  • 当覆盖索引指向的字段是varchar(380)及380以上的字段时,覆盖索引会失效!

3.group by关键字优化语句优化

  • groupby实质是先排序后进行分组,遵照索引建的最佳左前缀
  • 当无法使用索引列,增大max_length_for_sort_data参数的设置,增大sort_buffer_size参数的设置
  • where高于having,能写在where限定的条件就不要去having限定了。

4.order by优化

  • 尽量使用index 排序,避免使用file short排序
  • MySQL 支持俩种方式的排序,FileSort 和 index,Index 效率高,它指mysql 索引本身完成排序。
    • fileSort 方式效率低
    • order by 满足俩种情况,会使用Index方式排序、
      • order by 语句使用使用索引最左前列
      • 使用where 子句 与 order by 子句条件列 组合满足索引最左前列

5.mysql 启动双路排序 和 单路排序

  • 双路排序

    • mysql 4.1 版本之前使用双路排序,字面意思 扫描俩次磁盘,最终得到数据。
    • 读取行指针order by列对他们进行排序,然后扫描已经排序好的列表.
    • 从磁盘读取排序字段在buffer 进行排序,在从磁盘取其他字段。
    • 取一批数据对磁盘进行俩次扫描,众所周知,I/O时很耗时的,所以在mysql4.1之后,出现单路排序。
  • 单路排序

    • 从磁盘读取查询所有列,按照order by列,在buffer 对他们进行排序,然后扫描排序后的列表进行输出。并且把随机IO变成顺序IO,但是它会更多的空间,占用内存比较大。
  • 结论延申出的问题

    • 由于单路是后出的,总体而言好过双路排序

    • 但是用单路排序的问题

    • 在short_buffer 中,方法B要比方法A占用的空间更多,因为方法b是把所有的字段都取出,所以有可能取出的数据大于 short buffer的容量,导致每次只能取出short_buffer容量的数据,进行排序(创建tem文件,多路合并)排序完再取short buffer容量的大小,再排……,从而导致多次IO的操作。

    • 本来想节省IO操作的,结果导致大量的IO操作。

优化策略

  • 增大 short_buffer_ size 参数

  • 增大 max_length_for_short_data 参数

  • why --提高 order by 的速度

    • 1)order by时,select * 是大禁忌,只需要query 需要的字段。

      • 1.1)当query 字段的大小总和小于 max_length_for_short_data 而且排序字段 TEXT | BLOB类型时,会使用单路排序,否则使用多路排序(老算法)
      • 1.2)俩种算法都有可能超过short_buffer 的容量,超出后 会创建tem文件,进行合并排序,导致多次I\O,但是用单路排序算法会更大些,所以要提高 short_buffer_ size
    • 2)尝试提高 short_buffer_ size

      不管用那种算法,提高这个参数,都会提高效率,当然,要根据系统能力去提高,因为这个参数是针对每个进程。

    • 3)尝试 提高 max_length_for_short_data

      提高这个参数,会增加使用改进算法的概率。但是如果设置太高,数据总量超出 short_buffer_ size 的概率增大,明显症状高的IO活动和低的处理器使用率。
      Mysql的索引、性能分析与SQL优化_第1张图片

慢查询日志

1.查看是否开启及如何开启

查看:

SHOW VARIABLES LIKE '%slow_query_log%'

开启:

set global slow_query_log = 1

查看慢sql的时间阈值:

SHOW VARIABLES LIKE 'long_query_time%';

设置慢sql的时间阈值:

set global long_query_time=3;

使用配置文件如下:

slow_query_log=1;
slow_query_log_file=/var/lib/mysql/slow.log
long_query_time=3;
log_output=FILE

锁机制

1.表锁

  1. 特点:

    偏向MyISAM存储引擎,开销小,加锁快,无死锁,锁定粒度大,发生锁冲突的概率最高,并发最低

  2. 加读锁:

    lock table ### read
    
  3. .加写锁:

    lock table ### write
    
  4. 表锁分析:

    • 如何查看哪些表被锁了

      show open tables;
      

      通过检查 table_locks_waited table_locks_immediate 状态变量来分析系统上的表锁定:运行

      show status like 'table%'
      在这里插入图片描述
      在这里插入图片描述

  5. 总结:
    Mysql的索引、性能分析与SQL优化_第2张图片

简而言之就是读锁会阻塞写,但不会阻塞读;而写锁会把读和写阻塞。

2.行锁

  1. 特点

    偏向InnoDB存储引擎,开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。InnoDB与MyISAM的最大不同有两点:一是支持事务(TRANSACTION);二是采用了行级锁

  2. 事物的隔离级别
    Mysql的索引、性能分析与SQL优化_第3张图片

  3. 无索引行锁升级为表锁

varchar 不用 ’ ’ 导致系统自动转换类型, 行锁变表锁

  1. 间隙锁

当我们使用范围条件而不是相等条件检索数据,并请求共享或排它锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录叫做“间隙”,InnoDB也会对这个“间隙”加锁,这种锁机制就叫间隙锁。

危害:在执行过程中通过范围查找的话,他会锁定整个范围内所有的索引键值,即使这个键值不存在。

  1. 如何锁定一行

    使用 select ** for update;使用之后其他操作会被阻塞,直到锁定的行会话提交 commit。

  2. 优化建议

    • 尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁
    • 合理设计索引,尽量缩小锁的范围
    • 尽可能较少检索条件,避免间隙锁
    • 尽量控制事务大小,减少锁定资源量和时间长度
    • 尽可能低级别事务隔离

B树

Mysql的索引、性能分析与SQL优化_第4张图片
组成部分:

  • 磁盘块(浅蓝部分)
  • 数据项(深蓝部分)
  • 指针(黄色部分)

Btree索引(或Balanced Tree),是一种很普遍的数据库索引结构,。其特点是定位高效、利用率高、自我平衡,特别适用于高基数字段,定位单条或小范围数据非常高效。理论上,使用Btree在亿条数据与100条数据中定位记录的花销相同。

1.查找过程

若差找的数据是29,首先会将磁盘块1加载到内存中,此时发生一次IO,在内存中使用二分查找定诶29在17和35之间,锁定磁盘块1的P2指针没通过这个指针的磁盘地址将磁盘块3加载到内存,发生第二次IO,同样根据二分查找定位数据在磁盘块3的P2指针,通过指针加载磁盘块8到内存中,发生第三次IO,同时使用而分查找找到29,结束查询,总计三次IO。

2.Btree索引的特点

  • 树形结构:由根节(root)、分支(branches)、叶(leaves)三级节点组成,其中分支节点可以有多层。
  • 多分支结构:与binary tree不相同的是,btree索引中单root/branch可以有多个子节点(超过2个)。
  • 双向链表:整个叶子节点部分是一个双向链表(后面会描述这个设计的作用)

3.B树和B+树的区别

B树和B+树的最大区别在于非叶子节点是否存储数据的问题。

  • B树是非叶子节点和叶子节点都会存储数据。

  • B+树只有叶子节点才会存储数据,而且存储的数据都是在一行上,而且这些数据都是有指针指向的,也就是有顺序的。

  • 结构上Btree与Binary Tree的区别,在于Binary Tree中每节点代表一个数值,而balanced中root和Btree节点中记录了多条”值范围”条目(如:[60-70][70-80]),这些”值范围”条目分别指向在其范围内的叶子节点。既root与branch可以有多个分支,而不一定是两个,对数据块的利用率更高。

  • 在Leaf节点中,同样也是存放了多条索引记录,这些记录就是具体的索引列值,和与其对应的rowid。另外,在叶节点层上,所有的节点在组成了一个双向链表。

你可能感兴趣的:(Mysql)