mysql的索引

索引的本质?

  • 索引的本质就是帮助mysql高效获取数据的排好序的快速查找数据结构
  • 索引存储在文件系统中
  • 索引的文件存储形式和存储引擎有关。

索引数据结构:

  • 二叉树
  • 红黑树
  • Hash树
  • B-Tree
  • B+Tree

优点

  • 1.数据库的IO成本
  • 2.数据库的唯一性
  • 3.加速表与表之间的连接
  • 4.减少查询中分组和排序的时间

缺点

  • 1.创建和维护索引耗费时间
  • 2.索引占用磁盘空间
  • 3.提高查询速度,但是大大的降低了更新表的速度。


案例:从上面的t表中查询一个数据。
select * from t where t.col2=89;
1.这里需要将mysql数据库中的t表中的col2数据,拿出来和89做对比。

  • 分析不添加索引:因为mysql中的数据分布在磁盘上的位置并不是有顺序和固定的,所以,每次读取磁盘上的数据,都需要进行磁盘io。上面的情况最坏需要io6次磁盘。
  • 分析使用二叉树索引:索引的话也是需要进行磁盘的io的,第一次io找到34,第二次io找到了89,获取到89对应的磁盘地址,第三次io可以获取到89的数据在数据库中对应的位置。
  • 弊端:使用二叉树的话,如果是有序的数据,则会转变为线性结构,就回导致全表扫描。

设计索引

建一个表:

mysql> CREATE TABLE index_demo(
-> c1 INT,
-> c2 INT,
-> c3 CHAR(1),
-> PRIMARY KEY(c1)
-> ) ROW_FORMAT = Compact;

这个新建的 index_demo 表中有2个INT类型的列,1个CHAR(1)类型的列,而且我们规定了c1列为主键,这个表使用 Compact 行格式来实际存储记录的。这里我们简化了index_demo表的行格式示意图:

我们只在示意图里展示记录的这几个部分:

  • record_type :记录头信息的一项属性,表示记录的类型, 0 表示普通记录、 2 表示最小记录、 3 表示最大记录、 1 暂时还没用过,下面讲。
  • next_record :记录头信息的一项属性,表示下一条地址相对于本条记录的地址偏移量,我们用箭头来表明下一条记录是谁。
  • 各个列的值 :这里只记录在 index_demo 表中的三个列,分别是 c1 、 c2 和 c3 。 其他信息 :除了上述3种信息以外的所有信息,包括其他隐藏列的值以及记录的额外信息。
    将记录格式示意图的其他信息项暂时去掉并把它竖起来的效果就是这样:

    把一些记录放到页里的示意图就是:

一个简单的索引设计方案

我们在根据某个搜索条件查找一些记录时为什么要遍历所有的数据页呢?因为各个页中的记录并没有规
律,我们并不知道我们的搜索条件匹配哪些页中的记录,所以不得不依次遍历所有的数据页。所以如果
我们 想快速的定位到需要查找的记录在哪些数据页 中该咋办?我们可以为快速定位记录所在的数据页而 建 立一个目录 ,建这个目录必须完成下边这些事:

下一个数据页中用户记录的主键值必须大于上一个页中用户记录的主键值。

假设:每个数据页最多能存放3条记录(实际上一个数据页非常大,可以存放下好多记录)。

mysql> insert into index_demo values(1,4,'u'),(3,9.'d'),(5,3,'y');

那么这些记录已经按照主键值的大小串联成一个单向链表了,如图所示:

从图中可以看出来,index_demo表中的三条记录都被插入到了编号为10的数据页中了,此时我们再来插入一条记录

mysql> insert into index_demo values(4,4,'a');

假如页10最多只能放3条记录,所以我们不得不再分配一个新页:

注意:新分配的数据页编号可能并不是连接的。他们只是通过维护着上一个页和下一个页的编号也建立了链表关系。另外,页10中用户记录最大的主键值是5,而页28中又一条记录的主键值4,因为5>4,所以这就不符合下一个数据页中用户记录的主键值必须大于上一个页中用户记录的主键值的要求,所以在插入主键值是4的记录的时候需要伴随着一次记录移动,也就是把主键值为5的记录移动到页28中,然后再把主键值为4的记录插入到页10中,这个过程的示意图如下:

这个过程表明了在对页的记录进行增删改操作的过程中,我们必须通过一些诸如记录移动的操作来始终保证这个状态一直成立;下一个数据页中用户记录的主键值必须大于上一个页中用户记录的主键值。这个过程称为页的分裂。

给所有的页建立一个目录项。

由于数据页的编号可能不连续的,所以在向index_demo表中插入许多条记录后,可能是这样的效果:

因为这些16kb的页在物理存储上是不连续的,所以如果想从这么多页中根据主键值快速定位某些记录所在的页,我们需要给他们做个记录,每个页对应一个目录项,每个目录下面包括下边两个部分:

  • 页的用户记录最小的主键值,用key表示
  • 页号,用page_no表示。

所以我们为上边几个页做好的目录就是这个样子的:

以 页28 为例,它对应 目录项2 ,这个目录项中包含着该页的页号 28 以及该页中用户记录的最小主
键值 5 。我们只需要把几个目录项在物理存储器上连续存储(比如:数组),就可以实现根据主键值快速查找某条记录的功能了。比如:查找主键值为 20 的记录,具体查找过程分两步:

    1. 先从目录项中根据 二分法 快速确定出主键值为 20 的记录在 目录项3 中(因为 12 < 20 < 209 ),它对应的页是 页9 。
    1. 再根据前边说的在页中查找记录的方式去 页9 中定位具体的记录。
      至此,针对数据页做的简易目录就搞定了。这个目录有一个别名,称为 索引 。

B+Tree的检索原理

初始化介绍:
1.一棵b+树,浅蓝色的块是一个磁盘快,可以看到每个磁盘快包含了深蓝色(数据项)和黄色(指针)
2.如磁盘块包含数据项17和35,包含指针p1、p2、p3。p1表示小于17的磁盘块,p2表示在17和35之间的磁盘块,p3表示大于35的磁盘块。
3.真实的数据存放在叶子结点即:3、5、9、10、13、15、28、29、36、60、75、79、90、99
4.非叶子节点不存储真实的数据,只存储指引索引方向的数据项,如17、35并不存在于数据表中。

查找过程:
1.如果查找数据项29,就会把磁盘块1加载到内存中,此时发生一次io,找到29存在于17和35之间,锁定磁盘块1的P2指针
2.把p2指针对应的磁盘块加载到内存中,发生第二次IO,找到26到30之间,锁定磁盘块的P2指针
3.加载上一次的P2指针对应的磁盘块,可以查询到数据。总计3次IO。

posted @ 2022-09-25 01:14  King-DA  阅读(24)  评论(0)    收藏  举报