Подтвердить что ты не робот

Проверка наличия 350 миллионов файлов по сети

У меня есть таблица SQL Server с ~ 300 000 000 абсолютных UNC-путей, и я пытаюсь (быстро) проверить их, чтобы убедиться, что путь в таблице SQL Server фактически существует как файл на диске.

При значении лица я запрашиваю таблицу в партиях по 50 000 и увеличиваю счетчик, чтобы продвигать свою партию, когда я иду.

Затем я использую объект для чтения данных для хранения текущего набора пакетов и цикла через пакет, проверяя каждый файл командой File.Exists(path), как в следующем примере.

Проблема в том, что я обрабатываю прибл. 1000 файлов в секунду максимум на четырехъядерном ядре 3.4ghz i5 с 16-гигабайтным барабаном, который займет несколько дней. Есть ли более быстрый способ сделать это?

У меня есть индекс columnstore в таблице SQL Server, и я профилировал его. Я получаю пакеты из 50 тыс. Записей в < 1s, поэтому это не узкое место SQL при выпуске пакетов в консольное приложение .net.

while (counter <= MaxRowNum)
{
    command.CommandText = "SELECT id, dbname, location FROM table where ID BETWEEN " + counter + " AND " + (counter+50000).ToString();

    connection.Open();

    using (var reader = command.ExecuteReader())
    {
        var indexOfColumn1 = reader.GetOrdinal("ID");
        var indexOfColumn2 = reader.GetOrdinal("dbname");
        var indexOfColumn3 = reader.GetOrdinal("location");

        while (reader.Read())
        {
            var ID = reader.GetValue(indexOfColumn1);
            var DBName = reader.GetValue(indexOfColumn2);
            var Location = reader.GetValue(indexOfColumn3);

            if (!File.Exists(@Location.ToString()))
            {
                //log entry to logging table
            }
        }
    }

    // increment counter to grab next batch
    counter += 50000;

    // report on progress, I realize this might be off and should be incremented based on ID
    Console.WriteLine("Last Record Processed: " + counter.ToString());
    connection.Close();
}

Console.WriteLine("Done");
Console.Read();

EDIT: добавление дополнительной информации:

думал об этом все через саму базу; это серверное предприятие sql с 2tb оперативной памяти и 64 ядрами. Проблема заключается в том, что учетная запись службы sql-сервера не имеет доступа к путям nas, на которых размещаются данные, поэтому моя cmdshell работает через SP с ошибкой (я не контролирую материал AD), а UNC-пути имеют сотни тысяч отдельных подсетей каталоги на основе хеша MD5 файла. Таким образом, перечисление содержимого каталогов не будет полезным, потому что у вас может быть файл 10 каталогов, в котором находится только 1 файл. Вот почему я должен выполнить буквальный полный путь/проверить.

О, и пути очень длинные вообще. Я фактически попробовал загрузить их все в список в памяти, прежде чем я понял, что это эквивалент 90gb данных (lol, oops). Полностью согласен с другими комментариями по его разметке. База данных очень быстро, не беспокоится вообще. Если бы не рассматривалась болтовня SMB, это очень хорошо, возможно, это то, с чем я столкнулся. - JRats 13 часов назад

О! И я также обновляю базу данных только в том случае, если файл не существует. Если это произойдет, мне все равно. Таким образом, мои работы базы данных сводятся к минимуму, чтобы захватить партии путей. В принципе, мы перенесли кучу данных из более медленного хранилища в это проворное устройство, и меня попросили убедиться, что все на самом деле закончило это, написав что-то, чтобы проверить существование на файл.

Threading помог совсем немного. Я включил проверку файлов на 4 потока и получил мощность обработки до 3300 записей в секунду, что намного лучше, но я все еще надеюсь получить еще быстрее, если смогу. Есть ли хороший способ узнать, связан ли я с трафиком SMB? Я заметил, как только я попытался увеличить количество потоков до 4 или 5, моя скорость упала до струйки; Я думал, может быть, я где-то зашел в тупик, но нет.

О, и я не могу выполнить проверку FileOnNetwork по той причине, о которой вы сказали, там 3 или 4 раза больше файлов, размещенных там по сравнению с тем, что я хочу проверить. На этом проворном устройстве, вероятно, есть 1.5b файлов.

4b9b3361

Ответ 1

Оптимизация SQL-команды здесь спорна, потому что вы связаны с файлом IO.

Я бы использовал Directory.EnumerateFiles для получения списка всех существующих файлов. Перечисление файлов в каталоге должно быть намного быстрее, чем тестирование каждого файла по отдельности.

Вы можете даже полностью инвертировать проблему, и массовая вставка этого списка файлов в таблицу temp базы данных, чтобы вы могли выполнять SQL-обработку набора непосредственно в базе данных.

Если вы хотите продолжить и проверить индивидуально, вы, вероятно, должны сделать это параллельно. Непонятно, что процесс действительно связан с диском. Возможно, это связано с сетью или процессором.

Parallelism поможет здесь, перекрывая несколько запросов. Это сетевая латентность, а не полоса пропускания, которая может быть проблемой. В DOP 1 по крайней мере одна машина бездействует в любой момент времени. Бывают моменты, когда оба бездействия.


там 3 или 4x столько файлов, которые были там размещены, по сравнению с тем, что я хочу проверить

Используйте команду dir /b, чтобы передать список всех имен файлов в файл .txt. Выполните это локально на машине с файлами, но если это невозможно, выполните удаленно. Затем используйте bcp для массового ввода их в таблицу в базу данных. Затем вы можете выполнить быструю проверку существования в одном SQL-запросе, который будет сильно оптимизирован. Вы получите хеш-соединение.

Если вы хотите parallelism фазу dir этой стратегии, вы можете написать для нее программу. Но, возможно, нет необходимости, и dir достаточно быстро, несмотря на однопоточность.

Ответ 2

Узким местом, скорее всего, является сетевой трафик, а точнее: SMB-трафик. Ваша машина говорит SMB, чтобы получить информацию о файле из сетевого хранилища. SMB-трафик "chatty", вам нужно несколько сообщений для проверки существования файла и вашего разрешения на его чтение.

Для чего это стоит, в моей сети я могу запросить существование около сотни файлов в секунду по SMB, в то время как перечисление 15K файлов рекурсивно занимает 10 секунд.

Что может быть быстрее - предварительно извлечь список удаленных каталогов. Это будет тривиально, если структура каталогов будет предсказуемой - и, если хранилище не содержит много ненужных файлов в этих каталогах.

Тогда ваш код будет выглядеть так:

HashSet<string> filesOnNetwork = new HashSet<string>(Directory.EnumerateFiles(
                            baseDirectory, "*.*", SearchOption.AllDirectories));

foreach (var fileToCheck in filesFromDatabase)
{
    fileToCheckExists = filesOnNetwork.Contains(fileToCheck);
}

Это может ухудшиться, если в сети есть больше файлов, чем вам нужно проверить, так как заполнение и поиск через filesOnNetwork станет узким местом вашего приложения.

Ответ 3

В вашем текущем решении, получающем партии в 50 000, и открытии и закрытии соединения служит НЕТ, но для замедления работы. Потоки DataReader. Просто откройте его один раз и прочитайте их все по одному. Под обложками Reader будет отправлять партии одновременно. DataReader не будет пытаться заклинить клиента с 300 000 000 строк, если вы прочитали только 10.

Я думаю, вы беспокоитесь об оптимизации самого быстрого шага - чтение из SQL

Проверка пути к файлу будет самым медленным шагом

Мне нравится ответ от CodeCaster, но на 350 миллионов вы попадете в пределы размера объекта с помощью .NET. И, читая в HashSet, он не начинает работать, пока этот шаг не будет выполнен.

Я бы использовал BlockingCollection с двумя коллекциями

  • перечислять файлы
  • записать в db

Самый медленный шаг - это имена файлов для чтения, поэтому сделайте это как можно быстрее и не прерывайте. Сделайте это на устройстве, расположенном рядом с устройством хранения. Запустите программу на подключенном к SAN устройстве.

Я знаю, что вы скажете, что запись в db происходит медленно, но она должна быть быстрее, чем перечислить файл. Просто найдите двоичные столбцы для поиска - не записывайте полное имя файла в #temp. Я поставил доллары на пончики (оптимизированное) обновление быстрее, чем перечислять файлы. Сократите свои обновления, например, 10 000 строк за раз, чтобы не допустить округления. И я сделаю обновление asynch, чтобы вы могли создать следующее обновление во время обработки текущего.

Затем в конце вы проверяете БД для любого файла, который не был помечен как найденный.

Не переходите к промежуточной коллекции. Обработайте перечисление напрямую. Это позволяет немедленно начать работу и сохранить память.

foreach (string  fileName in Directory.EnumerateFiles(baseDirectory, "*.*", SearchOption.AllDirectories)) 
{
   // write filename to blocking collection
}

Ответ 4

Быстрая идея, если подход CodeCaster не работает из-за слишком большого количества файлов на удаленных серверах и если вы можете установить новые программы на удаленных серверах: Напишите программу, которую вы устанавливаете на каждом сервере, и который прослушивает какой-либо порт для HTTP-запросов (или какую бы технологию веб-сервисов вы предпочитаете). Программа, которая запрашивает базу данных, должна загружать имена файлов на сервер и отправлять запрос на каждый сервер со всеми именами файлов, которые расположены на этом сервере. Веб-служба проверяет существование файла (который должен быть быстрым, поскольку он теперь является локальной операцией) и отвечает, например, список, содержащий только имена файлов, которые действительно существовали. Это должно устранить большую часть служебных данных протокола и задержки в сети, поскольку количество запросов значительно сокращено.

Ответ 5

Если я сделаю такую ​​задачу, я знаю, что узкие места:

  • латентность доступа к дискам (~ 1 мс)
  • время ожидания доступа к сети (для 100mbps ~ 0.2ms)
  • база данных ограничена диском

Самая быстрая вещь - кеш процессора, второй - оперативная память.

Я предполагаю, что я мог бы использовать дополнительную таблицу базы данных для хранения временных данных. База данных, где теперь данные я буду называть основной базой данных.

Я буду выполнять задачи параллельно:

  • рекурсивное чтение каталога и сохранение второй базы данных в кусках для файлов размером около 50 тыс.
  • получить фрагменты записей из основной базы данных и сравнить ONE chunk с ONE из второй базы данных - все файлы, которые не были найдены, будут записываться в третью базу данных (а также отмечать файлы в первой базе данных).
  • после того, как все куски из основной базы данных по сравнению со второй базой данных - проверьте все куски из третьей базы данных со второй базой данных и удалите найденные файлы.

В конце в третьей базе данных останутся только несуществующие файлы, поэтому я могу просто получить строки из него и пометить данные в основной базе данных.

Могут быть дополнительные улучшения, можно обсудить, если интерес.

Ответ 6

Как насчет сортировки местоположений по мере их получения из БД (db хороши при сортировке). Тогда чеки могут быть полезны из кэшированной информации о каталоге в клиенте cifs,

вы можете получить список каталогов для следующей строки в наборе результатов, а затем проверить эту строку для существования в каталоге dir-list, а затем повторить проверку, находится ли следующая строка в результирующем наборе в том же каталоге, и если да, то проверьте уже выбранный список-дир, если не повторить внешний цикл.