基本概念
- 查找键,键:建立起索引的字段
- 索引:建立查找键与数据记录之间的关联(索引类似于字典的目录)
稠密索引与稀疏索引
- 稠密索引:数据文件的每个记录都有一个索引项
左边为索引的存储块:存放记录的键(10,20)和指向记录的指针
右边为数据文件一条条记录
1.索引项按搜索码排序,可以用二分法查找K,K对应的指针指向记录
2.记录一般比索引大
3.索引可以常驻内存
4.右边有多个相同的10(存在重复码值),建立重复索引项或只建立一个索引项指向第一个
5.缺点:索引占用空间,用稀疏索引改进
- 稀疏索引:数据文件中部分记录在索引中表现出来
1.稀疏索引一般为数据块的第一个记录建立索引
2.只有数据文件是按照某个查找键排序时,该查找键上建立的稀疏索引才能被使用
3.节省了更多的存储空间,但是增加了查找时间
4.存在重复码值时,为每个数据块的第一个记录建立多个索引项或者为每个块的最小新值(前面块没出现的)建立一个索引
5.行为对索引的影响:
主索引与辅助索引
- 主索引:能确定记录在数据文件中的位置,一般建立在主键
- 辅助索引:不能确定位置,只能是稠密索引,稀疏索引的辅助索引是无意义的
- 辅助索引的间接桶(重定位指针):解决稠密索引多个码值带来的空间开销
聚集索引与非聚集索引
- 聚集索引,类比按拼音查找,索引中的键值排列顺序决定了数据记录的排列顺序
- 非聚集索引,类比按部首查找
1.每个表只能有一种排列方式,所以只有一个聚集索引
多级索引
- 一级索引可以为稠密索引,也可以为稀疏索引
- 二级索引以上只能为稀疏索引,原因:一个索引需要和前一级索引同样多的键—指针对,需要同样的存储空间
- 一级索引大不能常驻主存,二级索引可以
- 一般不超过二级索引,因为还不如使用B+树索引效率更高
- 多级索引可以减少磁盘IO
B+树索引
优点
- B-树可以自动地保持与数据文件大小相适应的索引层次
- 对所使用的存储块空间进行管理,使每个块的充满程度在半满与全满之间
B+树结构
- 一般有三层:根,中间层,叶;根据数据大小也可以任意多层
- 每个存储块存放n个查找键值和n+1个指针
- 适用于主索引也适用与辅助索引
- 中间结点:
- n个码值划分n+1个子树
- 第i个码值是第i+1个子树中的最小码值
- 根结点至少2个指针
- 至少
⌈
n
+
1
2
⌉
\lceil \dfrac {n+1}{2} \rceil
⌈2n+1⌉个指针指向子树
- 叶节点
- 叶节点中的键是数据文件中键的拷贝
- 叶节点中的键是从左往右按顺序排好序
- 叶节点的最后一个指针是指向他右边的下一个叶节点存储块
B+树的查找键是数据文件的主键,且索引是稠密的(叶子结点)
数据文件按主键排序,且B+树是稀疏索引(中间结点)
- 例子
B+树的操作
- 查找
- 从根结点开始
- 沿着指针向下直到找到叶节点
- 在叶节点中顺序查找
- 插入
- 查找插入的叶节点
- (1)叶节点有空闲位置插入
- (2)叶节点无空间则分裂叶节点(父节点中插入一个子节点,递归向上分裂)
- (3)根结点分裂需要创建一个新的根结点
- 删除
- 查找要删除的码值,删除
- 码值的填充低于规定则进行调整
- 删除的是最小码值需要对父节点的码值进行调整
- 调整:合并相邻结点或者将相邻结点满则将其中的一个码值移到该结点中
B+树效率
- 一般来说,树高不超过三层,索引的IO代价不超过三,总代价不超过四
- 如果根结点常驻内存的话,索引的IO代价不超过二,总代价不超过三
索 引 的 I O 代 价 { 不常驻内存=树高 常驻内存=0 索引的IO代价 \begin{cases} \text{不常驻内存=树高}\\ \text{常驻内存=0} \end{cases} 索引的IO代价{不常驻内存=树高常驻内存=0
散列索引
-
散列的好处: 顺序文件组织必须访问索引结构或者二分法来定位数据,需要大量的IO操作,散列可以避免访问索引结构,因为基于散列的文件组织和索引是建立搜索码值与桶地址之间的映射
-
桶: 存储一条或多条记录的一个存储单位
-
散列函数: 如果K表示所有搜索码值的集合,B表示所有桶地址的集合,那么散列函数h是一个从K到B的函数
-
散 列 函 数 的 特 性 { 均匀:每个桶分配所有可能的搜索码值集合中有同样数量的搜索码值 随机:散列值不与搜索码的任何外部可见的排序有关 散列函数的查找时间一般是一个常数,与文件中搜索码的个数无关 散列函数的特性 \begin{cases} \text{均匀:每个桶分配所有可能的搜索码值集合中有同样数量的搜索码值} \\ \text{随机:散列值不与搜索码的任何外部可见的排序有关}\\ \text{散列函数的查找时间一般是一个常数,与文件中搜索码的个数无关} \end{cases} 散列函数的特性⎩⎪⎨⎪⎧均匀:每个桶分配所有可能的搜索码值集合中有同样数量的搜索码值随机:散列值不与搜索码的任何外部可见的排序有关散列函数的查找时间一般是一个常数,与文件中搜索码的个数无关
-
桶溢出
-
溢 出 原 因 { 桶不足:桶的数目应该大于存储的记录总数除以每桶能存放的记录数目 偏斜:某些桶所分配到的记录比其他桶多,发生偏斜是因为多个记录具有相同的搜索码或者散列函数造成搜索码的分布不均 溢出原因\begin{cases} \text{桶不足:桶的数目应该大于存储的记录总数除以每桶能存放的记录数目} \\ \text{偏斜:某些桶所分配到的记录比其他桶多,发生偏斜是因为多个记录具有相同的搜索码或者散列函数造成搜索码的分布不均} \end{cases} 溢出原因{桶不足:桶的数目应该大于存储的记录总数除以每桶能存放的记录数目偏斜:某些桶所分配到的记录比其他桶多,发生偏斜是因为多个记录具有相同的搜索码或者散列函数造成搜索码的分布不均
-
溢出桶:处理桶的益处问题,桶满的时候,系统为桶提供一个溢出桶,将记录插入到该溢出桶中
-
溢出链:溢出桶满的时候系统会提供另一个溢出桶,所有溢出桶使用一个链接列表链接在一起的时候,这个链接列表的溢出处理称为溢出链
-
开散列:当桶满后,系统将记录插入到初始桶集合B的其他桶中
动态散列
特点:
- 允许函数的动态改变
- 通过桶的分解和合并来实现数据库的增大或缩小的需求
- 通过逐步扩充散列值的位数来构造索引,通过位比较来实现散列值的定位
可扩充散列
构造过程
- 根据散列函数映射出各记录的一组二进制数的散列值
- 根据逐步扩充使用二进制数散列值的位数来构造散列表,建立
- 开始使用散列位数为零,初始化空桶,将记录逐条插入桶中
- 桶满的时候再往里面添加,使用散列函数位数的增加和桶的分裂,原桶里的记录按照新的散列值添加到新桶里,当前使用的散列位数加一
操作
- 查询:为获取搜索码值为 k L k_{L} kL的桶的位置,需要先或者h( k L k_{L} kL)的第I个高字节,为这个位串查找对应的表项,再根据表项中的指针来得到桶的位置
- 插入
- 查询定位到桶x
- 插入桶
(1)如果桶x有剩余的空间,直接插入
(2)如果桶x已满,分裂桶并将桶中现有的记录和新纪录一起重新分布 - 分裂桶:根据散列值确定是否需要增加使用位数
(1)如果i= i x i_{x} ix 需要增加桶地址表的大小,容纳由于桶x分裂而产生的两个桶指针
(2)如果i> i x i_{x} ix不需要增加