PostgresのTOAST技術

9197 ワード

一、紹介まず、Toastは名前の略語で、全書はThe OverSized Attribute Storage Technique、すなわち超サイズフィールド記憶技術であり、その名の通り、超長フィールドはPostgresの記憶方式である.Postgresが採用するストレージのデフォルトは、各ページに固定8 Kbサイズのデータを格納し、メタグループはページ間ストレージを許可しないため、大きなフィールドデータを直接格納することはできません.Toastは、大きなフィールド値を圧縮したり、複数の物理行に分散したりして格納します.ユーザーにとってこのテクノロジーの実装に全く注目する必要はなく、完全に透明です.二、TOASTのストレージ方式Postgresの一部のタイプのデータはtoastをサポートしているが、すべてのタイプではないのは、一部のフィールドタイプでは大きなフィールドデータが生成されないためであり、Toast技術(例えばdate、time、booleanなど)を全く使用する必要はない.Toastをサポートするデータ型は長くなるべきであり、長くなるフィールドは最大32 bitのヘッダを選択することができ、Toastは2つの長くなるbitビットをFLAGとして占有するため、Toastの論理サイズ制限は(2^30-1)~1 GBであり、2つのbitが0である場合、このデータ型の値はToastではない(untoasted).テーブルのフィールドのいずれかにToastがある場合、このテーブルには関連するToastテーブルがあり、OIDはpg_に格納される.class.reltoastrelidの中にあります.超えた数値はchunksに分割され、最大toast_max_chunk_size個byte(デフォルトは2 Kb)で、格納された行データがtoast_を超えるとtuple_threshold値(通常は2 kB)はtoastストレージをトリガーし、toastはtoastよりも部分的に大きいまでフィールド値を圧縮または移動します.tuple_targer値は小さい(この値も通常2 KB).一般テーブル(MAIN TABLE)と比較すると、TOASTには追加の3つのフィールドがあります.
chunk_id   : TOAST OID 
chunk_seq  :chunk , chunk_id 
chunk_data : TOAST 

Toastには、4つの異なるストレージ可能なtoastを識別するポリシーがあります.
--plain 
PLAIN prevents either compression or out-of-line storage; furthermore it disables use of single-byte headers for varlena types. This is the only possible strategy for columns of non-TOAST-able data types

--extended ( toast )
EXTENDED allows both compression and out-of-line storage. This is the default for most TOASTable data types. Compression will be attempted first, then out-of-line storage if the row is still too big

--external 
EXTERNAL allows out-of-line storage but not compression. Use of EXTERNAL will make substring operations on wide text and bytea columns faster(at the penalty of increased storage space) because these operations are optimized to fetch only the required parts of the out-of-line value when it is not compressed

--main 
MAIN allows compression but not out-of-line storage. (Actually, out-of-line storage will still be performed for such columns, but only as a last resort when there is no other way to make the row small enough to fit on a page

上記圧縮はLZ compression技術を採用しており、ソースコードはpostgresql-9.23/src/backend/utils/adt/pg_lzcompress.c TOASTはSQLでストレージ方式を変更できます.例:
ALTER TABLE table_name ALTER COLUMN  column_name SET STORAGE {PLAIN | EXTENDED | MAIN | EXTERNAL};

postgres=# \d+ t_kenyon
                            Table "public.t_kenyon"
Column |       Type        | Modifiers | Storage  | Stats target | Description
--------+-------------------+-----------+----------+--------------+-------------
dd     | character varying |           | extended |              |
Has OIDs: no

postgres=# alter table t_kenyon alter column dd set storage main;
ALTER TABLE
postgres=# \d+ t_kenyon
                            Table "public.t_kenyon"
Column |       Type        | Modifiers | Storage | Stats target | Description
--------+-------------------+-----------+---------+--------------+-------------
dd     | character varying |           | main    |              |
Has OIDs: no

三、TOAST表の計算一つの表の大きさを計算する時、統計Toastの大きさに注意しなければならない.超長フィールドを保存する時、基礎表に20%しか保存していない可能性があるので、ほかのデータはすべてToastの中に保存した.サイズを計算する時、結合して見ると、インデックスも同じで、表にmainやextendedタイプがある場合はToast表を作成し、両者の関連はpg_classのOIDが関連付けられています.次に例を示す:1.TOASTテーブル関連クエリーExample a:
[postgres@localhost ~]$ psql
psql (9.2.3)
Type "help" for help.

postgres=# create table t_kenyon(id int);
CREATE TABLE

postgres=# select relname,reltoastrelid from pg_class where relname = 't_kenyon';
relname  | reltoastrelid
----------+---------------
t_kenyon |             0
(1 row)

postgres=# \d+ t_kenyon
                       Table "public.t_kenyon"
Column |  Type   | Modifiers | Storage | Stats target | Description
--------+---------+-----------+---------+--------------+-------------
id     | integer |           | plain   |              |
Has OIDs: no

上のフィールドにはtoastテーブルはありません.フィールドintが一定長であるためです. Example b:
postgres=# select relname,reltoastrelid from pg_class where relname = 't_kenyon';
relname  | reltoastrelid
----------+---------------
t_kenyon |         16411
(1 row)

postgres=# select relname from pg_class where oid = 16411;
    relname    
----------------
pg_toast_16408
(1 row)

2.TOAST表計算サイズ
postgres=# drop table t_kenyon;
DROP TABLE
postgres=# create table t_kenyon(id int,vname varchar(48),remark text);
CREATE TABLE
postgres=# \d+ t_kenyon
                              Table "public.t_kenyon"
Column |         Type          | Modifiers | Storage  | Stats target | Description
--------+-----------------------+-----------+----------+--------------+-------------
id     | integer               |           | plain    |              |
vname  | character varying(48) |           | extended |              |
remark | text                  |           | extended |              |
Has OIDs: no

postgres=# select oid,relname,reltoastrelid from pg_class where relname = 't_kenyon';
  oid   | relname  | reltoastrelid
--------+----------+---------------
121174 | t_kenyon |        121177
(1 row)

postgres=# insert into t_kenyon select generate_series(1,2000),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God',500);
INSERT 0 2000

postgres=# insert into t_kenyon select generate_series(1,2),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',2000);
INSERT 0 2
postgres=# select pg_column_size(id),pg_column_size(vname),pg_column_size(remark) from t_kenyon limit 2;
pg_column_size | pg_column_size | pg_column_size
----------------+----------------+----------------
              4 |             29 |            851
              4 |             29 |            851
(2 rows)

-- Toast 
postgres=# select pg_relation_size(121174);
pg_relation_size
------------------
             8192
(1 row)

postgres=# select pg_relation_size(121177);
pg_relation_size
------------------
                0
(1 row)

-- , toast 2kb 
postgres=# insert into t_kenyon select generate_series(3,4),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',4000);
INSERT 0 2
postgres=# select pg_relation_size(121174);
pg_relation_size
------------------
             8192
(1 row)

postgres=# select pg_relation_size(121177);
pg_relation_size
------------------
                0
(1 row)


postgres=# insert into t_kenyon select generate_series(5,6),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',5500);
INSERT 0 2
postgres=# select pg_column_size(id),pg_column_size(vname),pg_column_size(remark) from t_kenyon;
pg_column_size | pg_column_size | pg_column_size
----------------+----------------+----------------
              4 |             29 |            851
              4 |             29 |            851
              4 |             29 |           1651
              4 |             29 |           1651
              4 |             29 |           2247
              4 |             29 |           2247
(6 rows)

postgres=# insert into t_kenyon select generate_series(1,2),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',10000);
INSERT 0 2
postgres=# select pg_relation_size(121174);
pg_relation_size
------------------
             8192
(1 row)

postgres=# select pg_relation_size(121177);
pg_relation_size
------------------
            16384
(1 row)

postgres=# insert into t_kenyon select generate_series(7,8),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',20000);
INSERT 0 2
postgres=# select id,pg_column_size(id),pg_column_size(vname),pg_column_size(remark) from t_kenyon;
id | pg_column_size | pg_column_size | pg_column_size
----+----------------+----------------+----------------
  1 |              4 |             29 |            851
  2 |              4 |             29 |            851
  3 |              4 |             29 |           1651
  4 |              4 |             29 |           1651
  5 |              4 |             29 |           2247
  6 |              4 |             29 |           2247
  7 |              4 |             29 |           8056
  8 |              4 |             29 |           8056
(8 rows)

postgres=# select pg_relation_size(121174);
pg_relation_size
------------------
             8192
(1 row)

postgres=# select pg_relation_size(121177);
pg_relation_size
------------------
            24576
(1 row)

後に挿入されるデータは、フィールドの内容が増えるにつれてtoastセグメントが大きくなっていることがわかります.これは、Oracleが格納している大きなフィールドの内容と比較して、OracleがBlobクラスのデータを格納する場合も、元のテーブルに格納するのではなく、別のsegmentを指定して格納することができます.もちろん、enable storage in rowを設定してテーブルに格納することができます.したがって、Oracleのテーブルが異常に大きい場合は、水位線が高すぎるのではなく、テーブルフィールドに大きなデータ型が格納されているのが一般的です.四、TOASTの長所と短所1.これまで直接保存できなかった制限を回避するために、超大型フィールドを格納することができる.物理的に通常のテーブルとは分離する、クエリを取得するときにこのフィールドを取得しないと、非常に高速になります.通常テーブルを更新する場合、そのテーブルのToastデータが更新されていない場合、ToastテーブルToastを更新する必要がないという劣勢:1.大きなフィールドのインデックス作成は問題であり、失敗する可能性がありますが、通常は大きなフィールドで作成することをお勧めしません.全文検索は解決策です. 2.大きいフィールドの更新は少し遅くなりますが、他のDBも存在します.