美文网首页
散列 & 线性散列

散列 & 线性散列

作者: Kevifunau | 来源:发表于2018-10-28 11:14 被阅读0次

Hashing 散列

原理: use key value to compute page address of tuple

Hashing

以查找健 为参数, 使用散列函数, 计算出一个介于[0,B-1] 之间的整数
散列函数 可以把任意类型的 查找健 转化为一个整数值
整数值与page index 一一对应
可以把 整数值 看作 一个 bit-string

PostgreSQL hash function (simplified):

uint32 hash_any(unsigned char *k, register int keylen)
{
   register uint32 a, b, c, len;
   /* Set up the internal state */
   len = keylen;  a = b = 0x9e3779b9;  c = 3923095;
   /* handle most of the key */
   while (len >= 12) {
      a += (k[0] + (k[1]<<8) + (k[2]<<16) + (k[3]<<24));
      b += (k[4] + (k[5]<<8) + (k[6]<<16) + (k[7]<<24));
      c += (k[8] + (k[9]<<8) + (k[10]<<16) + (k[11]<<24));
      mix(a, b, c);   k += 12; len -= 12;
   }
   /* collect any data from last 11 bytes into a,b,c */
   mix(a, b, c);
   return c;
}

map raw hash value into a page address

  1. if b = 2^k, bitwise AND with k low-order bits set to one
uint32 hashToPageNum(uint32 hval) {
    uint32 mask = 0xFFFFFFFF;
    return (hval & (mask >> (32-k)));
}
  1. use mod to produce value in range 0..b-1
uint32 hashToPageNum(uint32 hval) {
    return (hval % b);
}

散列函数的性能

散列函数的目的就是, 希望 tuple能均匀分布在bucket中,并且bucket尽可能的满。对于 overflow pages, 使用 溢出块 来存放。

负载率: L = r / b*c
平均溢出链长度: Ov = b_{ov} / b
理想的负载率 在 0.75 到0.9 之间。

Selection with Hashing 散列表的查询

策略:

  1. 通过 散列函数 计算 page address
  2. 读取该页面数据, 匹配相关元组
  3. 有可能需要读取 溢出块

复杂度:
Best: 1
Average: 1 + Ov/2
Worst: 1 + max(OvLen)

散列 不支持 range query 范围查找。
复杂度为:
Cost_{range} = b + b_{ov}

Insertion with Hashing 散列表的插入

// insert tuple t with key=val into rel R
// f = data file ... ovf = ovflow file
p = hash(val) % nPages(R)
P = getPage(f,p)
if (tup fits in page P) 
    { insert t into P; return }
for each overflow page Q of P {
    if (tup fits in page Q)
        { insert t into Q; return }
}
add new overflow page Q
link Q to previous overflow page
insert t into Q

散列表 插入 和 one query 类似,首先 根据散列函数 计算出 page index,
如果 该页 有空间直接插入, 否则插入到该页的溢出块中。如果所有存储页都满了, 则添加一个新的溢出块。

Deletion with Hashing 散列表删除

删除 和 查询 也类似, 唯一的区别就是 需要把 修改后的 page 从 buffer中在写回去。

// delete from R where k = val
// f = data file ... ovf = ovflow file
p = hash(val) % nPages(R)
buf = getPage(f,p)
ndel = delTuples(buf,k,val)
if (ndel > 0) putPage(f,buf,p)
p = ovFlow(buf)
while (p != NO_PAGE) {
    buf = getPage(ovf,p)
    ndel = delTuples(buf,k,val)
    if (ndel > 0) putPage(ovf,buf,p)
    p = ovFlow(buf)
}

散列表的问题

理想中静态散列表有足够的bucket(溢出块很少), 查询 为 一次 磁盘I/O, 插入和删除为两次磁盘 I/O, 不支持范围查找。

但如果 b_{ov}很大, 平均复杂度就退化到 1 + Ov/2 次I/O。
因此, 我们需要 动态的改变B桶数目的大小。
两种方法:
1.可扩展散列
extendible hashing, dynamic hashing (need a directory, no overflows)
2.线性散列
linear hashing (expands file "sytematically", no directory, has overflows)


linear hashing 线性散列

不需要辅助存储, 但是需要溢出块。

  1. 动态散列 : splitting 分裂
    比如101, 拓展一个比特位变成 0101, 1101 ,实现一个bucket分裂成2个.
    splitting

split 的方法:
1. split every time a tuple is inserted into full block
2. split when load factor reaches threshold (every k inserts) 负载率超过阀值


image.png
  1. 线性散列的文件组织结构
  • primary data blocks
  • overflow data blocks
  • split pointer

不需要额外的辅助存储, 桶线性增长

  1. 线性散列的插入


    image.png
P = bits(d,hash(val));

######## P在 Split pointer 左边, 多加一个比特位
if (P < sp) P = bits(d+1,hash(val));
// bucket P = page P + its overflow pages

######### 有空间 直接插入
for each page Q in bucket P {
    if (space in Q) {
        insert tuple into Q
        break
    }
}
######## 没有空间, 插入到 溢出块中
if (no insertion) {
    add new ovflow page to bucket P
    insert tuple into new page
}

###### r/n > 阀值 需要 分裂
if (need to split) {
    partition tuples from bucket sp
          into buckets sp and sp+2^d
    sp++;
    ####### split pointer 到头 
    if (sp == 2^d) { d++; sp = 0; }
}

插入 的复杂度

  1. 如果不需要 split
    Cost_{insert}: 1r + 1w (best), (1+Ov)r + 1w , wrost:(1+ max(Ov))r + 2w
  2. 如果 split : Cost_{insert} + split :
    read block sp (plus all of its overflow blocks)
    write block sp (and its new overflow blocks)
    write block sp+2d (and its new overflow blocks)
    On average, Costsplit = (1+Ov)r + (2+Ov)w

相关文章

  • 散列 & 线性散列

    Hashing 散列 原理: use key value to compute page address of t...

  • python数据结构教程 Day10

    本节重点: 散列 散列函数 完美散列函数 hashlib 散列函数设计 冲突解决方案 一、散列 能够使得查找的次数...

  • 散列 Hash

    一、什么是散列? 散列 Hash是和顺序、链接和索引一样,是存储集合或者线性表的一种方法。 散列的基本思想是:以集...

  • 散列

    散列值与相等性 等值对象的散列值必须相等。散列相等未必等值。 散列表算法 其他说明 key必须是可散列的。可散列需...

  • 散列

  • 散列

    定义 散列是一种常见的数据存储技术,散列后的数据可以快速插入或者取用。散列使用的数据解构叫做散列表。在散列中插入、...

  • 散列

    HashMap HashMap也是我们使用非常多的Collection,它是基于哈希表的 Map 接口的实现,以k...

  • 散列

    哈希码是一个散列值,通过单向函数求得,范围是int,数量有限,所以会发生散列值冲突。HashMap、Hashtab...

  • 散列

    散列的定义与整数散列 问题提出:给出 N 个正整数,再给出 M 个正整数,问这 M 个数中的每个数分别是否在 N ...

  • 散列

    散列函数:把查找表中的关键字映射成该关键字对应的地址。Hash(key)=Addr这里的地址可以是数组下标,索引或...

网友评论

      本文标题:散列 & 线性散列

      本文链接:https://www.haomeiwen.com/subject/sbxptqtx.html