Postgresql 小白學習之路之 PostgreSQL的體系結構

Postgresql 小白學習之路之 PostgreSQL的體系結構

PostgreSQL體系結構

PostgreSQL的物理結構非常簡單。它是由共享內存(shared memory)和少量的後臺進程以及數據文件組成。
在這裏插入圖片描述

共享內存(shared memory)

共享內存是指提供數據緩存和事務日誌緩存的內存。在共享內存中最重要的組成部分是shared Buffer和WAL buffer(Write-Ahead Logging)
共享內存是指提供數據緩存和事務日誌緩存的內存。在共享內存中最重要的組成部分是shared Buffer和WAL buffer(Write-Ahead Logging)

shared Buffer(共享緩衝區)
共享緩衝區的主要目的是最大限度的減少磁盤IO.爲達到這個目的,必須要滿足如下的這些要求
需要快速訪問非常大的緩衝區
當多個用戶同時訪問併發生爭用時,應該確保最小化爭用。
使用最頻繁的數據塊必須儘可能長時間的保存在緩衝區中。
WAL buffer(預寫日誌緩存)
WAL:Write-Ahead Logging 預寫日誌
WAL buffer 是一個臨時存儲數據更改的緩衝區,存儲在WAL buffer 中的內容將在預定的時間點寫入WAL文件。

從備份和恢復的角度,WAL buffer和WAL文件是非常重要的。

PostgreSQL 進程的類型

PostgreSQL有四種類型的進程
Postmaster(Daemon)進程
後臺進程(Background Process)
後端進程(Backend Process)
客戶端進程(Client Process)

Postmaster 進程

Postgmaster進程是PostgreSQL啓動的第一個進程。
負責實施恢復,初始化共享內存,並啓動後臺進程。
當客戶端進程有鏈接請求時,負責創建後端進程。

在這裏插入圖片描述
如果使用pstree命令檢查進程之間的關係,你會發現Postmaster是所有進程的父進程。(pstree命令並不顯示進程的名稱,爲了解釋清晰,我增加了進程的名稱和參數)
在這裏插入圖片描述

後臺進程(Background Process)

PostgreSQL操作所需的後臺進程列表如下。
在這裏插入圖片描述

後端進程(Backend Process)

後端進程的最大數量取決於max_connections參數的設置,默認值是100。
後端進程執行用戶的查詢請求,然後傳輸結果。
執行查詢操作需要一些特定的內存結構,我們稱爲本地內存。
與本地內存相關的主要參數
work_mem 空間主要用來進行排序、位圖操作、哈希連接、合併連接。默認值爲4M。
maintenance_work_mem 空間主要用於Vacuum和創建索引。默認值爲64M.
temp_buffers 空間用戶臨時表,默認值爲8M

客戶端進程(Client Process)

客戶端進程是指爲每個後端用戶連接分配的後臺進程。通常,postmaster進程將創建一個子進程,該子進程專用於服務於用戶連接。

數據庫結構

在這裏插入圖片描述
當我們嘗試理解PostgreSQL的數據庫結構時,有如下這些重要的事情需要了解

與數據庫相關的

PostgreSQL由幾個數據庫組成,我們稱之爲數據庫集羣。
當initdb()被執行的時候,template0 , template1 , and postgres 數據庫將被創建。
template0 , template1是用戶創建數據庫時的模板數據庫,其中包含了系統字典表。
在執行了initdb()後,template0 , template1中的表是相同的。然而,template1數據庫能夠創建用戶所需的數據庫對象。
用戶數據庫是通過克隆template1數據庫創建的。

與表空間相關

在這裏插入圖片描述
在執行完initdb()後,表空間pg_default和pg_global則立即被創建。

  1. 創建表時如果不指定表空間,則表默認存儲在pg_default表空間。(確切的說是存儲在該表空間的數據庫下面)
  2. 數據庫集羣級別管理的表存儲在pg_global表空間中。

pg_default表空間的物理位置是PGDATA/basepgglobalPGDATA/base pg_global表空間的物理位置是PGDATA/global
一個表空間可以被多個數據庫使用。此時,將在表空間目錄中創建特定於數據庫的子目錄。
創建用戶表空間將在$PGDATA/tblspc目錄中創建到用戶表空間的符號鏈接。
在這裏插入圖片描述

與表相關

首先創建一個測試表:

create table test(id int,name varchar(20));

注意:

  1. 視圖pg_tables提供對數據庫中每個表的信息的訪問
  2. pg_class記錄表和幾乎所有具有列或者像表的東西。這包括索引(但還要參
    見pg_index)、序列(但還要參見pg_sequence)、視圖、物化視圖、組合類型和TOAST表,
    參見relkind。下面,當我們提及所有這些類型的對象時我們使用“關係”。並非所有列對
    於所有關係類型都有意義。
    參見官方文檔 第 51 章 系統目錄
    系統目錄是關係型數據庫存放模式元數據的地方,比如表和列的信息,以及內部統計信息
    等。PostgreSQL的系統目錄就是普通表
mydb=# select relfilenode,oid from pg_class where relname = 'test';
-[ RECORD 1 ]------
relfilenode  |  24578
oid              |  24578

官方文檔 9.26.7. 數據庫對象管理函數
在這裏插入圖片描述

mydb=# select pg_relation_filenode('test');
-[ RECORD 1 ]--------+------
pg_relation_filenode | 24578

mydb=# select pg_relation_filepath('test');                      ---->查看錶的物理位置
-[ RECORD 1 ]--------+--------------------------------------------
pg_relation_filepath | pg_tblspc/24576/PG_10_201707211/24577/24578

默認創建的表的relfilenode 和 oid 是一樣的
表在數據庫中通過對象的id (oid) 來組織和管理
數據文件是由relfilenode來管理

爲測試表創建索引

mydb=# create index idx_test_id on test(id);
CREATE INDEX
mydb=# 
mydb=# 
mydb=# \d test
                       Table "public.test"
 Column |         Type          | Collation | Nullable | Default 
--------+-----------------------+-----------+----------+---------
 id     | integer               |           |          | 
 name   | character varying(20) |           |          | 
Indexes:
    "idx_test_id" btree (id)

mydb=# select oid,relfilenode from pg_class where relname = 'idx_test_id'; 
  oid  | relfilenode 
-------+-------------
 32770 |       32770
(1 row)

— 前面提到過 pg_class記錄表和幾乎所有具有列或者像表的東西

索引的 relfilenode 和 oid 通常和表的 oid 一致
注意:
通常 relfilenode 和 對象的oid 一致,但是如果使用以下命令後,表和索引的
relfilenode 值會改變
TRUNCATE
REINDX
CLUSTER

--爲測試表插入數據
mydb=# insert into test values(1,'PostgreSQL'); 
INSERT 0 1
mydb=# 
mydb=#  select * from test; 
 id |    name    
----+------------
  1 | PostgreSQL
(1 row)

mydb=# select oid,relfilenode from pg_class where relname = 'test';
  oid  | relfilenode 
-------+-------------
 24578 |       24578
(1 row)

mydb=# truncate table test;
TRUNCATE TABLE
mydb=# select oid,relfilenode from pg_class where relname = 'test';
  oid  | relfilenode 
-------+-------------
 24578 |       32771
 
 --relfilenode  變了



--再次插入數據
mydb=# insert into test select id,id || 'eareataetaewt' from generate_series(1,100) as id;  
INSERT 0 100

--查看錶的物理位置
mydb=# select pg_relation_filepath('test'); 
            pg_relation_filepath             
---------------------------------------------
 pg_tblspc/24576/PG_10_201707211/24577/32771
(1 row)
--查看錶的大小
mydb=# select pg_size_pretty(pg_relation_size('test')); 
 pg_size_pretty 
----------------
 16 kB
(1 row)


--插入數據,讓表超過1G
mydb=# insert into test select id,id || 'eareataetaewt' from generate_series(1,1000000) as id;
INSERT 0 1000000
mydb=# insert into test select id,id || 'eareataetaewt' from generate_series(1,10000000) as id;
INSERT 0 10000000
mydb=# insert into test select id,id || 'eareataetaewt' from generate_series(1,10000000) as id;
INSERT 0 10000000
mydb=# select pg_size_pretty(pg_relation_size('test'));  
 pg_size_pretty 
----------------
 1515 MB
(1 row)

mydb=# select pg_relation_filepath('test'); 
            pg_relation_filepath             
---------------------------------------------
 pg_tblspc/24576/PG_10_201707211/24577/32771
(1 row)

當表對象超過1G ,那麼 表對象將會以 relfilenode 加.編號的方式生成新的文件
該編號是從1開始
可以通過 --with-segsize 來更改單個表文件的大小
表對象除了有對應的物理的數據文件之外,還有兩個名爲 fsm後綴結尾和vm結尾的文件
fsm:表文件每個頁面上的空閒空間信息
vm: 表文件本身的可見性信息

索引本身沒有vm文件,只有fsm文件

[postgres@c7-pg10 24577]$ ls -l 32771* -h
-rw------- 1 postgres postgres 1.0G Jul 1 11:50 32771
-rw------- 1 postgres postgres 492M Jul 1 11:50 32771.1
-rw------- 1 postgres postgres 400K Jul 1 11:48 32771_fsm
-rw------- 1 postgres postgres 32K Jul 1 11:44 32771_vm


表空間物理路徑的佈局

sdedu_tbs
[postgres@sdedu PG_10_201707211]$ pwd
/data/sdedu_tbs/PG_10_201707211
PG_10_201707211
PG_數據庫主版本編號_創建的時間
數據庫目錄
[postgres@sdedu 16388]$ pwd
/data/sdedu_tbs/PG_10_201707211/16388

pg_tblspc

堆表文件的佈局

表是由多個page組成,每一個page大小爲8k
page的編號從 0 開始
pd_lsn ,該頁面最近一次變更寫入pg_xact中對應的lsn
pd_checksum:該頁面的校驗和
pd_lower:指向行指針的末尾
pd_upper: 指向最後一個插入元組的起始位置

補充:

每個表對應三個文件
一個是存儲數據的文件,文件名是該表的OID
一個是管理表空閒空間的文件,文件名爲OID_fsm
一個是管理表塊可見性的文件。文件名是OID_vm
索引沒有_vm文件。也就是說,OID和OID_fsm由兩個文件組成。
其他需要記住的
創建表和索引時的文件名是OID。OID和pg_class.relfilenode是相同的。
但是,當執行重寫操作(Truncate、CLUSTER、Vacuum Full、REINDEX等)時,將更改受影響對象的relfilenode值,文件名也將更改爲relfilenode值。您可以使用pg_relation_filepath (’< object name >’)輕鬆地檢查文件位置和名稱。
實際操作
如果在initdb()之後查詢pg_database視圖,可以看到已經創建了template0、template1和postgres數據庫。
postgres=# select oid,datname,datistemplate,datallowconn from pg_database order by 1;
oid | datname | datistemplate | datallowconn
-------±----------±--------------±-------------
1 | template1 | t | t
13210 | template0 | t | f
13211 | postgres | f | t
16394 | bucardo | f | t
(4 rows)
通過datistemplate列,您可以看到template0和template1數據庫是用於創建用戶數據庫模板的數據庫。
datlowconn列指示是否可以訪問數據庫。不能訪問template0數據庫,同時數據庫的內容也不能更改。
爲模板提供兩個數據庫的原因是,template0數據庫是初始狀態模板,template1數據庫是用戶添加的模板。
postgres數據庫是使用template1數據庫創建的默認數據庫。如果在連接時沒有指定數據庫,則將連接到postgres數據庫。
數據庫位於PGDATA/baseOID[postgres@localhostbase]PGDATA/base目錄下。目錄名是數據庫的OID號。 [postgres@localhost base] ll
總用量 48
drwx------. 2 postgres postgres 8192 7月 11 14:20 1
drwx------. 2 postgres postgres 8192 6月 28 13:49 13210
drwx------. 2 postgres postgres 8192 7月 11 14:20 13211
drwx------. 2 postgres postgres 8192 7月 11 14:21 16394
創建用戶數據庫
用戶數據庫由克隆template1數據庫創建。要驗證這一點,請在template1數據庫中創建一個用戶表T1。創建mydb01數據庫之後,檢查T1表是否存在。
postgres=# \c template1
You are now connected to database “template1” as user “postgres”.
template1=# create table t1 (c1 integer);
CREATE TABLE
template1=# \c postgres
You are now connected to database “postgres” as user “postgres”.
postgres=# create database mydb01;
CREATE DATABASE
postgres=# \c mydb01
You are now connected to database “mydb01” as user “postgres”.
mydb01=# \d
List of relations
Schema | Name | Type | Owner
--------±-----±------±---------
public | t1 | table | postgres

pg_default 表空間
如果在initdb()之後查詢pg_tablespace,可以看到已經創建了pg_default和pg_global表空間。
postgres=# select oid,* from pg_tablespace;
oid | spcname | spcowner | spcacl | spcoptions
-------±-----------±---------±-------±-----------
1663 | pg_default | 10 | |
1664 | pg_global | 10 | |
16999 | tbs_test | 10 | |
(3 rows)
pg_default表空間的位置是PGDATAbaseOID[postgres@localhostbase]PGDATAbase。這個目錄中有一個按數據庫OID劃分的子目錄 [postgres@localhost base] ls -l $PGDATA/base
總用量 60
drwx------. 2 postgres postgres 8192 7月 12 14:57 1
drwx------. 2 postgres postgres 8192 6月 28 13:49 13210
drwx------. 2 postgres postgres 8192 7月 11 14:20 13211
drwx------. 2 postgres postgres 8192 7月 11 14:21 16394
drwx------. 2 postgres postgres 8192 7月 12 14:59 17008

pg_global 表空間
pg_global表空間是用於存儲要在“數據庫集羣”級別管理的數據的表空間。
例如,與pg_database表類型相同的表無論是否從任何數據庫訪問,都提供相同的信息。
pg_global表空間的位置是$PGDATAglobal。
創建用戶表空間
postgres=# create tablespace myts01 location ‘/data01’;
CREATE TABLESPACE
postgres=# select oid,* from pg_tablespace;
oid | spcname | spcowner | spcacl | spcoptions
-------±-----------±---------±-------±-----------
1663 | pg_default | 10 | |
1664 | pg_global | 10 | |
16999 | tbs_test | 10 | |
17010 | myts01 | 10 | |
(4 rows)
PGDATA/pgtblspc[postgres@localhost ]PGDATA/pg_tblspc目錄中的符號鏈接指向表空間目錄。 [postgres@localhost ~] ls -l PGDATA/pgtblspc0lrwxrwxrwx.1postgrespostgres3771213:3916999>/usr/local/pgsql/data/tablespacedatalrwxrwxrwx.1postgrespostgres771215:1517010>/data01[postgres@localhost ]PGDATA/pg_tblspc 總用量 0 lrwxrwxrwx. 1 postgres postgres 37 7月 12 13:39 16999 -> /usr/local/pgsql/data/tablespace_data lrwxrwxrwx. 1 postgres postgres 7 7月 12 15:15 17010 -> /data01 [postgres@localhost ~]
更改表空間位置
PostgreSQL在創建表空間時指定一個目錄。因此,如果目錄所在的文件系統已滿,則不能再存儲數據。要解決這個問題,可以使用卷管理器。但是,如果不能使用卷管理器,可以考慮更改表空間位置。操作順序如下。
[postgres@localhost 13211]$ pg_ctl stop
waiting for server to shut down… done
server stopped

[root@localhost 13210]# cp -rp /data01/PG* /data02

[root@localhost pg_tblspc]# chown postgres.postgres /data02

[root@localhost pg_tblspc]# ll
總用量 0
lrwxrwxrwx. 1 postgres postgres 7 7月 12 15:15 17010 -> /data01

[root@localhost pg_tblspc]# rm 17010

[root@localhost pg_tblspc]# ln -s /data02 17010

[postgres@localhost 13211]$ pg_ctl start
Note: 表空間在使用分區表的環境中也非常有用。因爲可以爲每個分區表使用不同的表空間,所以可以更靈活地處理文件系統容量問題。
什麼是Vacuum?
Vacuum做了如下這些事
收集表和索引統計信息
重新組織表
清理表和索引無用的數據塊
由記錄XID凍結,以防止XID循環
1和2通常是DBMS管理所必需的。但是3和4是必要的,因爲PostgreSQL MVCC特性

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章