Indexing
indexing是一个辅助文件,包含不同类型的records,由(key_value,tuple)组成。
如果我们要搜索k2,在index file中找到k2对应的tid(即一个k2所在data page的引用)。当我们找到匹配的tuples,需要读它们的data pages,然后对它们本身进行操作。
Index file按key的大小排序,因此我们可以进行二分搜索。所以data file中的tuple乱序没有影响效率。
(key_value,tuple)这种entry远小于tuple本身,所以index file比data file小。
对于单个attribute的indexing
Attribute A可能是排好序的,也可能是primary key。它们的indexes结构随性质不同。
3种indexing type:
primary:unique field的indexing,A可能按顺序。
clustering: non-unique field的indexing,A按顺序。
secondary: A无序,且可能重复。
Index 的structure:
1.dense :一个tuple对应了一个index entry。
2.sparse :只有一些tuples被index entries引用。
3.single-level : index file只有一层index。
4.multi-level :index file有多层index。
index file有i个pages,i<<b(因为index file比data file小很多)
index file有 page的capacity ci ,ci >>c
Dense index: I =ceil(r/ci) r是tuples的总数
Sparse index: I = ceil(b/ci) b是pages的总数
Dense Primary Index
在Dense情况下,如果是无序的,则只能是primary index。
Sparse Primary Index
Sparse的情况仅允许tuples按照key attribute的直的顺序有序。
更新tuple的时候同时要更新data page 和index。
R是指每个tuple是128bytes。
在我们删除的时候可能会用到index page的header,去查看哪些index正在被使用。
对于Dense来说每个tuple对应一个entry,所以有1000个index entry。
对于Sparse来说,一个data page对应一个index entry。所以有1613个index entry。data file按照key的顺序存储。
Primary Index的selection
one query:输入一个key,找到一个匹配的tuple,或者找不到。
首先通过index file用key进行binary search,如果找到了就通过index getPage 再 gettuple。
最坏情况:读log2(i) of index pages + 读page+读它overflow page
对于range query,先使用binary search查它的lower bound,然后继续通过binary search直到查到upper bound。(index根据key的值已排序)建立index的集合,通过index找到pages取出tuples并写回。
不查data page的原因是data page中可能有重复的tuple。
pmr queries 和 one query一样。
如果query不涉及primary key,则index毫无帮助。
Selection with Primary Index
pages存的是符合的pageID,results存的是符合的tupleID。
Insertion with Primary Index
根据file的种类不同插入方式不同(heap,sorted,hash)。
通过key的值在index file上binary search。
在把新entry插入进index file时,index page不能有overflow page,因为查找效率不高,所以只能插入后把后面的值依次后推。平均情况下代价是i/2(1r+1w)。
Cost里的Delta是指假设只有page的最后一个overflow page有空余。
Deletion with Primary Index
比起删完后后面的补位,不如如第一种将deleted的标记,再将它所在的index page写回,并将对应的data page写回(在Postgres里设置xmax)。
Indexing on non-primary key
需要维持index file和data file都有序。
index指向第一个k。
所以在删除data file里的tuple不会删除index file里的index除非是这个key的最后一个tuple。当key对应的最后一个tuple被删除时,后面的key也可能改变。
如果data file是无序的,可以使用secondary index来建立索引。
第一层索引告诉我们key第一次出现在第二层的位置。
第一层和第二层都需要sorted on Key。
第二层索引告诉我们key在data page的位置。红色代表key1,蓝色代表key2…
首先在ix1上进行binary search,然后在Ix2上这个key的第一个entry开始scan直到下一个k的第一个entry。然后在这些bq页中便利找到匹配的tuples。
Ix1的长度一般小于Ix2,取决于lx2有多少重复。
实际中lx2也可不更新重复项。
对于新加的tuple,如果key重复可以不加lx1。新的entry要加入到lx2,它后面的要依次后移。
Ix2确定有序下,Ix1变的sparse,bIx1 = ceil(bIx2/ci)
如果Ix1变得太大,需要添加Ix3,使Ix2 sparse。
如果data file按照key的顺序排好序,则可以使Ix3变sparse。
因此,通过如果本层时sorted,可以使上一层变sparse来缩短上一层的长度。
Select with Multi-level index
d时index层的深度,d=ceil(logci®)
B-Tree
每个node至少有(n-1)/2个entries。
Postgre里每有一个primary key,就将其作为B-tree的index。
B-tree的insertion 和 deletion非常高效。在这两个操作之后不需要重新排序,每个node都有高的branching factor。
B-tree相比前面的Multi-level index的优点:更好的存储利用(2/3都满),更好的最坏情况(树的高度较小)。
图中一页能存3个index。
B-tree的深度取决于braching factor(nodes满的程度),一般为69%。如果100%full,则insertion等操作需要每次都调整。
Load Li= 0.68*ci, depth ~ ceil(logLi®)
上图我们可以看到,虽然存储的代价是681472,但search的代价是3(层).
Insertion on B-Tree
1)搜索树找到node的位置。
2)如果node没满,则在node中insert key并调整。
3)如果node满了,将新的key insert进node,然后将node对半分成两个node,将原来中间的那个值加入到它的父node。
4)如果父node满了,则重复步骤向上。
5)直至叶子结点,然后重复步骤向上,层数+1。
如果不允许重复,则在第1)步不insert 。
两种版本:1.Node内要保持顺序 2.Node内不保持顺序
最好的情况:找到leaf node,并且有空间,直接插入key 和 tuple id。
一般情况:leaf node满了,需要split和promote。则需要改动3个node(分开的两个和父node)。
最坏情况:每一层都满了。
原始方式(之前的):每层都需要操作3个node。
优化方式:将要修改的node加入内存中,直到决定是否满了才写入。即每层只写分开两个node,promote的node不写,promote的node等到轮到它这一层分开或不分开的时候才写。
Selection on B-Tree
bi是多少leaf node需要扫描。
bq是多少data page需要扫描。
PostgreSQL的B-trees
需要解决高并发问题。
PostgreSQL里是dense index
如果key是node page中最大的key,则通过最右边开始扫描尽量减少splitting的次数(新开一个node,将最右边的值复制到node中)。
REFERENCE
Comp9315 week6a lecture