第四章 索引

1. 概述

    索引是指按表中某些关键属性或表达式建立元组的逻辑顺序,它是由一系列表元组的标识号组成的一个列表。使用索引可快速访问表中的特定元组,被索引的表称为基表。索引并不改变表中元组的物理顺序,索引技术会将对于元组的逻辑排序保存在索引文件中。基表文件中的元组被修改或被删除时,索引文件会自动更新以保证能够准确地找到新的数据。
    postgresql的五种索引方式:唯一索引、主键索引、多属性索引、部分索引、表达式索引,以及4种索引类型:B-Tree、Hash、GiST、GIN。Create INDEX来创建索引,默认情况下将创建一个B-Tree索引。
    pg的所有索引在物理上与它描述的表文件分离。每个索引都在pg_class表里有记录,一个索引的内部结构与该索引的访问方法相关。
    索引本质上来说就是一些数据的键值与元组标识符(TID)之间的映射,这些标识符确定了该索引键值在表中对应的元组。

1.1 索引方式

  • 唯一索引:不允许出现多个索引值相同的元组。UNIQUE关键字只有B-Tree可以创建唯一索引;
  • 主键索引:一个表定义了一个主键,那么会自动在主键属性上创建唯一索引来实现主键约束;
  • 多属性索引:一个索引定义在多于一个的属性上,就称为多属性索引,它多用于组合查询。B-Tree、GiST和GIN支持多属性索引。多属性索引中不仅可以使用表中的属性,也可以使用函数或表达式计算得到的值;
  • 部分索引:建立在一个表的子集上的索引,该子集由一个条件表达式定义。eg. CREATE OMDEX stu_name_idx ON student(name) WHERE (id > 1 AND id < 255); 使用部分索引,能够减小索引的规模,提高索引的查询效率;
  • 表达式索引:索引并非一定要建立在一个表的属性上,还可以建立在一个函数或从一个或多个属性计算出来的标量表达式上。表达式索引只有在查询时使用与创建时相同的表达式才会起作用。
        创建:CREATE INDEX stu_low_name_idx ON student (lower (name));
        查找:SELECT * FROM student WHERE lower (name) = ‘Jack’;
        Note:部分索引的特点是通过表达式来限制索引元组的数量;而表达式索引则是用表达式来计算被索引元组的键值。

1.2 索引类型

  • B-Tree:适合支持比较查询及范围查询。在一个建立了B-Tree索引的属性涉及使用操作符(>、=、<操作符)进行比较时,PostgreSQL的查询优化器会考虑使用B-Tree索引进行查找;
  • Hash:使用Hash函数对索引的关键字进行散列。Hash索引只能处理简单的等于比较。当一个建立了Hash索引的属性涉及使用“=”操作符比较时,查询优化器会考虑使用Hash索引;
  • GiST:意为通用搜索树。严格来说,GiST索引不是一种独立的索引类型,而是一种架构或索引模板,可以在这种架构上实现不同的索引策略。因此使用GiST索引的操作符类型高度依赖于索引策略(操作符类);
  • GIN:倒排索引,它可以处理包含多个键的值(比如数组)。与GiST类似,GIN支持用户定义的索引策略,对于不同的索引策略,可以使用的操作符也是不同的。

1.3 索引相关系统表

    每种索引类型都在系统表pg_am(access method)里面用一个元组来记录。pg_am表中的每一个元组包括了该种索引类型提供的访问函数,这些函数是引用自pg_proc系统表中注册的函数。
在这里插入图片描述
    对于每一个创建的索引,会在pg_class系统表中添加一个元组,同时还会在pg_index系统表中添加一个元组。pg_index用于记录与索引有关的信息。
在这里插入图片描述
    每一种索引类型并不直接设定该类型的索引要操作的数据类型,而是由操作符类系统表pg_opclass进行管理,该表表明了索引方法在操作特定数据类型的时候需要使用的操作集合。
在这里插入图片描述
    在pg_opclass中,每一个操作符类都引用了系统表pg_opfamily中的一个元组,表明该操作符类的操作符集合。每个pg_opfamily元组定义了一个操作符的集合。在这里插入图片描述
在这里插入图片描述

1.4 索引的操作函数

  • ambuild函数用于创建一个新索引。在调用ambuild之前,索引文件已经在物理上创建好了,但是是空的,还需要使用索引元组进行填充。ambuild的工作就是生成索引元组并将它们填充到索引文件中;
  • aminsert函数向现有索引中插入一个新的索引元组。如果该索引要求是唯一索引,则在插入过程中会检查该索引元组是否已经存在;
  • ambulkdelete函数从索引中删除元组。该函数所做的删除可能是标记删除,也可能是物理上的删除。ambulkdelete是一个“大批删除”的操作,通常会扫描整个索引,检查每个元组是否需要被删除。一般调用该函数时,会给它传递一个回调函数。回调函数负责判断索引元组是否需要删除。ambulkdelete函数可能会调用amvacummcleanup来执行实际的删除操作;
  • amvacuumcleanup函数会在一个VACUUM操作之后调用,主要做一些额外的清理工作,比如对FSM文件进行清理。通常用于批量删除中;
  • amcostestimate函数用于估算一个索引扫描的代价。在最简单情况下,该函数实现的所有功能都可以通过调用优化器里面的标准过程完成。该函数存在的目的是允许索引访问方法提供与索引类型相关的信息,以改进标准的代价估计;
  • amoptions函数用于分析和验证一个索引的reloptions数组,仅当一个索引存在非空reloptions数组时才会被调用;
  • ambeginscan函数开始一个新的扫描。主要功能是构造索引扫描描述符结构IndexScanDescData,然后用该描述符进行扫描。索引访问必须通过调用该函数来创建这个结构,然后调用amrescan来执行扫描。在大多数情况下,ambeginscan本身除了调用RelationGetIndexScan函数之外几乎不干别的事;
  • amgettuple函数在给出的扫描描述符里获取下一个元组,向给出的方向移动;
  • amgetbitmap函数在给出的扫描符里获取所有可用的元组,返回的元组通过一个bitmap的方式表示;
  • amrescan函数用于重启一个扫描,该函数可以使用一个新的扫描关键字。实际上,函数RelationGetIndexScan中也是调用amrescan来完成描述符的创建的。因此该函数既可用于索引扫描的初始化扫描,也可用于重复扫描;
  • amendscan函数结束扫描并释放资源,扫描过程中使用的任何lock锁和pin锁都应该释放;
  • ammarkpos函数标记当前扫描位置,事实上该函数会将当前的扫描位置记载在扫描描述符的opaque字段中;
  • amrestrpos函数把扫描恢复到最佳标记的位置。

2. B-Tree索引

  1. 索引的创建:首先对每一个需要索引的表元组生成对应的索引元组,然后调用tuplesort函数对索引的索引元组进行排序,最后创建索引;每一个B-Tree索引都有一个元页,它主要说明该B-Tree的版本、根节点位置(块号)以及根节点在树中的层次等信息,元页始终位于B-Tree索引等第一页(编号为0);由于元页相关的信息只有在索引创建完成后才能够获得,因此比元页会预留,一直等到索引创建完成后,才会生成元页并赋值。
  2. 索引的插入:btinsert函数首先将表元组封装为索引元组,然后沿着B-Tree往下找到合适的插入点。找到正确的节点后,若该索引是唯一索引,则会在节点中验证待插入的元组是否已经存在,若存在则报错结束;如果索引不是唯一索引或没有重复元组,则将该索引插入到索引中。在从根节点往下查找合适的节点过程中,会使用结构BTStackData保存查找过程中的父节点,即保持查找路径。当插入后需要分裂叶子节点时,可以根据栈中存储的对应关系找到所有的父节点,并根据情况依次对父节点进行调整。在这里插入图片描述
    在这里插入图片描述
  3. 扫描索引
  • btgettuple函数:得到扫描中的下一个满足条件的索引元组。如果已经初始化了扫描信息,只需要得到上一次的扫描位置,然后调用_bt_next函数根据扫描方向获取下一个满足扫描条件的索引元组;如果没有初始化,就调用_bt_first函数开始新的扫描。该函数首先会预处理扫描信息,然后调用_bt_search函数来实现扫描,最后将获取到的索引元组TID返回;
  • btbeginscan函数:开始索引扫描。该函数根据需要扫描的索引的相关信息生成一个IndexScanDesc结构,该结构指向IndexScanDescData的指针,该结构保存了扫描索引的相关信息,如扫描的键值、查找到的位置等。
  • btrescan函数:重新开始一个索引扫描的过程;
  • btendscan函数:与btbeginscan函数成对出现,主要功能是释放进行一个索引扫描所占用的系统资源;
  • btmarkpos函数:当一个正在进行的索引扫描由于某种原因需要停止时,就需要调用btmarkpos函数,它将保存当前扫描位置的相关变量;
  • btrestrpos函数:与上面的btmarkpos相对应,用btmarkpos中所存储的最后扫描位置信息,导入到当前扫描位置信息变量中,从而恢复到上次的扫描位置以后再开始扫描,这样可以节省扫描时间。
  1. 删除索引元组
       删除B-Tree索引元组的函数主要有两个:一个是查找需要删除的节点信息的函数btvacuumcleanup,该函数寻找可以删除的页面;另一个是进行批量删除的函数btbulkdelete,该函数批量删除指向一个表元组集合所对应的所有索引元组。
       若删除了基表中的一个元组,系统并不会立即删除该表元组对应的索引元组,而是在VACUUM的时候删除。

3. Hash索引

在这里插入图片描述

3.1 Hash索引的组织结构

在这里插入图片描述

  • 元页:每个Hash索引都有一个元页,它是该索引的0号页,它不属于任何桶。元页记录了Hash的版本号、Hash索引记录的索引元组数目、桶信息、位图等相关信息。通过元页可以了解该Hash索引在总体上的分配和使用情况,在索引元组的插入、溢出页的分配回收及Hash表的扩展等过程中,都需要用到元页;
  • 桶页:每个桶的第一个页称为桶页,其他页称为溢出页;
  • 溢出页:如果某个元组在它所属的桶中放不下的时候,就需要给该桶增加一个溢出页。溢出页和桶页之间是通过双向链接接起来的;
  • 位图:用于管理Hash索引的溢出页和位图页本身的使用情况。

3.2 Hash索引的实现

    实现索引主要涉及以下工作:Hash表的构建、索引元组的插入、溢出页的分配与回收、以及Hash表的扩展。

4. GiST索引

    GiST通用搜索树是一种平衡的、树状结构的访问方法。它在系统中相当于一个基础的模板,几乎可以使用它实现任意索引模式。

4.1 GiST的扩展性

在这里插入图片描述

4.2 GiST索引组织结构

4.3 GiST索引的实现

5. GIN索引

    GIN通用倒排索引是一个存储对集合的索引结构,其中key是一个键值,posting list是一组出现过key的位置。通过这种素引结构可以快速地查找到包含指定关键字的元组,因此GIN索引特别适合于支持全文搜索。而开发GIN索引的主要目的就是为了让PostgreSQL能支持功能高度可扩展的全文搜索。在这里插入图片描述

6. TSearch2 全文搜索

    全文搜索提供了一种可以检索出满足某个查询条件的自然语言文档的能力,并还可以根据文档的相关性对文档进行排序。最常见的搜索是找出所有包含给出的查询词的文档,并且以它们符合查询的程度排序输出。
    全文搜索一般有三个步骤:

  • 对文档进行预处理,得到处理后的结果,创建索引;
  • 解析查询条件,使用索引进行查询;
  • 得到查询结果,对结果进行处理后返回。

7. 小结

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值