07 索引结构

顺序文件上的索引

在顺序文件上:

  • 记录按查找键排序

密集索引

  • 每个记录都有一个索引项
  • 索引项按查找键排序

优点:

  • 记录通常比索引项要大
  • 索引可以常驻内存
  • 要查找键值为 K 的记录是否存在,不需要访问磁盘数据块

缺点:

  • 索引占用空间

稀疏索引

  • 部分记录有索引项
  • 一般情况:为每个数据块第一个记录建立索引

优点

  • 节省了索引空间
  • 对同样的记录,稀疏索引可以使用更少的索引项

缺点

  • 对于 “是否存在键值为 K 的记录?”,需要访问磁盘数据块

多级索引

优点

  • 一级索引可能还太大而不能常驻内存
  • 二级索引更小,可以常驻内存
  • 减少磁盘 I/O 次数
  • 二级索引仅可用稀疏索引

二级密集索引有用吗?
密集索引对于二级索引可以有所帮助,但它们不能提供太多优势。

  • 密集索引可以降低索引项在磁盘中的碎片,使得索引项更容易被加载到内存中。
  • 但是,它并不能提供太多的查询性能的改善,因为二级索引的查询操作不是非常频繁的,而且数据量也不会太大。

例:一块= 4KB。一级索引 10,000 个块,每个块可存 100 个索引项,共 40MB。二级稀疏索引 100 个块,共 400KB。
按一级索引查找 (二分查找):平均 lg10000≈13 次 I/O 定位索引块,加一次数据块 I/O,共约 14 次 I/O
按二级索引查找:定位二级索引块 0 次 I/O,读入一级索引块 1 次 I/O,读入数据块 1 次 I/O,共 2 次 I/O

一般不考虑三级以上索引

辅助索引

主索引 (Primary Index)

  • 顺序文件上的索引
  • 记录按索引属性值有序
  • 根据索引值可以确定记录的位置

辅助索引(Secondary Index)

  • 数据文件不需要按查找键有序
  • 根据索引值不能确定记录在文件中的顺序
  • 辅助索引只能是密集索引
    • 稀疏的辅助索引没有意义

```sql
MovieStar(name char(10) PRIMARY KEY, address char(20))
Create Index adIndex On MovieStar(address)
```

- Name 上创建了主索引,记录按 name 有序
- Address 上创建辅助索引

间接桶

  • 间接桶介于辅助索引数据文件之间
  • 可以用来处理辅助索引的重复键值问题

倒排索引

  • 应用于文档检索,与辅助索引思想类似
  • 不同之处
    • 记录 -> 文档
    • 记录查找 -> 文档检索
    • 查找键 -> 文档中的词
  • 思想
    • 为每个检索词建立间接桶
    • 桶的指针指向检索词所出现的文档

B+树⭐

  • 一种树型的多级索引结构
  • 树的层数与数据大小相关,通常为 3 层
  • 所有结点格式相同: n 个值, n + 1 个指针
  • 所有叶结点位于同一层

叶节点

  • 至少 \(\lfloor(n+1)/2\rfloor\) 个指针指向键值(\(\lfloor(n+1)/2\rfloor\) 个键值)
  • 1 个指向相邻叶结点的指针
  • N 对键-指针对

中间结点

  • n 个键值划分 n+1 个子树
  • 第 i 个键值是第 i+1 个子树中的最小键值
  • 至少 \(\lceil (n+1)/2\rceil\) 个指针指向子树(\(\lfloor(n+1)/2\rfloor\) 个键值)
  • 根结点至少 2 个指针

查找

  • 从根结点开始
  • 沿指针向下,直到到达叶结点
  • 在叶结点中顺序查找

插入

  • 查找插入叶结点
  • 若叶结点中有空闲位置(键),则插入
  • 若没有空间,则分裂叶结点
    • 叶结点的分裂可视作是父结点中插入一个子结点
    • 递归向上分裂
    • 分裂过程中需要对父结点中的键加以调整
    • 例外:若根结点分裂,则需要创建一个新的根结点

删除

  • 查找要删除的键值,并删除之
  • 若结点的键值填充低于规定值,则调整
    • 相邻的叶结点中键填充高于规定值,则将其中一个键值移到该结点中
    • 否则,合并该结点与相邻结点,删除父节点的一个键值
      • 递归向上删除
  • 若删除的是叶结点中的最小键值,则需对父结点的键值加以调整

效率

  • 访问索引的 I/O 代价=树高( B+树不常驻内存)或者 0(常驻内存)
  • 树高通常不超过 3 层,因此索引 I/O 代价不超过 3(总代价不超过 4)
    • 通常情况下,根节点常驻内存,因此索引 I/O 代价不超过 2(总代价不超过 3)

\(h=\lceil log_m(n(m-1)+1)\rceil\)

散列表

散列函数 (Hash Functions)

  • h:查找键(散列键)->[0,...,B-1]

  • 桶(Buckets):numbered 0,1,…,B-1
    散列索引方法:

  • 给定一个查找键 K,对应的记录必定位于桶 h (K) 中

  • 若一个桶中仅一块,则 IO 次数 = 1

  • 否则由参数 B 决定,平均 = 总块数 / B

  • 一个块可存储两个记录,一个桶占用一个块
  • 一个键对应一个桶,一个桶可存储两个记录
    - 多余的记录需要存储到溢出块中

查找

  • 对于给定散列键值 K,计算 h (K)
  • 根据 h (K) 定位
  • 查找桶总的块

插入

  • 计算插入记录的 h (K),定位桶
  • 若桶中有空间,则插入
  • 否则创建一个溢出块并将记录置于溢出块中

删除

  • 根据给定键值 K 计算 h (K),定位桶和记录
  • 删除

空间利用

最好在 50%到 80%之间

文件增长

动态散列表解决

  • 可扩展散列表:成倍增长
  • 线性散列表:线性增长

可扩展散列表

h(K) 是一个 b (足够大) 位二进制序列,前 i 位表示桶的数目

  • 前 i 位表示桶的数目
  • i 值随文件的增长而增大
    • 散列表扩展时,i 值随之增大
  • 前 i 位可以构成一个桶数组
  • 在可扩展散列表中包括一个全局的 i 值,并且每个桶都有一个局部的 i 值


依次满足i=1,2,3,...的桶的索引要求:

  • 桶数组中 00 01 第一位都是0,索引指向第一个桶
  • 10 指向第2个桶,11 指向第3个桶
扩展


往可扩展散列表中插入 10100...,此时散列表需要扩展

  1. 增加全局的i值
  2. 重新分配桶数组
  3. 将插入位置的桶扩展为两个i+1的桶

    重新分配索引,依次分配i=1,2,3 的桶的索引:

    最后再将数据插入
优缺点

优点:

  • 大部分情况下不存在溢出块,因此当查找记录时,只需查找一个存储块
    缺点:
  • 增长速度快
  • 可能导致内存存放不下整个桶数组
  • 影响其他保存在主存中的数据,波动较大

线性散列表

h(k) 仍是二进制位序列,但使用右边 (低) i 位区分桶(用除余运算确定)

  • 桶数 = n, h(k) 的右 i 位 = m,当前存储的记录数=r
  • n 的选择:总是使 n 与 r 保存某个固定比例

插入记录:

  • \(n/r\) 大于某个值时,增加新桶;否则,如果桶不足以存放就使用溢出块

  • 增加新桶后,需要重新分配所有的记录到对应位置的桶

    • 若 m<n, 则记录位于第 m 个桶
    • \(n≤m<2^{i}\) , 则记录位于第 \(m−2^{i−1}\) 个桶
  • i:当前被使用的散列函数值的位数,从低位开始

  • n:当前的桶数

  • r:当前散列表中的记录总数


posted @ 2024-01-20 11:34  ConnorLan  阅读(79)  评论(0)    收藏  举报