騰訊雲技術社區-掘金主頁持續爲你們呈現雲計算技術文章,歡迎你們關注!javascript
做者介紹:胡彬 騰訊雲高級工程師html
TOAST是「The Oversized-Attribute Storage Technique」的縮寫,主要用於存儲一個大字段的值。要理解TOAST,咱們要先理解頁(BLOCK)的概念。在PG中,頁是數據在文件存儲中的基本單位,其大小是固定的且只能在編譯期指定,以後沒法修改,默認的大小爲8KB。同時,PG不容許一行數據跨頁存儲,那麼對於超長的行數據,PG就會啓動TOAST,具體就是採用壓縮和切片的方式。若是啓用了切片,實際數據存儲在另外一張系統表的多個行中,這張表就叫TOAST表,這種存儲方式叫行外存儲。java
在深刻細節以前,咱們要先了解,在PG中每一個表字段有四種TOAST的策略:node
PLAIN:避免壓縮和行外存儲。只有那些不須要TOAST策略就能存放的數據類型容許選擇(例如int類型),而對於text這類要求存儲長度超過頁大小的類型,是不容許採用此策略的sql
EXTENDED:容許壓縮和行外存儲。通常會先壓縮,若是仍是太大,就會行外存儲post
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表負責行外存儲,那麼這張表在哪裏?spa
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策略,不會影響現有數據的存儲方式
相關閱讀:
存儲總量達20T的MySQL實例,如何完成遷移?
騰訊存儲與雲存儲Redis免費體驗
此文已由做者受權騰訊雲技術社區發佈,轉載請註明文章出處
原文連接:www.qcloud.com/community/a…
獲取更多騰訊海量技術實踐乾貨,歡迎你們前每每騰訊雲技術社區