2021SC@SDUSC
本篇博客主要讲解GiST索引查询的相关函数,并结合具体实例来介绍GIST的使用过程

目录

  • GIST索引查询
    • GISTSearchHeapItem
    • GISTSearchItem
    • gistScanPage函数
    • gistgettuple函数
  • GIST索引的使用
  • 总结

GIST索引查询

GIST的相关搜索查询在9.0版本使用了Stack的相关结构,而在我下载12.0版本上查询相关的数据结构改成了与堆相关的数据结构,很显然这样加快了搜索的速度。

GISTSearchHeapItem

typedef struct GISTSearchHeapItem
{ItemPointerData heapPtr;bool       recheck;        /*T如果合格需要重新检查*/bool     recheckDistances;   /* T在距离内必须重新检查 */HeapTuple  recontup;       /*堆元组用于扫描索引 */OffsetNumber offnum;      /* 记录在页中的偏移量来标记元祖 */
} GISTSearchHeapItem;

GISTSearchItem

搜索到的堆元组的相关数据结构,每个元组都有该数据结构

typedef struct GISTSearchItem
{pairingheap_node phNode;BlockNumber blkno;         /* 索引页号*/union{GistNSN      parentlsn;  /*存储parentlsn来记录是否父页面发生分裂*/GISTSearchHeapItem heap; /* 索引查询堆元组 */}          data;IndexOrderByDistance distances[FLEXIBLE_ARRAY_MEMBER];
} GISTSearchItem;

gistScanPage函数

GIST的查询有index scan和bitmap scan两种方法,该函数是在一个页面中寻找匹配的数据,其中分为多种情况,如果是叶子节点会将匹配的数据放入队列中,如果不是则会将匹配的元组指向的子节点放入队列中

static void
gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem,IndexOrderByDistance *myDistances, TIDBitmap *tbm, int64 *ntids)
{//查询相关信息的初始化GISTScanOpaque so = (GISTScanOpaque) scan->opaque;GISTSTATE  *giststate = so->giststate;Relation   r = scan->indexRelation;Buffer      buffer;Page     page;GISTPageOpaque opaque;OffsetNumber maxoff;OffsetNumber i;MemoryContext oldcxt;Assert(!GISTSearchItemIsHeap(*pageItem));buffer = ReadBuffer(scan->indexRelation, pageItem->blkno);LockBuffer(buffer, GIST_SHARE);PredicateLockPage(r, BufferGetBlockNumber(buffer), scan->xs_snapshot);gistcheckpage(scan->indexRelation, buffer);page = BufferGetPage(buffer);TestForOldSnapshot(scan->xs_snapshot, r, page);opaque = GistPageGetOpaque(page);/*检查是否需要遵循右侧指针,通过查看父页面的数据结构中的parentisn,如果小于Nsn,那么就要用右指针*/if (!XLogRecPtrIsInvalid(pageItem->data.parentlsn) &&(GistFollowRight(page) ||pageItem->data.parentlsn < GistPageGetNSN(page)) &&opaque->rightlink != InvalidBlockNumber /* sanity check */ ){/* 页面分割,使用右指针*/GISTSearchItem *item;Assert(myDistances != NULL);oldcxt = MemoryContextSwitchTo(so->queueCxt);/*为新分裂的页面创造新的 GISTSearchItem数据结构*/item = palloc(SizeOfGISTSearchItem(scan->numberOfOrderBys));item->blkno = opaque->rightlink;item->data.parentlsn = pageItem->data.parentlsn;/*插入队列  */memcpy(item->distances, myDistances,sizeof(item->distances[0]) * scan->numberOfOrderBys);pairingheap_add(so->queue, &item->phNode);MemoryContextSwitchTo(oldcxt);}/*当查询到downlink,检测页面是否被删除,这是在并发分裂后可能出现的情况*/if (GistPageIsDeleted(page)){UnlockReleaseBuffer(buffer);return;}so->nPageData = so->curPageData = 0;scan->xs_hitup = NULL;       if (so->pageDataCxt)MemoryContextReset(so->pageDataCxt);/*
保存页面的LSN,用于检测是否安全*/so->curPageLSN = BufferGetLSNAtomic(buffer);/*检查页面上所有的元组*/maxoff = PageGetMaxOffsetNumber(page);for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)){ItemId       iid = PageGetItemId(page, i);IndexTuple    it;bool     match;bool      recheck;bool        recheck_distances;if (scan->ignore_killed_tuples && ItemIdIsDead(iid))continue;it = (IndexTuple) PageGetItem(page, iid);oldcxt = MemoryContextSwitchTo(so->giststate->tempCxt);/*调用gistindex_keytest函数*/match = gistindex_keytest(scan, it, page, i,&recheck, &recheck_distances);MemoryContextSwitchTo(oldcxt);MemoryContextReset(so->giststate->tempCxt);/*忽略不符合条件的元组*/if (!match)continue;if (tbm && GistPageIsLeaf(page)){//getbitmap扫描tbm_add_tuples(tbm, &it->t_tid, 1, recheck);(*ntids)++;}else if (scan->numberOfOrderBys == 0 && GistPageIsLeaf(page)){/**无序扫描*/so->pageData[so->nPageData].heapPtr = it->t_tid;so->pageData[so->nPageData].recheck = recheck;so->pageData[so->nPageData].offnum = i;/*仅索引扫描中,从元组获取数据,数据存储到pageDataCxt中*/if (scan->xs_want_itup){oldcxt = MemoryContextSwitchTo(so->pageDataCxt);so->pageData[so->nPageData].recontup =gistFetchTuple(giststate, r, it);MemoryContextSwitchTo(oldcxt);}so->nPageData++;}else{/*做有序索引搜索*/GISTSearchItem *item;int         nOrderBys = scan->numberOfOrderBys;oldcxt = MemoryContextSwitchTo(so->queueCxt);/* 为新的item创造属于它的GISTSearchItem */item = palloc(SizeOfGISTSearchItem(scan->numberOfOrderBys));if (GistPageIsLeaf(page))//如果是叶子节点{item->blkno = InvalidBlockNumber;item->data.heap.heapPtr = it->t_tid;item->data.heap.recheck = recheck;item->data.heap.recheckDistances = recheck_distances;/** 仅索引搜索中,获取元组中数据*/if (scan->xs_want_itup)item->data.heap.recontup = gistFetchTuple(giststate, r, it);}else{item->blkno = ItemPointerGetBlockNumber(&it->t_tid);item->data.parentlsn = BufferGetLSNAtomic(buffer);}/* 插入到队列中 */memcpy(item->distances, so->distances,sizeof(item->distances[0]) * nOrderBys);pairingheap_add(so->queue, &item->phNode);MemoryContextSwitchTo(oldcxt);}}UnlockReleaseBuffer(buffer);
}

gistgettuple函数

gistgettuple函数时GIST进行扫描的入口函数,它主要调用了gistScanPage函数,通过循环,来寻找匹配的元组。该函数思想与nbtree的搜索有些相似,将注释标注如下:

bool
gistgettuple(IndexScanDesc scan, ScanDirection dir)
{GISTScanOpaque so = (GISTScanOpaque) scan->opaque;if (dir != ForwardScanDirection)elog(ERROR, "GiST only supports forward scan direction");if (!so->qual_ok)return false;if (so->firstCall){/* 从根页开始扫描*/GISTSearchItem fakeItem;pgstat_count_index_scan(scan->indexRelation);so->firstCall = false;so->curPageData = so->nPageData = 0;scan->xs_hitup = NULL;if (so->pageDataCxt)MemoryContextReset(so->pageDataCxt);fakeItem.blkno = GIST_ROOT_BLKNO;memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));gistScanPage(scan, &fakeItem, NULL, NULL, NULL);//扫描根页匹配的数据}if (scan->numberOfOrderBys > 0){/* 按照距离顺序来取元组*/return getNextNearest(scan);}else{/*遍历索引页来寻找匹配的元组*/for (;;){if (so->curPageData < so->nPageData){if (scan->kill_prior_tuple && so->curPageData > 0){if (so->killedItems == NULL){MemoryContext oldCxt =MemoryContextSwitchTo(so->giststate->scanCxt);so->killedItems =(OffsetNumber *) palloc(MaxIndexTuplesPerPage* sizeof(OffsetNumber));MemoryContextSwitchTo(oldCxt);}if (so->numKilled < MaxIndexTuplesPerPage)so->killedItems[so->numKilled++] =so->pageData[so->curPageData - 1].offnum;}/* 继续返回索引元组*/scan->xs_heaptid = so->pageData[so->curPageData].heapPtr;scan->xs_recheck = so->pageData[so->curPageData].recheck;/* 仅索引扫描中,也返回重构的元组*/if (scan->xs_want_itup)scan->xs_hitup = so->pageData[so->curPageData].recontup;so->curPageData++;return true;}/*检查最后返回的元组,如果需要则添加到killedItems中*/if (scan->kill_prior_tuple&& so->curPageData > 0&& so->curPageData == so->nPageData){if (so->killedItems == NULL){MemoryContext oldCxt =MemoryContextSwitchTo(so->giststate->scanCxt);so->killedItems =(OffsetNumber *) palloc(MaxIndexTuplesPerPage* sizeof(OffsetNumber));MemoryContextSwitchTo(oldCxt);}if (so->numKilled < MaxIndexTuplesPerPage)so->killedItems[so->numKilled++] =so->pageData[so->curPageData - 1].offnum;}/* 循环寻找子页面的满足元组*/do{GISTSearchItem *item;if ((so->curBlkno != InvalidBlockNumber) && (so->numKilled > 0))gistkillitems(scan);item = getNextGISTSearchItem(so);if (!item)return false;CHECK_FOR_INTERRUPTS();so->curBlkno = item->blkno;/*调用gistscanPage函数寻找索引页上的元组,当到达叶子节点时会存放寻找到的数据*/gistScanPage(scan, item, item->distances, NULL, NULL);pfree(item);} while (so->nPageData == 0);}}
}

GIST索引的使用

首先在gistproc.c文件中,内建了对于boxes, polygons, and circles的数据类型的GIST索引的支持,这些数据类型可以直接构建GIST的索引,其他数据类型需要用户手动添加,并实现上一篇博客提到的7种实现方法。下面是关于circles类型的简单使用:

test=# create table t_gist (id int, pos point);
CREATE TABLE
test=#  insert into t_gist select generate_series(1,100000), point(round((random()*1000)::numeric, 2), round((random()*1000)::numeric, 2));
INSERT 0 100000
test=#  select * from t_gist  limit 3; id |       pos
----+-----------------1 | (614.19,511.21)2 | (463.64,34.88)3 | (862.41,906.76)
(3 行记录)
test=# create index idx_t_gist_1 on t_gist using gist (pos);
CREATE INDEX
test=#  explain (analyze,verbose,timing,costs,buffers) select * from t_gist where circle '((100,100) 10)'  @> pos;    QUERY PLAN                                                  -----------------------------------------------------------------------------------------------------------Bitmap Heap Scan on PUBLIC.t_gist  (cost=5.06..271.70 rows=100 width=20) (actual time=0.084..0.205 rows=26 loops=1)Output: id, posRecheck Cond: ('<(100,100),10>'::CIRCLE @> t_gist.pos)Heap Blocks: exact=26Buffers: shared hit=29->  Bitmap Index Scan on idx_t_gist_1  (cost=0.00..5.03 rows=100 width=0) (actual time=0.067..0.068 rows=26 loops=1)Index Cond: ('<(100,100),10>'::CIRCLE @> t_gist.pos)Buffers: shared hit=3Planning time: 0.246 msExecution time: 0.279 ms
(10 行记录)

postgresql提供了关于GIST的扩展模块,在12.0版本下的contrib文件夹中放置了关于GIST的一些扩展模块,有用于多维立方体的cube,有用于树状结构的ltree等。
我们以cube为例,具体使用步骤如下
1.make编译代码得到cube.so链接库,用于将实现的七种方法放入数据库中
2.执行make install将链接库添加到数据库生成cube.sql
3.修改cube.sql.in文件,修改文件中cube.so的路径
4.登录数据库,执行cube.sql.in语句,会将cube的信息添加到数据库中,便可以开始使用,实现方法和上文circles类似

总结

本文讲述了关于GIST的索引的查询相关函数,了解GITS索引支持实现了index scan 和bitmap scan,用heap来存储查询匹配的数据,加快了查询的效率。同时也掌握了GIST索引的相关使用和扩展方法。

postgreSQL源码分析——索引的建立与使用——GIST索引(3)相关推荐

  1. PostgreSQL源码分析

    PostgreSQL源码结构 PostgreSQL的使用形态 PostgreSQL采用C/S(客户机/服务器)模式结构.应用层通过INET或者Unix Socket利用既定的协议与数据库服务器进行通信 ...

  2. postgreSQL源码分析——索引的建立与使用——GIST索引(2)

    2021SC@SDUSC 本篇博客主要讲解GiST索引创建以及删除的相关函数 这里写目录标题 GIST创建 相关数据结构 GISTBuildState GISTInsertStack gistbuil ...

  3. postgreSQL源码分析——索引的建立与使用——Hash索引(3)

    2021SC@SDUSC 上一篇博客讲了关于Hash索引创建与插入的相关函数,这一篇博客讲述关于溢出页的操作函数以及Hash表的扩展相关的函数. 目录 溢出页的分配和回收 _hash_addovflp ...

  4. postgreSQL源码分析——索引的建立与使用——Hash索引(2)

    2021SC@SDUSC 目录 Hash索引创建 hashbuild函数 _hash_init函数 Hash索引的插入 hashinsert函数 _hash_doinsert函数 总结 Hash索引创 ...

  5. postgreSQL源码分析——索引的建立与使用——各种索引类型的管理和操作(2)

    2021SC@SDUSC 目录 上层操作函数 index_open index_beginscan() index_create() indexcmd.c 下层接口函数 IndexScanDescDa ...

  6. postgreSQL源码分析——索引的建立与使用——Hash索引(1)

    2021SC@SDUSC 目录 Hash索引 Hash索引原理 Hash表 Hash索引结构 Hash的页面结构 元页 桶页,溢出页,位图页 和B-Tree相比的优缺点 优点 缺点 总结 Hash索引 ...

  7. postgreSQL源码分析——索引的建立与使用——各种索引类型的管理和操作(1)

    2021SC@SDUSC 目录 概述 管理索引的系统表 记录索引相关的系统表 与索引系统表相关的后端源码 索引的操作函数 上层操作函数 下层接口函数 概述 索引是指按表中某些关键属性或表达式建立元组的 ...

  8. postgreSQL源码分析——索引的建立与使用——总结篇

    2021SC@SDUSC 在小组中我负责索引的建立与使用的相关部分,在此一共写了16篇相关的分析报告,着重分析各种索引的操作和管理方法,以及分析了PG中四种最重要的索引B-Tree索引,Hash索引, ...

  9. postgreSQL源码分析——索引的建立与使用——B-Tree索引(3)

    2021SC@SDUSC 目录 B-Tree的插入 bt_insert _bt_doinsert BTInsertStateData _bt_search函数 _bt_moveright函数 B-Tr ...

  10. postgreSQL源码分析——索引的建立与使用——B-Tree索引(2)

    2021SC@SDUSC 目录 B-Tree建立过程 IndexAmRoutine BTBuildState BTWriteState btbuild() _bt_leafbuild _bt_load ...

最新文章

  1. 关闭浏览器当前页面:Ctrl+W
  2. BERT不加载预训练模型的理由
  3. Java从零开始学二十三(集合Map接口)
  4. linux下安装mysql5.7.17及简单配置
  5. android 实现显示电量,Android开发:显示电池电量
  6. CSS页面布局解决方案大全
  7. 数据服务基础能力之元数据管理
  8. android java thread_Android中断并重启一个Thread线程的简单方法
  9. 零基础入门深度学习(2) - 线性单元和梯度下降
  10. ROS联合Webots之实现趣味机器人巡线刷圈
  11. 信息安全概论———网络安全协议
  12. 山东大学创新项目实训个人工作日志(十四)
  13. 电信天翼网关连接多个路由器
  14. LintCode 练习【C++】
  15. Mysql报错 Cannot delete or update a parent row: a foreign key constraint fails
  16. 【动量轮自平衡自行车】STM32_PID(开源-含硬件资料)
  17. 我的世界服务器的启动器在哪个文件夹,原神启动器在哪 启动器位置文件介绍...
  18. CISP-PTE靶机练习(me-and-my-girlfriend)
  19. 【预研】关于互联网广告:半小时读懂互联网广告新生态
  20. tar解压tgz文件出现gzip: stdin: not in gzip format报错问题

热门文章

  1. 【Clickhouse】Clickhouse 整合 Prometheus 监控 运行时状态
  2. 【Elastischearch】Elastischearch 的 ID 生成器 UUIDGenerator
  3. 【es】es 分布式一致性原理剖析(三)-Data篇
  4. 【jvm】jvm 执行模式设置
  5. 95-130-410-源码-source-RabbitMQ相关-RabbitMQ Connector
  6. 95-235-065-源码-task-SourceStreamTask
  7. Mac电脑开机出现带问号的文件夹并且闪烁 apple.com/support -2003F
  8. kylin启动:Failed to create /kylin
  9. 【impala】impala的shell命令使用
  10. java datetime long_Android Java datetime值从String到Long到String问题