mysql面试看这篇就够了

目录

        • 一、什么是索引
        • 二、为什么需要索引
        • 三、Mysql常见的几种索引数据结构
        • 四、Mysql结合B+Tree数据结构进行存储的细节
        • 五、使用索引时需要注意些什么
        • 六、explain SQL分析工具介绍
        • 七、优化SQL原则
        • 八、mysql锁分析

一、什么是索引

  • 索引可以类比成书本的目录方便我们查找数据使用

二、为什么需要索引

  • 想想我们平时在书本上查找资料时,如果没有目录的话我们得一页一页的去看才能找到自己想要的这个查找过程是极其慢的,但是我们有了目录就不一样了,我们只需先查阅目录然后快速的定位到我们需要的页
  • 系统设计大师根据生活经验,于是呼将我们的计算机文件系统也设计成类似书本一样拥有个一个类似目录的存储结构,称之为索引

三、Mysql常见的几种索引数据结构

Mysql表常用索引结构有两种BTree和Hash其中BTree又分为B-Tree和B+Tree本文只介绍BTree

  • 在谈Mysql常用的几种索引结构之前我们先回忆一下我们学过的二叉查找树,下图以二叉查找树为索引结构给student_info表建了个索引其中id为主键索引字段
    mysql面试看这篇就够了_第1张图片
    我们要查找王五的信息只需先在索引中找到id=7的节点(比较了三次)然后根据节点指向的内存读取王五的信息即可,速度很快比在表中一行一行查快多了,这样看来用二叉查找树当Mysql的表索引结构似乎很完美,其实不然,在实际生产中,图中的student _info表存储的数据可能会有几百万条,这个时候二叉查找树的高都会陡然上升,随之查找效率也陡然下降,在这里还要普及一个知识点记住即可,Mysql每个索引节点占用一个磁盘页(大小一般为4K)哪怕节点数据再小也会分配一个磁盘页给此节点,每读取一个节点数据(即一个磁盘页)需要做一次I/O操作,I/O操作对于CPU的运行速度来讲效率是相当低下的,随着数据的增多,树的高度随之曾高,I/O操作也跟着变多,这个时候我们就不得不考虑如何让每个节点存储更多的数据,以降低树的高度,减少I/O操作次数,由此引申出了B-Tree这种数据结构
  • 在谈BTree之前我们先普及一下磁盘读取数据的过程,下图是一个简易的磁盘寻址示意图,I/O设备在接收到CPU查询数据的指令时,首先磁头会在不同的磁道移动这个过程比较慢,当确定了数据存在哪个磁道就会读取数据,这个过程比较快,由此可知从磁盘读取数据时间主要消耗在寻道上
    mysql面试看这篇就够了_第2张图片
  • B-Tree的定义首先B-Tree是一颗多路平衡查找树,它的每个节点最多拥有k孩子,k被称为B-Tree的阶,k的大小由磁盘页的大小决定(磁盘页的大小÷节点中存储的记录大小=k -1)

1.根结点至少有两个孩子节点。
2.每个中间节点都包含k-1个元素和k个孩子,其中 m/2 <= k <= m
3.每一个叶子节点都包含k-1个元素,其中 m/2 <= k <= m
4.所有的叶子结点都位于同一层。
5.每个节点中的元素从小到大排列,节点当中k-1个元素正好是k个孩子包含的元素的值域分划。

  • 咱们以B-Tree为索引结构做出student_info表在内存中的结构图(其中data表示,表中的一条数据记录或指向表记录的文件指针,这里为了作图方便因此直接用data表示),此B-Tree为三阶B树,由图可知,由于B-Tree树每个节点可以存储多条记录,较上面的二叉查找树的“高瘦”特点来说B-Tree变成了“矮胖”,比如我们要查找王五的信息首先id=4的节点加载进内存(一次I/O操作)与王五的id进行比较,接着将id=4的节点的右孩子加载进内存(一次I/O操作)进行比较,接着讲id=7的节点加载进内存(一次I/O操作),分析可知,当数据量很大时,我们可以适当提高B-Tree的度,让B-Tree变的“矮胖”,这样就减少了比较次数,I/O操作的次数
    mysql面试看这篇就够了_第3张图片
  • 虽然B-Tree相对于二叉查找树来说大大提高了效率(减少了I/O次数)但是还不够,由于实际系统中确定B-Tree的度k的大小,其中一个因素之一就是取决于磁盘页大小÷记录大小=k -1在磁盘页大小(4k)已知的情况下只有减小data(记录)的大小才能提高B-Tree的度k,由此引申出B+Tree的概念,B-Tree与B+Tree最大的差别之一就是,B-Tree将数据(这里的数据同上文,中真正的记录,或者文件指针)与索引存在各个点中,而B+Tree非叶节点只存储索引字段,而叶子节点才存储真正的记录,这样就大大提高了B+Tree的度从而从而降低了B+Treed的高度在查询数据时减少了I/O操作次数
  • B+Tree的定义 首先B+树是B树的一种变形形式,B+树上的叶子结点存储索引字段以及相应记录的地址(又或者是记录),叶子结点以上各层作为索引使用。一棵m阶的B+树定义如下

1、每个结点至多有m个子女
2、除根结点外,每个结点至少有[m/2]个子女,根结点至少有两个子女
3、有k个子女的结点必有k个关键字B+树的查找与B树不同,当索引部分某个结点> 的关键字与所查的关键字相等时,并不停止查找,应继续沿着这个关键字左边的指针向下,一直查到该关键字所在的叶子结点为止
看完定义之后我们再来B+Tree的结构长什么样,如下图所示,其中的data表示数据或者文件指针
mysql面试看这篇就够了_第4张图片

四、Mysql结合B+Tree数据结构进行存储的细节

经过以上分析我们已经知道了B+Tree是一颗多路平衡查找树,数据都存储在叶子>节点,非叶子节点只存储索引字段的值,并且叶子节点之间是通过指针进行连接的

  • 在谈细节之前们必须先明确一下几个概念
    • 索引文件与数据文件之间的关系分为以下两种关系,有兴趣的同学可以新建两个表分别采用InnoDBMyISAM存储引擎进行存储,然后打开mysql安装目录下的的数据库文件就可以看到如下几个文件(我这里事先建好了两个表分别采用用不同的存储引擎),其中表table_1是采用InnDB存储引擎进行存储的,由于InnoDB是采用聚集索引进行存储的,我们可以看到table_1表只有一个table_1.ibd文件用来存储索引和数据,table_2是采用MyISAM进行存储的,由于MyISAM存储引擎是采用非聚集索引的,我们可以看到表table_2有两个文件table_2.MYD用来存储数据,table_2.MYI用来存储索引,经以上分析我们总结如下两点
      mysql面试看这篇就够了_第5张图片
      • ①聚集索引: 可以形象的理解为,数据都存储在叶子节点,即索引与数据文件存储在一起InnoDB存储引擎就是采用的聚集索引进行存储的
      • ②非聚集索引: 可以形象的理解为,叶子节点存储数据的文件指针,即索引与数据文件是分卡存储的MyISAM存储引擎就是采用非聚集索引进行存储的
    • 什么是主键索引,什么又是辅助索引,什么又是联合索引?
      • 主键索引: 可以理解为以主键为B+Tree的树节点,叶子节点存储数据或者文件指针
      • 辅助索引: 可以理解为非主键字段加了索引,非主键字段由于加了索引也会在系统中形成一颗B+树的索引结构,与主键索引形成的B+树结构唯一不同的就是,叶子节点指向的是主键索引(大家可能会有疑问,当我们没有指定主键索引时,该怎么办,如果没有指定时,系统会选择一个重复率最低的字段为主键索引,如果这样的字段也不存在的话,系统会采用,后台生成的,ROW_ID,为索引值,进行存储),为什么这么做? 这样避免了数据的重复存储
      • 联合索引: 可以理解为同时指定多个字段为索引字段,怎么理解呢?比如我们有张学生表,有id,name我们可以指定index(id,name)为索引字段,可以简单的认为id,name变成了一个整体,作为索引字段

五、使用索引时需要注意些什么

  • 主键索引尽量采用自增的数字 ,为什么呢? 一句话可以减少磁盘碎片,降低B+Tree的变换次数,详解如下
    • 在这里我们还需要明确一定的是,虽然采用B+Tree树进行索引结构的建立,提高了查询速度,但是新增或删除数据时,B+Tree需要进行变换(怎么变换是一个比较复杂的工程,后期会转门出文章进行讲解),以保持身的特点(具体是什么特点可以参考前文说的B+Tree的定义),这个变换的过程代价是不会有忽略的
    • 结合上面分析,我们知道Mysql采用B+Tree结构进行索引的建立,每个非叶子节点大小等于4k,刚好是一个磁盘页的大小
    • 如果我们采用自增的数字为主键索引,再结合B+Tree的特点(节点内的元素都是从小到大排列的),这样我们在每新增一个节点时,都会按顺序的一个一个节点排满,而后才进行分裂,这样不但减少了B+Tree变换的次数也减少了磁盘碎片,删除节点也同理,如果不太理解的话可以利用此网站进行一个B+Tree新增OR删除数据的节点的模拟操作
  • 使用联合索引时我们需要注意什么? 一句话最左匹配,详解如下
    • 首先假设我们有张student表,表中有A,B,C三个字段我们现在建立联合索引index(A,B,C)
    • 使用index(A),index(A,B),index(A,B,C)能命中索引
    • 使用index(A,C),index(B,C)不能命中索引

六、explain SQL分析工具介绍

explain打印参数图

  • id: 选择标识符(id越大越先执行)
  • select_type: 表示查询的类型(分为(SIMPLE)和复杂查询(PRIMARY)两大类)复杂查询又分为:简单子查询、派生表(from 语句中的子查询)、union 查询
  • table: 查询的是那张表(注意:mysql 能够在优化阶段分解查询语句,在执行阶段用不着再访问表或索引,例如:在索引列中选取最小值,可以单独查找索引来完成,不需要在执行时访问表)
  • partitions: 匹配的分区(当数据库有进行分区时,显示操作的是那个分区)
  • type: 表示关联类型或访问类型,即 MySQL 决定如何查找表中的行,查找数据行记录的大概范围(依次从最优到最差分别为:system > const > eq_ref > ref > range > index > ALL一般来说,得保证查询达到range级别,最好达到ref
    • system&constant: mysql 能对查询的某部分进行优化并将其转化成一个常量用于 primary key 或 unique key 的所有列与常数比较时,所以表最多只有一条行记录匹配,读取 1 次,速度比较快,system 是 const 的特例,表里只有一条元组匹配时为system
    • eq_ref: primary key 或 unique key 索引的所有部分被连接使用 ,最多只会返回一条符合条件的记录,这可能是在 const 之外最好的联接类型了,简单的select 查询不会出现这种 type
    • ref: 相比 eq_ref,不使用唯一索引,而是使用普通索引或者唯一性索引的部分前缀,索引要和某个值相比较,可能会找到多个符合条件的行
    • range: 范围扫描通常出现在 in(), between ,> ,<, >= 等操作中。使用一个索引来检索给定范围的行
    • index: 扫描全表索引,这通常比 ALL 快一些(index 是从索引中读取的,而 all 是从硬盘中读取)
    • ALL: 即全表扫描,意味着 mysql 需要从头到尾去查找所需要的行,通常情况下这需要增加索引来进行优化了
  • possible_keys: 表示查询时,可能使用的索引(有时可能出现possible_keys 有值,而 key 显示 NULL 的情况,这种情况是因为表中数据不多,mysql 认为索引对此查询帮助不大,选择了全表查询,如果该列是 NULL,则没有相关的索引,在这种情况下,可以通过检查 where 子句看看是否可以添加一个适当的索引来提高查询性能,然后用 explain 查看效果)
  • key: 表示实际使用的索引
  • key_len: 索引字段的长度
  • ref: 列与索引的比较
  • rows: 扫描出的行数(估算的行数)
  • filtered: 按表条件过滤的行百分比
  • Extra: 执行情况的描述和说明
    • Using index: 查询的列被索引覆盖,并且 where 筛选条件是索引的前导列,是性能高的表现,一般是使用了覆盖索引(索引包含了所有查询的字段)对于 InnoDBb 来说,如果是辅助索引性能会有不少提高
    • Using where: 查询的列未被索引覆盖,where 筛选条件非索引的前导列
    • Using where Using index: 查询的列被索引覆盖,并且 where 筛选条件是索引列之一但是不是索引的前导列,意味着无法直接通过索引查找来查询到符合条件的数据
    • NULL: 查询的列未被索引覆盖,并且 where 筛选条件是索引的前导列,意味着用到了索引,但是部分字段未被索引覆盖,必须通过“回表”来实现,不是纯粹地用到了索引,也不是完全没用到索引
    • Using index condition: 与 Using where 类似,查询的列不完全被索引覆盖,where 条件中是一个前导列的范围
    • Using temporary: Mysql 需要创建一张临时表来处理查询,出现这种情况一般是要进行优化的,首先是想到用索引来优化
    • Using filesort: Mysql 会对结果使用一个外部索引排序,而不是按索引次序从表里读取行,此时 mysql 会根据联接类型浏览所有符合条件的记录,并保存排序关键字和行指针,然后排序关键字并按顺序检索行信息。这种情况下一般也是要考虑使用索引来优化的

七、优化SQL原则

  • 最佳左前缀法则: 如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列
  • 不在索引列上做任何操作(计算、函数、(自动 or 手动)类型转换),会导致索引失效而转向全表扫描
  • 存储引擎不能使用索引中范围条件右边的列
  • 尽量使用覆盖索引(只访问索引的查询(索引列包含查询列)),减少 select *语句
  • mysql 在使用不等于(!=或者<>)的时候无法使用索引会导致全表扫描
  • is null,is not null 也无法使用索引
  • like 以通配符开头(’$abc…’)mysql 索引失效会变成全表扫描操作
  • 字符串不加单引号索引失效
  • 少用 or,用它连接时很多情况下索引会失效

八、mysql锁分析

  • 后续更新

你可能感兴趣的:(Mysql)