3

私は一種の DWH プロジェクトに参加しています (完全ではありませんが、まだ)。そして、私たちが常に遭遇するこの問題があり、より良い解決策があるかどうか疑問に思っていました. フォロー

次のような、ユーザーが経験したすべての状態を含むレコードを含むいくつかの大きなファイルを受け取ります。

UID | State    | Date
1   | Active   | 20120518
2   | Inactive | 20120517
1   | Inactive | 20120517
...

そして、私たちは通常、各ユーザーの最新の状態に関心があります。これまでのところ、少しソートするだけで、希望どおりの方法で取得できました。唯一の問題は、これらのファイルは通常大きいことです.. 20 ~ 60 GB のように、並べ替えのロジックが通常それほど単純ではないため、これらのファイルを並べ替えるのが面倒な場合があります。

私たちが一般的に行っていることは、すべてを Oracle にロードし、中間テーブルとマテリアライズド ビューを使用して実行することです。それでも、パフォーマンスが私たちを悩ませることがあります。

20 ~ 60 GB は大きいかもしれませんが、それほど大きくはありませ。つまり、これらのレコードを処理するには、もう少し専門的な方法である必要がありますね。

この問題に取り組むには、次の 2 つの基本的な方法があると思います。

1) DBMS、スクリプト、およびコンパイルされたものの外部でのプログラミング。しかし、何かを開発するためにもっと多くの時間が費やされない限り、これはあまり柔軟ではないかもしれません。また、ボックスのリソースを管理するために忙しくしなければならないかもしれませんが、それについて心配したくありません。

2)すべてをDBMS(私の場合はOracle)にロードし、提供されるツールを使用してデータをソートおよびクリップします。これは私の場合ですが、すべてのツールを使用しているか、単に Oracle 10g の正しい方法で行っているかはわかりません。

質問は次のとおりです。

上記のような何百万もの履歴レコードを含む 60 GB のファイルがあり、ユーザーは DB に各ユーザーの最後の状態を含むテーブルを必要としています。

あなたたちはどうしますか?

ありがとう!

4

4 に答える 4

2

プロセスをスピードアップするためにできることは 2 つあります。

まず第一に、それに計算能力を投入することです。Enterprise Edition と多数のコアを使用している場合は、並列クエリを使用してロード時間を大幅に短縮できます。

もう 1 つは、不要なレコードをロードしないようにすることです。これが、ファイルの前処理について言及している理由です。Hadoop クラスターにアクセスしてファイルに対していくつかの map-reduce ジョブを実行できない限り、そこでできることはあまりありません (まあ、主に reduce です。投稿する構造は、既に可能な限りマッピングされています)。

しかし、別の方法があります: 外部テーブルです。外部テーブルは、テーブルスペースではなく OS ファイルにデータを持つテーブルです。また、それらは並行して有効にすることができます (ファイルが特定の基準を満たしている場合)。 詳細をご覧ください

したがって、このような外部テーブルがあるかもしれません

CREATE TABLE user_status_external (
   uid     NUMBER(6),
   status      VARCHAR2(10),
   sdate        DATE
ORGANIZATION EXTERNAL
(TYPE oracle_loader
 DEFAULT DIRECTORY data_dir
 ACCESS PARAMETERS
 (
  RECORDS DELIMITED BY newline
  BADFILE 'usrsts.bad'
  DISCARDFILE 'usrsts.dis'
  LOGFILE 'usrsts.log'
  FIELDS TERMINATED BY ","  OPTIONALLY ENCLOSED BY '"'
  (
   uid     INTEGER EXTERNAL(6),
   status     CHAR(10),
   sdate       date 'yyyymmdd' )
 )
 LOCATION ('usrsts.dmp')
)
PARALLEL
REJECT LIMIT UNLIMITED;

DATA_DIR ディレクトリ オブジェクトに対する読み取りおよび書き込み権限が必要であることに注意してください。

外部テーブルを作成したら、insert ステートメントを使用して、必要なデータのみをターゲット テーブルにロードできます。

insert into user_status (uid, status, last_status_date)
    select  sq.uid
            ,  sq.status
            ,  sq.sdate
    from (
        select /*+ parallel (et,4) */ 
               et.uid
               , et.status
               , et.sdate
               , row_number() over (partition by et.uid order by et.sdate desc) rn  
        from user_status_external et
        ) sq
    where sq.rn = 1

すべてのパフォーマンスに関するアドバイスと同様に、保証はないことに注意してください。自分の環境で物事をベンチマークする必要があります。

もう 1 つのことは、INSERT の使用です。投稿が示唆するシナリオであるため、これらはすべて新しい USERID であると想定しています。より複雑なシナリオがある場合は、おそらく MERGE または別のアプローチを完全に検討することをお勧めします。


最後に 1 つ: これは一般的な状況であり、いくつかの標準的なアプローチがあると想定しているようです。しかし、ほとんどのデータ ウェアハウスは、取得したすべてのデータを読み込みます。その後、さまざまな用途やデータ マートなどに合わせてフィルター処理する場合があります。ただし、ほとんどの場合、実際のウェアハウスにはすべての個別のレコードの履歴が保持されます。そのため、業界標準のソリューションが得られない場合があります。

于 2012-05-18T12:41:57.280 に答える
1

APCが最初に言ったことに沿ったものを使用します。ただし、並列テーブルはデータが複数のファイルにある場合にのみデータを並列にロードできると思うので、ファイルをいくつかに分割する必要があるかもしれません。ファイルはどのように生成されますか? 20 ~ 60 GB のファイルを扱うのは本当に面倒です。たとえば、X 2 GB のファイルを取得できるように、ファイルの世代を変更できますか?

データベースにすべてのレコードを取得した後、60 GB のデータを並べ替えようとすると問題が発生する可能性があります。最新のステータスを抽出するために使用しているクエリの並べ替え段階を確認することをお勧めします。過去に、ソートするフィールドの 1 つ (この場合は user_id) のデータをハッシュ分割することで、大規模なソートを支援しました。そうすれば、Oracle は X 個の小さなソートを実行するだけで済み、それぞれを並行して進めることができます。

したがって、私の考えは次のようになります。

  1. 1 つの大きなファイルではなく、多数の小さなファイルを生成してみてください
  2. 外部テーブルを使用して、必要なデータを外部テーブルから直接抽出できるかどうかを確認します
  3. そうでない場合は、ファイルの内容全体をハッシュ パーティション テーブルにロードします。この段階で、必ず /*+ append nologging */ を挿入して、元に戻す生成とやり直し生成を回避してください。データベースで force_logging が true に設定されている場合、nologging ヒントは効果がありません。
  4. ステージングされたデータに対して選択を実行して、関心のある行のみを抽出し、ステージングされたデータを破棄します。

nologging オプションは、良好なパフォーマンスを得るためにおそらく重要です。60GB のデータをロードするには、少なくとも 60GB の REDO ログを生成することになるため、それを回避できればなおさらです。それについては、おそらく DBA とチャットする必要があります。

大量の CPU を使用できると仮定すると、データを圧縮してステージング テーブルに一括ロードすることも理にかなっている場合があります。繰り返しフィールドがある場合、圧縮によりディスク上のデータのサイズが半分になる可能性があります。通常、書き込み時に節約されるディスク IO は、ロード時に消費される余分な CPU よりも多くなります。

于 2012-05-18T13:34:31.210 に答える
0

私は問題を単純化しすぎているかもしれませんが、次のようなものではないでしょうか:

create materialized view my_view
tablespace my_tablespace
nologging
build immediate
refresh complete on demand
with primary key
as
select uid,state,date from
(
  select /*+ parallel (t,4) */ uid, state, date, row_number() over (partition by uid order by date desc) rnum
  from my_table t;
)
where rnum = 1;

その後、必要に応じて完全にリフレッシュしてください。

編集: 統計を再構築することを忘れずに、おそらく uid に一意のインデックスをスローしてください。

于 2012-05-18T11:22:25.340 に答える
-1

各レコードを反復処理し、以前に表示されたレコードよりも新しいレコードのみを保持するプログラムを作成します。最後に、データをデータベースに挿入します。

それがどれほど実用的かは、私たちが話しているユーザーの数によって異なります。最終的には、中間ストレージについて慎重に検討する必要があります。

一般に、これは(擬似コードで)次のようになります。

foreach row in file
    if savedrow is null
        save row
    else
        if row is more desirable than savedrow
             save row
        end
    end
end

send saved rows to database

つまり、ある行が別の行よりも望ましいと見なされる方法を定義する必要があります。単純なケースでは、特定のユーザーの場合、現在の行の日付は、保存した最後の行よりも遅くなります。最後に、ユーザーごとに1つの行のリストがあり、各行には最新の日付が含まれています。

フレームワークが各データファイルを理解するコードから分離されるように、スクリプトまたはプログラムを一般化することができます。

それでもしばらく時間がかかります、気に:-)

于 2012-05-18T10:18:41.773 に答える