PostgreSQL 基础模块---表和元组组织方式
参考资料
《PostgreSQL数据库内核分析》 彭智勇 彭煜玮:P58~P60
概述
PostgreSQL是堆表,其中每个文件由多个块组成,块在物理磁盘中的存储形式如下图所示:
块由4个部分组成:
- 块头:PageHeaderData
- 记录:记录由两部分组成
a. Linp:Linp是ItemIdData类型,长度固定,在块中从前向后分配。每个ItemIdData都记录了一个偏移,用于指向Tuple。
b. Tuple:记录的头信息,一条记录由Tuple+记录本身构成。记录本身长度不固定,在块中从后向前分配。
c. 空闲空间:Freespace,位于Linp和Tuple之间。 - 特定数据:Special space,用于存放于索引方法相关的特定数据。
PageHeaderData
PageHeaderData结构如下:
typedef struct PageHeaderData
{/* XXX LSN is member of *any* block, not only page-organized ones */PageXLogRecPtr pd_lsn; /* LSN: next byte after last byte of xlog* record for last change to this page */uint16 pd_checksum; /* checksum */uint16 pd_flags; /* flag bits, see below */LocationIndex pd_lower; /* offset to start of free space */LocationIndex pd_upper; /* offset to end of free space */LocationIndex pd_special; /* offset to start of special space */uint16 pd_pagesize_version;TransactionId pd_prune_xid; /* oldest prunable XID, or zero if none */ItemIdData pd_linp[FLEXIBLE_ARRAY_MEMBER]; /* line pointer array */
} PageHeaderData;
其中:
- pd_lower、pd_upper:分别表示空闲空间的起始偏移和结束偏移,pd_upper - pd_lower即可获得空闲空间的大小。当插入一条记录时pd_upper向低地址移动,pd_lower向高地址移动。
- pd_linp:为ItemIdData数组。通过pd_linp可以很方便的定位到一条记录。
ItemIdData
ItemIdData结构如下:
typedef struct ItemIdData
{unsigned lp_off:15, /* offset to tuple (from start of page) */lp_flags:2, /* state of item pointer, see below */lp_len:15; /* byte length of tuple */
} ItemIdData;
其中:
- lp_off:表示tuple的偏移(相对于块的起始位置)。
- lp_flags:表示记录的状态:未使用、正常使用、HOT重定向、死亡。
- lp_len:表示记录的长度。
HeapTupleHeader
HeapTupleHeader结构如下:
struct HeapTupleHeaderData
{union{HeapTupleFields t_heap;DatumTupleFields t_datum;} t_choice;ItemPointerData t_ctid; /* current TID of this or newer tuple (or a* speculative insertion token) *//* Fields below here must match MinimalTupleData! */uint16 t_infomask2; /* number of attributes + various flags */uint16 t_infomask; /* various flag bits, see below */uint8 t_hoff; /* sizeof header incl. bitmap, padding *//* ^ - 23 bytes - ^ */bits8 t_bits[FLEXIBLE_ARRAY_MEMBER]; /* bitmap of NULLs *//* * MORE DATA FOLLOWS AT END OF STRUCT * 元组的具体数据将跟在这个结构后面*/
};
typedef struct HeapTupleHeaderData HeapTupleHeaderData;
typedef HeapTupleHeaderData *HeapTupleHeader;
其中:
t_choise:是具体两个成员的联合类型:
- t_heap:用于记录对元组执行插入/删除操作的事务ID和命令ID。
- t_datum:当一个新的元组在内存中形成的时候,并不关心事务可见性,因此t_choise中只需用DatumTupleFields来记录元组长度等信息,这是临时信息。在把该元组插入到表文件时,需要在元组头信息中记录插入该元组的事务和命令ID,此时会把t_choise转换为HeapTupleFields结构并填充相应数据后再进行元组的插入。
t_ctid:用于记录当前元组或新元组的物理位置,若元组被更新(删除旧版本元组,然后插入新版本元组),则记录的是新版本元组的物理位置。
t_ctid是一个ItemPointerData类型的结构体,其定义如下:
typedef struct ItemPointerData {BlockIdData ip_blkid;OffsetNumber ip_posid; } /* If compiler understands packed and aligned pragmas, use those */ #if defined(pg_attribute_packed) && defined(pg_attribute_aligned) pg_attribute_packed() pg_attribute_aligned(2) #endif ItemPointerData;
其中:
- ip_blkid:表示文件块编号。
- ip_posid:表示该元组对应的ItemIdData数组的下标(ItemIdData数组见PageHeaderData)。
值得注意的是ip_blkid是一个BlockIdData的结构体,其定义如下:
typedef struct BlockIdData {uint16 bi_hi;uint16 bi_lo; } BlockIdData;
乍一看这个定义,感觉BlockIdData暗藏玄机,看起来bi_hi和bi_lo有什么特殊的用途,其实并不是。其实完全可以把BlockIdData看作一个uint32,之所以这里将一个uint32分为两个uint16,是为了ItemPointerData在字节对齐时节省空间。
/* sizeof(BlockIdData1) = 8*/ typedef struct ItemPointerData1 {uint32 ip_blkid;uint16 ip_posid; }BlockIdData1;/* sizeof(BlockIdData2) = 6*/ typedef struct ItemPointerData2 {uint16 ip_blkid_hi;uint16 ip_blkid_lo;uint16 ip_posid; }BlockIdData2;
注意:在插入元组时,t_ctid并不是在构建元组时就存在,而是在元组写入文件块对应的共享内存文件页后才设置的,其代码如下:
void RelationPutHeapTuple(Relation relation,Buffer buffer,HeapTuple tuple,bool token) {Page pageHeader;OffsetNumber offnum;Assert(!token || HeapTupleHeaderIsSpeculative(tuple->t_data));pageHeader = BufferGetPage(buffer);/* * Add the tuple to the page * 此时tuple中的t_ctid还是一个非法值*/offnum = PageAddItem(pageHeader, (Item) tuple->t_data,tuple->t_len, InvalidOffsetNumber, false, true);if (offnum == InvalidOffsetNumber)elog(PANIC, "failed to add tuple to page");/* Update tuple->t_self to the actual position where it was stored */ItemPointerSet(&(tuple->t_self), BufferGetBlockNumber(buffer), offnum);/** Insert the correct position into CTID of the stored tuple, too (unless* this is a speculative insertion, in which case the token is held in* CTID field instead)*/if (!token){/** 修改tuple中的t_ctid*/ItemId itemId = PageGetItemId(pageHeader, offnum);Item item = PageGetItem(pageHeader, itemId);((HeapTupleHeader) item)->t_ctid = tuple->t_self;} }
t_infomask2:使用其低11位表示当前元组的属性个数,其他位用于包含用于HOT技术及元组可见性的标志位。
t_infomask:标识元组的当前状态,比如:是否具有OID、是否有空属性等,t_infomask的每一位对应不同的状态,共16种状态。
t_hoff:表示元组头的大小。
t_bits:用于标识该元组哪些字段为空。
HOT技术
PostgreSQL对元组采用多版本技术,对于元组的每个更新操作都会产生一个新版本,版本之间从老到新形成一条版本链。此外,更新操作不但会在表文件中产生元组的新版本,在表的每个索引中也会产生新版本的索引记录。即使更新操作没有修改索引属性,也会在每个索引中都产生一个新版本,这样就会浪费存储空间。
为了解决这个问题,PostgreSQL使用一种HOT机制,当更新的元组同时满足如下条件时,成为HOT元组:
- 所有索引属性都没有被修改。
- 更新的元组新版本与旧版本在同一个文件块内。
更新一条HOT元组将不会在索引中引入新版本,当通过索引获取元组时首先会找到同一块中最老的版本,然后顺着版本链向后找,直到遇到HOT元组为止。(这就是为什么需要限制新旧版本在同一文件块内,如果不在同一个块内,那么就会有额外的I\O)。
PostgreSQL 基础模块---表和元组组织方式相关推荐
- PostgreSQL 基础模块---缓冲池管理
参考资料 <PostgreSQL数据库内核分析> 彭智勇 彭煜玮:P99~P101 概述 在PostgreSQL中,任何对于表.元组.索引等操作都在缓冲池中进行,缓冲池的数据调度都以磁盘块 ...
- 给出一种符号表的组织方式和结构设计,要考虑数组类型和函数(不得与课件上的雷同)
给出一种符号表的组织方式和结构设计,要考虑数组类型和函数(不得与课件上的雷同) 给出一种符号表的组织方式和结构设计,要考虑数组类型和函数(不得与课件上的雷同) 符号表的组织方式和结构设计: nameT ...
- 计算机应用基础模块四,第四部分计算机应用基础(基础模块)考试说明
第四部分计算机应用基础(基础模块)考试说明 (7页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 9.9 积分 第四部分 计算机应用基础(基础模块)考试说明 ...
- 开源的数据库,PostgreSQL 基础入门实战
PostgreSQL 简介与安装 实验介绍 大家好,本课程是关于 PostgreSQL 数据库的使用说明,细致讲解 PostgreSQL 的特性与使用方法,尽量做到描述朴实.深入浅出.示例充足.覆盖重 ...
- 00815 计算机基础,国开(山东)00815-计算机应用基础-模块1 windows 7 操作系统——客观题-辅导资料...
国开(山东)00815-计算机应用基础-模块1 windows 7 操作系统--客观题-辅导资料 (9页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 14 ...
- 多应用多平台支付模块设计-基础模块开篇
近来,欲将三方支付平台对接入笔者框架内,简化,后期业务功能的开发. 为满足此功能的可扩展性,欲定义"订单业务接口"."支付方式接口"规则. 当,用户下单时,根据 ...
- Django基础(11): 表单集合Formset的高级用法详解
Formset(表单集)是多个表单的集合.Formset在Web开发中应用很普遍,它可以让用户在同一个页面上提交多张表单,一键添加多个数据,比如一个页面上添加多个用户信息.今天小编我就介绍下Djang ...
- mysql堆表和索引组织_从堆表(Heap Table)到索引组织表
对关系型数据库产品(RDBMS)而言,一个重要特性就是:数据信息都被组织为二维数据表,信息的表达可以通过一系列的关联(Join)来完成.具体数据库产品在实现这个标准的时候,又有千差万别的特点.就是一个 ...
- 计算机专业电子技术基础教学,计算机专业电子技术基础课程教学内容的组织
计算机专业电子技术基础课程教学内容的组织 摘 要:电子技术基础课程是计算机专业的一门专业基础课,涵盖了基本电路分析.模拟电路分析及数字电路分析三大内容.本文针对课程教学中存在的问题,以提高学生对课程学 ...
最新文章
- 《用广义CNOT门产生质数幂维的图态》
- 机器学习需要理解的五个基本概念
- 三款功能强大代码比较工具Beyond compare、DiffMerge、WinMerge
- django - 替换admin的textarea为 富文本
- javascript array添加图片_史上最全的web前端面试题汇总及答案JavaScript之二(二)...
- Git中的日常使用 码云
- activemq 内存_ActiveMQ中的温度,存储和内存使用百分比
- IDE日志分析方法pt。 2
- Linux 命令之 ifconfig -- 配置和显示网卡的网络参数
- mysql5.6 1g内存_1G内存用MySQL5.6还是用MySQL5.5比较好
- Visual Studio 开源控件扩展 NuGet 常用组件安装命令
- 【Unity】Unity Pivot 与 Center Globle 与Lical
- robotframework使用之 下拉框的选择的几种用法
- PTA数据结构-01-复杂度1 最大子列和问题
- 西电计算机本科毕业,不在一线城市,不是顶级大学,西电毕业生薪酬凭啥陕西第一?...
- 努力不是为了追赶别人,只是为了超越自己
- 摩尔定律、安迪-比尔定律、反摩尔定律
- 设计师必看的十部电影
- python中斐波那契系数实现的几种方法
- JSON 格式是什么?