7

PostgreSQL 9.0 で次の構造を持つテーブルを想像してください。

create table raw_fact_table (text varchar(1000));

簡単にするために、1 つのテキスト列だけを取り上げますが、実際には 12 のテキスト列があります。このテーブルには 100 億行あり、各列には多くの重複があります。テーブルは、COPY FROM を使用してフラット ファイル (csv) から作成されます。

パフォーマンスを向上させるために、次のスター スキーマ構造に変換したいと考えています。

create table dimension_table (id int, text varchar(1000));

ファクト テーブルは、次のようなファクト テーブルに置き換えられます。

create table fact_table (dimension_table_id int);

私の現在の方法は、基本的に次のクエリを実行してディメンション テーブルを作成することです。

Create table dimension_table (id int, text varchar(1000), primary key(id));

次に、使用するディメンション テーブルを作成します。

insert into dimension_table (select null, text from raw_fact_table group by text);

その後、次のクエリを実行する必要があります。

select id into fact_table from dimension inner join raw_fact_table on (dimension.text = raw_fact_table.text);

すべての文字列を他のすべての文字列と数回比較することで得られる恐ろしいパフォーマンスを想像してみてください。

MySQL では、COPY FROM 中にストアド プロシージャを実行できました。これにより、文字列のハッシュが作成され、その後のすべての文字列比較は、長い生の文字列ではなくハッシュに対して行われます。これは PostgreSQL ではできないようですが、どうすればよいですか?

サンプル データは、次のようなものを含む CSV ファイルになります (整数と倍精度も引用符で囲みます)。

"lots and lots of text";"3";"1";"2.4";"lots of text";"blabla"
"sometext";"30";"10";"1.0";"lots of text";"blabla"
"somemoretext";"30";"10";"1.0";"lots of text";"fooooooo"
4

5 に答える 5

7

すべての文字列を他のすべての文字列と数回比較することで得られる恐ろしいパフォーマンスを想像してみてください。

これをしばらく行っていると、パフォーマンスを想像するのをやめて、パフォーマンスを測定し始めます。「時期尚早の最適化は諸悪の根源です。」

あなたにとって「10億」とは何ですか?私にとって、アメリカでは、それは 1,000,000,000 (または 1e9) を意味します。これも当てはまる場合は、おそらく 1 ~ 7 テラバイトのデータを見ていることになります。

私の現在の方法は、基本的に次のクエリを実行してディメンション テーブルを作成することです。

Create table dimension_table (id int, text varchar(1000), primary key(id));

主キーに整数を使用するテーブルに 100 億行を収めるにはどうすればよいでしょうか? 行の半分が重複しているとしましょう。あなたがそれを行うとき、その算術はどのように機能しますか?

想像しないでください。最初に読んでください。次に、テストします。

PostgreSQL を使用したデータ ウェアハウジングを参照してください。これらのプレゼンテーション スライドからいくつかのアイデアが得られると思います。

また、データベースへのデータの取り込みを読み、実装する提案を検討してください。

「分割統治」プロセスに従って、100 万 (1e6) 行でテストします。つまり、一度に 100 万をロードしようとしないでください。それを小さなチャンクに分割する手順を書きます。走る

EXPLAIN <sql statement>

あなたは、少なくとも 99% の重複行を見積もっていると言いました。大まかに言えば、だまされた人を取り除くには2つの方法があります

  1. データベース内では、本番環境で使用するプラットフォームと必ずしも同じではありません。
  2. データベース外のファイルシステムでは、本番環境で使用するファイルシステムと必ずしも同じではありません。

読み込んだテキスト ファイルがまだ残っている場合は、まずデータベースの外で試してみることを検討します。この awk ワンライナーは、各ファイルから一意の行を出力します。これは、データを 1 回しかパスしないため、比較的経済的です。

awk '!arr[$0]++' file_with_dupes > file_without_dupes

本当に 99% の重複がある場合、このプロセスの終わりまでに、1 から 7 テラバイトを約 50 ギガに削減する必要があります。さらに、データ ウェアハウスにコピーする前に、一意の各行に番号を付けて、タブ区切りファイルを作成することもできます。それは別のワンライナーです:

awk '{printf("%d\t%s\n", NR, $0);}' file_without_dupes > tab_delimited_file

Windowsでこれを行う必要がある場合は、Cygwinを使用します。

データベースでこれを行う必要がある場合は、実稼働データベースまたは実稼働サーバーを使用しないようにします。でも、私が慎重になりすぎているのかもしれません。数テラバイトを移動するのはコストがかかります。

しかし、私はテストします

SELECT DISTINCT ...

GROUP BY を使用する前に。大規模なデータセットでいくつかのテストを行うことができるかもしれませんが、おそらく今週はできません. (私は通常、テラバイト サイズのファイルを扱うことはありません。ちょっと興味深いです。お待​​ちいただければ幸いです。)

于 2011-01-23T04:29:22.940 に答える
2
-- add unique index
CREATE UNIQUE INDEX uidx ON dimension_table USING hash(text);
-- for non case-sensitive hash(upper(text))

ハッシュ(テキスト)を試してください。と btree(text) は、どちらが速いかを確認します

于 2011-01-25T06:24:52.197 に答える
2

最後にいくつかの詳細を省略していますが、必ずしも問題があるとは思いません。すべての文字列が実際に他のすべての文字列と比較されるという証拠はありません。結合を行う場合、PostgreSQL はハッシュ結合などのよりスマートな結合アルゴリズムを選択する可能性が非常に高く、これにより、MySQL ソリューションで独自に実装しているのと同じハッシュが得られる可能性があります。(繰り返しますが、あなたの詳細はぼんやりしています。)

于 2011-01-03T12:02:27.017 に答える
2

質問: - データを 1 つまたは 2 つのステップで変換する必要がありますか? - 変換中にテーブルを変更できますか?

より単純なクエリを実行すると、パフォーマンスが向上する可能性があります (実行中のサーバー負荷も向上します)。

1つのアプローチは次のとおりです。

  1. ディメンションテーブルを生成します(私が正しく理解していれば、これでパフォーマンスの問題はありません)(一時的なブール値フィールドが追加されている可能性があります...)
  2. 繰り返し: ディメンション テーブルから以前に選択されていないエントリを 1 つ選択し、それを含む raw_fact_table からすべての行を選択して、ファクト テーブルに挿入します。dimension_table レコードを完了としてマークし、次へ... これをストアド プロシージャとして記述し、バックグラウンドでデータを変換して、最小限のリソースを消費することができます...

または別の(おそらくより良い):

  1. raw_fact_table と 1 つの dimension_id からすべてのレコードとして fact_table を作成します。(つまり、dimension_text および dimension_id 行を含む)
  2. 次元テーブルを作成
  3. 次のように、fact_table の after insert トリガーを作成します。
    • fact_table で dimension_text を検索します
    • 見つからない場合は、dimension_table に新しいレコードを作成します
    • dimension_id をこの ID に更新します
  4. 単純なループで、raw_fact_table から fact_table までのすべてのレコードを挿入します
于 2011-01-25T09:27:11.573 に答える
1

私はあなたの問題を解決するいくつかの方法を見ますPostgreSqlにmd5関数がありますmd5(string)文字列のMD5ハッシュを計算し、結果を16進数で返します

ディメンションテーブルに挿入します(null、md5(テキスト)、テキストによるraw_fact_tableグループからのテキストを選択します)

md5フィールドをraw_fact_tableに追加し、idをディメンションinner join raw_fact_table on(dimension.md5 = raw_fact_table.md5);からfact_tableに選択します。

提出されたMD5のインデックスも役立つ可能性があります

または、データのロード中にその場でMD5を計算できます。たとえば、ETLツールの高度なETLプロセッサがそれを実行できます。さらに、データを複数のテーブルに同時にロードできます。

当社のWebサイトには、多数のオンラインチュートリアルがあります。たとえば、これは、変化の遅いディメンションの読み込みを示しています。

http://www.dbsoftlab.com/online-tutorials/advanced-etl-processor/advanced-etl-processor-working-with-slow-changing-dimension-part-2.html

于 2011-01-24T19:22:36.423 に答える