PostgreSQL TOAST 技術解析
轉載:https://cloud.tencent.com/developer/article/1004455
TOAST是“The Oversized-Attribute Storage Technique”的縮寫,主要用於儲存一個大欄位的值。要理解TOAST,我們要先理解頁(BLOCK)的概念。在PG中,頁是資料在檔案儲存中的基本單位,其大小是固定的且只能在編譯期指定,之後無法修改,預設的大小為8KB。同時,PG不允許一行資料跨頁儲存,那麼對於超長的行資料,PG就會啟動TOAST,具體就是採用壓縮和切片的方式。如果啟用了切片,實際資料儲存在另一張系統表的多個行中,這張表就叫TOAST表,這種儲存方式叫行外儲存。
在深入細節之前,我們要先了解,在PG中每個表欄位有四種TOAST的策略:
PLAIN:避免壓縮和行外儲存。只有那些不需要TOAST策略就能存放的資料型別允許選擇(例如int型別),而對於text這類要求儲存長度超過頁大小的型別,是不允許採用此策略的
EXTENDED:允許壓縮和行外儲存。一般會先壓縮,如果還是太大,就會行外儲存
EXTERNA:允許行外儲存,但不許壓縮。類似字串這種會對資料的一部分進行操作的欄位,採用此策略可能獲得更高的效能,因為不需要讀取出整行資料再解壓。
MAIN:允許壓縮,但不許行外儲存。不過實際上,為了保證過大資料的儲存,行外儲存在其它方式(例如壓縮)都無法滿足需求的情況下,作為最後手段還是會被啟動。因此理解為:儘量不使用行外儲存更貼切。 現在我們通過實際操作來研究TOAST的細節:
首先建立一張blog表:
postgres=# create table blog(id int, title text, content text);
CREATE TABLE
postgres=# \d+ blog;
Table "public.blog"
Column | Type | Modifiers | Storage | Stats target | Description
---------+---------+-----------+----------+--------------+-------------
id | integer | | plain | |
title | text | | extended | |
content | text | | extended | |
可以看到,interger預設TOAST策略為plain,而text為extended。PG資料告訴我們,如果表中有欄位需要TOAST,那麼系統會自動建立一張TOAST表負責行外儲存,那麼這張表在哪裡?
postgres=# select relname,relfilenode,reltoastrelid from pg_class where relname='blog';
relname | relfilenode | reltoastrelid
---------+-------------+---------------
blog | 16441 | 16444
(1 row)
通過上訴語句,我們查到blog表的oid為16441,其對應TOAST表的oid為16444(關於oid和pg_class的概念,請參考PG官方文件),那麼其對應TOAST表名則為:pg_toast.pg_toast_16441(注意這裡是blog表的oid),我們看下其定義:
postgres=# \d+ pg_toast.pg_toast_16441;
TOAST table "pg_toast.pg_toast_16441"
Column | Type | Storage
------------+---------+---------
chunk_id | oid | plain
chunk_seq | integer | plain
chunk_data | bytea | plain
TOAST表有3個欄位:
chunk_id:用來表示特定TOAST值的OID,可以理解為具有同樣chunk_id值的所有行組成原表(這裡的blog)的TOAST欄位的一行資料
chunk_seq:用來表示該行資料在整個資料中的位置
chunk_data:實際儲存的資料。 現在我們來實際驗證下:
postgres=# insert into blog values(1, 'title', '0123456789');
INSERT 0 1
postgres=# select * from blog;
id | title | content
----+-------+------------
1 | title | 0123456789
(1 row)
postgres=# select * from pg_toast.pg_toast_16441;
chunk_id | chunk_seq | chunk_data
----------+-----------+------------
(0 rows)
可以看到因為content只有10個字元,所以沒有壓縮,也沒有行外儲存。然後我們使用如下SQL語句增加content的長度,每次增長1倍,同時觀察content的長度,看看會發生什麼情況?
postgres=# update blog set content=content||content where id=1;
UPDATE 1
postgres=# select id,title,length(content) from blog;
id | title | length
----+-------+--------
1 | title | 20
(1 row)
postgres=# select * from pg_toast.pg_toast_16441;
chunk_id | chunk_seq | chunk_data
----------+-----------+------------
(0 rows)
反覆執行如上過程,直到pg_toast_16441表中有資料:
postgres=# select id,title,length(content) from blog;
id | title | length
----+-------+--------
1 | title | 327680
(1 row)
postgres=# select chunk_id,chunk_seq,length(chunk_data) from pg_toast.pg_toast_16441;
chunk_id | chunk_seq | length
----------+-----------+--------
16439 | 0 | 1996
16439 | 1 | 1773
(2 rows)
可以看到,直到content的長度為327680時(已遠遠超過頁大小8K),對應TOAST表中才有了2行資料,且長度都是略小於2K,這是因為extended策略下,先啟用了壓縮,然後才使用行外儲存
下面我們將content的TOAST策略改為EXTERNA,以禁止壓縮。
postgres=# alter table blog alter content set storage external;
ALTER TABLE
postgres=# \d+ blog;
Table "public.blog"
Column | Type | Modifiers | Storage | Stats target | Description
---------+---------+-----------+----------+--------------+-------------
id | integer | | plain | |
title | text | | extended | |
content | text | | external | |
然後我們再插入一條資料:
postgres=# insert into blog values(2, 'title', '0123456789');
INSERT 0 1
postgres=# select id,title,length(content) from blog;
id | title | length
----+-------+--------
1 | title | 327680
2 | title | 10
(2 rows)
然後重複以上步驟,直到TOAST表中產生新的行:
postgres=# update blog set content=content||content where id=2;
UPDATE 1
postgres=# select id,title,length(content) from blog;
id | title | length
----+-------+--------
2 | title | 2560
1 | title | 327680
(2 rows)
postgres=# select chunk_id,chunk_seq,length(chunk_data) from pg_toast.pg_toast_16441;
chunk_id | chunk_seq | length
----------+-----------+--------
16447 | 0 | 1996
16447 | 1 | 1773
16448 | 0 | 1996
16448 | 1 | 564
(4 rows)
這次我們看到當content長度達到2560(按照官方文件,應該是超過2KB左右),TOAST表中產生了新的2條chunk_id為16448的行,且2行資料的chunk_data的長度之和正好等於2560。通過以上操作得出以下結論:
- 如果策略允許壓縮,則TOAST優先選擇壓縮
- 不管是否壓縮,一旦資料超過2KB左右,就會啟用行外儲存
- 修改TOAST策略,不會影響現有資料的儲存方式
相關文章
- 如何理解postgresql toast表SQLAST
- PostgreSQL資料庫toast表損壞解決SQL資料庫AST
- DNS隧道技術解析DNS
- 技術乾貨 | WebRTC 技術解析之 Android VDMWebAndroid
- PostgreSQL技術大講堂 - Part 2:PostgreSQL原始碼安裝SQL原始碼
- PostgreSQL技術內幕(七)索引掃描SQL索引
- bitmap技術解析:redis與roaringBitmapRedis
- 技術分享 | AlertManager 原始碼解析原始碼
- PostgreSQL技術週刊第2期:用PostgreSQL解海盜分金問題SQL
- PostgreSQL技術週刊第20期:PostgreSQL何以支援豐富的NoSQL特性?SQL
- PostgreSQL的表檔案以及TOAST表檔案對應關係SQLAST
- 基於雲技術的域名解析系統研究:傳統解析技術的侷限性
- 技術前刊:PostgreSQL12 COPY和bulkloading提升SQL
- PostgreSQL 技術內幕(五)Greenplum-Interconnect模組SQL
- Hybrid App技術解析 — 實戰篇APP
- Hybrid App技術解析 -- 原理篇APP
- Hybrid App技術解析 — 原理篇APP
- Hybrid App技術解析 -- 實戰篇APP
- 智慧語音技術的深度解析
- 智慧雲解析有哪些核心技術?
- 視訊技術詳解:RTMP H5 直播流技術解析H5
- ToastAST
- PostgreSQL技術週刊第12期:PostgreSQL時空資料排程實踐SQL
- 基於雲技術的域名解析系統研究:雲解析技術的應用(國科雲)
- 技術解讀:Hadoop、PostgreSQL與Storm正面比拼報告!HadoopSQLORM
- 直播|PostgreSQL 技術內幕(五)Greenplum-Interconnect模組SQL
- 2018深入解析Android熱修復技術Android
- 《現代前端技術解析》讀後鬼扯前端
- Android NFC技術解析,附Demo原始碼Android原始碼
- 【朝夕技術專刊】RabbitMQ路由解析(上篇)MQ路由
- OCR識別的技術流程解析1
- OCR識別的技術流程解析2
- 影片結構化技術棧全解析
- 匯入地址表鉤取技術解析
- 前端技術分享:JavaScript正則全面解析前端JavaScript
- PostgreSQL 14中TOAST的新壓縮演算法LZ4,它有多快?SQLAST演算法
- Tech Talk · 雲技術有話聊 | 深信服混合雲容災技術解析
- PostgreSQL技術大講堂 - 第31講:SQL調優技巧SQL