web-dev-qa-db-ja.com

メモリの制約が与えられた膨大な量のデータを含むファイルを並べ替える

ポイント:

  • 1日に数千のフラットファイルを同時に処理します。
  • メモリの制約は大きな問題です。
  • 各ファイルプロセスにスレッドを使用します。
  • 列でソートしません。ファイルの各行(レコード)は1つの列として扱われます。

できないこと:

  • Unix/linuxのsortコマンドは使用できません。
  • どんなに軽くてもデータベースシステムは使用できません。

現在、コレクション内のすべてをロードして、ソートメカニズムを使用することはできません。それはすべてのメモリを使い果たし、プログラムはヒープエラーを取得します。

その場合、ファイル内のレコード/行をどのようにソートしますか?

32
Erika Gomez

あなたが探しているのは 外部ソート のようです。

基本的に、データの小さな塊を最初にソートし、それをディスクに書き戻し、次にそれらを繰り返してすべてをソートします。

47
phisch

ファイルをより小さな部分で読み取り、並べ替えて一時ファイルに書き込むことができます。次に、それらのうちの2つを再び順番に読み取り、より大きな一時ファイルにマージします。残りが1つしかない場合は、ファイルをソートします。基本的には、外部ファイルに対して実行されるMegresortアルゴリズムです。任意の大きなファイルに非常にうまく対応できますが、余分なファイルI/Oが発生します。

編集:ファイル内の行の分散の可能性についてある程度の知識がある場合は、より効率的なアルゴリズム(配布ソート)を使用できます。簡略化すると、元のファイルを1回読み取り、同じ最初の文字(または最初の文字の特定の範囲)を持つ行のみを取得する一時ファイルに各行を書き込みます。次に、すべての(現在は小さい)一時ファイルを昇順で反復処理し、メモリ内で並べ替えて、出力ファイルに直接追加します。一時ファイルがメモリ内でソートするには大きすぎる場合、行の2番目の文字などに基づいて同じプロセスを繰り返すことができます。したがって、最初のパーティショニングが十分に小さいファイルを生成するのに十分であれば、ファイルの大きさに関係なく100%のI/Oオーバーヘッドしかありませんが、最悪の場合、パフォーマンスが安定したマージソートよりもはるかに大きくなる可能性があります。

12
x4u

あなたの制限にもかかわらず、私は組み込みデータベース SQLITE を使用します。あなたと同じように、私は毎週1,000万から1500万行のフラットファイルを使用しており、ソートされたデータをインポートおよび生成するのは非常に高速です。例:.exeファイルをダウンロードしたら、コマンドプロンプトで次の操作を実行できます。

C:> sqlite3.exe dbLines.db
sqlite> create table tabLines(line varchar(5000));
sqlite> create index idx1 on tabLines(line);
sqlite> .separator '\r\n'
sqlite> .import 'FileToImport' TabLines

その後:

sqlite> select * from tabLines order by line;

or save to a file:
sqlite> .output out.txt
sqlite> select * from tabLines order by line;
sqlite> .output stdout
11
Eduardo

EC2クラスターを起動し、Hadoopの MergeSort を実行します。

Edit:どれだけの詳細が必要か、または何を確認しますEC2はAmazonのElastic Compute Cloudです。1時間ごとに低コストで仮想サーバーをレンタルできます。 ウェブサイト です。

Hadoopは、大規模なデータセットの並列処理用に設計されたオープンソースのMapReduceフレームワークです。ジョブは、通常キーでソートすることによって(つまり、分割統治戦略)、個別に処理してからマージできるサブセットに分割できる場合、MapReduceの適切な候補です。 ウェブサイト です。

他のポスターで述べたように、外部ソートも優れた戦略です。どちらを選択するかは、データのサイズと速度の要件に依存すると思います。単一のマシンは、一度に単一のファイルの処理に制限される可能性があります(使用可能なメモリを使い果たすため)。そのため、それよりも速くファイルを処理する必要がある場合にのみ、EC2のようなものを調べてください。

8
danben

他の言及したように、ステップで処理できます。
これを自分の言葉で説明したい(ポイント3で異なる):

  1. ファイルを順番に読み取り、メモリ内で一度にNレコードを処理します(メモリの制約と必要な一時ファイルの数Tに応じて、Nは任意です)。

  2. メモリ内のN個のレコードを並べ替え、一時ファイルに書き込みます。完了するまでTでループします。

  3. すべてのT tempファイルを同時に開きますが、ファイルごとに1つのレコードのみを読み取ります。(もちろん、バッファーを使用)。これらのTレコードごとに、小さい方を見つけて最終ファイルに書き込み、そのファイルでのみ進めます。


利点:

  • memory消費量は、必要なだけ低くなります。
  • メモリ内のすべてのポリシーと比較して、ディスクアクセスのdoubleのみを実行します。悪くない! :-)

数字の例:

  1. 100万レコードの元のファイル。
  2. 100の一時ファイルを選択するので、一度に10000レコードを読み取ってソートし、独自の一時ファイルにドロップします。
  3. 一度に100の一時ファイルを開き、メモリ内の最初のレコードを読み取ります。
  4. 最初のレコードを比較し、小さい方を書き込み、この一時ファイルを進めます。
  5. ステップ5で100万回ループします。

[〜#〜]編集済み[〜#〜]

あなたはマルチスレッドアプリケーションについて言及したので、私は疑問に思う...

この必要性に関するこれらの議論からわかるように、メモリを少なくするとパフォーマンスが低下し、この場合は劇的な要因になります。したがって、マルチスレッドアプリケーションとしてではなく、一度に1種類のみを処理するために1つのスレッドのみを使用することを提案することもできます。

それぞれが使用可能なメモリの10分の10個のスレッドを処理する場合、パフォーマンスは悲惨なものになり、最初の10分の1よりはるかに少なくなります。スレッドを1つだけ使用し、他の9つの要求をキューに入れて順番に処理すると、グローバルパフォーマンスが大幅に向上し、10個のタスクをより速く完了できます。


この応答を読んだ後: メモリ制約が与えられた膨大な量のデータを持つファイルをソートする この分散ソートを検討することをお勧めします。それはあなたのコンテキストで大きな利益になる可能性があります。

私の提案に対する改善点は、すべての一時ファイルを一度に開く必要はなく、そのうちの1つだけを開くことです。それはあなたの一日を節約します! :-)

7
KLE

次の分割統治戦略を使用できます。

入力ファイルの各レコードに番号を割り当てることができる関数H())を作成します。レコードr1の後ろでソートされるレコードr2の場合、r1よりもr2の方が大きい数値を返す必要があります。この関数を使用して、すべてのレコードをメモリに収まる個別のファイルに分割し、ソートできるようにします。一度、ソートしたファイルを連結して、1つの大きなソート済みファイルを取得できます。

各行がレコードを表すこの入力ファイルがあるとします

Alan Smith
Jon Doe
Bill Murray
Johnny Cash

H()をビルドして、レコードの最初の文字を使用して最大26個のファイルを取得できるようにしますが、この例では3を取得します。

<file1>
Alan Smith

<file2>
Bill Murray

<file10>
Jon Doe
Johnny Cash

これで、個々のファイルをソートできます。 <file10>の「Jon Doe」と「Johnny Cash」を入れ替えます。これで、3つのファイルを連結するだけで、入力のソートされたバージョンが得られます。

最初に分割し、後で征服(ソート)するだけであることに注意してください。ただし、並べ替える必要のある結果の部分が重複しないようにパーティション化を行い、結果のマージをより簡単にします。

パーティショニング関数H())を実装する方法は、入力データの性質に大きく依存します。その部分がわかれば、残りは簡単です。

2
VoidPointer

externalデータベースシステムのみを使用しないという制限がある場合は、組み込みデータベースを試すことができます(例 Apache Derby )。これにより、外部インフラストラクチャに依存することなく、データベースのすべての利点が得られます。

2
FRotthowe

並べ替えを頻繁に使用しないでJavaを使用し、DBを使用しないで行う方法です。仮定:1TBのスペースがあり、ファイルは一意の番号で始まるか、一意の番号で始まりますが、並べ替えられません

ファイルをN回分割します。

これらのN個のファイルを1つずつ読み取り、行/番号ごとに1つのファイルを作成します

そのファイルに対応する番号を付けます。名前を付けながら、カウンタを更新して最小カウントを保存します。

これで、ファイルのルートフォルダーを名前で並べ替えるようにマークしたり、プログラムを一時停止して、OSでコマンドを起動してファイルを名前で並べ替えたりすることができます。プログラムで行うこともできます。

これで、名前でファイルがソートされたフォルダーができました。カウンターを使用して各ファイルを1つずつ取得し、OUTPUTファイルに番号を入力して閉じます。

完了すると、並べ替えられた数値を含む大きなファイルが作成されます。

1
MayurRB

SQL Liteファイルdbを使用して、データをdbにロードし、ソートして結果を返すことができます。利点:最適なソートアルゴリズムの作成について心配する必要はありません。欠点:ディスク領域が必要になり、処理が遅くなります。 https://sites.google.com/site/arjunwebworld/Home/programming/sorting-large-data-files

0
user2071703

2つの一時ファイル(ソースとデスティネーション)と必要なだけの少ないメモリでそれを行うことができます。最初のステップでは、ソースは元のファイルであり、最後のステップでは、宛先は結果ファイルです。

各反復で:

  • ソースファイルからスライディングバッファーに、バッファーの半分のサイズのデータ​​のチャンクを読み取ります。
  • バッファ全体をソートします
  • 宛先ファイルにバッファの前半を書き込みます。
  • バッファの後半を先頭に移動して繰り返します

現在の反復でいくつかのレコードを移動する必要があるかどうかを示すブールフラグを保持します。フラグがfalseのままの場合、ファイルはソートされます。発生した場合は、宛先ファイルをソースとして使用してプロセスを繰り返します。

最大反復回数:(ファイルサイズ)/(バッファサイズ)* 2

0
user7932299

どんなに軽くてもデータベースを使用しないと言っていたことは知っています...それで、これはオプションではないかもしれません。しかし、メモリ内のhsqldbについてはどうでしょうか。それを送信し、クエリでソートし、パージします。ちょっとした考え。

0
PaulP1975