MySQL 索引机制以及调优

Posted 老邋遢

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL 索引机制以及调优相关的知识,希望对你有一定的参考价值。

mysql 索引机制以及调优

此文不是特别全,建议和另一篇文章结合着看

目录

文章目录

开篇

一起来看一下我们工作中常见的几个数据库问题

  • 数据库常见的慢查询优化方式是什么?
  • 为什么加索引能优化慢查询?
  • 哪些数据结构可以提高查询速度?
  • 众多数据结构中为什么Mysql选择使用B+树?

下面一起带着这些问题进入以下学习

基础知识储备

局部性原理

其实这个原理的提出源于1968年研究的一个问题

作业信息不全部装入主存的情况下能否保证作业的正确运行?

答案是肯定的,研究发现程序和数据的访问都有聚集成群的倾向。即在一个时间段内,仅会使用一小部分数据或代码(空间局部性),并且在未来很快被再次访问的可能性很大(时间局部性)。

这个类似于二八法则,80%时间会去访问20%的数据。

实际场景中就是我想从磁盘中读取1字节数据,实际上读取的是磁盘1个page的数据(页大小通常为4k),当然极端场景下这1字节的数据可能是从2页甚至4页里读取的

磁盘预读

上面已经简单介绍了磁盘的存储机制,页是存储器的逻辑块,操作系统往往将主存和磁盘存储划分为连续且大小相等的块,每个存储块为一页,主存和磁盘以页为单位交换数据

索引是什么?

  • 索引是帮助MySQL高效获取数据的数据结构
  • 索引存储在文件系统中
  • 索引的文件存储形式与存储引擎有关
  • 索引文件的结构
    • hash
    • 二叉树
    • B树
    • B+树

hash

hash通常是采用hash算法计算出一个hash值

最简单的就是通过%运算计算出余数,然后将数据放到与余数对应的内存地址上

如果产生了hash碰撞,通常会将数据放到同一地址的链表里

  • 缺点

    • 使用hash存储需要将所有的数据文件添加到内存中,比较耗费内存空间
    • 如果所有查询都是等值查询,那么hash确实很快,但是实际生产场景中范围查询会比较多,这时候hash就不太适合了

二叉树和红黑树

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pcXGppoa-1623210785331)(image-20201018220203092.png)]

红黑树也是二叉树的一种,是通过左旋/右旋来维持二叉树的平衡

通过将值较小的放到左叶子节点,数值较大的放到右叶子节点来保证查找效率(类似于二分查找)

  • 缺点

    无论是二叉树还是红黑树,都会因为树的深度过深而造成IO次数变多,影响数据读取的效率

    结合之前讲的“局部性原理”,一次读取都是4K的整数倍,也就是一个节点是4K的话,那么上图二叉树找13这个数值需要找5次

  • 总结

    所以我们提升索引查询效率主要从两个维度,一个是减少IO次数(主要手段),一个是减少单次IO数据量

B树

首先纠正一个错误的认识

没有B-树一说,实际上B-树就是B树

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-swB7hDkd-1623210785337)(image-20201018221325539.png)]

  • B树的特点(m为树的阶)
    1. 所有的键值分布在整棵树中
    2. 搜索有可能在非叶子节点结束,在关键字全集内做一次查找,性能逼近二分查找
    3. 每个节点最多拥有m个子树
    4. 根节点至少有2个子树
    5. 分支节点至少拥有m/2棵子树(除根节点和叶子节点都是分支节点)
    6. 所有叶子节点都在同一层,每个节点最多可以有m-1个key,并且以升序排列

这里不再赘述,摘自知乎某文的说法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xExLVrn0-1623210785340)(image-20201020172432399.png)]

  • B树的查询流程:

    如上图我要从上图中找到E字母,查找流程如下

    (1)获取根节点的关键字进行比较,当前根节点关键字为M,E<M(26个字母顺序),所以往找到指向左边的子节点(二分法规则,左小右大,左边放小于当前节点值的子节点、右边放大于当前节点值的子节点);

    (2)拿到关键字D和G,D<E<G 所以直接找到D和G中间的节点;

    (3)拿到E和F,因为E=E 所以直接返回关键字和指针信息(如果树结构里面没有包含所要查找的节点则返回null);

  • 缺点

    会存在某个指针对应数据块过大的问题,比如4K一个数据块,每个只存储了1.9k,最终会造成空间浪费,并加深树的结构,导致读取(查)效率降低

B+树

  • B+ 树是B树的基础上做的一个优化

    • 每个节点可以包含更多的节点,这么做的理由有二:第一个是降低树的高度,第二个原因是将数据范围变为多个区间,区间越多,数据检索越快(有点类似跳表)
    • 非叶子节点存储key,叶子节点存储key+value
    • 叶子节点两两指针相互连接(符合磁盘预读特性,InnoDB每次预读为16K),顺序查询性能更高
  • 特点

    1、B+树的层级更少:相较于B树B+每个非叶子节点存储的关键字数更多,树的层级更少所以查询数据更快;

    2、B+树查询速度更稳定:B+所有关键字数据地址都存在叶子节点上,所以每次查找的次数都相同所以查询速度要比B树更稳定;

    3、B+树天然具备排序功能:B+树所有的叶子节点数据构成了一个有序链表,在查询大小区间的数据时候更方便,数据紧密性很高,缓存的命中率也会比B树高。

    4、B+树全节点遍历更快:B+树遍历整棵树只需要遍历所有的叶子节点即可,,而不需要像B树一样需要对每一层进行遍历,这有利于数据库做全表扫描。

    B树相对于B+树的优点是,如果经常访问的数据离根节点很近,而B树非叶子节点本身存有关键字其数据的地址,所以这种数据检索的时候会要比B+树快。

    因为B+树具备以上特点,所以可以对B+树进行两种查找运算:一种是对于主键的范围查找和分页查找,另一种是从根节点开始,进行随机查找

    B+树能做到3次IO取到百万甚至千万级数据(取决于数据大小)

InnoDB和MyISAM文件存储和索引区别

MySQL文件存储结构

1、如果表格b采用MyISAM,在数据库/usr/local/mysql/data/user 中会产生3个文件:
b.frm :描述表结构文件,字段长度等
b.MYD(MYData):数据信息文件,存储数据信息(如果采用独立表存储模式)
b.MYI(MYIndex):索引信息文件。

2、如果表格user采用InnoDB,在数据库/usr/local/mysql/data/user中会产生1个或者2个文件:
b.frm :描述表结构文件,字段长度等
如果采用独立表存储模式,/usr/local/mysql/data/user中还会产生b.ibd文件(存储数据信息和索引信息)
如果采用共存储模式的,数据信息和索引信息都存储在ibdata1中

索引

InnoDB

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YCazgp70-1623210785346)(image-20201020181711863.png)]

注意:

  1. InnoDB是通过B+树结构对主键创建索引,然后叶子节点中存储记录,如果没有主键,那么会选择唯一键,如果没有唯一键,那么会生成一个6位的row_id作为主键

  2. 如果创建索引的键是其它字段,那么在叶子节点中存储的是该记录的主键,然后再通过主键索引找到对应的记录

    比如有(id,name,age)三列,如果是对name创建索引,那么实际通过name查询的时候会先在name对应的索引树里找到对应记录,但是记录中只存了ID,第二步会通过这个ID去主键索引树找到并将数据返回

综上,主键索引和非主键索引创建和查询方式都是不同的

MyISAM

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0OfkhCoE-1623210785352)(image-20201020182832643.png)]

MyISAM也是采用B+树的方式,但是不再存储具体的数据,而是一个地址,然后根据地址去磁盘里读取对应数据

这也是为什么MyISAM会存储两个文件,而InnoDB只会存储一个文件

存储引擎对比
MyISAMInnoDB
索引类型非聚簇索引聚簇索引
支持事务
支持表锁
支持行锁
支持外键
支持全文索引是(5.6 or later)
适合操作类型大量select大量insert、delete、update

索引的分类

MySQL索引分五类

  • 主键索引

    主键是一种唯一性索引,但它必须指定为Primary Key,每个表只能有一个主键(一个主键并不表示一个列,也有可能是多个列组成的复合主键

    (这个我们无需创建,MySQL会自动帮我们创建)

  • 唯一索引

    索引列的所有值都只能出现一次,即必须唯一,值可以为空

    (主键索引也是唯一索引的一种,除了非空,MySQL会自动帮我们创建)

  • 普通索引

    基本的索引类型,值可以为空,没有唯一性的限制

  • 全文索引

    全文索引的索引类型为FullText。全文索引可以在varchar、char、text类型的列上创建

    (基本不用,现在一般全文检索都用Solor、Lucene、ES)

  • 组合索引

    多列值组成的一个索引,专门用于组合搜索

索引基本知识

谈到索引,一定要往存储引擎上靠,常见的存储引擎有基于InnoDB、MyIsam、Memory三种

索引有什么优点?

  • 大大减少了需要扫描的数据量
  • 避免了排序和临时表
  • 将随机IO变成了顺序IO

索引的用处?

  • 快速查找匹配where子句的行
  • 从约束中消除行,如果可以在多个索引之间进行选择,mysql通常会使用找到最少行的索引
  • 如果表具有多列索引,则优化器可以使用索引的任何最左前缀来查找行(最多匹配原则,后文会有详细阐述)
  • 当有表连接的时候,从其他表检索行数据
  • 查找特定索引列的min或者max值
  • 如果排序和分组时在可用索引的最左前缀上完成,则对表进行排序和分组
  • 在某些情况下,可以优化查询以检索值而无需查询数据行

为什么MySQL不自动为所有字段创建索引?

首先索引越多查询越快这个认知是错误的

索引最终会写到磁盘里,所以查询的时候会有读盘操作会因为索引增多而变慢

增加删除数据MySQL会重新维护索引,而B+树会存在页分裂和页合并问题

每一次读取磁盘数据都是基于磁盘上的”页“

如果插入数据且页已经装满

这时候对页会有一个分裂的操作

如果数据量比较小,且页有富余空间,这时候会有页合并的操作

这个过程会消耗IO

MySQL建表的时候主键应该怎么选择?

  • 自然主键

    跟业务挂钩的主键

  • 代理主键

    跟业务无关的主键,比如ID列

    ID列是否需要设置自增?

    需要,这样方便MySQL去维护索引

常见技术名词?

  • 回表

    在InnoDB和MyIsam存储引擎中,底层用的数据结构是B+树

    而Memory方式底层数据结构为Hash表

    而InnoDB下叶子节点直接放置数据

    主键索引可以通过索引直接找到数据并返回

    但是实际场景下,我们不经常使用主键去查询数据,可能是name列

    我们就可以给name列添加索引

    但是name的索引叶子节点并不会存储数据,而是会存储主键的值

    查找到对应name后会取主键的值,然后再去主键表中取数据的这种方式就叫回表

  • 覆盖索引

    首先看两条查询语句

    select * from student where name='zhangsan' # 有回表操作
    select id from student where name='zhangsan' # 无回表操作
    

    因为基于name的B+树存储的本来就是主键id的值,所以第二条sql不会有回表操作

    以上不需要回表操作的过程就被称为索引覆盖(或覆盖索引)

  • 最左匹配

    组合索引(多列索引),比如用name和age建立组合索引idx_name_age

    那么在查找的时候会先查找name再查找age

    select * from student where name='zhangsan' # 走索引
    select * from student where name='zhangsan' and age=10 # 走索引
    select * from student where age=10 # 不走索引,因为跳过了name直接查age,所以按照最左匹配原则不会走进索引
    select * from student where age=10 and name='zhangsan' # 走索引,MySQL优化器会自动优化我们的查询
    
  • 索引下推

https://www.bilibili.com/video/BV1K64y1F76m?p=6 3:04


以上是关于MySQL 索引机制以及调优的主要内容,如果未能解决你的问题,请参考以下文章

mysql索引

数据库原理以及SQL优化:数据库调优基础入门

数据库原理以及SQL优化:数据库调优基础入门

数据库原理以及SQL优化:数据库调优基础入门

MySQL调优 - 读写分离

页面分配策略:驻留集大小调入页面的时机以及从何处调入页面