1. 程式人生 > >postgresql基礎學習(二)——TOAST,分割槽表

postgresql基礎學習(二)——TOAST,分割槽表

目錄

TOAST簡介

變長型別

TRUNK

TOAST策略

heap-only tuple技術簡介

表繼承

分割槽表

分割槽表操作

建立父表

建立子表

建立子表索引

建立觸發器:

小結:


TOAST簡介

全稱The Oversized-Attributes Storage Technique,超大欄位儲存技術。

PG頁面(block)通常為8kb,不允許跨越多個頁面。大欄位(超過1/4 blocksize)會被壓縮 或者 分片成多行存到另一張系統表——TOAST表。

只有變長的資料型別才能支援TOAST。( 定長的大欄位如何處理? 暫不知道,日後再說)

變長型別

bit0 bit1 bits 2~31 N bytes
壓縮標誌 行外標誌 length value or pointer

前2 bits為標誌位,30bits長度,value實際長度,非壓縮後長度。

壓縮標誌,如果置1,那麼value是壓縮的。

行外標誌,如果置1,那麼長度後面的value將是一個指標,指標指向TOAST表中的位置。

TRUNK

行外儲存切成多個chunk塊,chunk為四分之一block大小。

TOAST表裡包含N個chunk塊,每一個chunk塊當做一個獨立的行。

trunk_id trunk_seq trunk_data
OID 序列號 實際資料

TOAST指標資料:TOAST‘s oid, chunk_id(oid), 未壓縮資料長度,壓縮後資料長度。共20 bytes

TOAST策略


PLAIN:避免壓縮和行外儲存
EXTENDED:允許壓縮和行外儲存,先壓縮,猶大則行外存
EXERNA:允許行外存,不虛壓縮
MAIN: 允許壓縮,禁止行外存

指定策略

testdb=# ALTER TABLE t2 ALTER title SET STORAGE MAIN;
ALTER TABLE
testdb=# \d+ t2 
                          Table "public.t2"
 Column |  Type   | Modifiers | Storage | Stats target | Description 
--------+---------+-----------+---------+--------------+-------------
 id     | integer |           | plain   |              | 
 title  | text    |           | main    |              | 

testdb=# 

 

heap-only tuple技術簡介

表上設定儲存引數fillfactor,這個叫填充因子,設定一個數據塊中只寫入百分之多少,剩下的部分不再填充

那剩下的空間幹嘛用呢 ? 資料更新。

當更新一條資料,如果有空閒空間,直接再後面插入一條新的資料,再把舊行和新行形成連結串列。

這樣不需要更新索引,也可以快速找到更新後的資料。

如圖所示更新data1, 將新資料填入該block空閒部分,再將data1->next 指向新資料。

 

表繼承

類似於C++等面向物件的語言中類的繼承,PostgreSql支援表繼承。

Select父表,會顯示其下所有子表的資料。

舉例,父表computer, 子表有dell和hasee。

testdb=# select * from only computer;
 name  |   cpu    | memory 
-------+----------+--------
 oldPC | i5-4570k |     12
(1 row)

testdb=# select * from computer;
     name      |    cpu    | memory 
---------------+-----------+--------
 oldPC         | i5-4570k  |     12
 laptop_ck     | i5-8250u  |     12
 andoverlaptop | i7-7700hq |     16
 gamebook      | i7-8579H  |      8
(4 rows)

testdb=# 
testdb=# 
testdb=# select * from dell;
     name      |    cpu    | memory |    model    
---------------+-----------+--------+-------------
 laptop_ck     | i5-8250u  |     12 | 15E8525
 andoverlaptop | i7-7700hq |     16 | inspire5000
(2 rows)

testdb=# select * from hasee;
   name   |   cpu    | memory |  model   | usedfor | graphisc 
----------+----------+--------+----------+---------+----------
 gamebook | i7-8579H |      8 | z7m-kp7s | Game    | 1050Ti
(1 row)

testdb=# \d+ computer
                       Table "public.computer"
 Column |  Type   | Modifiers | Storage  | Stats target | Description 
--------+---------+-----------+----------+--------------+-------------
 name   | text    |           | extended |              | 
 cpu    | text    |           | extended |              | 
 memory | integer |           | plain    |              | 
Child tables: dell,
              hasee

testdb=# 

分割槽表

分割槽表是通過繼承來實現。 父表不定義任何約束條件,N個子表與父表一樣結構,子表加約束條件。

按照一定規則,把資料分別儲存到不同的子表裡面去。

分割槽表的好處是啥?

1. 按時間分割槽,刪除歷史資料很快,舊資料也可以遷移到便宜慢速的儲存介質
2. 按時間分割槽,近時間高頻使用的分割槽表索引可以完全快取到記憶體
3. 一個分割槽是集中的,針對一個分割槽的操作,不會像大表一樣離散

建立步驟:
1. 建立父表,不定義任何檢查約束
2. 建立幾個字表,一般不增加欄位
3. 分割槽表增加約束
4. 分割槽表字段增加索引
5. 定義規則or觸發器,對主表的操作重定向到分割槽表。這一點很像面向物件的多型。

分割槽表操作

建立父表


testdb=# create table mileage_detail(
testdb(# plate_number varchar(10) not null,
testdb(# mdate         date not null,
testdb(# mileage     int not null,
testdb(# brand   varchar(10),
testdb(# model      varchar (10),
testdb(# PRIMARY KEY(plate_number, mdate)
testdb(# );
CREATE TABLE
testdb=# 
testdb=# \d
             List of relations
 Schema |      Name      | Type  |  Owner   
--------+----------------+-------+----------
 public | computer       | table | postgres
 public | dell           | table | postgres
 public | hasee          | table | postgres
 public | mileage_detail | table | postgres
 public | t1             | table | postgres
 public | t2             | table | postgres
(6 rows)

testdb=# 
testdb=# \d+ mileage_detail
                              Table "public.mileage_detail"
    Column    |         Type          | Modifiers | Storage  | Stats target | Description 
--------------+-----------------------+-----------+----------+--------------+-------------
 plate_number | character varying(10) | not null  | extended |              | 
 mdate        | date                  | not null  | plain    |              | 
 mileage      | integer               | not null  | plain    |              | 
 brand        | character varying(10) |           | extended |              | 
 model        | character varying(10) |           | extended |              | 
Indexes:
    "mileage_detail_pkey" PRIMARY KEY, btree (plate_number, mdate)

testdb=# 
testdb=# 

建立子表

create table mileage_detail_y2018m09(CHECK (mdate >= DATE '2018-09-01' 
AND mdate < DATE '2018-10-01')) INHERITS(mileage_detail);

create table mileage_detail_y2018m10(CHECK (mdate >= DATE '2018-10-01' 
AND mdate < DATE '2018-11-01')) INHERITS(mileage_detail);

create table mileage_detail_y2018m11(CHECK (mdate >= DATE '2018-11-01' 
AND mdate < DATE '2018-12-01')) INHERITS(mileage_detail);

testdb=# \d+ mileage_detail
                              Table "public.mileage_detail"
    Column    |         Type          | Modifiers | Storage  | Stats target | Description 
--------------+-----------------------+-----------+----------+--------------+-------------
 plate_number | character varying(10) | not null  | extended |              | 
 mdate        | date                  | not null  | plain    |              | 
 mileage      | integer               | not null  | plain    |              | 
 brand        | character varying(10) |           | extended |              | 
 model        | character varying(10) |           | extended |              | 
Indexes:
    "mileage_detail_pkey" PRIMARY KEY, btree (plate_number, mdate)
PROCEDURE mileage_detail_insert_trigger()
Child tables: mileage_detail_y2018m09,
              mileage_detail_y2018m10,
              mileage_detail_y2018m11

建立子表索引

CREATE INDEX mileage_detail_y2018m09_mdate ON mileage_detail_y2018m09 (mdate);
CREATE INDEX mileage_detail_y2018m10_mdate ON mileage_detail_y2018m10 (mdate);
CREATE INDEX mileage_detail_y2018m11_mdate ON mileage_detail_y2018m11 (mdate);

建立觸發器:

觸發函式

CREATE OR REPLACE FUNCTION mileage_detail_insert_trigger()
RETURNS TRIGGER AS $$
BEGIN
IF (NEW.mdate >= DATE '2018-09-01' AND NEW.mdate < DATE '2018-10-01') THEN
INSERT INTO mileage_detail_y2018m09 VALUES (NEW.*);
ELSIF (NEW.mdate >= DATE '2018-10-01' AND NEW.mdate < DATE '2018-11-01') THEN
INSERT INTO mileage_detail_y2018m10 VALUES (NEW.*);
ELSIF (NEW.mdate >= DATE '2018-11-01' AND NEW.mdate < DATE '2018-12-01') THEN
INSERT INTO mileage_detail_y2018m11 VALUES (NEW.*);
ELSE
RAISE EXCEPTION 'Date out of range. Fix the mileage_detail_insert_trigger function';
END IF;
RETURN NULL;
END;
$$
LANGUAGE plpgsql;

觸發器

觸發器
CREATE TRIGGER insert_mileage_detail_trigger
	BEFORE INSERT ON mileage_detail
	FOR EACH ROW EXECUTE PROCEDURE mileage_detail_insert_trigger ();
	

testdb=# \d+ mileage_detail
                              Table "public.mileage_detail"
    Column    |         Type          | Modifiers | Storage  | Stats target | Description 
--------------+-----------------------+-----------+----------+--------------+-------------
 plate_number | character varying(10) | not null  | extended |              | 
 mdate        | date                  | not null  | plain    |              | 
 mileage      | integer               | not null  | plain    |              | 
 brand        | character varying(10) |           | extended |              | 
 model        | character varying(10) |           | extended |              | 
Indexes:
    "mileage_detail_pkey" PRIMARY KEY, btree (plate_number, mdate)
Triggers:
    insert_mileage_detail_trigger BEFORE INSERT ON mileage_detail FOR EACH ROW EXECUTE PROCEDURE mileage_detail_insert_trigger()
Child tables: mileage_detail_y2018m09,
              mileage_detail_y2018m10,
              mileage_detail_y2018m11

testdb=# 
testdb=# 

驗證觸發器分表

testdb=# 
testdb=# INSERT INTO mileage_detail VALUES('AJ1E08', DATE '2018-09-05', 10000, 'toyota', 'corolla');
INSERT 0 0
testdb=# INSERT INTO mileage_detail VALUES('AJ1E08', DATE '2018-09-07', 10200, 'toyota', 'corolla');
INSERT 0 0
testdb=# 
testdb=# 
testdb=# INSERT INTO mileage_detail VALUES('AJ1E08', DATE '2018-10-22', 11200, 'toyota', 'corolla');
INSERT 0 0
testdb=# 
testdb=# INSERT INTO mileage_detail VALUES('AJ1E08', DATE '2018-11-21', 12200, 'toyota', 'corolla');
INSERT 0 0
testdb=# 
testdb=# select * from mileage_detail
testdb-# ;
 plate_number |   mdate    | mileage | brand  |  model  
--------------+------------+---------+--------+---------
 AJ1E08       | 2018-09-05 |   10000 | toyota | corolla
 AJ1E08       | 2018-09-07 |   10200 | toyota | corolla
 AJ1E08       | 2018-10-22 |   11200 | toyota | corolla
 AJ1E08       | 2018-11-21 |   12200 | toyota | corolla
(4 rows)

testdb=# select * from mileage_detail
mileage_detail           mileage_detail_y2018m09  mileage_detail_y2018m10  mileage_detail_y2018m11
testdb=# select * from mileage_detail_y2018m09 
testdb-# ;
 plate_number |   mdate    | mileage | brand  |  model  
--------------+------------+---------+--------+---------
 AJ1E08       | 2018-09-05 |   10000 | toyota | corolla
 AJ1E08       | 2018-09-07 |   10200 | toyota | corolla
(2 rows)

testdb=# select * from mileage_detail_y2018m10;
 plate_number |   mdate    | mileage | brand  |  model  
--------------+------------+---------+--------+---------
 AJ1E08       | 2018-10-22 |   11200 | toyota | corolla
(1 row)

testdb=# select * from mileage_detail_y2018m11;
 plate_number |   mdate    | mileage | brand  |  model  
--------------+------------+---------+--------+---------
 AJ1E08       | 2018-11-21 |   12200 | toyota | corolla
(1 row)

testdb=# 

分割槽表貌似實現了多型,按照觸發規則自動分類資料,便於日後資料的操作。

 

小結:

這一篇介紹了對於行外儲存COAST表,分割槽表等內容。後面將學習事件觸發器、表空間、檢視等。