索引
索引的作用
引入索引是为了 加快数据的访问,就像查字典一样,我们根据拼音或者偏旁查找具体的字会更快
索引项
- 搜索键:用于查找记录的属性或属性集合
- 指针:指向与搜索键值匹配的一个或多个记录

索引文件
索引文件是由一系列 索引项 组成的,索引文件通常比数据文件小
索引的基本类型
- 有序索引:搜索键按顺序存储
- 哈希索引:用哈希函数将搜索键分布到不同的桶中
索引评估指标
- 支持的访问类型
- 指定某属性值的记录,例如查找
age=20的人 - 范围查询,即属性值在某范围内的记录,例如查找
10 <= age <= 20的人
- 指定某属性值的记录,例如查找
- 访问时间
- 插入时间
- 删除时间
- 空间开销
有序索引
有序索引就是指,索引项要根据搜索键的值排序
主索引 (聚集索引)
主索引是与文件的顺序结构紧密相关的索引,它的搜索键决定了文件记录的顺序,通常在 顺序文件 中使用
顺序文件就是指,文件记录已经按照某个规则(比如某个属性的值)进行排序过了
主索引通常是主键,但并不绝对
辅助索引 (非聚集索引)
辅助索引为非主键字段提供索引支持,它的搜索键与文件的顺序无关
顺序索引文件
索引顺序文件是一种特殊的顺序文件,它结合了顺序文件的优势和索引的优势。它使用了一个主索引来对记录进行排序
数据记录通过主索引的查询指针快速访问,而无需扫描整个文件
密集索引和稠密索引
密集索引
每个搜索键值都有一个索引记录,简单来说,就是每条记录的搜索键值都必须有一个对应的索引项(可以是多对一,即多条记录有相同的索引键值)

密集聚集索引
存储每个搜索键值的索引记录,并指向该值的第一个记录,后续记录按顺序存储

也就是相同的搜索键就只有一个索引项,该索引项的指针指向第一个记录,后续的记录按顺序存储
密集非聚集索引
存储相同搜索键值的所有记录的指针

相同的搜索键只有一个索引项,每个索引项的指针指向一个 桶,这个桶里存储了对应搜索键的所有记录的指针
稠密索引
只为某些搜索键值存储索引条目,适用于按搜索键顺序排列的记录

有些记录的搜索键值没有对应的索引项,索引项中,每个搜索键就是一组记录的开头,就相当于对记录进行分组,然后每个组中开头的记录的搜索键值作为索引项,所以要求要按顺序排列
稀疏索引相比于密集索引占用更少的空间和维护开销,但查找记录时可能较慢
优化:在每个 数据块 中放一个索引条目,指向该块中最小的搜索键值

多级索引
如果主索引太大而无法放入内存,可以将其作为顺序文件,再对其构建稀疏索引
- 外层索引:指向主索引的稀疏索引
- 内层索引:实际的主索引文件

如果外层索引还是太大,可以在构建一层索引
多级索引插入或者删除记录时,要同时更新每一层的索引,所以层级越多,相应的开销也越大
索引更新
多级索引,插入和删除的算法只是单级索引的简单扩展。也就是说,多级索引的插入和删除操作遵循相同的基本规则,只是它们会作用于多个索引层次
单级索引删除:
-
稠密索引:
- 删除对应记录,如果搜索键值只有这么一个对应的记录,那么这个搜索键的索引项也要删除
-
稀疏索引:
- 如果删除的记录的搜索键在索引文件中,那么删除该记录后,它对应的索引项也要删除,然后使用这条记录的下一条记录的搜索键替换
- 如果下一个记录的搜索键值已经在索引中有对应的条目,那么就直接删除,不用替换
单级索引插入:
-
稠密索引:
- 如果插入记录的搜索键在索引中不存在,就插入这个搜索键到索引中
-
稀疏索引:
- 如果索引是稀疏的,并且每个索引条目代表的是文件中的一个块,那么只有在新插入的记录导致了新块的创建时,才需要修改索引。
- 如果新块创建了新的记录,新块中的第一个搜索键值会被插入到索引中
位图索引
-
Bitmap 索引本质上是一个 位图
-
对于关系中的 每一条记录,都有一个对应的位
-
每个属性的每个可能值都有一个独立的位图。位图的长度等于关系中记录的数量,每一位代表一条记录

如上图所示,我们有一张表,具有5条记录,然后我们为 性别和收入水平 这两个字段创建了位图索引
比如说,下面这个,就是 字段性别的一个取值(男)的位图,10010表示第1条和第4条记录是男 (第1位和第4位是1)

然后我们可以对不同的位图进行与操作等等,从而实现查询,比如 m和L1这两个位图相与,只有第1位为1,也就表示,性别为男,收入水平为L1的记录只有第一条
B树索引
B树
B树是一种多路平衡查找树
- 平衡:所有的叶节点都在同一层
- 有序:节点内有序,任意节点的左子树都小于它,右子树都大于它
- 多路:对于 m 阶B树
- 最多:m个分支,m-1个元素
- 最少:
- 根节点:2个分支,1个元素
- 其他节点:⌈m/2⌉个分支,⌈m/2⌉-1个元素 (向上取整)
B树是向上生长的,也就是说,当某个节点溢出时,它分进行分裂,分裂成一颗子树,这颗子树的根节点合并到之前的父节点,是一个逐渐往上生长的过程
B树的节点结构
- K 表示搜索键
- P 表示指向子节点指针
- B 表示指向记录的指针
非叶子节点

叶子节点

整体结构如下:每个节点的元素都能直接指向记录

B+树索引
B+树
B+树的大致结构和B树相同,以下介绍不同的点:
- B+树的叶子节点通过一个链表串联起来
- B+树的父节点的搜索键会出现在子节点中,B树不会
B+树的节点结构

对于叶子节点来说,P表示的是指向记录的指针,对于非叶子节点来说,P表示的是指向子节点的指针
其中叶子节点这一层通过链表串联起来

整体结构如下:只有子节点的元素能够直接指向记录

B树和B+树索引的对比
- B树的每个节点的元素都能直接指向记录,B+树只有子节点的元素能够直接指向记录,B+树的子节点是记录的索引,内部节点是索引(子节点)的索引,所以B+树是一个多级索引
- B树的搜索键只出现一次,不会重复出现,B+树的搜索键会重复出现,父节点的搜索键也会出现在对应子树的子节点中,从而使得B+树的子节点包含所有的搜索键
- B树能够以更小的空间开销建立索引,每个节点都包含数据,遍历不方便,适合较小范围记录查询,不适合范围查询;B+树会造成重复搜索键的开销,但只有子节点包含数据,且通过链表串联,适合范围查询
- B树的节点更新更加复杂,因为所有节点都包含数据,B+树的更新会简单点,内部节点只包含索引,子节点包含数据
总结
-
B树:适合单记录查询,尤其是对于查找操作要求较高的场景,且数据较为简单。B树在范围查询时的性能较差,且由于 数据存储在每个节点中,可能会导致空间利用率低
-
B+树:由于叶子节点的链表结构,特别适合范围查询和顺序扫描,空间利用率较高。它的 多级索引结构 使得它在处理大规模数据集时,尤其是数据库的索引查询中,具有显著优势
哈希索引
静态哈希索引
概念
- 桶:
- 桶是存储单元,用来容纳一个或多个记录。通常,一个桶对应于一个磁盘块,即一个固定大小的存储单元
- 如果记录的大小适合桶的容量,桶中可能会存放多个记录
- 哈希函数
- 哈希函数,记作
h,是一个将搜索键值(从集合K)映射到桶地址(从集合B)的函数,h: K -> B - 搜索键值是用来标识或查询记录的值
- 桶地址是存储记录的桶所在的地址或位置
- 哈希函数,记作
文件组织
以下是静态哈希索引的文件组织,记录被存储在不同的桶中,每个桶的大小固定

从记录在桶中的分布情况看
- 最差的哈希函数:所有记录都分布在同一个桶中,这样查询效率低,且浪费其他桶的空间
- 理想的哈希函数:哈希函数能够将所有的搜索键值均匀地分布到各个桶中,确保每个桶存储的搜索键值数量大致相同
桶溢出
哈希冲突 是指两个或多个不同的搜索键被哈希函数映射到同一个桶地址上
当桶的搜索键过多时就会发生桶溢出,这时候可以采取 链式哈希,也叫 闭哈希 的方法解决

每个桶中不仅存储一个记录,而是存储一个链表或其他集合类型的记录。如果多个记录被映射到同一个桶中,它们将以链表的形式存放在桶内,查找时通过链表遍历
整体结构

动态哈希索引
概念
关键概念和静态哈希一样,但使用的方式不同,最大的区别是 动态哈希索引的哈希函数可以动态调整
可扩展哈希
-
可扩展哈希是动态哈希的一种实现方式,主要解决了哈希表大小动态变化时的问题。在这种方法中,哈希值不是通过整个哈希函数的值进行映射,而是通过哈希值的前缀来决定桶的位置
-
哈希值的前缀:假设哈希函数生成的是一个b位的值(通常为32位),而我们只取其中 前i位 作为索引来访问桶地址表
- i的值是动态调整的,范围为 0 ≤ i ≤ 32。桶地址表的大小为 2^i
- 在初始化时,i的值为0,表示哈希值前0位(即没有有效位)被用来索引,这样只有一个桶
-
总结:
- i的值随数据的变化而动态调整,增加时扩展桶地址表的大小,减少时缩小
- 多个桶地址表项可能指向同一个桶,桶地址表项取决于搜索键的前i位,可能相同,即会指向同一个桶,所以桶的数量通常少于桶地址表项
以下是可扩展哈希表的整体结构

可扩展哈希的使用
-
查找
查找操作就是 定位桶- 计算哈希值
- 选择高 i 位 (桶地址表的索引)
-
插入
- 定位桶
- 判断是否有空间 (根据每个桶存储的当前记录数量的值)
- 有空间,直接插入
- 无空间,桶拆分
-
删除
- 定位并删除记录
- 移出空桶
- 桶合并
- 减少桶地址表的大小(操作较昂贵)
可扩展哈希表的具体使用参见这篇博客
https://blog.csdn.net/MelroseLbt/article/details/129329316
posted on
浙公网安备 33010602011771号