postgresql源码学习(55)—— 列中的NULL值是如何存储和判断的?

news2025/2/25 5:17:33

问题来自 《PostgreSQL面试题集锦》学习与回答_Hehuyi_In的博客-CSDN博客 第11题

一、 NULL值存储位置

https://img-blog.csdnimg.cn/31c94388072a4bac98bcaa77e0807a10.png

       在pg元组头数据中,有一个t_bits数组,用于存储空值位图。当元组中没有null值的时候,t_bits可以被认为是空的,当元组有null值的列时,t_bits使用一个bit来表示列是否为null。

htup_details.h

struct HeapTupleHeaderData
{
…
    ItemPointerData t_ctid;     /* current TID of this or newer tuple (or a
                                 * speculative insertion token) */
…
    bits8       t_bits[FLEXIBLE_ARRAY_MEMBER];  /* bitmap of NULLs */
};

FLEXIBLE_ARRAY_MEMBER默认为空

#define FLEXIBLE_ARRAY_MEMBER   /* empty */

判断字段是否为空代码在 tupmacs.h,留待后面研究

/*
 * Check a tuple's null bitmap to determine whether the attribute is null.
 * Note that a 0 in the null bitmap indicates a null, while 1 indicates
 * non-null.
 */
#define att_isnull(ATT, BITS) (!((BITS)[(ATT) >> 3] & (1 << ((ATT) & 0x07))))

二、 pageinspact观察空值存储

创建测试表

create table t(a int,b int,c int);

insert into t values(4,7,1);
insert into t values(6,NULL,3);

postgres=# select * from t;
 a | b | c 
---+---+---
 4 | 7 | 1
 6 |   | 3
(2 rows)

pageinspact可以观察空值是如何存储的,infomask函数的定义参考:pg事务篇(三)—— 事务状态与Hint Bits(t_infomask)_access/htup_details.h:没有那个文件或目录_Hehuyi_In的博客-CSDN博客

select lp,infomask(t_infomask, 1) as infomask,t_bits,t_data from  heap_page_items(get_raw_page('t',0));

  • t_infomask可以看出其包含空值
  • t_bits数组不为空:10100000,第一和第三个1表示这两列不为空,中间红色的0表示第二列为空,其余的0表示这些列未被使用。
  • t_data中则不包含空值数据

为了看t_bits数组更清晰,我们加多几列

create table t0(i1 int,i2 int,i3 int,i4 int,i5 int,i6 int, i7 int,i8 int,i9 int,i10 int,i11 int,i12 int);
insert into t0 values(1,2,3,4,5,6,7,8,9,10,NULL,12);
insert into t0 values(1,2,3,4,5,6,7,8,9,NULL,NULL,12);
insert into t0 values(1,2,3,4,5,6,7,8,NULL,NULL,NULL,12);
insert into t0 values(1,2,3,4,5,6,7,8,9,10,11,12);

select t_bits from  heap_page_items(get_raw_page('t0', 0));

      t_bits     

------------------

 1111111111010000

 1111111110010000

 1111111100010000

(4 rows)

  • 第四行没有空值,因此对应t_bits数组为空
  • 一共12列,因此数组中后四位均未用到,为0
  • 红色的3个0分别对应前三行中3个null值的列位置

观察删掉其中一列的效果

alter table t0 drop column i1;
select t_bits from  heap_page_items(get_raw_page('t0', 0));

发现位图并没有变化

再插入一行非空值

insert into t0 values(2,3,4,5,6,7,8,9,10,11,12);
select t_bits from  heap_page_items(get_raw_page('t0', 0));

     可以看到,表中已删除列会被视为空列。当表中有许多列时,删除列将为每条记录生成额外的t_bit,这将导致存储膨胀。

三、 源码如何根据t_bits数组判断列是否为空

前面有提到过,判断字段是否为空代码如下

/*
 * Check a tuple's null bitmap to determine whether the attribute is null.
 * Note that a 0 in the null bitmap indicates a null, while 1 indicates
 * non-null.
 */
#define att_isnull(ATT, BITS) (!((BITS)[(ATT) >> 3] & (1 << ((ATT) & 0x07))))

1. 不超过8个字段

t表为例,第二行t_bits数组值为10100000,注意实际存储的时候值是颠倒的,所以是00000101

postgres=# select * from t;

 a | b | c

---+---+---

4 | 7 | 1

6 |   | 3

以第1列为例,ATT是列号(从0开始)

  • BITS[ATT>>3]计算该列在数组的下标,第0~7列下标都为0,例如BITS[0 >> 3] = BITS[0]
  • 1 << ((ATT) & 0x07)) 求字段顺序,例如0 & 0x07 = 0,再左移1位为1
  • !(BITS[0] & 1) = !( 00000101 & 00000001) = !(1) = 0
  • att_isnull函数返回0,所以第一列非空,与实际一致

2. 超过8个字段

insert into t0 values(1,2,3,4,5,6,7,8,9,10,NULL,12);

t_bits数组值为1111111111010000,颠倒后为0000101111111111

以第11为例,其ATT=10,即00001010

  • BITS[ATT>>3]计算该列在数组的下标BITS[(00001010) >> 3] = BITS[1]
  • 1 << ((ATT) & 0x07)) 求字段顺序,例如00001010 & 0x07 = 000000102),再左移1位为000001004
  • !(BITS[1] & 4) = !(00001011& 00000100) = !(0) = 1
  • att_isnull函数返回0,所以第10列为空,与实际一致

参考

https://www.cnblogs.com/abclife/p/13855150.html

Postgres是如何管理空值的

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

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

相关文章

javaweb学习 html+css基础1

1、学习路线 2、web网站的开发模式 课程安排 web 前端 前端学习内容 html和css的帮助网站 HTML 系列教程 (w3school.com.cn) html快速入门 使用VSCode开发工具&#xff0c;输入 &#xff01;enter&#xff0c;即可出现骨架。 ctrl/ 可以将文字转换为注释 右击一个页面…

【JavaEE】计网之IP协议+以太网+DNS

博主简介&#xff1a;想进大厂的打工人博主主页&#xff1a;xyk:所属专栏: JavaEE初阶 网络层重点协议——IP协议&#xff0c;在复杂的网络环境中确定一个合适的路径~ 本篇文章我们来讲解什么是 IP协议及以太网&#xff0c;在IP协议中&#xff0c;有 地址管理 和 路由选择 两个…

C++中基类和派生类的构造函数与析构函数的调用顺序分析

基类为B&#xff0c;派生类为C&#xff0c;直接上代码以及运行结果。 目录 思路分析 代码一 运行结果一 代码二&#xff1a;B(i)改为b(i) 运行结果二 代码三&#xff1a;加上B(i) 运行结果三 代码四&#xff1a;删掉C类定义的B对象b&#xff0c;删除b(i) 运行结果四 思路…

Linux使用全应用

一、CentOS安装Docker Docker CE 支持 64 位版本 CentOS 7&#xff0c;并且要求内核版本不低于 3.10&#xff0c; CentOS 7 满足最低内核的要求&#xff0c;所以我们在CentOS 7安装Docker。 基础命令 搜索镜像&#xff1a;docker search mysql 下载镜像&#xff1a;docker p…

[架构之路-197]-《软考-系统分析师》- 关键技术 - 问题分析阶段重要的四个任务

目录 前言&#xff1a; 一、信息系统/软件产品的问题分析概述 二、信息系统/软件产品的问题/痛点分析四步骤 步骤1、问题领域分析&#xff1a;研究遇到问题的业务领域&#xff08;诉求&#xff09; 步骤2、 领域问题和机会分析&#xff1a;分析业务领域的问题、痛点、难点…

计算机网络学习 一 (计算机网络体系结构)

计算机网络 基本概念 计算机网络是一个将分散的,具有独立功能的计算机系统. 简单来说,计算机网络就是一些互连的,自治的计算机系统的集合 分类 广义上:是一个资源共享的系统. 资源共享上: 1.目的–资源共享 2.组成单元–分布在不同地理位置的多台独立的"自治计算机"…

第十四届蓝桥杯青少组模拟赛Python真题 (2022年11月8日)

第十四届蓝桥杯青少组模拟赛Python真题 (2022年11月8日) 编程题 第 1 题 问答题 二进制位数 十进制整数2在十进制中是1位数&#xff0c;在二进制中对应10&#xff0c;是2位数。 十进制整数22在十进制中是2位数&#xff0c;在二进制中对应10110&#xff0c;是5位数。 请问十…

Linux高级---k8s之service服务

文章目录 一、service基本概念二、service类型三、service的使用1、实验环境准备2、ClusterIP类型的Service3、HeadLiness类型的Service4、NodePort类型的Service5、LoadBalancer类型的Service6、ExternalName类型的Service 一、service基本概念 在kubernetes中&#xff0c;pod…

【Java多线程编程】线程的六种状态

前言&#xff1a; 在我们进行多线程编程&#xff0c;脑海里会想到线程运行的状态到底是什么&#xff1f;因此我整理出这线程的状态这篇博文。线程的状态分为六种&#xff1a;新建状态&#xff08;NEW&#xff09;、就绪状态&#xff08;RUNNABLE&#xff09;、阻塞状态&#xf…

1-Linux环境安装JDK

Linux环境安装JDK 准备&#xff1a; ① Linux 环境 本文中Linux环境为 CentOS Linux 7 可使用以下命令查询 linux 系统版本&#xff1a; hostnamectl② 准备JDK包 进入官网 https://www.oracle.com/java/technologies/downloads/#java17下载对应jdk包 此处使用以前下载的旧…

Linux驱动编程(驱动程序基石)(下)

一、中断的线程化处理 复杂、耗时的事情&#xff0c;尽量使用内核线程来处理。上节视频介绍的工作队列用起来挺简单&#xff0c;但是它有一个缺点&#xff1a;工作队列中有多个 work&#xff0c;前一个 work 没处理完会影响后面的 work。解决方法有很多种&#xff0c;比如干脆…

String类的学习笔记(下):字符串拼接以及StringBuilder和StringBuffer的学习

本文介绍了String类对字符串进行拼接的方法 和拼接字符串的效率分析 以及能对字符串内容进行修改的StringBuilder和StringBuffer类其常用方法和区别 , 最后介绍了两个字符串经典面试题 StringBuilder和StringBuffer的学习 一.String类概括二.StringBuilder和StringBuffer1.字符…

是未来的超级计算机还是只是一场炒作?

随着科技的飞速发展和创新&#xff0c;量子计算技术逐渐成为了人们关注的热点话题。量子计算作为一种前沿的计算方式&#xff0c;具有超强的运算能力和突破性的创新潜力&#xff0c;因此备受瞩目。然而&#xff0c;随着各大公司和机构纷纷加入到这一领域的竞争中&#xff0c;一…

超详细github配置(仔细看,看完不会,你怪我)

github的重要性&#xff1a; 网络时代的程序员必备。 github的作用&#xff1a; 版本管理多人协作开源共享 常用方案&#xff1a; gitTortoiseGitgithub [Tortoise&#xff0c;程序员常称其为小乌龟&#xff0c;小海龟] 安装配置步骤 1.注册 GitHub: Where the world bu…

服务(第二十二篇)主从复制和读写分离

主从复制原理&#xff1a; 首先主节点会开启二进制日志&#xff0c;从节点会开启中继日志&#xff0c;从节点会开启io线程检测主节点是否有更新&#xff0c;如果更新了就会向主节点请求二进制事件&#xff0c;主会开启dump线程发送二进制事件&#xff0c;然后保存在从节点的中…

假如面试官让你十分钟完成双向循环链表

&#x1f48c; 博客内容&#xff1a;假如面试官让你十分钟完成双向循环链表&#xff0c;多一秒都不行 &#x1f600; 作  者&#xff1a;陈大大陈 &#x1f680; 个人简介&#xff1a;一个正在努力学技术的准前端&#xff0c;专注基础和实战分享 &#xff0c;欢迎私信&…

大前端技能讲解:NodeJS、Npm、Es6、Webpack

文章目录 1. 基础概述2. Nodejs2.1 Nodejs 了解和快速入门2.2 Nodejs 实现 Httpserver 服务&#xff08;实现请求响应&#xff09;2.3 Nodejs 操作 MySQL 数据库 3. ES63.1 ES6 的概述3.2 ES6 的语法&#xff1a;let 和 const 命令3.3 ES6 的语法&#xff1a;模板字符串3.4 ES6…

基于SSM的在线电影购票系统设计与实现【附源码】

基于SSM的在线电影购票系统设计与实现 互联网的不断迅猛发展&#xff0c;每个行业都在寻找新的机会&#xff0c;都在从传统的人工方式向先进的信息化过度。随着人民生活水平的提高伴随的精神文化层次的享受&#xff0c;而现代互联网时代人们的重要精神消费之一是电影行业&…

NAS +AList实现云盘映射(本地硬盘扩容大法)

准备工具&#xff1a; 1&#xff09;Alist的docker &#xff1a;xhofe/alist 2&#xff09;RailDrive软件 安装&#xff1a; 1&#xff09;安装alist的docker 注意一定要给读写权限&#xff0c;装载路径和我一样 端口一般和容器端口一致 环境变量 网络桥接就行 记得勾选自…

【Prompting】ChatGPT Prompt Engineering开发指南(1)

ChatGPT Prompt Engineering开发指南1 Prompting指南设置 提示原则策略1&#xff1a;使用分隔符清楚地指示输入的不同部分策略2&#xff1a;要求结构化输出策略3&#xff1a;让模型检查条件是否满足策略4: “Few-shot”提示 原则2&#xff1a;给模型时间“思考”策略1&#xff…