1. Mysql索引数据结构详解

一. 索引优化面试题分析

1.1 分析以下几条sql的索引使用情况

SELECT * FROM titles WHERE emp_no='10001' AND title='Senior Engineer' AND from_date='1986-06-26';
SELECT * FROM titles WHERE title='Senior Engineer' ;
SELECT * FROM titles WHERE emp_no > ‘10001';
SELECT * FROM titles WHERE emp_no > ‘10001' and title='Senior Engineer';
SELECT * FROM titles WHERE emp_no > ‘10001' order BY title;

二. 索引到底是什么

  • 索引是帮助MySQL高效获取数据的排好序的数据结构
  • 索引存储在文件里
  • 索引结构
    • 二叉树
    • 红黑树(相对于二叉树多了自动平衡)
    • HASH
    • BTREE
    • 数据结构教学网站:https://www.cs.usfca.edu/~galles/visualization/Algorithms.html

二叉树:某些特定情况(列递增)查询次数和正常查询次数几乎一样多
红黑树:每一层存储的数据是2的(n-1)次方的数据量,如果有几百万数据,有20层,差的数据在叶子节点,下面,深度太大,查询速度还是很慢
HASH:可以通过hash计算到具体的磁盘位置,速度非常快,但是不能范围计算(a>10)
BTREE:在节点的横向做文章,增大横向节点数据,减少高度,层数,几百万的数据高度可能控制到3-5层,那么查询任何一条数据通过索引最多查找5次,大多情况下查3次就完了

0x77 是文件存在磁盘的地址

2.1 索引概述

三. 索引底层数据结构与算法

3.1 B-Tree

  • 度(Degree)-节点的数据存储个数
  • 叶节点具有相同的深度
  • 叶节点的指针为空
  • 节点中的数据key从左到右递增排列


引入的概念,相当于原来那个节点,把这个节点扩容,让这个节点可以存储多个索引,每个索引对应数据库的行的数据。原来红黑树是一个小节点,B-Tree实际上在那个小节点里面又存储了很多很多小节点,相当于把原来的小节点变成了大节点,大节点里面的每一个小节点就是数据库对应的一行记录,记录里面有一个是key, value的结构,key对应这一行的缩影,value对应这一行的数据

横向怎么查:比如要查询索引是77的元素,先去磁盘里吧77那一层的大节点查出来,这个节点全部load到内存里面,实际上在内存里面去查找的,内存里面做的是随机访问,是非常快的,跟磁盘的寻道旋转的查找时间去比,基本可以忽略不计的,主要是查这个节点费时间,查到这个节点之后把这个节点整个数据放到内存之后,在这个大节点里面查某一个小节点,也就是某一行记录的索引是非常快的,因为是在内存里面做操作,都是内存的随机访问,直接通过地址在内存里面去找,非常快。

疑问:度越大越好,100万数据,度设置100万,那么只有一层了,查找更快了?

不行,如果越大越好,那么所有数据都在一个节点了,这个节点非常大,几十上百M,磁盘的操作不可能一次把所有都拿出来。如果读取磁盘每次IO操作读取一页的数据(大小:4K,磁盘数据的基本单位),每次IO读取页数跟计算机的性能有关,如果大节点是10M,那么要读取完这个节点要很多次IO操作,最好的是一次性IO操作读取完一个节点,所以不能越大越好。这个是有上限的,不能无限扩大,是根据计算机硬件情况mysql来自动优化的。一般mysql会把一个节点的大小设置成一页(4K),能算出度的大小。4K/小节点数据=度,所以小节点越小,度越大。

3.2 B+Tree(B-Tree变种)

  • 非叶子节点不存储data,只存储key,可以增大度
  • 叶子节点不存储指针
  • 顺序访问指针,提高区间访问的性能


B+Tree: 把数据放到叶子节点(将key依次复制到下面的非叶子节点,并将数据移到叶子节点),非叶子节点不存储任何数据,存储索引key(非常小),同等的节点大小可以存储更多的索引,非叶子节点的度更大,高度就越低,只有非叶子节点才影响查找次数,叶子节点是最后一次查找,无所谓,总的查找次数是没有任何影响。key有一定的冗余(相同),key很小,适量的冗余可以接受。
B+Tree在底层的叶子节点之间(节点的尾元素和头元素之间)还有一个指针的存储:存储指针是为了范围查询,直接通过指针去找

3.3 B+Tree索引的性能分析

  • 一般使用磁盘I/O次数评价索引结构的优劣
  • 预读:磁盘一般会顺序向后读取一定长度的数据(页的整数倍)放入内存
  • 局部性原理:当一个数据被用到时,其附近的数据也通常会马上被使用
  • B+Tree节点的大小设为等于一个页,每次新建节点直接申请一个页的空间,这样就保证一个节点物理上也存储在一个页里,就实现了一个节点的载入只需一次I/O
  • B+Tree的度d一般会超过100,因此h非常小(一般为3到5之间)

存储引擎设置在级别,不是数据库,同一个库两张表可以用不同的存储引擎


innodb引擎数据库文件:

test_innodb_lock.frm: innodb表结构文件
test_innodb_lock.ibd: 索引和数据文件,在一起

myisam引擎数据库文件:

test_myisam.frm: myisam表结构文件
test_myisam.MYD: 数据文件
test_myisam.MYI:索引文件

3.4 MyISAM索引实现(非聚集: 索引和数据分开存储)

MyISAM索引文件和数据文件是分离的
主键索引:

非主键索引,辅助索引:(myisam非主键索引和主键索引存储方式一样)

MyISAM叶子节点存的是指针,通过指针去文件里面再找数据

3.5 InnoDB索引实现(聚集:将索引和数据聚集到一起在叶子节点上)

  • 数据文件本身就是索引文件
  • 表数据文件本身就是按B+Tree组织的一个索引结构文件
  • 聚集索引-叶节点包含了完整的数据记录
  • 为什么InnoDB表必须有主键,并且推荐使用整型的自增主键?

为什么InnoDB表必须有主键:表数据文件本身就是按B+Tree组织的一个索引结构文件 ,如果没有设置,会默认一列不重复的作为主键,如果没有字段不重复的,后台会默认生成一个不重复字段作为主键(整型的),推荐的是自增的主键

UUID和整型的自增主键区别:

  • UUID比较大,比自增主键浪费一点存储空间;
  • 查找的时候,索引会比较大小去查找(比这个大的去找右边,小的找左边),int类型根据大小比较,UUID根据字符的ASCII编码大小比较,显然,整型的int类型的比较大小比字符串类型的比较大小速度快
  • 主键自增插入直接在已有的数据后面插入,是连续的,查找范围的时候可以锁定某一页的某个范围,查找非常快,而UUID是随机生成的,可能会在插在已有的数据之间,导致已有的数据中间分裂,还可能造成其他节点分裂,速度很慢
  • 为什么非主键索引结构叶子节点存储的是主键值?(一致性和节省存储空间)

innodb主键索引将一行数据存储到叶子节点上

innodb非主键索引存储的是主键索引的值:为了一致性和节省存储空间

3.6 联合索引的底层存储结构长什么样?

varchar类型两个索引(key1, key2)之间是 key1 + key2

这里是最上面的三个索引(emp_no(int), title(varchar), from_date(data))数据结构。先比较第一个字段,如果符合条件后面的字段就不用比了,然后索引顺序依次比较。。。

分库,主键递增,保证id不冲突,设置步长

第一个库主键存1, 3, 5...,第二个库存2, 4, 6...

四. 索引最左前缀原理

单值索引实际上也是一种联合索引,只不过值只有一个。

  • 联合索引的底层数据结构长什么样?

    例如:员工表字段分别是员工号员工职位员工入职日期,还有其他字段,选择这三个字段作为联合索引,那么叶子节点下面蓝色的(2002-06-03)是什么:如果这三个字段作为主键索引的话,可以标识某一行记录,那么剩下的蓝色区域value这一行就是存的其他字段;如果说这三个字段只是建的一个辅助索引,那么蓝色区域存的肯定是主键,我们这里三个字段建的是主键索引,联合主键。

你可能感兴趣的:(1. Mysql索引数据结构详解)