PgSQL技术内幕-Bitmap Index Scan

news2024/11/14 20:55:41

PgSQL技术内幕-Bitmap Index Scan

1、简介

Bitmap索引扫描是对索引扫描的一个优化,通过建立位图的方式将原来的随机堆表访问转换成顺序堆表访问。主要分为两点:1)管理每个Bitmap的hash slot没用完时,每个Bitmap代表每个heap页中满足条件元组的ItemIDs,通过Bitmap扫描heap页时需要将所有Bitmap按照页号进行排序,然后依次获取heap页中记录,依次完成顺序回表。2)当hash slot用完时,就需要将heap页的bitmap范围扩大,转换成一个chunk的bitmap,也就是Bitmap中一位代表页内具有满足条件元组的页。此时,整个Bitmaps有chunk的bitmap也有页的bitmap,该chunk的页号为chunk内最小页号,所以Bitmaps排序后,整体上也是有序的。如此完成顺序扫描heap页,只不过对于Chunk的bitmap中一位代表的heap 页需要再次进行条件检测,将满足条件的tuple输出。

2、Bitmap Index Scan中的Bitmap是什么

Bitmap index scan先利用索引获取满足条件的Tid,将其保存到TIDBitmap中。由TIDBitmap管理满足条件的heap tuple的Bitmap。TIDBitmap结构主要成员如下图所示:

c08016319958a4b8485e1f80ad585259.png

各个成员变量的说明:

1)每一页的bitmap由PagetableEntry结构来管理,里面成员主要有blockno:页号,用做hash表的key。最初仅使用entry1,entry1满了,才会使用hash表。这样btgetbitmap扫描完成所有存在的TID,就完成了按照页聚合。

2)pagetable哈希表,初始时(tbm_create调用时指定)仅创建128个hash桶。若一个page对应一个PagetableEntry,当有大量page需要构建bitmap时,就不够用了。所以Hash桶用完则转换chunk进行lossy,从而腾出空闲槽。等hash桶都变成chunk时,就需要扩展了,每次扩展2倍大小(2*128)。

3)nentries表示hash表中已使用桶的个数

4)maxentries为hash表hash桶的最大个数限制。该成员主要作用:控制何时进行lossy,也就是nentries > maxentries时,需要tbm_lossify。大小由work_mem控制,至少16个。当然,如果最终扩展的超过work_mem时,桶仍旧都是chunk,则更新maxentries扩展2倍大小。

5)entriy1表示最开始使用的entry,不用申请到hash表

6)spages和schunks则是从hash桶弄过来排过序的entry。在BitmapHeapScan阶段使用。当然,分别存储Page和lossy的chunk。这样就可以顺序访问了。

另外TIDBitmap中的几个成员有:

1)TBMStatus status:

typedef enum
{
  TBM_EMPTY, /* no hashtable, nentries == 0 */
  TBM_ONE_PAGE, /* entry1 contains the single entry */
  TBM_HASH /* pagetable is valid, entry1 is not */
} TBMStatus;

为什么会有TBM_ONE_PAGE和TBM_HASH呢?因为如果TIDBitmap只存储一个PagetableEntry,不需要耗费实际构建动态hash表,查找时也不需要通过hash查找,只需要使用entry1即可。

3、Bitmap Index Scan阶段

MultiExecBitmapIndexScan函数实现了Exec逻辑,主要通过调用index_getbitmap函数,获取bitmap,然后将bitmap返回给上一层算子。我们这里以btree索引为例,所以index_getbitmap指向btgetbitmap索引扫描函数:

7f34eddaec40c760726f99bef1716bba.png

btgetbitmap函数的逻辑:当然时先创建TIDBitmap,然后调用_bt_first/_bt_next逐条获取满足条件的item,接着通过tbm_add_tuples将其添加到TIDBitmap中,最终构建一个完整的bitmap,核心函数为_bt_first/_bt_next/tbm_add_tuples:

0291ffd509c9d55f8e8ef068b99d3388.png

1)_bt_first函数时索引扫描的开始。首先调用_bt_preprocess_keys预处理扫描key,所扫描key条件无法满足,则设置BTScanOpaque->qual_ok为false,提前结束扫描。若没有找到有用的边界keys,需要调用_bt_endpoint从第一页开始,否则调用_bt_search从btree的root节点_bt_getroot开始扫描,直到找到符合扫描key和快照的第一个叶子节点。之后使用二分查找_bt_binsrch找到符合扫描key的页内item偏移,最好将找到的页面载入buffer并返回tuple。

2)_bt_next函数从当前页获取下一条tuple,若当前页没有tuple,则调用_bt_steppage拿到下一页页号,之后调用_bt_readnextpage读取文件块中的内容,然后_bt_next获取吓一跳tuple,重复以上过程,直至扫描结束。

3)tbm_create创建TIDBitmap:

eeeb1e996b6e4766faf472c5299c6010.png

4)tbm_add_tuples函数添加CTID,构建TIDBitmap

837d2f9ed80b6ef265a63c4b76b06c99.png

tbm_add_tuples要干的事如上图所示:

(1)btgetbitmap调用tbm_add_tuples每次仅添加一个TID,从TID中解析出对应heap tuple的页号blk及页内偏移off

(2)判断blk是否是lossy:页号定位到所属chunk;然后据该chunk页号从hash表中查找;hash表中找到,再看下页号所属的bitmap位是否1,即是否lossy;bitmap为1,则返回true:

a31c88e749272f9f4756d306abfff515.png

blk非lossy,则调用tbm_get_pageentry从hash表中找一个PagetableEntry(不存在则会创建)。但是若此时只有一个PagetableEntry(TBM_ONE_PAGE状态)则直接返回entry1,不需要从hash查找:

b06e45eb9630b5b883058630889187e2.png

blk是lossy:已经位于chunk中的一位了,不必再向hash表添加了,因为btree下仅一个TID,所以退出循环

(3)计算bitmap的位于哪个字节wordnum及哪一位bitnum,标记到PagetableEntry的bitmap中words,并设置recheck为false

(4)tbm_get_pageentry创建了一个新PagetableEntry,发现npages超过tbm->maxentries只,则会调用tbm_lossify函数,将TIDBitmap中部分PagetableEntry转成成lossy chunk,同时按照exact page的减少和lossy page的增加,相应修改npages和nchunks

tbm_lossify函数

8a7ec42e8fc7794fbf990a791edf2ab9.png

那么hash表何时扩展呢?只要向hash表插入PagetableEntry,就有可能涉及到扩展,扩展后maxentries并不是立即更新;pagetable_insert调用结束后,若插入则需要更新nentries

de1d2939506a64ff34fdd3db9407c222.png

当然,还会有Bitmap And和Bitmap Or的情况。BitmapAnd节点对两个Bitmap进行与操作,生成交集位图;BitmapOr节点对两个Bitmap进行或操作,生成并集位图。

至此,bitmap index scan阶段完成bitmap的构建,下一步就是根据TID bitmap来扫描heap,返回符合条件的tuple,即Bitmap Heap Scan。

4、Bitmap Heap Scan阶段

Bitmap Heap Scan使用Bitmap Index Scan阶段生成的bitmap来查找相关数据。位图的每个页可以是精确的(直接指向heap页的tuple),也可以是有损的(指向包含至少一行与查询匹配的页)。算子由ExecBitmapHeapScan函数执行,主要实现函数为BitmapHeapNext:

c128cb415214e9767e8dd248449d69ac.png

BitmapHeapNext的核心逻辑如下:

4b2da4979f78377cebef69e0aecd8b34.png

1)从下层节点拿到TIDBitmap结果tbm

2)tbm_generic_begin_iterate->tbm_begin_iterate基于tbm构建一个iterator:从hash表中取出PagetableEntry,根据它是exact page还是lossy page,分别放到spages[]和schunks[]数组;然后根据页号进行排序

cfc344a30120f2ee5be3ba2f826b183d.png

3)先调用tbm_iterate从spages[]和schunks[]数组拿一个较小页号的页,然后通过table_scan_bitmap_next_block->heapam_scan_bitmap_next_block读取一个page到ScanDesc的rs_buffer里。

cd18133056957e18cefc68fbcb0a1bf6.png

4)调用table_scan_bitmap_next_tuple->heapam_scan_bitmap_next_tuple根据TBMIterateResult里的偏移,再内存buffer里获取相应的tuple。当这一页扫描完,则重置node->tbmres = tbmres = NULL,重新获取下一个PagetableEntry的bitmap继续循环。

5)如果是lossy,则还需要继续过滤

5、总结

Bitmap索引扫描分为两个阶段,第一阶段通过索引进行扫描,将满足条件的元组TID构建到bitmap中,一般情况一个页一个bitmap;第二阶段将bitmap按照页号进行排序,按次序从页的bitmap中取出heap tuple的TID,从而达到索引顺序扫描heap的目的。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LeetCode【92】翻转链表II

题目: 思路: https://blog.csdn.net/DarkAndGrey/article/details/122146216 public ListNode reverseBetween(ListNode head, int left, int right) {if(head null || right left){return head;// 头节点为null,链表为空,反…

340条样本就能让GPT-4崩溃,输出有害内容高达95%?OpenAI的安全防护措施再次失效

仅需340个示例微调GPT-4,即可绕过安全限制,让模型说出“枪支改装方法”、“生化武器制作过程”等有害内容? OpenAI的安全防护措施再次失效,攻击的成功率高达95%! 近日,美国顶尖大学UIUC与斯坦福联合对GPT…

华为麒麟服务器--硬盘问题

记录以下今天处理的服务器: 情况说明:linux 系统,不知道什么原因系统就突然不能用了(据说是前段时间断电来着,但是机房有应急电源)。 系统环境: 服务器:华为RH2288H V3 服务器 服…

(数据库管理系统)DBMS与(数据库系统)DBS的区别

数据库管理系统(DBMS,Database Management System)和数据库系统(DBS,Database System)是两个相关但不同的概念。 DBS是一个更广泛的概念,指的是计算机系统引入数据库后的系统,包括数…

vscode设置代码模板

一键生成vue3模板代码 效果演示 输入vue3 显示快捷键 按回车键 一键生成自定义模板 实现方法 进入用户代码片段设置 选择片段语言 vue.json输入自定义的代码片段 prefix是触发的内容,按自己的喜好来就行; body是模板代码,写入自己需要的…

SpringBoot常见注解

✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏:每天一个知识点 ✨特色专栏&#xff1a…

leetcode刷题日志-68.文本左右对齐

给定一个单词数组 words 和一个长度 maxWidth ,重新排版单词,使其成为每行恰好有 maxWidth 个字符,且左右两端对齐的文本。 你应该使用 “贪心算法” 来放置给定的单词;也就是说,尽可能多地往每行中放置单词。必要时可…

基于截图页面生成前端项目

前两天,在群里看见一个视频,视频中,作者截图twitter首页,然后使用截图直接生成与截图布局非常相近的前端项目,效果还是比较惊艳的。 今天陪老婆回老家,路上clone这个项目的代码到本地,学习了一下…

IDEA自动注解设置(中文版)

IDEA自动注解设置 1、添加类自动注释 文件 - 设置 - 编辑器 - 文件和代码模板 - Include - File Header /** *description:TODO *author: ${USER} *create: ${DATE} ${TIME} */2、添加类方法自动注释 文件 - 设置 - 编辑器 - 实时模版 - …

ARouter出现 there‘s no route matched in group问题排查

在使用ARouter时候会出现找不到映射路径的问题,这里给兄弟们总结下踩过的坑 所有用到的模块都要有填写依赖 android {defaultConfig {......javaCompileOptions {annotationProcessorOptions {arguments [AROUTER_MODULE_NAME: project.getName()]}}} } ... depe…

超详细~25考研规划~感恩现在努力的你!!!

25考研规划 俄语,翻译过来叫我爱你 考试时间 第一天 8.30-11.30政治——100分 2.00-5.00英语——100分 第二天 8.30-11.30数学——150分 2.00-5.00专业课——150分 1.什么是25考研 将在2024年12月参加考研,2025年本科毕业,9月读研究…

ANR问题分析的一般套路

目录 一.ANR初步了解1.发生原因2.ANR分类 二.ANR的Log解读1.Log获取2.案例一:sp耗时问题导致应用ANR 三.系统耗时分析方案1.binder_sample2.dvm_lock_sample3.binder starved4.案例二:疯狂Binder Call导致应用ANR5.案例三:广播超时导致App的A…

Django+Vue项目创建 跑通

参考链接: 【精选】DjangoVue项目构建_django vue-CSDN博客 一、背景 主要介绍如何使用后端Django 前端Vue 的技术栈快速地搭建起一套web项目的框架。 为什么使用Django和Vue? Django是Python体系下最成熟的web框架之一,由于Python语言的易用…

012 C++ AVL_tree

前言 本文将会向你介绍AVL平衡二叉搜索树的实现 引入AVL树 二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序普通的二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M…

直流电机干扰的产生-EMC和EMI

直流电机干扰的产生-EMC和EMI 干扰的产生电路滤波处理EMC处理措施 干扰的产生 带电刷的电动机,由于在电刷切换时,电动机线圈中的电流不能突变,当一路线圈通电断开时,会在该线圈的两端产生较高的反电动势,这个电动势会…

2023.11.17使用flask将多个图片文件上传至服务器

2023.11.17使用flask将多个图片文件上传至服务器 实现功能: 1、同时上传多个图片文件 2、验证文件扩展名 3、显示上传文件的文件名 4、显示文件上传结果 程序结构 main.py from flask import Flask, request, jsonify, render_template import osapp Flask(__n…

vscode快捷键使用总结

) 1、格式化选中的代码 1、格式化选中的代码 vscode中选中所要格式化的代码: ctrl k,ctrlf 其实可以查到该命令 ctrlshiftp打开命令窗口输入format

单片机实验(二)

前言 实验一:用AT89C51单片机控制LCD 1602,使其显示两行文字,分别显示自己的学号和姓名拼音。 实验二:设计一个中断嵌套程序。要求K1和K2都未按下时,单片机控制8只数码管,滚动输出完整的学号。当按一下K1…

中间件安全:Apache 目录穿透.(CVE-2021-41773)

中间件安全:Apache 目录穿透.(CVE-2021-41773) Apache 的 2.4.49、2.4.50 版本 对路径规范化所做的更改中存在一个路径穿越漏洞,攻击者可利用该漏洞读取到Web目录外的其他文件,如系统配置文件、网站源码等&#xff0c…

微服务调用链路追踪

概述 本文介绍微服务调用链路追踪,涉及技术有:sleuth和zipkin。sleuth负责追踪调用链路数据,zipkin负责调用链路数据可视化展现。 本文的操作是在 服务网关实践 的基础上进行。 环境说明 jdk1.8 maven3.6.3 mysql8 spring cloud2021.0.8 …