2021SC@SDUSC
目录
Hash索引
postgreSQL除了可以使用B-Tree索引,也可以使用Hash索引,Hash索引拥有更快的查询速率,我们本篇主要讲解一下Hash索引的原理并且比较它与B-Tree的区别,同时分析源码中关于Hash索引的相关的数据结构。
Hash索引原理
Hash表
哈希表也为散列表,其原理如下图所示
Hash索引的核心是构建一个Hash表,主要原理就是在Hash表中有一个Hash函数,通过查找键为参数,计算出映射到的桶号,将键值分配到各个桶中。然后仍然需要遍历桶中的内容,并仅返回具有适当哈希码的匹配ctid(行号)。由于存储的“hash code - ctid”对是有序的,因此可以高效地完成此操作。 同时Hash索引表可以分为静态Hash表和动态Hash表,静态Hash表不会增加桶的数目,而动态Hash表可以动态增加桶的数目。PostgreSQL则使用了动态Hash表的结构
Hash索引结构
每一个Hash索引结构如下所示,每一个Hash索引都有四种类型的页面,分别为元页,桶页,溢出页,位图页,接下来会介绍下关于Hash索引的相关的数据结构。
Hash的页面结构
Hash的页面结构与B-Tree相似,而其special space的字段填充为HashPageOpaqueData结构,我们从pg的源码找出该结构
typedef struct HashPageOpaqueData
{
BlockNumber hasho_prevblkno;
BlockNumber hasho_nextblkno;
Bucket hasho_bucket;
uint16 hasho_flag;
uint16 hasho_page_id;
} HashPageOpaqueData;
元页
每个Hash索引都有一个元页,它是该索引的初始页面,它也不属于任何桶,它记录了Hash的版本号,Hash索引记录的索引元组数目,桶的信息,位图等相关信息,其他页的信息也与元页息息相关,我们来看一下pg源码中的元页的数据结构:
typedef struct HashMetaPageData
{
uint32 hashm_magic;
uint32 hashm_version;
double hashm_ntuples;
uint16 hashm_ffactor;
uint16 hashm_bsize;
uint16 hashm_bmsize;
uint16 hashm_bmshift;
uint32 hashm_maxbucket;
uint32 hashm_highmask;
uint32 hashm_lowmask;
uint32 hashm_ovflpoint;
uint32 hashm_firstfree;
uint32 hashm_nmaps;
RegProcedure hashm_procid;
uint32 hashm_spares[HASH_MAX_SPLITPOINTS];
BlockNumber hashm_mapp[HASH_MAX_BITMAPS];
} HashMetaPageData;
桶页,溢出页,位图页
桶页:Hash表有多个桶组成,每个桶有一个或多个页组成,每一个桶的第一个页叫做桶页,其他页面称作溢出页,桶页成2的幂次分配,当在元页中记录的splitpoint增加时,桶页成2的幂次增长。 溢出页:当一个元组的内容超过桶页的大小时,需要给该桶增加一个溢出页,溢出页和桶页是通过双向链链接起来的。 位图页:用于管理Hash索引的溢出页和位图页本身的使用情况,如果某个溢出页上的元组都被移除,则将溢出页回收。位图的页面格式如下图所示,他是用比特位来表示位图页或者溢出页是否可用,0不可用,1可用
和B-Tree相比的优缺点
优点
Hash索引的执行效率很快,时间复杂度低,,索引检索一次到位你,不需要和B-Tree索引一样由根节点再到叶子节点,可以用于等值查询,以及btree索引不支持的大字段类型
缺点
1.由于Hash索引指向的数据是无序的,Hash索引不能进行范围查询,而B+树可以。 2.Hash索引无法被用来避免数据的排序操作。Hash索引中存放的是经过Hash计算之后的Hash值,而且Hash值得大小关系不一定和Hash运算前的键值完全一样。 3当产生大量的Hash冲突时,Hash索引的执行效率会降低,浪费多次数据的访问。
总结
postgreSQL的Hash 索引的相关算法主要是由Margo Seltzer and Ozan Yigit提出的,使用动态索引表,页面结构由元页,位图页,桶页,溢出页来共同组成并介绍了相关数据结构,下一篇我们进行对于Hash索引建立的相关讲解。
|