1

次のような物理ディレクトリ構造があります。

ルート ディレクトリ (X) -> サイド ルート (1,2,3,4..) の多くのサブディレクトリ -> 各サブ ディレクトリに多くのファイルが存在します。

Photos(Root)    
            ----        
               123456789(Child One)
                 ----
                     1234567891_w.jpg (Child two)
                     1234567891_w1.jpg(Child two)
                     1234567891_w2.jpg(Child two)
                     1234567892_w.jpg (Child two)
                     1234567892_w1.jpg(Child two)
                     1234567892_w2.jpg(Child two)
                     1234567893_w.jpg(Child two)
                     1234567893_w1.jpg(Child two)
                     1234567893_w2.jpg(Child two)
                     -----Cont      
              232344343(Child One)      
              323233434(Child One)      
              232323242(Child One)      
              232324242(Child One)      
              ----Cont..

データベースには、「1234567891_w.jpg」タイプの膨大な数の名前を持つテーブルが 1 つあります。

注 : データベースのデータ数と写真の数はどちらも lacs です。

データベース テーブルから物理ディレクトリ構造まで、各名前の存在を効果的かつ迅速に確認する方法が必要です。

  • 例:写真(ルート)内の物理フォルダに「1234567891_w.jpg」という名前のファイルが存在するかどうか。

ここで提供する情報が不足している場合はお知らせください。

アップデート :

ディレクトリ内のファイル名の存在を見つける方法を知っています。しかし、40 GB を超えるデータで各ファイル名 (レコードの不足から) の存在を確認するにはリソースを消費しすぎるため、効率的な方法を探しています。

4

4 に答える 4

2

それらが存在するディレクトリに基づいて、データベースからのデータをグループ化することができます。それらを何らかの方法で(たとえばファイル名に基づいて)ソートし、そのディレクトリ内のファイルの配列を取得します string[] filePaths = Directory.GetFiles(@"c:\MyDir\");。これで、文字列を比較するだけで済みます。

于 2012-10-17T08:05:46.100 に答える
1

このようなことができる場合、ファイルは一意に名前が付けられているように見えます

var fileNames = GetAllFileNamesFromDb();
var physicalFiles = Directory.GetFiles(rootDir, 
                                        string.Join(",",fileNames),
                                        SearchOptions.AllDirectories)
                                        .Select(f=>Path.GetFileName(f));
var setOfFiles = new Hashset<string>(physicalFiles);
var notPresent = from name in fileNames
                 where setOfFiles.Contains(name)
                 select name;
  • 最初にデータベースからすべてのファイル名を取得します
  • 次に、ルートから検索し、すべてのサブディレクトリを含めてすべてのファイルを一度に検索して、すべての物理ファイルを取得します。
  • 高速ルックアップ用のハッシュセットを作成する
  • 次に、ファイル名をセットに一致させ、セットに含まれていないものを選択します。

ハッシュセットは基本的に単なるセットです。これは、アイテムを 1 回だけ含めることができるコレクションです (つまり、重複はありません)。Hashset の等価性は HashCode に基づいており、アイテムがセット内にあるかどうかを判断するルックアップは O(1) です。

このアプローチでは、潜在的に巨大な Hashset をメモリに保存する必要があり、そのセットのサイズによっては、アプリケーションの速度を最適化するのではなく、代わりに最適化を渡すという拡張までシステムに影響を与える可能性があります。

ほとんどの最適化の場合と同様に、それらはすべてトレードオフであり、重要なのは、アプリケーションがエンド ユーザーにもたらす価値のコンテキストで、すべてのトレードオフのバランスを見つけることです。

于 2012-10-17T08:15:43.497 に答える
0

残念ながら、パフォーマンスを向上させるために使用できる特効薬ではありません。いつものように、速度とメモリのトレードオフになります。また、パフォーマンスが不足する可能性のある 2 つの側面があります。データベース サイトと HDD ドライブの I/O 速度です。

したがって、速度を上げるために、最初のステップでデータベースクエリのパフォーマンスを改善して、検索用の名前を十分に高速に返すことができるようにします。したがって、クエリが高速であり、おそらく (MS SQL の場合) キーワードを使用していることを確認してくださいREAD SEQUENTIAL。名前を大きなブロックとして。

反対側の hdd 側では、いずれかを呼び出すことができますがDirectory.GetFiles()、この呼び出しは、すべてのファイルを反復処理するまでブロックされ、すべてのファイル名を含む大きな配列が返されます。これはメモリを消費するパスであり、最初の検索には時間がかかりますが、後でその配列のみを操作すると、すべての連続検索で速度が向上します。別のアプローチは、呼び出すことですDirectory.EnumerateFiles()これは、すべての呼び出しでオンザフライでドライブを検索するため、最初の検索では速度が向上する可能性がありますが、次の検索ではメモリストレージが発生せず、メモリフットプリントが改善されますが、速度が低下します。検索可能なメモリ内の配列。一方、OS は、同じファイルを何度も繰り返し処理し、下位レベルでキャッシュが発生することを検出した場合、キャッシュも実行します。

したがって、hdd サイトのチェックではDirectory.GetFiles()、返された配列がメモリを消費せず、これですべての検索を実行する場合に使用します (HashSetファイル名のみまたはフル パスがデータベースから取得するものに依存する場合は、パフォーマンスをさらに向上させるために、おそらくそれを に入れます)それ以外の場合はDirectory.EnumerateFiles()、OS で行われるキャッシングに最善を尽くしてください。

アップデート

あなたの質問とコメントを読み直した後、私が理解している限り、あなたのような名前があり、1234567891_w.jpg名前のどの部分がディレクトリ部分を表しているのかわかりません。したがって、この場合、明示的な検索を行う必要があります。これは、すべてのディレクトリの反復処理に時間がかかるためです。これは、最初のショットでこれを解決する方法についてのアイデアを提供するサンプルコードです。

string rootDir = @"D:\RootDir";

// Iterate over all files reported from the database
foreach (var filename in databaseResults)
{
    var fullPath = Path.Combine(rootDir, filename);

    // Check if the file exists within the root directory
    if (File.Exists(Path.Combine(rootDir, filename)))
    {
        // Report that the file exists.
        DoFileFound(fullPath);
        // Fast exit to continue with next file.
        continue;
    }

    var directoryFound = false;

    // Use the filename as a directory
    var directoryCandidate = Path.GetFileNameWithoutExtension(filename);
    fullPath = Path.Combine(rootDir, directoryCandidate);

    do
    {
        // Check if a directory with the given name exists
        if (Directory.Exists(fullPath))
        {
            // Check if the filename within this directory exists
            if (File.Exists(Path.Combine(fullPath, filename)))
            {
                // Report that the file exists.
                DoFileFound(fullPath);
                directoryFound = true;
            }

            // Fast exit, cause we looked into the directory.
            break;
        }

        // Is it possible that a shorter directory name
        // exists where this file exists??
        // If yes, we have to continue the search ...
        // (Alternative code to the above one)
        ////// Check if a directory with the given name exists
        ////if (Directory.Exists(fullPath))
        ////{
        ////    // Check if the filename within this directory exists
        ////    if (File.Exists(Path.Combine(fullPath, filename)))
        ////    {
        ////        // Report that the file exists.
        ////        DoFileFound(fullPath);

        ////        // Fast exit, cause we found the file.
        ////        directoryFound = true;
        ////        break;
        ////    }
        ////}

        // Shorten the directory name for the next candidate
        directoryCandidate = directoryCandidate.Substring(0, directoryCandidate.Length - 1);
    } while (!directoryFound
              && !String.IsNullOrEmpty(directoryCandidate));

    // We did our best but we found nothing.
    if (!directoryFound)
        DoFileNotAvailable(filename);
}

私が考えることができる唯一のさらなるパフォーマンスの改善は、見つかったディレクトリをに入れ、これHashSetDirectory.Exists()使用して既存のディレクトリをチェックすることですが、OSがすでにディレクトリルックアップでキャッシュを作成しているため、何も得られない可能性があります。ローカルキャッシュとほぼ同じ速度になります。しかし、これらのことについては、具体的な問題を測定するだけです。

于 2012-10-17T08:25:20.547 に答える