从根上聊聊数据库索引的事

282 阅读6分钟

数据库索引

谈谈我们常见的数据结构

基本数据结构

1. 数组

(1) 是由相同类型的元素(element)的集合所组成的数据结
(2) **分配一块连续的内存来存储**
(3) 利用元素的索引(index)可以计算出该元素对应的存储地址。

数组.png

2. 链表

(1) 各对象按照线性顺序进行排序.
(2) 链表中的顺序是由各对象中的指针所决定的.

链表.png

3. 栈/队列

(1) 栈和队列是动态集合,其中的delete操作去除的元素是预先设定好的.
(2) 栈实现的是LIFO(last in, first out)的策略
(3) 队列实现的是FIFO(first in,first out)的策略

栈.png

队列.png

4. 二叉树

p.left,right存放指向父节点,左孩子节点和右孩子节点的指针

二叉树.png

5. 有根树

(1) 在二叉树的基础上,将left,right属性用child1,child2,...,childn来代替
(2) 使用左孩子右兄弟表示法,每个节点包含2个指针,分别是
    <1> 指向最左侧孩子节点
    <2> 指向自己的右侧相邻的兄弟节点
    

有根树.png

6. 散列表

(1) 普通数组概念的推广.支持插入/查找/删除字典操作.
(2) 关键字集合+散列函数+冲突解决(链地址法/开放地址法/二次探查/双重散列)
    

哈希表.png

7. 二叉搜索树

(1) 通过二叉树进行组织
(2) 二叉搜索树性质:设x是二叉搜索树中的一个节点.如果y是x左子树中的一个节点,y.key<=x.key,如果y是x
右子树的一个节点,y.key>=x.key

二叉搜索树.png

8. 平衡搜索树(avl tree):

(1) 是一棵二叉搜索树
(2) 平衡搜索树的性质:
    <1> 是一棵空树或它的左右两个子树的高度差的绝对值不超过1
    <2> 左右两个子树都是一棵平衡二叉树。

AVL.png

9. 红黑树

(1) 红黑树是二叉搜索树
(2) 红黑树的性质
    <1> 每个节点是红色或者黑色
    <2> 根节点是黑色的
    <3> 叶节点(NIL)是黑色的
    <4> 如果一个节点是红色的,则它的的子节点是黑色的
    <5> 对每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点
(3) 一颗有n个节点的红黑树的高度至多为2lg(n+1)

红黑树.png

高级数据结构

1. b-tree

1. btree是一颗有根树
2. btree的性质:
    <1> 每个节点x有如下的属性
         a. x.n, 当前存储在节点x的关键字个数
         b. x.n个关键字x.key1,x.key2,...,x.keyn 满足 x.key1 <= x.key2 <= ... <= x.keyn
         c. x.leaf,如果x是叶节点,x.leaf = True;如果x是内部节点, xleaf = False
    <2> 每个内部节点 x 还包括 x.n+1 个指向其孩子的指针, x.c1, x.c2, ... , x.cn+1.叶节点没有儿子,所以叶节点的x.ci没有定义
    <3> 关键字x.keyi对存储在各子树的关键字范围加以分割:如果ki为任意一个存储在以x.ci为根的子树中的关键字,那么有k1 <= x.key1 <= <= k2 <= x.key2 <= x.keyx.n <= kx.n+1 
    <4> 每个叶节点具有相同的深度,即树的高度h
    <5> 每个节点所包含的关键字个数有上界和下界,用一个被称为b树的最小度数和固定整数t>=2来表示
        a. 除了根节点以外的每个节点必须至少有 t-1 个关键字,因此,除了根节点以外的每个内部节点至少有t个孩子.如果树非空,根节点至少有一个关键字
        b. 每个节点之多可以包含 2t-1 个关键字.因此一个内部节点至多可以有2t个孩子.但一个节点恰好有2t-1个关键字的时候,称这个节点是满的.
3. 需要特别说明的是,一棵树中每个存储数据的地方,应该既有键值(key)也有数据(value)

btree.png

2. b+tree

    1. 根节点至少包含一个元素
    2. b+tree存在两种节点,内部节点和叶子节点.内部节点不存储数据(value),存储键值和指向子节点的指针.(实现了内部节点和数据的解耦)
    3.内部节点中的key按照从小到大的顺序排序,对于内部节点中的一个key,作数中的所有key都小于它,右子树的key都大于它.叶子节点中的记录也按照key的大小排列.
    4. 叶子节点都包含先练叶子节点的指针,叶子节点本身依照关键字大小链接
    5. 父节点存有有孩子节点第一个元素的索引.
    6. 叶子节点不只存储键值(key),还存储了数据(value)

B+tree.png

索引与数据结构

(1) 索引本质: 是为了加快查找速度而设计的数据结构.其本质是把一个关键字和其对应的记录进行关联.

(2) 索引的实现逻辑 = 特定的数据结构(存储) + 算法(逻辑)

(3) 索引实现的矛盾点:

1. 查找速度
2. 修改/删除/增加速度
3. 占用内存大小

回到数据库存储

可能的支持索引的数据结构:

1. 数组.
    通过关键字存储在有序数组,通过二分查找等方法进行检索.
2. 链表
    通过改造使用跳跃表根据关键字有序存储,通过二分查找进行检索.
3. 哈希表
    支持单个key的直接查找.
4. 红黑树
    红黑树本身有序,根据二叉搜索树规则检索即可.
5. b/b+树

首先几个现状:

1. 从内存读取磁盘以页为(默认4kB)单位进行读取.
2. 除了单条记录的查找,还需要orderby,大于,小于等批量数据查找.
3. 总的数据量级很大,一般情况认为无法全量加载到内存.

我们尝试从0开始构建索引

在磁盘中我们首先构造一条记录的存储

单条数据.png

当然我们不可能只存一条数据,多条数据我们用指针关联起来

多条数据.png

我们的磁盘存储是使用页进行调度和读取的,因此我们也把我们的数据放在一个页中,增加页码,上一页指针和下一页指针

用页保存.png

此时我们一个页之内有多条数据了,为了更快地检索页内的数据,我们增加一个页内的索引

增加页内索引.png

Ok,这时候我们有了一个看起来比较合理的页,事实上我们会拥有很多的页,像这样

多页之间关联.png

这时候重要的问题来了,底层的数据已经存在了,怎么进行高效的查找呢?

我们尝试对页增加索引

试图组织存储结构.png

这个结构并不能解决问题.1. 不是按照页的格式进行存放的,2. 索引的数量随着页的增加正比例增加

尝试采用多级索引的方式.

多级索引.png

等等,这不就是b+树吗?

参考资料:

  1. 算法导论第二版
  2. wiki
  3. github.com/roy2220/pla…
  4. www.bilibili.com/video/BV1Aa…
  5. www.bilibili.com/video/BV1sQ…