Postgres中tuple的组装与插入

1.相关的数据类型

我们先看相关的数据类型:

HeapTupleData(src/include/access/htup.h)

typedef struct HeapTupleData
{uint32      t_len;          /* length of *t_data */ItemPointerData t_self;     /* SelfItemPointer */Oid         t_tableOid;     /* table the tuple came from */HeapTupleHeader t_data;     /* -> tuple header and data */
} HeapTupleData;

HeapTupleHeaderData(src/include/access/htup_details.h)


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 */
};

t_choice具有2个成员的联合类型:

  • 1.t_heap 用于记录对元组执行插入/删除操作事物ID和命令ID,这些信息主要用于并发控制是检查元组对事物的可见性

  • 2.t_datum一个新的元组在内存中形成的时候,我们不关心事物的可见性,因此在t_choice中需要用DatumTupleFields结构来记录元组的长度等信息,把内存的数据写入到表文件的时候,需要在元组中记录事物和命令ID,因此会把t_choice所占的内存转换成HeapTupleFields结构并且填充响应数据后再进行元组的插入。

t_ctid用于记录当前元组或者新元组的物理位置,块号和块内偏移量,例如(0,1)第一个块内的第一个linp,若tuple被跟新,那么就记录新版本的物理位置。

t_infomask2使用其低11位标识当前tuple的attribute的个数,其他位用于HOT以及tuple可见性的标志位

t_infomask用于标识tuple当前的状态,比如是否有OID,是否空的字段,t_infomask每一位代表一种状态,总共16种。


2.Tuple的构造

构造tuple的函数(src/backend/access/common/heaptuple.c)

HeapTuple
heap_form_tuple(TupleDesc tupleDescriptor,Datum *values,bool *isnull)

该函数使用给定的values数组和isnull数组来组装生成一个tuple。

该函数的主要流程是先计算整个tuple所需要的长度(这个长度是指tuple中除掉HeapTupleData结构以外的长度。事实上,该长度存储在HeapTupleData的t_len的属性中。)然后以此申请内存,最后根据values和isnull来填充tuple数据。

我们稍微说一下这个t_len的计算。

len = offsetof(HeapTupleHeaderData, t_bits);

首先计算heaptupleheaderdata的长度,这个offsetof计算了从HeapTupleHeaderData的首址到它的成员变量t_bit的偏移量。

所以为什么不直接sizeof(HeapTupleHeaderData)呢?

原因是t_bits描述了NULL的bitmap关系,它的实际长度与列(属性)个数有关,是一个可变的值,

因此,在计算完HeapTupleHeaderData长度的时候,我们便根据是否存在着null列,来计算相应的数据(如下)。

    if (hasnull)len += BITMAPLEN(numberOfAttributes);

以及是否有oid:

    if (tupleDescriptor->tdhasoid)len += sizeof(Oid);

再加上padding大小(涉及到C语言的数据对齐):

hoff = len = MAXALIGN(len); /* align user data safely */

最后再获取data的长度:

    data_len = heap_compute_data_size(tupleDescriptor, values, isnull);len += data_len;

获取了tuple的长度申请好内存后,向里面添加数据,就获得了如下的tuple(结构):

579102-20171212215105691-256965612.png

其中,hoff中包括了: 从TupleHeaderData起始位置到t_bits的位置;用户数据是从t_hoff开始,加上t_bits的偏移,以及oid的偏移,开始真正存储的。 这些由上图可以得知。

heap_fill_tuple 函数中依据tupledesc中atts所提供的信息来保存数据到相应的位置。att[i]->attlen == -1 当为此种情况时候,表明其是varlen数据,例如varchar之类的数量类型,att[i]->attlen == -2 当为此种情况时候,为cstring,即字符串形式的数据。never needs alignment 无需进行对齐操作。否则,为固定长度的类型。
如果是varlen类型数据时候。还需要使用VARATT_IS_EXTERNAL来判定是否是存储在外存上面。

做好了一条tuple之后,我们还要把它插入到数据库对应的表中才算完事。


3.Tuple的插入

插入tuple到heap的函数

Oid
heap_insert(Relation relation, HeapTuple tup, CommandId cid,int options, BulkInsertState bistate)

这个函数还挺复杂的,涉及到了内存和disk的数据交换。内存主要涉及到了缓冲区buffer和lock,对于disk涉及到了FSM映射表和Page。

首先,预处理函数设置元组头部的字段,分配一个OID,并在必要时为元组提供Toast。请注意,在这里heaptup是传进来的tuple,而变量tup是作为一个临时变量存在的。

heaptup = heap_prepare_insert(relation, tup, xid, cid, options);

我们要将元组插入到page,涉及到内存和disk的数据交换,这就要用到buffer。我们知道insert的本质也是先"select"再"insert"。也就是说我们先要找到该表上合适的Page来装这个tuple。因此,我们为该Page申请一个buffer并加上执行锁,将该Page载入申请到的buffer中。注意,此时要插入的tuple并未写到buffer中

buffer = RelationGetBufferForTuple(relation, heaptup->t_len,InvalidBuffer, options, bistate,&vmbuffer, NULL);

这样以后,所有的准备工作都做好了,就差临门一脚了。成与不成就在一举了。是不是听起来有点。。。?

是的,我们要进入临界区了,谁都不要打扰我:

START_CRIT_SECTION();

这个语句其实是设置了全局变量CritSectionCount,就相当于信号量了,这里不多说。

然后我们开始写数据吧:

RelationPutHeapTuple(relation, buffer, heaptup,(options & HEAP_INSERT_SPECULATIVE) != 0);

但是话说,真的写了?并没有!你忘了我们postgresql有WAL么?你WAL log都还没写,数据怎么能先到磁盘?

那么这里我们有什么?我们buffer里面有Page,我们"手上"有tuple,好的,我们把tuple放到这个buffer装的Page里面对应的位置上。

就是说,我们的数据还在buffer里。

那么怎么通知Postgres我有脏数据要写啊?

MarkBufferDirty(buffer);

设置buffer为脏,这样Postgres在下次写磁盘(checkpointer)的时候就知道把这个buffer里的数据丢回disk了。

那么,我们也就知道了,接下来我们就要开始准备WAL和数据了。

这里大致用到了这几个函数:

XLogBeginInsert
XLogRegisterData
XLogRegisterBuffer
XLogRegisterBufData
PageSetLSN

好的,WAL也设置好了。(只等插入这条tuple的命令commit之后,WAL数据立即落盘,写到disk上,也就是pg_xlog目录下的WAL段里面。)此时退出临界区。

这个时候要放开buffer了。

最后我们再做一做清理工作,打完收工。

最最最后,实际的元组仍然在内存,不过没事,因为你的查询也是要先走buffer和cache的,所以你已经可以查询到这条数据了。等到系统调用了checkpointer进程,你的数据才真正落了盘,然而,这对你是透明的。

这里关于数据落盘的先后顺序和时机,我还是借网上的两张图吧:
WAL和data进入buffer的时机:

579102-20171214141517076-2083705249.png

WAL和data写到disk的时机:
579102-20171214141534592-1060621040.png

好的就是这样~

恩,这次对WAL的插入的分析比较简略,下次我弄清楚了再细说吧各位。

参考文章:

http://blog.jobbole.com/106585/

http://www.cnblogs.com/sangli/p/6404771.html

http://www.jianshu.com/p/a37ceed648a8

转载于:https://www.cnblogs.com/flying-tiger/p/8029941.html

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

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

相关文章

Python 自动生成环境依赖包 requirements

一、生成当前 python 环境 安装的所有依赖包 1、命令 # cd 到项目路径下,执行以下命令 pip freeze > requirements.txt# 或者使用如下命令 pip list --formatfreeze > requirements.txt 2、常见问题 1、中使用 pip freeze > requirements.txt 命令导出…

DenyHosts 加固centos系统安全

DenyHosts是Python语言写的一个程序,它会分析sshd的日志文件(/var/log/secure),当发现重 复的攻击时就会记录IP到/etc/hosts.deny文件,从而达到自动屏IP的功能 DenyHosts官方网站 http://denyhosts.sourceforge.net 下…

手机uc怎么放大页面_手机网站怎样做可以提高用户体验度?——竹晨网络

目前,手机已经占据了人们大多数的闲暇时间,互联网的流量开始逐渐向移动端倾斜,重视移动端的用户体验,就可以给客户端增加很多意想不到的功能。但是还是有很多公司和站长不知道手机网站应该怎么建才能符合用户的使用习惯。下面&…

科技申报项目总结

这个项目分为三大模块,管理员,专家以及单位模块,具体页面有:1单位信息;2项目申报;3专家信息;4项目评审;5 项目信息;6申报设置;7专家信息。 —-项目框架SSM&am…

UML之涉众/参与者(角色/执行者)(Actor)/业务主角(BusinessActor)/业务工人(BusinessWorker)/用户/角色辨析【图解】...

参考文档: 【业务建模】(http://www.baike.com/wiki/%E4%B8%9A%E5%8A%A1%E5%BB%BA%E6%A8%A1) 【UML 核心元素之参与者】(http://www.voidcn.com/article/p-obarwwaq-tp.html) 【UML核心元素之参与者】(http://www.voidcn.com/article/p-ntpnhoue-da.html)转载于:htt…

git 报错:Please make sure you have the correct access rights and the repository exists

提示:Warning: Permanently added gitee.com,120.55.226.24 (ECDSA) to the list of known hosts.是公钥出问题了,要先设置用户和邮箱再重新生成ssh公钥即可。 1、首先我得重新在git设置一下身份的名字和邮箱 进入到需要提交的文件夹底下(…

java 实现excel 导出功能

实现功能&#xff1a;java导出excel表 1、jsp代码 1 <form id"zhanwForm" action"<%path%>/conferences.do?" target"_self" method"get" > 2 <input type"hidden" name"method" value…

什么是CI/CD

一、简介 CI / CD的采用改变了开发人员和测试人员如何发布软件。 最初是瀑布模型&#xff0c;后来是敏捷开发&#xff0c;现在是DevOps&#xff0c;这是现代开发人员构建出色的产品的技术路线。随着DevOps的兴起&#xff0c;出现了持续集成&#xff08;Continuous Integration…

部署WEB项目到服务器(三)安装mysql到linux服务器(Ubuntu)详解

突发奇想&#xff0c;想在自己电脑上部署一个web网站。 1&#xff0c;首先是下载一个适合自己已安装服务器版本的mysql数据库。 这里使用网上的链接http://dev.mysql.com/downloads/mysql/5.6.html#downloads 或者使用代理网站上下载&#xff1a;https://mirrors.huaweicloud.c…

在Windows下编译ffmpeg完全手册

本文的内容几乎全部来自于FFmpeg on Windows&#xff0c;但是由于国内的网络封锁&#xff0c;很难访问这个域名下的内容&#xff0c;因此我一方面按照我自己的理解和实践做了翻译&#xff0c;另一方面也是为了能提供一个方便的参考方法。 注&#xff1a; 1. 对于compil…

padding和卷积的区别_TensorFlow笔记1——20.CNN卷积神经网络padding两种模式SAME和VALID...

第1种解说&#xff1a;(核心最后一张图&#xff0c;两种填充方式输出的形状尺寸计算公式)在用tensorflow写CNN的时候&#xff0c;调用卷积核api的时候&#xff0c;会有填padding方式的参数&#xff0c;找到源码中的函数定义如下&#xff08;max pooling也是一样&#xff09;&am…

循环神经网络变形之 (Long Short Term Memory,LSTM)

1、长短期记忆网络LSTM简介 在RNN 计算中&#xff0c;讲到对于传统RNN水平方向进行长时刻序列依赖时可能会出现梯度消失或者梯度爆炸的问题。LSTM 特别适合解决这种需要长时间依赖的问题。 LSTM&#xff08;Long Short Term Memory&#xff0c;长短期记忆网络&#xff09;是R…

UE4 ShooterGame Demo的开火的代码

之前一直没搞懂按下鼠标左键开火之后&#xff0c;代码的逻辑是怎么走的&#xff0c;今天看懂了之前没看懂的部分&#xff0c;进了一步 ShooterCharacter.cpp void AShooterCharacter::OnStartFire() {AShooterPlayerController* MyPC Cast<AShooterPlayerController>(Co…

Windows系统使用minGW+msys 编译ffmpeg 0.5的全过程详述

一.环境配置 1.下载并安装 MinGW-5.1.4.exe (http://jaist.dl.sourceforge.net/sourcef … -5.1.4.exe)&#xff0c;安装时选中 g, mingw make。建议安装到c:/mingw. 2.下载并安装 MSYS-1.0.11-rc-1.exe (http://jaist.dl.sourceforge.net/sourcef … 1-rc-1.exe)&#xff0c;安…

程序员 赚钱

业余编程赚钱 程序员的好方法 现在的人生活水平高了&#xff0c;开销也大了&#xff0c;同时对于一些技术性人员来说有很多种&#xff0c;有些程序员自己开公司&#xff0c;开发自己的产品&#xff0c;年赚百万&#xff0c;有些程序员还在给别人打工&#xff0c;每天累死累活的…

代码 优化 指南 实践

C代码优化方案 华中科技大学计算机学院 姓名&#xff1a; 王全明 QQ&#xff1a; 375288012 Email&#xff1a; quanming1119163.com 目录 目录 C代码优化方案 1、选择合适的算法和数据结构 2、使用尽量小的数据类型 3、减少运算的强度 &#xff08;1&…

.12-浅析webpack源码之NodeWatchFileSystem模块总览

剩下一个watch模块&#xff0c;这个模块比较深&#xff0c;先大概过一下整体涉及内容再分部讲解。 流程图如下&#xff1a; NodeWatchFileSystem const Watchpack require("watchpack");class NodeWatchFileSystem {constructor(inputFileSystem) {this.inputFileSy…

Python 第三方模块之 beautifulsoup(bs4)- 解析 HTML

简单来说&#xff0c;Beautiful Soup是python的一个库&#xff0c;最主要的功能是从网页抓取数据。官方解释如下&#xff1a;官网文档 Beautiful Soup提供一些简单的、python式的函数用来处理导航、搜索、修改分析树等功能。 它是一个工具箱&#xff0c;通过解析文档为用户提供…

modal vue 关闭_Vue弹出框的优雅实践

引言页面引用弹出框组件是经常碰见的需求,如果强行将弹出框组件放入到页面中,虽然功能上奏效但没有实现组件与页面间的解耦,非常不利于后期的维护和功能的扩展.下面举个例子来说明一下这种做法的弊端.click"openModal()">点击 :is_open"is_open" close…

开放平台大抉择

开放平台大抉择之新浪SAE&#xff1a;为个人应用开发带来福音 导读&#xff1a;继上期淘宝网副总裁王文彬从平台功能特色、运营状况等多方面分享了淘宝开放平台的历程和挑战之后。国内另一家云平台服务方的典型代表——Sina App Engine(简称SAE)&#xff0c;作为新浪研发中心于…