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.pswp.cn/news/453899.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/453899.shtml
英文地址,請注明出處:http://en.pswp.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 下…

在windows xp下編譯出ffmpeg.exe

找了好多資料,把自己的編譯成功過程詳細敘述,以避免后來者可以少浪費點時間。 1.安裝MSys 到http://sourceforge.net/project/showfiles.php?group_id2435下載文件:   bash-3.1-MSYS-1.0.11-tar.bz2   msysCORE-1.0.11-2007.01.19-1.ta…

手機uc怎么放大頁面_手機網站怎樣做可以提高用戶體驗度?——竹晨網絡

目前,手機已經占據了人們大多數的閑暇時間,互聯網的流量開始逐漸向移動端傾斜,重視移動端的用戶體驗,就可以給客戶端增加很多意想不到的功能。但是還是有很多公司和站長不知道手機網站應該怎么建才能符合用戶的使用習慣。下面&…

科技申報項目總結

這個項目分為三大模塊,管理員,專家以及單位模塊,具體頁面有:1單位信息;2項目申報;3專家信息;4項目評審;5 項目信息;6申報設置;7專家信息。 —-項目框架SSM&am…

kafka 異常:ERROR Failed to clean up log for __consumer_offsets-30 in dir /tmp/kafka-logs due to IOExce

問題概述 kafka進程不定期掛掉。ERROR Failed to clean up log for __consumer_offsets-30 in dir /tmp/kafka-logs due to IOException (kafka.server.LogDirFailureChannel),報錯如下 [2020-12-07 16:12:36,803] ERROR Failed to clean up log for __consumer_o…

樹形控件(CTreeCtrl和CTreeView)

如何插入數據項目?如何添加鼠標右擊事件?插入數據項 通過InsertItem()方法,有四種重載樣式: HTREEITEM InsertItem(LPTVINSERTSTRUCT lpInsertStruct); HTREEITEM InsertItem(UINT nMask, LPCTSTR lpszItem, int nImage,int nSelectedImage, …

ffmpeg編譯(生成Windows或Win32平臺dll, lib)

ffmpeg編譯(生成Windows或Win32平臺dll, lib) 介紹:本文簡要介紹通過cygwin環境來編譯生成ffmpeg。 包括解碼組件libfaad與libopencore-amrnb的編譯。 1)安裝msys mingw環境 具體安裝過程可以看網上教程 我用的是:http://code.google.com/p/msys-cn/ 假…

2019python課件_2019版經典Python學習路線分享

Python有三大神器,包括numpy,scipy,matplotlib,因此適合用于數據處理。spark,Hadoop都開了Python的接口,所以使用Python做Python的mapreduce也非常簡單。因此它也備受歡迎,python學習大綱分享給大家。一、Python基礎1.2數據的存儲…

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…

python 內置模塊 subprocess

1、介紹 subprocess模塊可以生成新的進程&#xff0c;連接到它們的input/output/error管道&#xff0c;同時獲取它們的返回碼。 2、基本操作方法 2.1、subprocess的run、call、check_call、check_output函數 subprocess.run(args[, stdout, stderr, shell ...]) 執行args命…

Windows下的FFmpeg 、MEncoder、MPlayer下載地址

視頻轉碼常用的是MEncoder&#xff0c;這個工具包含在MPlayer中&#xff0c;也可以使用FFmpeg&#xff0c;但據說有些格式轉換的時候效率不及MEcoder&#xff0c;所以能用MEncoder轉換的盡量用MEncoder轉換&#xff0c;由于MEncoder暫時還沒有研究&#xff0c;等研究過了再補充…

功率曲線k值_什么叫離心泵的流量——功率曲線?它們之間有什么關系?

流量和功率之間為正比例關系。流量增加時功率也增加&#xff0c;增加快、慢與比轉數有關。比轉數越小&#xff0c;流量增加后功率增加越快&#xff1b;比轉數越大&#xff0c;流量增加后功率增加越慢。 離心泵的性能參數與特性曲線泵的性能及相互之間的關系是選泵和進行流量調節…

什么是CI/CD

一、簡介 CI / CD的采用改變了開發人員和測試人員如何發布軟件。 最初是瀑布模型&#xff0c;后來是敏捷開發&#xff0c;現在是DevOps&#xff0c;這是現代開發人員構建出色的產品的技術路線。隨著DevOps的興起&#xff0c;出現了持續集成&#xff08;Continuous Integration…

FFmpeg在Windows系統下的編譯過程

由于FFMpeg是基于Linux開發的開源項目&#xff0c;源代碼和Windows下最常見的Visual Studio提供的C/C編譯器不兼容&#xff0c;因此它不能使用MSVC編譯。要想使用FFMpeg&#xff0c;最先要解決的問題就是在Windows下配置一個類似Linux的編譯環境&#xff0c;將FFMpeg編譯為二進…

centos 文件夾網絡連接_centos8 安裝后網絡不通及網絡配置

一 修改配置文件設置網絡時首先打開配置文件&#xff0c;配置文件默認如下所示&#xff0c;如果使用dhcp自動獲取ip&#xff0c;只需將ONBOOTno修改為ONBOOTno即可。#網卡配置文件按默認配置TYPEEthernetPROXY_METHODnoneBROWSER_ONLYnoBOOTPROTOdhcpDEFROUTEyesIPV4_FAILURE_F…

STM32 HAL庫詳解 及 手動移植

源&#xff1a; STM32 HAL庫詳解 及 手動移植轉載于:https://www.cnblogs.com/LittleTiger/p/8044047.html

Python 內置函數之 open (文件操作)

正常的文件操作都分三步走&#xff1a;打開文件&#xff0c;操作文件&#xff0c;關閉文件。 python open() 函數用于打開一個文件&#xff0c;創建一個 file 對象&#xff0c;相關的方法才可以調用它進行讀寫 語法 文件句柄 open(文件名,模式) 例如&#xff1a;f open(a…