80

私は次のような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)
);

上記の合計は、1行あたり最大50バイトです。私の経験では、上記のユーザー作成インデックスさえなくても、システムオーバーヘッドのためにさらに40%から50%が必要です。したがって、1行あたり約75バイトです。テーブルには非常に多くの行があり、場合によっては1,450億行を超える可能性があるため、テーブルは13〜14テラバイトをプッシュします。このテーブルを圧縮するために、もしあれば、どのようなトリックを使用できますか?以下の私の考えられるアイデア...

real値をに変換しますinteger。として保存できる場合smallintは、フィールドごとに2バイト節約できます。

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

SELECT a, arr[5] FROM t;

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

他のアイデアはありますか?

4

4 に答える 4

230

「コラムテトリス」

実際、何かを行うことはできますが、これにはより深い理解が必要です。キーワードは配置パディングです。すべてのデータ型には、特定の整列要件があります

列を優先的に並べることで、列間のパディングで失われるスペースを最小限に抑えることができます。次の (極端な) 例では、多くの物理ディスク領域が浪費されます。

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

ここでdb<>fiddle
古いsqlfiddle

経験則として、最初に 8 バイトの列を配置し、次に 4 バイト、2 バイト、および 1 バイトの列を最後に配置しても問題ありません。

booleanuuid(!) およびその他のいくつかのタイプは、配置パディングを必要としません。textvarcharおよびその他の "varlena" (可変長) 型では、名目上、"int" アライメント (ほとんどのマシンで 4 バイト) が必要です。しかし、ディスク形式では (RAM とは異なり) アラインメント パディングは見られませんでした。最終的に、ソースコードのメモに説明が見つかりました。

また、「パックされた」varlenas を保存するときに、公称アライメントに違反することを許可していることにも注意してください。TOAST メカニズムは、ほとんどのコードからそれを隠します。

そのため、「int」アラインメントは、先頭の単一の長さバイトを含む (おそらく圧縮された) データが 127 バイトを超える場合にのみ適用されます。次に、varlena ストレージは先頭の 4 バイトに切り替わり、「int」アラインメントが必要になります。

通常、 「列テトリス」をプレイすると、せいぜい行ごとに数バイト節約できます。ほとんどの場合、これは必要ありません。しかし、何十億もの行があると、簡単に数ギガバイトになる可能性があります。

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

最後に、一部の型は圧縮または "トースト" (行外に格納)、またはその両方を行うことができます。

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

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

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

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

最終的な回答を得るには、 Postgres バイナリ ディレクトリで次のコマンドを実行します。

./pg_controldata /path/to/my/dbcluster

マニュアル:

実際のユーザー データ (行の列) は、 で示されるオフセットから始まります。オフセットは、常に プラットフォームの距離t_hoffの倍数でなければなりません。MAXALIGN

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

あなたが投稿した例には何もありません。もうぎゅっと詰まっています。最後の の後に 2 バイトのパディング、最後int2に 4 バイト。最後にパディングを 6 バイトに統合できますが、何も変更されません。

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

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

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

配列型?

評価していたような配列型では、型に24 バイトのオーバーヘッドが追加されます。さらに、配列要素は通常どおりスペースを占有します。そこで得られるものは何もありません。

于 2011-09-15T13:14:07.477 に答える
11

複数の数値フィールドを配列に格納しても、得るものは何もありません (失うものもありません)。

各数値タイプのサイズは明確に文書化されています。目的の範囲解像度と互換性のある最小サイズのタイプを使用するだけです。できることはこれだけです。

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

ところで、行ごとに約23 バイトの固定オーバーヘッドがあります。

于 2010-06-03T14:01:10.503 に答える
0

Erwin の列の並べ替えの提案に関するクールなツールは次のとおりです: https://github.com/NikolayS/postgres_dba

そのための正確なコマンドがあります -- p1:

ここに画像の説明を入力

次に、すべてのテーブルで列を並べ替える本当の可能性を自動的に示します。

ここに画像の説明を入力

于 2022-01-29T12:25:17.933 に答える