数据库系统笔记indexing and hashing


前言

本章对应数据库系统lecture8 indexing and hashing课件的部分内容,主要学习index和hashing。

一、Indexing

许多查询只涉及数据集合中少量记录,如果为了查找一个集合中的个别记录,而遍历所有的记录,代价会很大。

1.index的基本概念

需要掌握的概念:
	Search key:一个关系中一个或多个属性,常用于查找文件中的数据记录。
	Index文件:一个特殊的文件,由一些记录构成,称作索引项(index entries),两个部分:search key和pointer,pointer指向数据文件中search key对应的数据记录。

2.index种类

Index的种类:

(1)顺序索引:基于记录中某个属性值的顺序建立的索引,比如成绩顺序

(2)hash索引:根据记录的查找键的值,使用一个函数计算得到的函数值作为磁盘块的地址,对记录进行存储和访问,通常用桶作为基本的存储单位,一个桶可以存放多个记录

评估索引的指标:
	访问时间:查询时找到数据项或数据项集合的时间
	修改时间:在数据文件中增加或删除数据的时间,以及更新索引(插入索引项和删除索引项)的时间
	空间开销:索引文件占用的存储空间。

2.1 Ordered Indices 顺序索引

顺序索引:索引项根据search key排好序,索引文件中的索引记录都是按照search key排好序的 —— 按search-key有序。

主要掌握两个概念:primary index主索引(clustering index聚集索引)和secondary index辅助索引(non-clustering index非聚集索引)。

对于数据文件,可以按照某个属性的值排好序存放在文件中。

主索引指的是索引项中的search key是数据文件中排序的属性,而且顺序与数据文件一致。

辅助索引指的是数据文件中的记录顺序与索引文件中索引项的顺序不一致。

2.1.1 主索引
2.1.1.1 稠密索引

索引文件中search key所有取值都出现在索引文件中。

比如工号作为search key的话,数据记录中工号的所有取值都在索引项中出现。
在这里插入图片描述

2.1.1.2 稀疏索引

索引项中只包含search key的部分取值。

如图中的例子,searchkey是ID,但索引项中仅仅存储了个别的ID值。

在这里插入图片描述

2.1.1.3 稠密索引和稀疏索引的比较

稀疏索引的空间开销比稠密索引,定位数据比稠密索引

将两种索引结合起来形成多级索引。首先为顺序文件的每一块建立一个索引记录,得到一个以块为基本单位的稠密索引,然后再在稠密索引的基础上建立一个稀疏索引。(稠密索引+稀疏索引)

查找时,首先在稀疏索引中确定记录在哪一块,最后在数据文件的块中顺序查找,找到所在的记录。(二级索引)在此基础上可以延伸出多级索引(multilevel index
在这里插入图片描述

2.1.2 辅助索引
一个关系表可以有多个索引,但数据文件只有一个顺序。

辅助索引和主索引的目的一样,但是索引文件中search key的顺序与数据文件的顺序不一样。

下图示意了数据文件中记录的顺序是按照name排序,但是索引文件searchkey的顺序是年龄,其中的框可以想象成数据块。

在这里插入图片描述

3.Index文件结构——B+树索引文件

3.1 产生背景

索引顺序文件(index-sequential file—在search key上有主索引的文件,数据文件按照search key的值排好序)的缺陷是,随着文件的增大,溢出块不断产生,为了保持顺序,文件需要阶段性地重组,导致索引查找性能和数据顺序扫描的性能都会下降。

B+树是一种最广泛使用的索引文件结构之一,采用平衡树结构,是在数据插入和删除的情况下仍能保持执行效率的结构,只需很小的、局部的变化自动重组文件。

B+树的缺点:会有插入数据和删除数据的额外开销,增加空间开销。

3.2 B+ Tree

B+树采用平衡树的结构,从树根到树叶的每一条路径长度相等

给定n,树中非根、非叶子节点有 ⌈ n / 2 ⌉ \lceil n/2 \rceil n/2 ~ n n n个孩子;叶子节点有 ⌈ ( n − 1 ) / 2 ⌉ \lceil (n-1)/2 \rceil (n1)/2~ ( n − 1 ) (n-1) (n1)个值。

特殊情况下:如果根不是叶子,其有至少2个孩子;如果根是叶子(这棵树只有一个节点),那么这个根节点存储0~(n-1)个值。

在这里插入图片描述
从图中更可以看到,这棵树的n=4,按照B+树的结构定义,根节点最多有4个孩子,最少有 ⌈ 4 / 2 ⌉ \lceil 4/2 \rceil 4/2=2个孩子。非叶子节点最多有4个孩子,最少有 ⌈ 4 / 2 ⌉ \lceil 4/2 \rceil 4/2 =2个孩子。叶子节点最多有4-1=3个值,最少有 ⌈ ( 4 − 1 ) / 2 ⌉ \lceil (4-1)/2 \rceil 41/2 =2个值。

图中的数据记录存储在磁盘空间的数据块中,按照ID排序。

在这里插入图片描述

B+树用于索引文件结构实际是一种多级索引,但是不同于多级索引顺序文件(每个数据块中的数据记录按照某个属性值排好序)。典型的B+树节点的数据结构如图示意:包含n-1个search key : K 1 K_1 K1~ K n – 1 K_{n–1} Kn1以及n个指针 P 1 P_1 P1~ P n P_n Pn 。对于指针 P i P_i Pi,如果是非叶子节点的指针,指向孩子节点;如果是叶子节点,指向具体的记录或记录桶。

P i P_i Pi指向具有 search-key值Ki的文件记录,若search-key是数据文件的primary key,则指向具有search-key的数据文件的记录(唯一性);若search-key不是数据文件的主键,且search-key值的顺序也不是数据文件的顺序(数据文件按照另外的属性值排序),则指向一个桶,桶中的记录具有search-key的值 。

如果 L i L_i Li L j L_j Lj 是叶子节点而且i < j, 那么 L i L_i Li 的search-key值小于等于 L j L_j Lj的search-key值。

叶子节点的指针 P n P_n Pn有特殊作用,每个叶子节点的 P n P_n Pn指针指向下一个叶子节点,将所有的叶子节点按照search-key值的顺序链起来,提高对文件的顺序处理效率。

B+树非叶子结点(也称内部结点)形成叶子结点上的多级稀疏索引,非叶子结点的结构有叶子结点的结构一样,但是非叶子的指针都是指向树中结点的指针,有以下特征(定义):

P 1 P_1 P1指向的子树的所有search-keys值小于 K 1 K_1 K1
P n P_n Pn指向的子树的所有search-keys值大于等于 K n – 1 K_{n–1} Kn1
P i P_i Pi指向的子树的所有search-keys值大于等于 K i – 1 K_{i–1} Ki1且小于 K i K_i Ki

扇出(fan-out):结点的指针数

在这里插入图片描述
如上图所示,search-key是name。可以看到根节点有3个孩子(3~6个),这棵树只有两层,叶子节点存储了search-key的值。

3.2.1 B+ tree的查找操作

寻找所有search key值等于k的记录:

1.首先在根结点中找>k的最小查找键值。
2.如果存在这样的值, 设为 K i K_i Ki 然后沿着 K i K_i Ki 左边的指针 P i P_i Pi 到达第二层的结点。
3.如果不存在这样的结点(k ≥ K n – 1 K_{n–1} Kn1),沿着指针 P n P_n Pn到达第二层的结点。重复1,2,3步骤。
4.直到叶子结点,找到一个指针直接指向数据文件的记录,或指向一个桶。

需要注意的是,如果文件中一共有k个search key的值,那么构造出来的B+tree的高度不会超过 l o g x log_x logx k k k (x= ⌈ n / 2 ⌉ \lceil n/2 \rceil n/2)。

也就是说,查询的效率也很高,可以访问很少的数据块找到想要的数据。

3.2.2 B+ tree的查询次数和存储块之间的关系

如果search-key的值有K个,给定n,基于B+树查询时,查询次数与存储块之间的关系则为:

假设一个块的大小为4KB,search-key的长度为12个字节,指针占8个字节,则每个块可以存储200个search-key和pointer,那么n=200;如果search-key的长度为32个字节,指针仍为8字节,则每块大约可存储100search-key和pointer,那么n = 100。

当n确定了,search-key的值的个数k也确定了,那么一次查询需要读索引块的数目也确定了。如果B+Tree索引的的根结点常驻内存(pinned),那么查找时只需读 ⌈ \lceil log_x ( ( (K ) ⌉ )\rceil )-1个索引块,其中x= ⌈ n / 2 ⌉ \lceil n/2 \rceil n/2

3.2.3 B+ tree的插入操作

插入操作的算法实现逻辑。

  1. 查找search-key的值在叶子结点上出现的位置(调用B+树查询算法)
  2. 如果search-key的值出现在叶子结点上(不需要重复),则
    a)将数据记录加到入到数据文件中;
    b)如果有必要,增加一个指向存储桶的指针(search-key不是key属性)
  3. 如果search-key的值未出现在叶子结点中,则
    a)将数据记录加到入到数据文件中(如果有必要的话增加一个桶-溢出块)
    b)(修改B+树)如果叶子结点有空间,插入(key-value, pointer) 对到相应的位置(保持节点的结构)
    c)否则,分裂这个包含了新(key-value, pointer)对的节点(实现时,申请一个临时空间N‘存储原来结点N的那些(key-value, pointer)以及新插入的新(key-value, pointer),排好序,使之符合B+树结点的定义。然后再将这个临时N’进行分裂)
  4. 更新父结点,在父结点插入新结点中最小Search-key的值,同样按照顺序插入到父结点中。
  5. 如果父结点因为插入了新的value导致结点分裂(不满足B+树的定义),则继续递归分裂父结点,直到结点不再进行分裂。有可能树长高了一层。
    在这里插入图片描述
    已知当前B+树的结构如图示意,N=3。新的记录的search-key属性值为Clearview。

首先定位:通过调用B+树查询算法可以知道,这个新search-key值(Clearview)应该排在树上最左边的叶子结点Brighton之后。可以看到,最左边的结点已经有两个search-key值,N=3,所以这个结点是满的。没有空间给Clearview。

这时需要进行节点的分裂操作。过程是这样的:原来的结点设为N,申请临时存储空间为N’,存储三个(searchKey-value, pointer)对,包括(clearview,pointer),且按照searchKey的值排好序。然后分裂这个N’,分裂时将N’中前 ⌈ n / 2 ⌉ \lceil n/2 \rceil n/2个值放入原来的N结点,剩下的值放到新生成的结点中。

图示可以看到,新结点中存放的是Downtown这个值。

针对非叶子节点的分裂步骤如下:
前面介绍了叶子节点的分裂,非叶子结点满了也要分裂,分裂算法如讲义所示。

  1. 申请临时存储M,大小为n+1个pointer和n个search-key,并将结点N(插入新结点导致分裂的结点)的所有信息copy到M中
  2. 将孩子结点传递上来的(k,p)(新叶子结点的最小的key)按Key的顺序插入到M中
  3. 将M中 P 1 P1 P1, K 1 K1 K1, …, K ⌈ n / 2 ⌉ − 1 K _{⌈n/2⌉-1} Kn/21, P ⌈ n / 2 ⌉ P_{⌈n/2⌉} Pn/2从M中拷贝回结点N,剩下的 P ⌈ n / 2 ⌉ + 1 P _{⌈n/2⌉+1} Pn/2+1, K ⌈ n / 2 ⌉ + 1 K _{⌈n/2⌉+1} Kn/2+1,…, K n K_n Kn, P n + 1 P_{n+1} Pn+1 拷贝到新生成的结点中
  4. (同样,新结点不能是个孤儿,得有父母),将( K ⌈ n / 2 ⌉ K_{⌈n/2⌉} Kn/2,N’) 插入到父亲结点中。
  5. 如果父亲结点满了,继续分解父结点(递归执行)

在这里插入图片描述

总的来说,对于插入分裂的操作,先把新插入的放入找到的位置排好序(一共m个值),然后前m/2(即如果是奇数向下取整)个保持原来的块,其余的分到另一个新创建的块中。然后将中间的(m/2)+1的值提到上面的父节点并排好序。如果父节点也要分裂,则继续这样操作。

3.2.4 B+ tree的删除操作

删除数据记录时,B+树要更新。

  1. 首先找到要删除的数据,从数据文件中删掉(如果search key不是primary key,有多条满足条件的记录,从桶中删掉)
  2. 然后,修改B+树index。如果没有桶或者桶是空的,就将 (search-key value, pointer)从叶子结点中删掉。

当从叶子结点删除(search-key value, pointer) 后,结点可能会变得不符合B+树的结构定义(给定N)。

①有可能是结点中的(search-key value, pointer)数目太少,但是兄弟结点还有空间(符合B+树结构定义),这时做merge siblings–兄弟合并操作。

  1. 两个结点的(search-key value, pointer)都插入到一个结点中(左边的结点中),删除另一个结点。
  2. 从父结点那里递归地删除(Ki–1, Pi)对,其中Pi是指向删除掉的结点的指针。

②也可能是结点中(Search-key value, pointer)数目太少,但是与兄弟结点不能合并至一个结点中,这时进行redistribute pointers—重新分布指针操作(给定N,有效指针数目的范围是确定的)。

  1. 在删除search-key的结点和兄弟结点之间重新分布指针,使得每个结点的指针满足最小指针数的条件
  2. 修改父结点的相应search-key值。
  3. 如果删除操作后,根结点只有一个指针,就删除这个根结点,它的一个孩子成为新的根。B+树矮了一层。

在这里插入图片描述

4.hash索引

4.1 静态hash索引

:表示存储一个或多个记录的存储单位。通常一个桶就是一个磁盘块,但也可以小于或大于一个磁盘块。

令K表示Search-key的集合,令B表示所有桶的地址,Hash函数h是从K到B的一个映射。

Hash函数可用于数据访问和增加、删除数据。

当插入一条search-key为Ki的记录,计算h(Ki),得到存放该记录的桶位置。

当查找search-key=Ki的记录时,只需计算h(Ki),然后搜索具有这个地址的桶。如果不同search-key值的记录在一个桶中,必须检查桶中的记录的search-key的值,以确定该记录是否是所要查找的记录。

删除的操作类似。

Hash函数的设计很关键,理想的hash函数是

  1. 均匀的:每个桶内分配的search-key值大致相同
  2. 随机的:函数值不受search-key值的顺序影响。

溢出桶overflow bucket:解决桶空间小存储不了过多的数据记录。

如果一条记录必须插入到桶b中,如果桶b已满,系统会为桶b提供一个溢出桶,如果溢出桶也满了,会继续提供另一个溢出桶。一个给定的溢出桶用链表链接起来,这种处理称为溢出链

在这里插入图片描述

5.位图索引—bitmap index

位图索引就是一个长度为记录数的数组,数组存储一连串二进制数,长度为数据库属性的数量。规定每一个属性对应二进制的哪一个比特位,如果这个属性满足了条件,相应的数组的相应比特位就置为1,否则为0.

在这里插入图片描述
位图索引的例子。数据表中有5个记录,编号为0-4,三个属性ID,gender,income_level。
在这里插入图片描述

对于gender属性的位图索引如图示意,是一个array,长度为5(记录个数)。如果某记录的gender取值为m,相应的记录位置置为1,否则置为0。如记录0、3的gender为m,则位图相应第0位和第3位置为1,其他记录的gender取值为f,则置为0.

income_level上的位图索引类似。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值