没有索引的查找
在一个页查找
当数据量不多的时候,只有一个页的时候
- 以主键为搜索条件: 通过页目录进行二分法查找页,找到对应的槽。遍历当前的槽找到对应的数据
- 以其他列作为搜索条件: 因为非主键没有设立页的概念,所以只能从小到大的链表进行查找。从最小记录进行查找。
在很多页进行查找
大部分情况我们应该是这个思路
- 找到对应的页
- 在对应的页进行查找 但是在没有索引的前提下,是需要通过双向链表进行每个页的查找,在通过每个页找到对应的槽。这个效率非常低
有索引的情况
索引分两种情况,聚簇索引(唯一主键)和二级索引
- 当前页(16K)满的时候,在插入下一页,页号不是连续的
- 由于主键5 >4 需要移动记录,使得 主键是递增排序的
- 下一个数据页中用户记录的主键值必须大于上一个页中用户记录的主键值。这个过程我们也可以称为页分裂
- 由于数据页不是连续的,为了快速定位是哪个数据页,我们需要给数据页建立一个目录,既每个页建立一个目录页
- 页的用户记录中最小的主键值,我们用key来表示。
- 页号,我们用page_no表示。
如果我们想寻找主键值为20的记录
- 首先定位要目录项3 (主键值为20 所以 12<20<209)
- 找到对应的页9
- 在通过二分法找到页9主键为20的记录,找到对应的记录
遇到的问题
- 最多能保证16KB的连续存储空间,而随着表中记录数量的增多,需要非常大的连续的存储空间才能把所有的目录项都放下
- 假设我们把页28中的记录都删除了,页28也就没有存在的必要了,那意味着目录项2也就没有存在的必要了,这就需要把目录项2后的目录项都向前移动一下,这种牵一发而动全身的设计不是什么好主意
- 所以我们这时候在回到行格式查看record_type的属性,这个时候就能区分出当前记录的类型了
0:普通的用户记录 1:目录项记录 2:最小记录 3:最大记录
- 目录项记录的record_type值是1,而普通用户记录的record_type值是0。
- 目录项记录只有主键值和页的编号两个列,而普通的用户记录的列是用户自己定义的,可能包含很多列,另外还有InnoDB自己添加的隐藏列。
- 还记得我们之前在唠叨记录头信息的时候说过一个叫min_rec_mask的属性么,只有在存储目录项记录的页中的主键值最小的目录项记录的min_rec_mask值为1,其他别的记录的min_rec_mask值都是0。
多个目录页
我们常说的b+树则形成
- 我们的实际用户记录其实都存放在B+树的最底层的节点上,这些节点也被称为叶子节点或叶节点
- 其余用来存放目录项的节点称为非叶子节点或者内节点
- 其中B+树最上边的那个节点也称为根节点。
- b+最大不能超过4层
聚簇索引
- 页内的记录是按照主键的大小顺序排成一个单向链表。
- 各个存放用户记录的页也是根据页中用户记录的主键大小顺序排成一个双向链表。
- 存放目录项记录的页分为不同的层次,在同一层次中的页也是根据页中目录项记录的主键大小顺序排成一个双向链表。
- B+树的叶子节点存储的是完整的用户记录。
二级索引
- 页内的记录是按照c2列的大小顺序排成一个单向链表。
- 各个存放用户记录的页也是根据页中记录的c2列大小顺序排成一个双向链表。
- 存放目录项记录的页分为不同的层次,在同一层次中的页也是根据页中目录项记录的c2列大小顺序排成一个双向链表。
- B+树的叶子节点存储的并不是完整的用户记录,而只是c2列+主键这两个列的值。
- 目录项记录中不再是主键+页号的搭配,而变成了c2列+页号的搭配。
- 在定位一条记录的时候,需要通过c2列的值找到对应的主键,在通过主键进行查询记录。这里面统称为回表
联合索引
c2和c3列为联合索引
- 先把各个记录和页按照c2列进行排序。
- 在记录的c2列相同的情况下,采用c3列进行排序
建立索引小记
- 主键一定会创建一套b+树,子节点是记录用户数据
- 在创建普通索引的时候,会另外创建一套b+树,子节点数据为当前索引列+主键,如果想查询一条记录需要查询两次b+树,因为只有主键的索引的子节点会记录用户数据
- 联合索引,先按照索引1进行排序,在按照索引2进行排序,查询方式一样,如果查询索引列的内容,则不需要进行回表,否则需要进行回表操作。
关于查询记录小记
查询当前表的记录是否已经进入到下一页了如何进行判断和查看
- 使用命令SHOW TABLE STATUS LIKE 'emp'; emp为你这边当前表
- 获取Data_length(单位为byte),需要除以1024 得到结果3600KB。