web-dev-qa-db-ja.com

PostgreSQLでのスペースの計算と節約

私はpgのようにテーブルを持っています:

CREATE TABLE t (
    a BIGSERIAL NOT NULL,               -- 8 b
    b SMALLINT,                         -- 2 b
    c SMALLINT,                         -- 2 b
    d REAL,                             -- 4 b
    e REAL,                             -- 4 b
    f REAL,                             -- 4 b
    g INTEGER,                          -- 4 b
    h REAL,                             -- 4 b
    i REAL,                             -- 4 b
    j SMALLINT,                         -- 2 b
    k INTEGER,                          -- 4 b
    l INTEGER,                          -- 4 b
    m REAL,                             -- 4 b
    CONSTRAINT a_pkey PRIMARY KEY (a)
);

上記では、行ごとに最大50バイトが追加されます。私の経験では、システムオーバーヘッドのためにさらに40%から50%が必要です。ユーザーが作成した上記のインデックスもありません。つまり、行あたり約75バイトです。テーブルには多数の行があり、1450億行を超える可能性があるため、テーブルは13〜14テラバイトをプッシュします。この表を圧縮するために使用できるトリックがあれば教えてください。以下の私の考え...

real値をintegerに変換します。それらをsmallintとして保存できる場合、フィールドあたり2バイトの節約になります。

列b .. mを配列に変換します。これらの列を検索する必要はありませんが、一度に1つの列の値を返すことができる必要があります。したがって、列gが必要な場合、次のようなことができます

SELECT a, arr[5] FROM t;

配列オプションでスペースを節約できますか?速度ペナルティはありますか?

他のアイデアは?

62
punkish

いくつかの数値フィールドを配列に格納することで、何も得られる(そして失うもの)はありません。

各数値タイプのサイズ は明確に文書化されています。必要な範囲解像度と互換性のある最小サイズのタイプを使用してください。そして、それはあなたができるすべてのことです。

行に沿った列にバイトアライメントの要件があるかどうかはわかりませんが(その場合はわかりません)、その場合、列の並べ替えによって使用されるスペースが変わる可能性がありますが、そうは思いません。

ところで、行ごとに修正オーバーヘッドがあり、約 23バイト です。

11
leonbloy

「コラムテトリス」

実際、あなたは something を行うことができますが、これはより深い理解が必要です。キーワードは alignment padding です。 すべてのデータ型には特定の配置要件があります

パディングによって失われるスペース between columns を優先的に並べることで、スペースを最小化できます。次の(極端な)例は、多くの物理ディスク領域を浪費します。

_CREATE TABLE t (
    e int2    -- 6 bytes of padding after int2
  , a int8
  , f int2    -- 6 bytes of padding after int2
  , b int8
  , g int2    -- 6 bytes of padding after int2
  , c int8
  , h int2    -- 6 bytes of padding after int2
  , d int8)
_

24バイト行ごとに保存するには、代わりに次を使用します。

_CREATE TABLE t (
    a int8
  , b int8
  , c int8
  , d int8
  , e int2
  , f int2
  , g int2
  , h int2)   -- 4 int2 occupy 8 byte (MAXALIGN), no padding at the end
_

SQLフィドル。

経験則として、8バイトの列を最初に配置し、次に4バイト、2バイト、1バイトの列を最後に配置すると、間違いはありません。

booleanuuidおよび他のいくつかのタイプでは、位置合わせのパディングは必要ありません。 textvarcharおよびその他の "varlena"(可変長)タイプ nominally には、 "int"アライメントが必要です(ほとんどのマシンでは4バイト)。しかし、実際には(RAMとは異なり)ディスク形式では位置合わせパディングはありません。多くのテストで確認しました。最終的に、私は ソースコードの注記: で説明を見つけました

「パックされた」ヴァレナを格納するときに、名目上の配置に違反することを許可することにも注意してください。

通常、最高の再生 "column tetris" で、行ごとに数バイトを節約できます。ほとんどの場合、これは必要ありません。しかし、数十億行の場合、数ギガバイトを簡単に意味する可能性があります。

関数 pg_column_size() を使用して、実際の列/行のサイズをテストできます。
一部のタイプは、ディスク上のよりも多くのスペースを占有しますRAM(圧縮または「パック」形式)。テストすると、定数(RAM形式)の方がテーブル列よりも大きな結果を得ることができますpg_column_size()と同じ値(または値の行と表の行)。

最後に、一部のタイプは 圧縮または「トースト」 (行外に格納)またはその両方にすることができます。

タプルあたりのオーバーヘッド(行)

アイテムポインタの1行あたり4バイト-上記の考慮事項の対象ではありません。
タプルヘッダー用に少なくとも24バイト(23 +パディング)。 データベースページレイアウトのマニュアル:

固定サイズのヘッダー(ほとんどのマシンで23バイトを占める)があり、その後にオプションのnullビットマップ、オプションのオブジェクトIDフィールド、およびユーザーデータが続きます。

ヘッダーとユーザーデータの間のパディングについては、サーバーのMAXALIGNを知っている必要があります。通常、64ビットOSでは8バイト(32ビットOSでは4バイト)です。わからない場合は、 _pg_controldata_ を確認してください。

Postgresバイナリdir で次のコマンドを実行して、明確な回答を取得します。

_./pg_controldata /path/to/my/dbcluster
_

マニュアル:

実際のユーザーデータ(行の列)は、_t_hoff_で示されるオフセットから始まります。これは、プラットフォームのMAXALIGN距離の倍数である必要があります。

したがって、通常、データを8バイトの倍数でパックすることにより、最適なストレージを取得します。

あなたが投稿した example で得るものは何もありません。すでにしっかりと詰め込まれています。最後の_int2_の後に2バイトのパディング、最後に4バイト。最後にパディングを6バイトに統合しても、何も変更されません。

データページあたりのオーバーヘッド

データページサイズは通常8 KBです。このレベルでもオーバーヘッド/膨張:残りの部分は別のタプルに収まるほど大きくなく、さらに重要なのはデッド行または FILLFACTOR設定 で予約されたパーセンテージ。

考慮すべきディスク上のサイズには、他にもいくつかの要因があります。

配列タイプ?

評価するようなarrayタイプの場合、タイプに24バイトのオーバーヘッドを追加します。さらに、配列要素は通常どおりスペースを占有します。そこに得るものは何もない。

174