一. 索引优化面试题分析
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这一行就是存的其他字段;如果说这三个字段只是建的一个辅助索引
,那么蓝色区域存的肯定是主键
,我们这里三个字段建的是主键索引,联合主键。
网友评论