参考资料

《PostgreSQL数据库内核分析》 彭智勇 彭煜玮:P58~P60

概述

PostgreSQL是堆表,其中每个文件由多个块组成,块在物理磁盘中的存储形式如下图所示:

块由4个部分组成:

  1. 块头:PageHeaderData
  2. 记录:记录由两部分组成
    a. Linp:Linp是ItemIdData类型,长度固定,在块中从前向后分配。每个ItemIdData都记录了一个偏移,用于指向Tuple。
    b. Tuple:记录的头信息,一条记录由Tuple+记录本身构成。记录本身长度不固定,在块中从后向前分配。
    c. 空闲空间:Freespace,位于Linp和Tuple之间。
  3. 特定数据: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 基础模块---表和元组组织方式相关推荐

  1. PostgreSQL 基础模块---缓冲池管理

    参考资料 <PostgreSQL数据库内核分析> 彭智勇 彭煜玮:P99~P101 概述 在PostgreSQL中,任何对于表.元组.索引等操作都在缓冲池中进行,缓冲池的数据调度都以磁盘块 ...

  2. 给出一种符号表的组织方式和结构设计,要考虑数组类型和函数(不得与课件上的雷同)

    给出一种符号表的组织方式和结构设计,要考虑数组类型和函数(不得与课件上的雷同) 给出一种符号表的组织方式和结构设计,要考虑数组类型和函数(不得与课件上的雷同) 符号表的组织方式和结构设计: nameT ...

  3. 计算机应用基础模块四,第四部分计算机应用基础(基础模块)考试说明

    第四部分计算机应用基础(基础模块)考试说明 (7页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 9.9 积分 第四部分 计算机应用基础(基础模块)考试说明 ...

  4. 开源的数据库,PostgreSQL 基础入门实战

    PostgreSQL 简介与安装 实验介绍 大家好,本课程是关于 PostgreSQL 数据库的使用说明,细致讲解 PostgreSQL 的特性与使用方法,尽量做到描述朴实.深入浅出.示例充足.覆盖重 ...

  5. 00815 计算机基础,国开(山东)00815-计算机应用基础-模块1 windows 7 操作系统——客观题-辅导资料...

    国开(山东)00815-计算机应用基础-模块1 windows 7 操作系统--客观题-辅导资料 (9页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 14 ...

  6. 多应用多平台支付模块设计-基础模块开篇

    近来,欲将三方支付平台对接入笔者框架内,简化,后期业务功能的开发. 为满足此功能的可扩展性,欲定义"订单业务接口"."支付方式接口"规则. 当,用户下单时,根据 ...

  7. Django基础(11): 表单集合Formset的高级用法详解

    Formset(表单集)是多个表单的集合.Formset在Web开发中应用很普遍,它可以让用户在同一个页面上提交多张表单,一键添加多个数据,比如一个页面上添加多个用户信息.今天小编我就介绍下Djang ...

  8. mysql堆表和索引组织_从堆表(Heap Table)到索引组织表

    对关系型数据库产品(RDBMS)而言,一个重要特性就是:数据信息都被组织为二维数据表,信息的表达可以通过一系列的关联(Join)来完成.具体数据库产品在实现这个标准的时候,又有千差万别的特点.就是一个 ...

  9. 计算机专业电子技术基础教学,计算机专业电子技术基础课程教学内容的组织

    计算机专业电子技术基础课程教学内容的组织 摘 要:电子技术基础课程是计算机专业的一门专业基础课,涵盖了基本电路分析.模拟电路分析及数字电路分析三大内容.本文针对课程教学中存在的问题,以提高学生对课程学 ...

最新文章

  1. 《用广义CNOT门产生质数幂维的图态》
  2. 机器学习需要理解的五个基本概念
  3. 三款功能强大代码比较工具Beyond compare、DiffMerge、WinMerge
  4. django - 替换admin的textarea为 富文本
  5. javascript array添加图片_史上最全的web前端面试题汇总及答案JavaScript之二(二)...
  6. Git中的日常使用 码云
  7. activemq 内存_ActiveMQ中的温度,存储和内存使用百分比
  8. IDE日志分析方法pt。 2
  9. Linux 命令之 ifconfig -- 配置和显示网卡的网络参数
  10. mysql5.6 1g内存_1G内存用MySQL5.6还是用MySQL5.5比较好
  11. Visual Studio 开源控件扩展 NuGet 常用组件安装命令
  12. 【Unity】Unity Pivot 与 Center Globle 与Lical
  13. robotframework使用之 下拉框的选择的几种用法
  14. PTA数据结构-01-复杂度1 最大子列和问题
  15. 西电计算机本科毕业,不在一线城市,不是顶级大学,西电毕业生薪酬凭啥陕西第一?...
  16. 努力不是为了追赶别人,只是为了超越自己
  17. 摩尔定律、安迪-比尔定律、反摩尔定律
  18. 设计师必看的十部电影
  19. python中斐波那契系数实现的几种方法
  20. JSON 格式是什么?

热门文章

  1. Android APP的安装路径
  2. 国内CDN行业优质服务商
  3. Android 30. 广播-Broadcast(一)
  4. 取消漫游费,移动通信服务资费对农村用户来说不公平
  5. 计算机网络搭建比赛申请书,C世界计算机社团成立申请书
  6. 如何在Outlook中创建自定义导航窗格
  7. ArcGIS Enterprise 备份
  8. 计算机网络——交换机的生成树协议STP
  9. 安装Blue Prism Chrome插件
  10. Mac是不是基于Linux系统开发?