24

PostgreSQLでスパースデータマトリックスを表現する最良の方法は何ですか? 私が見る2つの明白な方法は次のとおりです。

  1. 考えられるすべてのフィーチャ (潜在的に数百万) に対して個別の列を持つ単一のテーブルにデータを格納しますが、未使用のフィーチャのデフォルト値は NULL です。これは概念的には非常に単純ですが、ほとんどの RDMS 実装では、通常、NULL 値がある程度のスペースを占めるため、これは通常非常に非効率的であることを知っています。ただし、PG は NULL 値のデータを取得しないため、スパース データの保存に適していると主張する記事 (残念ながらリンクが見つかりません) を読みました。

  2. 「行」テーブルと「列」テーブルを別々に作成し、中間テーブルを作成してそれらをリンクし、その行の列の値を格納します。これはより伝統的な RDMS ソリューションだと思いますが、それに伴う複雑さとオーバーヘッドが大きくなります。

また、スパース データをより適切にサポートすると主張するPostgreDynamicも見つけましたが、この機能のためだけにデータベース サーバー全体を PG フォークに切り替えたくありません。

他の解決策はありますか?どちらを使用する必要がありますか?

4

4 に答える 4

17

数学的な文脈からスパース行列を考えていると思います: http://en.wikipedia.org/wiki/Sparse_matrix (そこで説明されている保存手法は、永続ストレージ (低ディスク) ではなく、メモリ ストレージ (高速算術演算) 用です利用方法)。)

通常、サーバー側ではなくクライアント側でこの行列を操作するため、SQL-ARRAY[] が最適です。

問題は、マトリックスのスパース性をどのように利用するかです。ここで、いくつかの調査の結果を示します。

設定:

  • ポストグル 8.4
  • 倍精度 (8 バイト) の 400*400 要素を持つ行列 --> 行列ごとに 1.28MiB の raw サイズ
  • 33% の非ゼロ要素 --> 行列あたり 427kiB の有効サイズ
  • ~1000 の異なるランダムに入力された行列を使用して平均化

競合する方法:

  • SET STORAGE MAIN または EXTENDED を使用した列の自動サーバー側圧縮に依存します。
  • ゼロ以外の要素と、マトリックス内のゼロ以外の要素の場所を説明するビットマップ( )のみを格納します。bit varying(xx)(1 つの倍精度は 1 ビットの 64 倍です。理論的には (オーバーヘッドを無視して)、<=98% が非ゼロの場合、この方法は改善されるはずです ;-)) サーバー側の圧縮が有効になっています。
  • 行列のゼロを NULLに置き換えます。(RDBMS は NULL の格納に非常に効果的です。) サーバー側の圧縮がアクティブになります。

(2 番目の index-ARRAY[] を使用したゼロ以外の要素のインデックス付けはあまり有望ではないため、テストされていません。)

結果:

  • 自動圧縮
    • 追加の実装作業は不要
    • ネットワーク トラフィックが減少しない
    • 最小限の圧縮オーバーヘッド
    • 永続ストレージ = raw サイズの 39%
  • ビットマップ
    • 許容可能な実装作業
    • ネットワーク トラフィックがわずかに減少しました。スパース性に依存
    • 永続ストレージ = raw サイズの 33.9%
  • ゼロを NULL に置き換える
    • いくつかの実装作業 (API は、INSERT クエリの構築中に ARRAY[] のどこにどのように NULL を設定するかを知る必要があります)
    • ネットワークトラフィックに変化なし
    • 永続ストレージ = raw サイズの 35%

結論: EXTENDED/MAIN ストレージ パラメータから始めます。空き時間がある場合は、データを調査し、スパース レベルで私のテスト セットアップを使用してください。しかし、その効果はあなたが期待するよりも低いかもしれません。

マトリックスのシリアル化 (例: 行優先順) に加えて、マトリックスの次元 NxM に対して 2 つの整数列を常に使用することをお勧めします。ほとんどの API はテキスト SQL を使用するため、ネストされた "ARRAY[ARRAY[..], ARRAY[..], ARRAY[..], ARRAY[..], ..]" のネットワーク トラフィックとクライアント メモリを大幅に節約できます。 !!!

テバス


CREATE TABLE _testschema.matrix_dense
(
  matdata double precision[]
);
ALTER TABLE _testschema.matrix_dense ALTER COLUMN matdata SET STORAGE EXTERN;


CREATE TABLE _testschema.matrix_sparse_autocompressed
(
  matdata double precision[]
);

CREATE TABLE _testschema.matrix_sparse_bitmap
(
  matdata double precision[]
  bitmap bit varying(8000000)
);

すべてのテーブルに同じ行列を挿入します。具体的なデータは、特定のテーブルに依存します。未使用のページが割り当てられているため、サーバー側のデータを変更しないでください。または、VACUUM を実行します。

SELECT 
pg_total_relation_size('_testschema.matrix_dense') AS dense, 
pg_total_relation_size('_testschema.matrix_sparse_autocompressed') AS autocompressed, 
pg_total_relation_size('_testschema.matrix_sparse_bitmap') AS bitmap;
于 2011-04-28T11:50:30.297 に答える
9

いくつかの解決策が思い浮かびますが、

1)機能を通常は一緒に設定されるグループに分割し、メインデータと1対1の外部キー関係を持つ各グループのテーブルを作成し、クエリ時に必要なテーブルにのみ結合します

2)EAVアンチパターンを使用し、プライマリテーブルの外部キーフィールドとフィールド名および値の列を使用して「機能」テーブルを作成し、機能をプライマリの属性ではなく、そのテーブルの行として格納します。テーブル

3)PostgreDynamicの場合と同様に、プライマリテーブルの「列」ごとにテーブルを作成し(これらのテーブルには個別の名前空間を使用します)、データへのアクセスと更新を簡素化(および効率的にインデックス化)する関数を作成します。それらのテーブル

4)XMLまたはVARCHARを使用してプライマリデータに列を作成し、その中にデータを表す構造化テキスト形式を格納し、機能インデックスを使用してデータにインデックスを作成し、データを更新する関数を記述します(または、XML関数を使用する場合その形式を使用しています)

5)contrib / hstoreモジュールを使用して、キーと値のペアを保持でき、インデックス付けと更新が可能なタイプhstoreの列を作成します

6)たくさんの空のフィールドと一緒に暮らす

于 2010-04-08T11:25:55.617 に答える
3

NULL 値は、NULL の場合、スペースを占有しません。タプルヘッダーのビットマップで1ビットを占めますが、それは関係ありません。

ただし、システムは数百万の列を処理できません。理論上の最大値は 1,000 を少し超える IIRC ですが、実際にはそこまで行きたくありません。

単一のテーブルで本当に多くが必要な場合は、EAV メソッドを使用する必要があります。これは基本的に (2) で言っていることです。

各エントリに比較的少数のキーしかない場合は、3 番目のオプションとして、このタイプのデータを非常に効率的に格納できる「hstore」contrib モジュールを検討することをお勧めします。今後の 9.0 バージョンではさらに強化されているため、本番環境へのデプロイから少し離れている場合は、それを直接確認することをお勧めします。ただし、8.4 でも十分に価値があります。また、非常に効率的なインデックス ベースのルックアップもサポートしています。間違いなく検討する価値があります。

于 2010-04-08T11:00:03.623 に答える