在文章:PostgreSQL 數(shù)據(jù)存儲(chǔ)結(jié)構(gòu) 中我們介紹了控制頁和數(shù)據(jù)頁的基本存儲(chǔ)結(jié)構(gòu)卓舵,那是從物理上進(jìn)行說明各種頁面的用途开睡。
下面我們是從代碼邏輯上來分析頁面是如何進(jìn)行操作和控制的萨赁。
頁面布局示意圖
PageHeader
先簡單的看一下源代碼中定義的Page頭部信息結(jié)構(gòu)體寻馏,中文是我自己的理解:
源碼位置:/src/include/storage/bufpage.h
typedef struct PageHeaderData
{
/* XXX LSN is member of *any* block, not only page-organized ones */
/*
日志文件信息,保存了該頁面最后一次被修改的操作對(duì)應(yīng)到確切的日志文件
位置壁熄,包括了日志文件ID和日志文件偏移量
*/
XLogRecPtr pd_lsn; /* LSN: next byte after last byte of xlog
* record for last change to this page */
uint16 pd_tli; /* least significant bits of the TimeLineID
* containing the LSN */
/* 用來表示頁面狀態(tài) */
uint16 pd_flags; /* flag bits, see below */
/* 空閑空間起始位置 */
LocationIndex pd_lower; /* offset to start of free space */
/* 空閑空間結(jié)束位置 */
LocationIndex pd_upper; /* offset to end of free space */
/* 特殊用途空間的起始位置帚豪,結(jié)束位置是page尾部,直到頁面結(jié)束 */
LocationIndex pd_special; /* offset to start of special space */
uint16 pd_pagesize_version;
/* 頁面類型: 有多種控制頁面類型和數(shù)據(jù)頁面 */
uint8 pd_type;
/* 物理存儲(chǔ)與邏輯存儲(chǔ)的關(guān)聯(lián)對(duì)象的OID */
Oid pd_oid; /* oid of related object,
* pg_class.oid for IAM, pg_class.relfilenode for others */
/* 數(shù)據(jù)開始位置 */
ItemIdData pd_linp[1]; /* beginning of line pointer array */
} PageHeaderData;
header用來保存該page相關(guān)的數(shù)據(jù)草丧。
pd_lsn狸臣、pd_tli 記錄日志相關(guān)的信息。
pd_flags 表示頁面狀態(tài)
PD_ALL_VISIBLE(0x0001) - 表示所有元組可以訪問昌执。
PD_VALID_FLAG_BITS(0x0003) - 加密存儲(chǔ)相關(guān)烛亦。
PG_PAGE_ENCRYPTED(0x0002) - 支持統(tǒng)一存儲(chǔ)加密引擎。pd_lower
空閑空間起始位置懂拾,隨著插入和刪除操作位置發(fā)生變化煤禽。初始化時(shí)就是pd_linp的偏移位置。pd_upper
空閑空間結(jié)束位置岖赋,隨著插入和刪除操作位置發(fā)生變化檬果。初始化時(shí)與pd_special相同。pd_special
相當(dāng)于畫了一條線唐断,從pd_special這個(gè)位置到page的結(jié)尾选脊,都是special的地盤,普通插入Tuple脸甘,都不許進(jìn)入這個(gè)私有地盤恳啥。而且這個(gè)pd_special一旦初始化之后,這個(gè)值就不會(huì)動(dòng)了斤程。
主要用于加密存儲(chǔ)是保存加密相關(guān)的數(shù)據(jù)角寸。pd_pagesize_version
高位字節(jié)表示page大小。
低位字節(jié)表示版本號(hào)(PG_PAGE_LAYOUT_VERSION)忿墅,代碼寫死扁藕。
宏P(guān)ageSetPageSizeAndVersion用于設(shè)置大小和版本。
宏P(guān)ageGetPageSize()獲取Page大小疚脐。
宏P(guān)ageGetPageLayoutVersion()獲取版本號(hào)亿柑。
對(duì)應(yīng)的實(shí)現(xiàn):
#define PageSetPageSizeAndVersion(page, size, version) \
( \
AssertMacro(((size) & 0xFF00) == (size)), \
AssertMacro(((version) & 0x00FF) == (version)), \
((PageHeader) (page))->pd_pagesize_version = (size) | (version) \
)
#define PageGetPageSize(page) \
((Size) (((PageHeader) (page))->pd_pagesize_version & (uint16) 0xFF00))
#define PageGetPageLayoutVersion(page) \
(((PageHeader) (page))->pd_pagesize_version & 0x00FF)
- pd_type
有這些類型:P_GAM、P_IAM棍弄、P_PFS望薄、P_DCM疟游、P_BCM、P_SGAM痕支、P_VM颁虐。
對(duì)應(yīng)的定義:
typedef enum PageType{
P_TEMPDATA,
P_HEAP,
P_BTREE,
P_HASH,
P_GIN,
P_GIST,
P_SEQUENCE,
P_GAM,
P_PFS,
P_SGAM,
P_BCM,
P_DCM,
P_IAM,
P_VM, /* vm page*/
P_UNKNOWN,
}PageType;
Page初始化
通過如下函數(shù)初始化頁面:
void
PageInit(Page page, Size pageSize, Size specialSize, PageType type)
{
PageHeader p = (PageHeader) page;
uint16 pd_flags = p->pd_flags;
specialSize = MAXALIGN_DISK(specialSize);
Assert(pageSize == BLCKSZ);
Assert(pageSize > specialSize + SizeOfPageHeaderData);
/* Make sure all fields of page are zero, as well as unused space */
MemSet(p, 0, pageSize);
/* p->pd_flags = 0; done by above MemSet */
PageSetPageSizeAndVersion(page, pageSize, PG_PAGE_LAYOUT_VERSION);
PageSetPageType(page, type);
PageSetOid(page, InvalidOid); /* we will set it later for heap page */
/* Support the uniform store encryption engine. */
p->pd_lower = SizeOfPageHeaderData;
if ((pd_flags & PG_PAGE_ENCRYPTED) && !PageIsCtrlPage(page))
p->pd_special = pageSize - specialSize -
G_EncryptData->block_key_num * (G_EncryptData->key_len + G_EncryptData->verify_len);
else
p->pd_special = pageSize - specialSize;
p->pd_upper = p->pd_special;
/*
* Empty page is all visible. We must set PD_ALL_VISIBLE because
* recycled page's "visibility map bit" may be set before. (Recycled page --
* means page was deallocated by Vacuum or something else, and then
* be allocated.)
*/
PageSetAllVisible(p);
if ((pd_flags & PG_PAGE_ENCRYPTED) && !PageIsCtrlPage(page))
PageSetEncrypted(p);
}
待分析...
Page有效性檢查
通過如下函數(shù)實(shí)現(xiàn):
bool
PageHeaderIsValid(PageHeader page)
{
char *pagebytes;
int i;
/* Check normal case */
/* Support the uniform store encryption engine. */
if (PageGetPageSize(page) == BLCKSZ &&
PageGetPageLayoutVersion(page) == PG_PAGE_LAYOUT_VERSION &&
(page->pd_flags & ~PD_VALID_FLAG_BITS) == 0 &&
page->pd_lower >= SizeOfPageHeaderData &&
page->pd_lower <= page->pd_upper &&
page->pd_upper <= page->pd_special &&
(PageIsEncrypted(page) ?
page->pd_special <= BLCKSZ - G_EncryptData->block_key_num * (G_EncryptData->key_len + G_EncryptData->verify_len) :
page->pd_special <= BLCKSZ) &&
page->pd_special == MAXALIGN_DISK(page->pd_special))
return true;
/* Check all-zeroes case */
pagebytes = (char *) page;
for (i = 0; i < BLCKSZ; i++)
{
/* Support the uniform store encryption engine. */
if (pagebytes[i] != 0 && pagebytes[i] != 2)
return false;
}
return true;
}
待分析...
Insert操作分析
當(dāng)初始化的時(shí)候,pd_lower設(shè)置為SizeOfPageHeaderData卧须,pd_upper設(shè)置為和pd_special一樣另绩。但是注意,這個(gè)lower和upper不是固定的花嘶,隨著Tuple的不斷插入笋籽,lower變大,而upper不斷變小椭员。當(dāng)我們每插入一條Tuple车海,需要在當(dāng)前的lower位置再分配一個(gè)Item,記錄Tuple的長度隘击,Tuple的起始位置offset侍芝,還有flag信息。這個(gè)Page Header中的pd_lower就是記錄分配下一個(gè)Item的起始位置闸度。所以如果不斷插入竭贩,lower不斷增加,每增加一條Tuple莺禁,就要分配一個(gè)Item(4個(gè)字節(jié))
對(duì)應(yīng)實(shí)現(xiàn)函數(shù):
- PageAddItem
OffsetNumber
PageAddItem(Page page,
Item item,
Size size,
OffsetNumber offsetNumber,
Size tupleSize,
ItemIdFlags flags)
待分析...
Delete操作分析
對(duì)應(yīng)實(shí)現(xiàn)函數(shù):
- PageIndexMultiDelete
void
PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
offnum指示第幾個(gè)記錄留量,offnum是從1開始計(jì)數(shù)的,查找對(duì)應(yīng)item 是offnum-1.
我們找到Item哟冬,就可以找到Tuple對(duì)應(yīng)的offset和size楼熄。
待分析...
發(fā)現(xiàn)更多寶藏
我在喜馬拉雅上分享聲音
《PostgreSQL數(shù)據(jù)庫內(nèi)核分析》,點(diǎn)開鏈接可以聽聽浩峡,有點(diǎn)意思可岂。
《數(shù)據(jù)庫系統(tǒng)概論(第4版)》,點(diǎn)開鏈接可以聽聽翰灾,有點(diǎn)意思缕粹。
更多IT有聲課程,點(diǎn)我發(fā)現(xiàn)更多
其他相關(guān)文章分享列表:
第 23 課 PostgreSQL 創(chuàng)建自己的數(shù)據(jù)庫纸淮、模式平斩、用戶
第 22 課 PostgreSQL 控制文件
第 21 課 PostgreSQL 日志系統(tǒng)
第 16 課 查詢過程源碼分析
第 15 課 PostgreSQL 系統(tǒng)參數(shù)配置
第 14 課 PostgreSQL 數(shù)據(jù)存儲(chǔ)結(jié)構(gòu)
第 13 課 PostgreSQL 存儲(chǔ)之Page(頁面)源碼分析
第 12 課 PostgreSQL 認(rèn)證方式
第 11 課 PostgreSQL 增加一個(gè)內(nèi)核C函數(shù)
第 10 課 PostgreSQL 在內(nèi)核增加一個(gè)配置參數(shù)
第 09 課 PostgreSQL 4種進(jìn)程啟動(dòng)方式
第 08 課 PostgreSQL 事務(wù)介紹
第 07 課 PostgreSQL 數(shù)據(jù)庫、模式咽块、表绘面、空間、用戶間的關(guān)系
第 06 課 PostgreSQL 系統(tǒng)表介紹
第 05 課 PostgreSQL 編譯源代碼進(jìn)行開發(fā)
第 04 課 PostgreSQL 安裝最新的版本
第 03 課 PostgreSQL 代碼結(jié)構(gòu)
第 02 課 PostgreSQL 的特性、應(yīng)用揭璃、安裝
第 01 課 PostgreSQL 簡介及發(fā)展歷程
上面文章都在專輯中:PostgreSQL專輯鏈接晚凿,點(diǎn)我查看
如果有用,可以收藏這篇文件瘦馍,隨時(shí)在更新....
更多交流加群: PostgreSQL內(nèi)核開發(fā)群 876673220
親歼秽,記得點(diǎn)贊、留言情组、打賞額U芤!呻惕!