哈希索引(PostgreSQL 14 Internals翻译版)

news/2025/3/29 6:40:47/

概览

哈希索引提供了根据特定索引键快速查找tuple ID (TID)的功能。粗略地说,它只是一个存储在磁盘上的哈希表。哈希索引唯一支持的操作是根据相等条件进行搜索

当一个值插入到索引中时,将计算索引键的哈希函数。PostgreSQL哈希函数返回32位或64位整数;这些值中最低的几个位用作相应桶的编号。将TID和键的哈希码添加到所选的桶中。键本身不存储在索引中,因为这样处理固定长度的小值更方便。

索引的哈希表是动态扩展的。桶的最小数量是两个。随着索引元组数量的增长,其中一个存储桶被分成两个。该操作使用了多一位哈希码,因此元素仅在拆分产生的两个桶之间重新分布;哈希表的其他桶的组成保持不变。

索引搜索操作计算索引键和对应桶号的哈希函数。在所有桶内容中,搜索将只返回那些与键的哈希码对应的TID。由于bucket元素是按键的哈希码排序的,因此二进制搜索可以非常有效地返回匹配的TID。

由于键不存储在哈希表中,因此索引访问方法可能会由于哈希冲突而返回冗余的tid。因此,索引引擎必须重新检查访问方法获取的所有结果。出于同样的原因,不支持仅索引扫描

页面布局

与常规哈希表不同,哈希索引存储在磁盘上。因此,必须将所有数据安排到页中,最好是这样一种方式,即索引操作(搜索、插入、删除)需要访问尽可能少的页。

哈希索引使用四种类型的页面:

  • metapage—提供索引“目录”的页零
  • bucket pages—索引的主要页面,每个bucket一个
  • overflow pages—当主桶页不能容纳所有元素时使用的附加页
  • bitmap pages—包含位数组的页面,用于跟踪已被释放并可被重用的溢出页面

我们可以使用pageinspect扩展查看索引页。

让我们从一张空表开始:

在这里插入图片描述

我已经分析了表,因此创建的索引将具有尽可能小的大小;否则,将根据表包含10个页面的假设选择桶的数量。

索引包含四个页面:元页面,两个桶页面和一个位图页面(一次创建以备将来使用):

在这里插入图片描述

元页面包含有关索引的所有控制信息。我们现在只对几个值感兴趣:

在这里插入图片描述

每个桶的估计行数显示在ffactor字段中。该值是根据块大小和fillfactor存储参数值计算得出的。通过绝对统一的数据分布和没有散列冲突,您可以使用更高的填充因子值,但在实际数据库中,它会增加页面溢出的风险。

对于散列索引来说,最糟糕的情况是当一个键被重复多次时,数据分布出现很大的倾斜。由于哈希函数将返回一个相同的值,因此所有数据将被放置到相同的桶中,并且增加桶的数量不会有帮助。

现在索引为空,如ntuples字段所示。让我们通过插入具有相同索引键值的多行来导致桶页溢出。索引中出现溢出页:

在这里插入图片描述

综合所有页面的统计数据,桶0是空的,而所有的值都被放到了桶1中:其中一些位于主页面,不适合的可以在溢出页中找到。

在这里插入图片描述

很明显,如果同一个bucket的元素分布在几个页面上,性能将受到影响。如果数据分布是均匀的,散列索引将显示最佳结果。

现在让我们来看看如何分割桶。当索引中的行数超过可用桶的估计因子值时,就会发生这种情况。这里我们有两个桶,因子是307,所以它将在第615行插入索引时发生:

在这里插入图片描述
maxbucket值已经增加到2:现在我们有三个桶,编号从0到2。但是,尽管我们只添加了一个桶,页面的数量却翻了一番:

在这里插入图片描述
其中一个新页面由bucket 2使用,而另一个页面保持空闲状态,一旦出现就会被bucket 3使用。

在这里插入图片描述
在这里插入图片描述
因此,从操作系统的角度来看,哈希索引是快速增长的,尽管从逻辑的角度来看哈希表是逐渐增长的。

为了在一定程度上平衡这种增长并避免一次分配过多的页面,从第10次增加开始,将页面分配为四个相等的批次,而不是一次分配所有的页面。

元页面的另外两个字段实际上是位掩码,提供了桶地址的详细信息:

在这里插入图片描述
桶号由与高掩码对应的哈希码位定义。但是如果接收到的桶号不存在(超过maxbucket),则采用低掩码位。在这个特殊的例子中,我们取两个最低的位,得到0到3的值;但是如果我们得到3,我们只会取一个最低的位,也就是说,用桶1代替桶3。

每次大小增加一倍时,新的桶页被分配为单个连续块,而溢出页和位图页则根据需要插入这些片段之间。元页面保留插入到备件数组中每个块中的页面数,这使我们有机会使用简单的算术根据桶号计算其主页的数量。

在这个特殊的例子中,第一次增加之后插入了两个页面(一个位图页面和一个溢出页面),但是在第二次增加之后没有发生新的添加:

在这里插入图片描述
当指向死元组的指针被删除时,索引页中的空间将被释放。它发生在页面修剪期间(在尝试将元素插入完全填充的页面时触发)或执行常规真空时。

但是,哈希索引不能缩小:一旦分配,索引页将不会返回给操作系统。主页被永久地分配到它们的bucket中,即使它们根本不包含任何元素;清除的溢出页在位图中被跟踪,并且可以被重用(可能由另一个桶)。减小索引物理大小的唯一方法是使用REINDEX或
VACUUM FULL命令。

查询计划没有索引类型的指示:

在这里插入图片描述
在这里插入图片描述

文章来源:https://blog.csdn.net/Post_Yuan/article/details/133990197
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ppmy.cn/news/1171860.html

相关文章

python之代理ip的配置与调试方法详解

代理IP在Python中是一种强大的工具,它可以用于隐藏真实IP地址、绕过访问限制、提高数据爬取和网络请求的效率等。下面将详细介绍Python中代理IP的配置与调试方法,帮助您更好地理解和应用代理IP。 1. 选择合适的代理IP 在使用代理IP之前,需要…

雅可比矩阵和雅可比坐标

雅可比行列式的简要介绍 一、说明 在本教程中,您将回顾一下雅可比行列式的简单介绍。完成本教程后,您将了解: 雅可比矩阵收集了可用于反向传播的多元函数的所有一阶偏导数。雅可比行列式在变量之间变化时非常有用,它充当一个坐标空…

Visa股票仍然值得投资

来源:猛兽财经 作者:猛兽财经 总结: (1)尽管Visa(V)的估值高于市场平均水平,但仍值得买入。 (2)Visa拥有强劲的基本面,销售额和每股收益一直在稳定增长,股息…

Unity3D 基础——鼠标悬停更改物体颜色,移走恢复

方法介绍 【unity学习笔记】OnMouseEnter、OnMouseOver、OnMouseExit_unity onmouseover_一白梦人的博客-CSDN博客https://blog.csdn.net/a1208498468/article/details/117856445 GetComponent()详解_getcomponet<> 动态名称-CSDN博客https://blog.csdn.net/kaixindrag…

Jprofiler V14中文使用文档

JProfiler介绍 什么是JProfiler? JProfiler是一个用于分析运行JVM内部情况的专业工具。 在开发中你可以使用它,用于质量保证,也可以解决你的生产系统遇到的问题。 JProfiler处理四个主要问题: 方法调用 这通常被称为"CPU分析"。方法调用可以通过不同的方式进行测…

爬虫进阶-反爬破解7(逆向破解被加密数据:全方位了解字体渲染的全过程+字体文件的检查和数据查看+字体文件转换并实现网页内容还原+完美还原上百页的数据内容)

目录 一、全方位了解字体渲染的全过程 1.加载顺序 2.实践操作&#xff1a;浏览器中调试字体渲染 3.总结&#xff1a; 二、字体文件的检查和数据查看 1.字体文件的操作软件 2.映射关系的建立 3.实践操作&#xff1a;翻找样式和真实内容 4.总结&#xff1a; 三、字体文…

深度学习_4_实战_直线最优解

梯度 实战 代码&#xff1a; # %matplotlib inline import random import torch import matplotlib.pyplot as plt # from d21 import torch as d21def synthetic_data(w, b, num_examples):"""生成 Y XW b 噪声。"""X torch.normal(0,…

C++ 火车调度

火车调度 #include<stdio.h> #define MAX 100 typedef struct Q {int data[MAX];int len;int last; }Q_t;Q_t a[MAX]; //MAX个队列 void Init(Q_t* a) {a->len 0; }void En(Q_t* a, int num) {a->last num; //最后进入的值a->data[a->len] num; }void…