postgresql磁盤存儲結構


1.  Page header24Byte

1.1.  描述

記錄頁頭的信息。

1.2.  get_raw_page函數

將指定表文件中的頁面內容返回,param1:表名,param2main/fsm/vm, param3:第幾頁

1.3.  page_header(函數)

作用:

返回本頁面中的page header信息。param1get_raw_page函數的返回值。

page的前24字節存儲head data

字段:

       lsn:記錄最后一次對頁修改的xlog記錄

       checksum:頁面的校驗和,用於判斷當前頁是否完整

       flags:(指示當前頁的狀態)

       lower:本頁空閑位置的起始指針

       uper:本業空閑位置的結束指針

       special:頁預留的位置

       pagesize:頁面大小

       version:當前版本

       prune_xid:最后一次刪除或更新的xid

       頁后面存儲的是元組(tuple)信息的數據(表的數據行,一個元組信息就是一行),也就是下一節的page item

1.4.  圖解

 

2.  Page item(頁內存儲的tuple數據)

一條數據(數據庫行)由Item ID + Tuple Header + data(實際數據,由用戶寫入)組成。

Item IdPage head存放在一起,存在頁的一端,Tuple Header + data 存放在一起,存放在頁的另一端。

 

 

1.5.  Item ID(行指針,4字節)

       

 

1.6.  Tuple Header(元組頭,23字節)

描述元組數據的數據,描述本條數據(數據庫行)的狀態,事物id等。

由於數據存儲需要對齊(8的整數倍,整體對外長度為24字節),空行的長度為24字節。另外還有4字節的指針。即一條空行總共占用24+4=28字節。

       

 

1.7.  heap_page_items函數

顯示堆頁面上的所有行指針。param1get_raw_page函數的返回值

       lp:

       lp_off: tuple在頁面中的位置

       lp_len:tuple的實際長度(tuple header + data,最終長度需要取整為8B的整數倍)

       t_xmint_xmax: 插入,刪除和更新時的事物id,插入時xmin寫入當前事物id,刪除時xmax寫入事物id。更新也是先刪除再插入

       t_field3:

       t_ctid:物理id,用於尋址數據在該頁的位置(或者用於指向下一頁索引)

1.8.  實戰1-單條數據占用內存

l  sql語句

       drop table if EXISTS test1;

create table test1(id int);

--vacuum analyze test1;

insert into test1 values(1);

select * from page_header(get_raw_page('test1', 'main', 0));

insert into test1 values(2);

select * from page_header(get_raw_page('test1', 'main', 0));

insert into test1 values(300);

select * from page_header(get_raw_page('test1', 'main', 0));

select * from heap_page_items(get_raw_page('test1',0));

l  分析

       通過下圖可以看出,每插入一條int數據,lower增加4字節(item id是從頁頭開始寫的,寫在header的后面),upper減少32字節(tuple是從頁尾開始寫的,tuple header + data = 24 + 4 = 28,因為要取8的整數倍,所以為32字節)。

t_data存放的是數據的內容,注意下面標紅的2c010000(實際是0X012c)對應值為300.

 

l  額外結論

根據上面的理論,目前來看,存儲4字節的int8字節的int消耗磁盤大小是一樣的。(該定論局限於本表只有一個字段的情況下)

可以通過以下方式證明:

              drop table if EXISTS test1;

create table test1(id char(7));

insert into test1 values('a');

select * from page_header(get_raw_page('test1', 'main', 0));

insert into test1 values('b');

select * from page_header(get_raw_page('test1', 'main', 0));

insert into test1 values('c');

select * from page_header(get_raw_page('test1', 'main', 0));

select * from heap_page_items(get_raw_page('test1',0));

說明:char的長度可以手動指定,char中的長度7實際占用8字節。

另外:

  null字段不占用tuple中的空間

  定長字段(int,char...)占用磁盤空間固定,和聲明的一致

  變長字段(varchar, text...)占用的磁盤空間由實際數據而定,而不是聲明的長度。

1.9.  實戰2-計算單頁的存儲量(226行)

計算一頁可以存儲的單列int類型數據數量

l  已知

       page頁默認大小為8KB8192字節)

page header 24字節

       每插入一行,生成一個item id4字節)和一個tupletuple header + data,共32字節)。

l  理論值

       (8192-24/ 36 = 226

(8192-24% 36 = 32

所以理論上,一頁可以存儲226行只有一個int列的數據,屬於空間大小為32B

l  測試sql

       drop table if EXISTS test1;

create table test1(id int);

insert into test1 select generate_series(1,226);

select * from page_header(get_raw_page('test1', 'main', 0));

select * from heap_page_items(get_raw_page('test1',0));

select count(*) from heap_page_items(get_raw_page('test1',0));

insert into test1 values(300);

l  分析

       插入226條數據后,通過page heaader查看upper-lower=32

       通過page items查看第一頁存在226條數據。

       再插入一條,發現第一頁還是226條數據,第二頁有一條數據

       說明第一頁已經插滿。

 

3.  存儲結構

l  存儲方式共有4

PLAIN :避免壓縮和行外存儲。

EXTENDED :先壓縮,后行外存儲。

EXTERNAL :允許行外存儲,但不許壓縮。

MAIN :允許壓縮,盡量不使用行外存儲更貼切。

l  何時壓縮Tuple數據

       Tuple大小超過大概2KB時,PostgreSQL會嘗試基於LZ壓縮算法進行壓縮。

l  何時行外存儲(TOAST

行外存儲toasted屬性的本意是The Oversized-Attribute Storage Technique,對於某個超長的屬性單獨存儲。當某行數據超過PostgreSQL頁大小(8k)后,會將這個頁放到系統命名空間pg_toast下的一個單獨的表中,而在原表中存儲一個TOAST pointer

l  實戰解讀

create table tab3(id int primary key,info text);

 備注:

  https://www.postgresql.org/docs/current/storage-page-layout.html     官方文檔


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM