Назад | Перейти на главную страницу

Хранение миллиона изображений в файловой системе

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

Как вы порекомендуете эффективно хранить эти изображения? (Файловая система NTFS в настоящее время)

Я рассматриваю схему именования ... для начала все изображения будут иметь инкрементное имя от 1 до Я надеюсь, что это поможет мне позже отсортировать их, если потребуется, и бросить их в разные папки.

какая схема именования будет лучше:

a/b/c/0 ... z/z/z/999

или

a/b/c/000 ... z/z/z/999

есть идеи по этому поводу?

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

Не сохранять фактический путь к базе данных. Лучше сохранить порядковый номер изображения в базе данных и иметь функцию, которая может генерировать путь из порядкового номера. например:

 File path = generatePathFromSequenceNumber(sequenceNumber);

С этим проще справиться, если вам нужно как-то изменить структуру каталогов. Возможно, вам нужно переместить изображения в другое место, возможно, у вас закончилось место, и вы начинаете хранить некоторые изображения на диске A, а некоторые - на диске B и т. Д. Легче изменить одну функцию, чем изменить пути в базе данных .

Я бы использовал такой алгоритм для создания структуры каталогов:

  1. Сначала введите порядковый номер с ведущими нулями, пока не получите хотя бы 12-значную строку. Это имя вашего файла. Вы можете добавить суффикс:
    • 12345 -> 000000012345.jpg
  2. Затем разделите строку на блоки из 2 или 3 символов, где каждый блок обозначает уровень каталога. Иметь фиксированное количество уровней каталогов (например, 3):
    • 000000012345 -> 000/000/012
  3. Сохраните файл в сгенерированном каталоге:
    • Таким образом, полный путь и имя файла для файла с идентификатором последовательности 123 является 000/000/012/00000000012345.jpg
    • Для файла с идентификатором последовательности 12345678901234 путь будет 123/456/789/12345678901234.jpg

Некоторые вещи, которые следует учитывать в отношении структур каталогов и хранилища файлов:

  • Вышеупомянутый алгоритм дает вам систему, в которой каждый конечный каталог имеет максимум 1000 файлов (если у вас меньше 1000000000000 файлов).
  • Могут быть ограничения на количество файлов и подкаталогов, которые может содержать каталог, например файловая система ext3 в Linux имеет ограничение в 31998 подкаталогов на один каталог.
  • Обычные инструменты (WinZip, Windows Explorer, командная строка, оболочка bash и т. Д.) Могут работать не очень хорошо, если у вас большое количество файлов в каталоге (> 1000).
  • Сама структура каталогов займет некоторое место на диске, поэтому вам не нужно слишком много каталогов.
  • С приведенной выше структурой вы всегда можете найти правильный путь к файлу изображения, просто посмотрев на имя файла, если вы случайно испортили структуру каталогов.
  • Если вам нужно получить доступ к файлам с нескольких машин, рассмотрите возможность совместного использования файлов через сетевую файловую систему.
  • Приведенная выше структура каталогов не будет работать, если вы удалите много файлов. Это оставляет «дыры» в структуре каталогов. Но поскольку вы не удаляете файлы, все должно быть в порядке.

Я собираюсь вложить свои 2 цента в негативный совет: не используйте базу данных.

Я работаю с базами данных для хранения изображений в течение многих лет: большие (1 мегабайт -> 1 гигабайт) файлы, часто меняющиеся, несколько версий файла, доступ к которым осуществляется достаточно часто. Проблемы с базой данных, с которыми вы сталкиваетесь при хранении больших файлов, чрезвычайно утомительны, проблемы с записью и транзакциями являются запутанными, и вы сталкиваетесь с проблемами блокировки, которые могут вызвать серьезные крушения поездов. У меня больше практики в написании сценариев dbcc и восстановлении таблиц из резервных копий, чем следовало бы любому нормальному человеку. Когда-либо иметь.

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

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

Допустим, у вас есть хэш файла, который выглядит примерно так 515d7eab9c29349e0cde90381ee8f810
Вы можете хранить это в следующем месте и использовать столько уровней, сколько вам нужно, чтобы количество файлов в каждой папке было небольшим.
\51\5d\7e\ab\9c\29\349e0cde90381ee8f810.jpg

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

В идеале вы должны запустить несколько тестов на время произвольного доступа для различных структур, так как настройки вашего конкретного жесткого диска, кэширование, доступная память и т. Д. Могут изменить эти результаты.

Предполагая, что у вас есть контроль над именами файлов, я бы разделил их на уровне 1000 сек на каталог. Чем больше уровней каталогов вы добавляете, тем больше inodes вы записываете, поэтому здесь есть push-pull.

Например.,

/ корень / [0-99] / [0-99] / имя файла

Заметка, http://technet.microsoft.com/en-us/library/cc781134(WS.10).aspx есть более подробная информация о настройке NTFS. В частности, «Если вы используете большое количество файлов в папке NTFS (300 000 или более), отключите генерацию коротких имен файлов для повышения производительности, особенно если первые шесть символов длинных имен файлов похожи».

Вам также следует изучить возможность отключения функций файловой системы, которые вам не нужны (например, время последнего доступа). http://www.pctools.com/guides/registry/detail/50/

Что бы вы ни делали, не храните их все в одном каталоге.

В зависимости от распределения имен этих изображений вы можете создать структуру каталогов, где у вас будут папки верхнего уровня с одной буквой, где у вас будет другой набор подпапок для второй буквы изображений и т. Д.

Так:

Папка img\a\b\c\d\e\f\g\ будет содержать изображения, начинающиеся с 'abcdefg' и так далее.

Вы можете ввести свою необходимую глубину.

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

У нас есть система хранения фотографий с 4 миллионами изображений. Мы используем базу данных только для метаданных, и все изображения хранятся в файловой системе с использованием обратной системы именования, где имена папок генерируются из последней цифры файла, последней цифры 1 и т. Д. например: 000001234.jpg хранится в структуре каталогов типа 4 \ 3 \ 2 \ 1 \ 000001234.jpg.

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

В новом MS SQL 2008 есть новая функция для обработки таких случаев, она называется FILESTREAM. Взглянуть:

Обзор Microsoft TechNet FILESTREAM

Я бы сохранил их в файловой системе, но это зависит от того, насколько быстро будет расти количество файлов. Размещены ли эти файлы в Интернете? Сколько пользователей получат доступ к этому файлу? Это вопросы, на которые нужно ответить, прежде чем я смогу дать вам лучшую рекомендацию. Я бы также посмотрел на Haystack из Facebook, у них есть очень хорошее решение для хранения и обслуживания изображений.

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

Нужно ли будет давать вашим изображениям однозначное название? Может ли процесс, генерирующий эти изображения, создавать одно и то же имя файла более одного раза? Трудно сказать, не зная, какое устройство создает имя файла, но сказать, что устройство «перезагружено» и после перезапуска оно начинает именовать изображения, как это было в последний раз, когда оно было «сброшено» - если это такая проблема ..

Кроме того, вы говорите, что получите 1 миллион изображений за месяц. Как насчет этого? Как быстро эти изображения будут продолжать заполнять файловую систему? В какой-то момент они завершатся и выровняются примерно до 1 миллиона ВСЕГО изображений или будет ли он продолжать расти и расти месяц за месяцем?

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

imgs\yyyy\mm\filename.ext

where: yyyy = 4 digit year
         mm = 2 digit month

example:  D:\imgs\2009\12\aaa0001.jpg
          D:\imgs\2009\12\aaa0002.jpg
          D:\imgs\2009\12\aaa0003.jpg
          D:\imgs\2009\12\aaa0004.jpg
                   |
          D:\imgs\2009\12\zzz9982.jpg
          D:\imgs\2010\01\aaa0001.jpg (this is why I ask about uniqueness)
          D:\imgs\2010\01\aab0001.jpg

Месяц, год и даже день подходят для изображений типа защиты. Не уверен, что это то, что вы делаете, но я сделал это с помощью домашней камеры безопасности, которая снимала фото каждые 10 секунд ... Таким образом, ваше приложение может перейти к определенному времени или даже к диапазону, в котором вы могли подумать, что изображение было сгенерировано . Или вместо года, месяца - есть ли другое «значение», которое можно извлечь из самого файла изображения? Какие-то другие дескрипторы, кроме приведенного мной примера даты?

Я бы не стал хранить двоичные данные в БД. Никогда не было хорошей работы / удачи с такими вещами. Не могу представить, что он хорошо работает с 1 миллионом изображений. Я бы сохранил имя файла и все. Если все они будут в формате JPG, даже не сохраняйте расширение. Я бы создал управляющую таблицу, в которой хранился бы указатель на сервер, диск, путь к файлу и т. Д. Таким образом, вы можете переместить эти изображения в другое поле и по-прежнему находить их. Вам нужно пометить изображения ключевыми словами? Если это так, то вам нужно создать соответствующие таблицы, которые допускают такую ​​пометку.

Вы / другие, возможно, обсуждали эти идеи, пока я отвечал .. Надеюсь, это поможет ..

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

Я участвую в проекте, в котором в течение года хранится 8,4 миллиона изображений для документирования состояния различных устройств. Более свежие образы получают доступ чаще, а старые образы ищут редко, если не обнаружено условие, побудившее кого-то покопаться в архивах.

Мое решение, основанное на таком использовании, заключалось в постепенном архивировании изображений в сжатые файлы. Изображения представлены в формате JPG, каждое примерно по 20 КБ и не сильно сжимаются, поэтому схема сжатия ZIP отсутствует. Это делается просто для объединения их в одну запись файловой системы, что значительно помогает NTFS с точки зрения скорости, когда дело доходит до их перемещения с диска на диск или просмотра списка файлов.

Изображения старше суток объединяются в «ежедневный» zip; почтовые индексы старше месяца объединяются в соответствующие "ежемесячные" почтовые индексы; и, наконец, все, что больше года, больше не нужно и, следовательно, удаляется.

Эта система работает хорошо, потому что пользователи могут просматривать файлы (либо через операционную систему, либо через ряд клиентских приложений), и все имена названы на основе имен устройств и временных меток. Обычно пользователь знает эти две части информации и может быстро найти любое из миллионов изображений.

Я понимаю, что это, вероятно, не связано с вашими конкретными деталями, но я подумал, что поделюсь.

Я был бы склонен создать структуру папок на основе даты, например \ год \ месяц \ день и используйте метки времени для имен файлов. Если необходимо, метки времени могут иметь дополнительный компонент счетчика, если изображения должны создаваться так быстро, что их может быть больше одного в пределах миллисекунды. Используя последовательность от наиболее значимого до наименее значимого для сортировки имен, поиск и обслуживание становятся легкими. например ччммссмм [seq] .jpg

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

String fileName = "cat.gif";
int hash = fileName.hashCode();
int mask = 255;
int firstDir = hash & mask;
int secondDir = (hash >> 8) & mask;

Это приведет к тому, что путь будет:

/172/029/cat.gif

Затем вы можете найти cat.gif в структуре каталогов путем воспроизведения алгоритма.

Использовать HEX в качестве имен каталогов так же просто, как преобразовать int ценности:

String path = new StringBuilder(File.separator)
        .append(String.format("%02x", firstDir))
        .append(File.separator)
        .append(String.format("%02x", secondDir)
        .toString();

В результате чего:

/AC/1D/cat.gif

Я написал об этом статью несколько лет назад и недавно переместил ее на Medium. В нем есть еще несколько деталей и пример кода: Хеширование имени файла: создание хэшированной структуры каталогов. Надеюсь это поможет!

Хотя я не подавал изображения в таком масштабе, я ранее написал небольшую галерею для обслуживания ~ 25k изображений на машине с частотой 400 МГц w. 512 МБ ОЗУ или около того. Некоторый опыт;

  • Любой ценой избегайте реляционных баз данных; хотя базы данных, без сомнения, умеют обрабатывать данные, они не предназначены для такого использования (у нас есть специализированные иерархические базы данных ключ-значение для того, что называется файловые системы). Хотя у меня нет ничего, кроме догадки, я готов поспорить, что кеш БД вылетит из окна, если вы бросите в него действительно большие капли. В то время как мое доступное оборудование было малочисленным, не касаясь БД при поиске изображений, скорость была на порядки выше.

  • Изучите, как ведет себя файловая система; на ext3 (или тогда это была ext2 - не могу вспомнить) предел возможности эффективного поиска подкаталогов и файлов был около 256; так что в любой папке есть только такое количество файлов и папок. Опять же заметное ускорение. Хотя я ничего не знаю о NTFS, такие вещи, как XFS (которые, насколько я помню, используют B-деревья), работают очень быстро просто потому, что они могут выполнять поиск очень быстро.

  • Равномерно распределять данные; когда я экспериментировал с вышеизложенным, я попытался равномерно распределить данные по всем каталогам (я сделал MD5 URL-адреса и использовал его для каталогов; /1a/2b/1a2b...f.jpg). Таким образом, достижение любого установленного предела производительности занимает больше времени (и кеш файловой системы в любом случае пуст для таких больших наборов данных). (напротив, вы можете захотеть увидеть, где находятся ограничения на раннем этапе; затем вы захотите выбросить все в первый доступный каталог.

Вы рассматриваете возможность аварийного восстановления?

Некоторые из предлагаемых здесь решений приводят к искажению имени файла (например, если физический файл был перемещен, вы потеряете представление о том, что это за файл на самом деле). Я рекомендую сохранить уникальное физическое имя файла, чтобы, если ваш главный список местоположений файлов будет поврежден, вы можете восстановить его с помощью небольшой оболочки, э-э, powershell, скрипта;)

Из того, что я здесь прочитал, похоже, что все эти файлы будут храниться в одной файловой системе. Рассмотрите возможность хранения их в нескольких файловых системах на нескольких машинах. Если у вас есть ресурсы, определите систему хранения каждого файла на двух разных машинах на случай, если вы потеряете блок питания и замена займет 2 дня.

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

Вы можете рассмотреть возможность использования GUID в качестве физического имени файла вместо инкрементного номера в случае, если ваш счетчик инкрементных номеров (столбец идентификаторов базы данных?) Испортился.

При необходимости рассмотрите возможность использования CDN, например Amazon S3.

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

  • Каждый день генерируется несколько изображений: Year/Month/Day/Hour_Minute_Second.png
  • Пару в месяц: Year/Month/Day_Hour_Minute_Second.png

и т.д. Вы меня поняли ... =)

Я вижу другие упоминания о базе данных, но не вижу упоминания об этом в вашем сообщении. В любом случае, мое мнение по этому поводу таково: придерживаться базы данных или файловой системы. Если вам нужно смешать два, будьте осторожны. Все становится сложнее. Но вам, возможно, придется. Хранить миллион фотографий в базе данных - не лучшая идея.

Возможно, вас заинтересует следующая спецификация, большинство цифровых камер следуют ей для управления хранилищем файлов: https://en.wikipedia.org/wiki/Camera_Image_File_Format

По сути, создается папка, например 000OLYMPUS и фотографии добавляются в эту папку (например, DSC0000.RAW). Когда счетчик имени файла достигает DSC9999.RAW создается новая папка (001OLYMPUS) и изображение добавляются снова, счетчик сбрасывается, возможно, с другим префиксом (например: P_0000.RAW).

В качестве альтернативы вы также можете создавать папки на основе частей имени файла (уже упоминалось несколько раз). Например, если ваше фото называется IMG_A83743.JPG, хранить в IMG_\A8\3\IMG_A83743.JPG. Его сложнее реализовать, но он упростит поиск файлов.

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

Я только что провел тест на zfs, потому что мне нравится zfs, и у меня был раздел размером 500 ГБ, на котором было сжатие. Я написал сценарий, который сгенерировал 50-100k файлов и поместил их во вложенные каталоги 1/2/3/4/5/6/7/8 (глубина 5-8 уровней) и позволил ему работать, я думаю, 1 неделю. (это был не лучший сценарий.) Он заполнил диск, и в результате оказалось около 25 миллионов файлов или около того. Доступ к любому файлу с известным путем был мгновенным. Список любого каталога с известным путем был мгновенным.

Однако подсчет списка файлов (с помощью find) занял 68 часов.

Я также провел тест, поместив много файлов в один каталог. Прежде чем я остановился, у меня было около 3,7 миллиона файлов в одном каталоге. Вывод каталога для подсчета занял около 5 минут. Удаление всех файлов в этом каталоге заняло 20 часов. Но поиск и доступ к любому файлу был мгновенным.

Если они ВСЕ не требуются немедленно, и вы можете сгенерировать их на лету, а это небольшие изображения, почему бы не реализовать LRU-память или дисковый кеш над генератором изображений?

Это могло бы спасти вас от хранения и сохранить горячие изображения для обслуживания из памяти?

Если у вас Windows, как насчет файловой системы exFat?

http://msdn.microsoft.com/en-us/library/aa914353.aspx

он был разработан с учетом хранения медиафайлов и доступен сейчас.

Простой способ сгенерировать путь из большого числа - легко преобразовать его в шестнадцатеричный, а затем разделить!

например 1099496034834 > 0xFFFF1212 > FF/FF/12/12

public string GeneratePath(long val)
{  
    string hex = val.ToString("X");
    hex=hex.PadLeft(10, '0');
    string path="";
    for(int i=0; i<hex.Length; i+=2 )
    {
        path += hex.Substring(i,2);
        if(i+2<hex.Length)
            path+="/";
    }
    return path;
}

Хранить и загружать:

public long Store(Stream doc)
{
   var newId = getNewId();
   var fullpath = GeneratePath(newId)
   // store into fullpath 
   return newId;
}

public Stream Load(long id)
{
   var fullpath = GeneratePath(newId)
   var stream = ... 
   return stream;
}

Полные исходные коды: https://github.com/acrobit/AcroFS

Возможно, вы захотите взглянуть на ZFS (файловая система, менеджер томов от Sun) С уважением

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

Использование менеджера баз данных - безусловно, лучший вариант; простой, например, BDB или GDBM; даже относительная СУБД вроде MySQL была бы лучше. Только ленивые люди, которые не разбираются в файловых системах и базах данных (например, те, кто отклоняет транзакции), склонны использовать файловые системы в качестве баз данных (или несколько реже, наоборот).

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

Если вы ожидаете масштабирования, почему бы не масштабировать его сейчас? Вы сэкономите время как сейчас, так и позже, ИМО. Реализуйте уровень базы данных один раз, что довольно легко начать. Или реализуйте что-нибудь с папками и именами файлов и бла-бла-бла, а затем переключитесь на что-то другое, когда вы начнете взрывать MAX_PATH.