Хранение файлов

    За последнее время мне пришлось активно работать с сайтами, которые большие объемы информации хранят в файловой системе. Это разнообразные сайты фото и файловых хостингов, а также сайты с загрузкой видео контента, некоторые сайты проектировались и программировались мной с нуля, некоторые переписывались, дописывались или «приводились в порядок».
    Должен отметить, что хранение файлов в файловой системе является для многих программистов областью, которая проходит мимо их внимания.
    Для начала дам небольшой обзор распространенных ошибок:
    1. файл храниться в файловой системе под кириллическим названием. Собственно, происходит следующее: пользователь загружает файл под именем, скажем, «безымянный-1.jpg», программист с тем же именем запихивает его в каталог, в котором хранятся файлы. Надеюсь, не нужно объяснять какие проблемы это за собой может повлечь?
    2. файл храниться под тем же названием, под которым он был загружен пользователем, но символы не входящие в латинский алфавит транслитерированы. Уже лучше, но все равно данный способ вызывает множество проблем, например пользователи очень любят грузить файлы с одинаковыми названиями))) И дело не в том, что они такие злые, например мой фотоаппарат после каждой очистки карты памяти начинает нумерацию фотографий с 00001.
    И третья самая распространенная ошибка:
    3. Хранения в директории количества файлов превышающих возможности файловой системы. Рассмотрим эту ситуацию на конкретном примере, переписывал я файловый хостинг, большой, на момент переписывания объем информации вплотную приближался к четырем терабайтам, и это притом, что 80 процентов файлов были картинками. Все файлы на диске (дисков было 4 каждый по терабайту) случайным образом раскидывались по двум десяткам директорий, и так до заполнения диска, потом программа переходила на следующий диск. В результате для того что бы открыть директорию вебсерверу требовалось около трех секунд. Согласитесь, это катастрофически много. В каждой директории на диске находилось около двадцати тысяч файлов.
    Проанализировав несколько таких ситуаций, я попробовал вывести способ хранения файлов, который бы удовлетворял следующим условиям:
    1. директория не должна тормозить, то есть в одной директории не должно храниться более 1000 файлов или каталогов (число взято с запасом).
    2. имена файлов не должны повторяться.
    3. желательно не хранить две копии одного файла.
    После некоторых раздумий, я пришел к следующей схеме, которой и хочу поделиться с коллегами программистами.
    Начну с последнего требования не хранить две копии файла. Для определения целостности файла давно и вполне успешно используется md5 хеш для php, эта задача решается функцией md5_file(filename), которая вычисляет MD5 хэш файла, имя которого задано аргументом filename используя алгоритм MD5 RSA Data Security, Inc. и возвращает этот хэш. Хэш представляет собой 32-значное шестнадцатеричное число.
    Если два файла одинаковы у них и хеш будет одинаков, если разные — то разный. Сейчас в меня «полетят камни» сопровождающиеся рассуждениями о коллизиях и ненадежности md5. Отвечу по порядку md5 не надежна? Но мы же не ставим задачу обмануть «вероятного противника»! Мы просто получаем уникальный идентификатор файла и все. А по поводу коллизий… я не настаиваю на повторении моего метода один в один, используйте другую функцию. Только задумайтесь, два в двести пятьдесят шестой степени это — очень много! Если мне говорят, про возможность возникновения коллизий, я прошу человека привести пример двух строк или двух файлов, md5 хеш, которых одинаков… пока еще мне не было приведено такой пары так, что возможность является чисто теоретической.
    Пункт второй – «имена файлов не должны повторяться, напрямую вытекает из третьего. Если в качестве имени файла на диске мы используем строку его md5 хеша, то имена файлов не повторяются (реальные имена файлов (те, которые загрузил пользователь), мы можем хранить в базе данных). В случае загрузки пользователями двух одинаковых файлов, мы получаем у них одинаковые имена. И первое — файлы не будут дублированы, второе — мы не беспокоимся по поводу имен в директориях.
    Теперь чуть более сложно, по поводу хранения файлов на диске. Я создаю структуру вложенных каталогов, опираясь на имена файлов. Здесь тоже полный простор для фантазии. Я ни в коем случае не призываю слепо копировать мой способ. Обычно я делаю два, три уровня вложенности каталогов. Первый уровень — это первые две буквы названия файла (не забыли, название файла это его md5 хеш!); второй уровень это третья и четвертая буквы…
    Каждый уровень вложенности дает мне * на 256 каталогов.
    То есть, если в один каталог я могу загрузить не более 1000 файлов, то при одном уровне вложенности я могу безопасно разместить на диске 256 000 файлов; при двух уровнях вложенности – 65 536 000; при трех – 16 777 216 000 и так далее. Длинна строки md5 хеша позволяет нам сделать 16 уровней вложенности в каталогах. На мой взгляд, этого хватит для обеспечения работы самых емких дисков. Хотя, исходя из практика, обычно, и трех уровней хватает «за глаза» для проектов любой сложности.
    P.S. Обновленный и развернутый вариант (написан по итогам обсуждения)
    Поделиться публикацией

    Комментарии 45

      +5
      >1. файл храниться в файловой системе под кириллическим названием.
      Файловые системы уже давно поддерживают юникод, а там хоть в арабском названии — проблем нет.

      >2. файл храниться под тем же названием, под которым он был загружен пользователем, но символы не входящие в латинский алфавит транслитерированы.
      Зачем?

      >3. Хранения в директории количества файлов превышающих возможности файловой системы.
      Поменяй файловую систему, xfs, gfs — выбирай какую хочешь.

      >1. директория не должна тормозить, то есть в одной директории не должно храниться более 1000 файлов или каталогов (число взято с запасом).

      Тормоза «директории» отсутсвуют при ее кеширование в память, что делает нормальная реализация ФС и ОС

      >2. имена файлов не должны повторяться.
      Спасибо, К.О.!

      >3. желательно не хранить две копии одного файла.
      А вот если сервера разные — то желательно, lustre в помощь

      Мораль — если сисадмин криво засетапил сервак изначально, то потом программист вынужден делать анальный велосипед без сидлушки
        0
        извините первый раз не туда написал…
        *******
        Это Вы меня спрашиваете зачем? откуда я знаю? открываю сайт который меня наняли переписать и ВИЖУ…
        админ это клево конечно… пример из жизни: сайт называть не буду да бы не обвинили в пиаре или контр пиаре))) Переписал сайт выкатил на тестовый хостинг, тестируем, устраняем мелкие баги… тут звонок от заказчика диалог примерно следующий:
        — будте добры потеряйте нагрузку на базу которую ваш сайт создает!
        — извините я не могу у меня нет доступа попросите своего администратора
        — Наш администратор говорит, что он это сделать не может…
        А Вы говорите кривые руки администратора…
        В жизни и не такое иногда встречается…
          0
          исправление опечатки: *Проверьте
            –7
            Если нужен нормальный сисадмин — направляйте http:/www.esupport.org.ru
              +4
              да не вопрос! как только возникнет проблема, тут же направлю!
              Скажите, вот мне звонит заказчик — «у нас сайт тормозит, мы хотим заказать Вам его переделку» А я в ответ — «У Вас сисадмин идиот наймите другого — перестанет тормозить!»
              Вопросов два:
              1) Заменит ли заказчик своего системного администратора (ага особенно если звонил менеджер а Одминит в их конторе племянник генерального) (встречал в жизни....)
              2) Сколько денег я потеряю если начну так делать?
              **********
              К сожалению Мы живем в мире где правило — «Если хочешь, что бы было хорошо сделай сам» работает в полный рост, Если Вы живете там где администраторы по первому требованию готовы пересобрать сервер, а заказчик как только Вы скажите, что железо не тянет, тут же купит новое… Я Вам завидую и хочу в Вам! ))))
                0
                Та не вопрос, скиньте контакты в хабрапочту
                А то меня постоянно спрашивают за программиста, так что буду советовать :)
                  0
                  Дык уже! )))
          • НЛО прилетело и опубликовало эту надпись здесь
              +2
              Это написали, Вы а не я))))
              Предлагаю закрыть тему сисадминов, примеров можно приводить массу и в ту и в другую сторону)))
              Статья была не об этом)))
            0
            Это Вы меня спрашиваете зачем? откуда я знаю? открываю сайт который меня наняли переписать и ВИЖУ…
            админ это клево конечно… пример из жизни: сайт называть не буду да бы не обвинили в пиаре или контр пиаре))) Переписал сайт выкатил на тестовый хостинг, тестируем, устраняем мелкие баги… тут звонок от заказчика диалог примерно следующий:
            — будте добры потеряйте нагрузку на базу которую ваш сайт создает!
            — извините я не могу у меня нет доступа попросите своего администратора
            — Наш администратор говорит, что он это сделать не может…
            А Вы говорите кривые руки администратора…
              +3
              Извините, но ни о чем. Долго разливались о проблемах и неправильных (точнее откровенно глупых) приемах хранения файлов, а потом предложили то, что и так используется во всех более менее серьезных проектах: хранение инфы о файле в бд с изменением имени файла на уникальный ключ.

              Вот вы бы лучше провели более глубокие тесты на производительность директорий в зависимости от иерархии, уровня вложенности и количества файлов в них. На разных фс и с разными кешерами. Было бы и то полезнее, хотя думаю такие тесты тоже проводились кем то уже не раз
                0
                кстати как то искал такой тестик, увы не нашел
                –1
                Вы не написали ничего нового — уж сколько я видел «разумных» хостингов тех же фотографий, большинство из них хранили изображение по хеш-ключу (не обязательно md5), и били на папки по одной/двум буквам имени файла. Это обычная практика.
                  +1
                  Разумные фотохостинги, переписывать не нанимают, они как работали так и будут работать и описанных проблем иметь не будут.
                  А кстати, как хранит файлы самый популярный движок для блогов? (имеется ввиду вордпрес) и сколько в интернете советов — «для ускорения работы» отключить вложенность директорий?
                  –2
                  что такое «директория не должна тормозить»?
                    +1
                    ок. переформулирую фразу. — «открытие директории веб сервером, не должно занимать более одной миллисекунды»
                      –3
                      все равно не понял. что такое «открытие директории веб сервером»?
                        +1
                        Попробуйте погуглить по запросу «открытие директории» я уверен, что на первых двух страницах Вы найдете намного более развернутый и аргументированный ответ на свой вопрос, чем тот, что мне позволит сделать здесь формат комментариев на хабре.
                          –1
                          Вы это делали? Попробуйте.
                          «Что ты понимаешь под открытием директории мне непонятно.» © третья ссылка
                          Остальные ссылки относятся к файловым менеджерам и другие мимо цели.

                          Я без ехидства спрашиваю ведь. Под «открытием», Вы подразумеваете получение списка файлов?
                            0
                            Получение списка файлов, проверка существования файлов, считывание файла с определенной позиции…
                            Насколько я помню все эти операции так или иначе требуют получения дескриптора каталога в файловой системе.
                              0
                              Список файлов, как я понимаю, в рассматриваемых случаях не нужен, т.к. информация о файлах хранится в БД.

                              Скажите, чтение файла, лежащего в каталоге вместе с 100000 других файлов произойдет медленнее, чем чтение файла, лежашего в каталоге в гордом одиночестве? И как отдельный случай: а если запрашиваемый файл отсутствует?
                                0
                                Конечно медленнее, потому как полный путь к файлу еще не указывает напрямую на физическое расположение файла, а чтобы его узнать необходимо просканировать директорию линейным поиском либо, при включенном dir_index, будет использован b-tree индекс, что значительно ускорит поиск файла
                                  0
                                  Понятно, спасибо.
                    +4
                    Относительно к
                    Если мне говорят, про возможность возникновения коллизий, я прошу человека привести пример двух строк или двух файлов, md5 хеш, которых одинаков… пока еще мне не было приведено такой пары так, что возможность является чисто теоретической.

                    Насчет вероятных коллизий википедия говорит:
                    В 2005 году Ван Сяоюнь и Юй Хунбо из университета Шаньдун в Китае опубликовали алгоритм, который может найти две различные последовательности в 128 байт, которые дают одинаковый MD5-хеш. Одна из таких пар (отличающиеся разряды выделены):

                    d131dd02c5e6eec4693d9a0698aff95c
                    2fcab58712467eab4004583eb8fb7f89
                    55ad340609f4b30283e488832571415a
                    085125e8f7cdc99fd91dbdf280373c5b
                    d8823e3156348f5bae6dacd436c919c6
                    dd53e2b487da03fd02396306d248cda0
                    e99f33420f577ee8ce54b67080a80d1e
                    c69821bcb6a8839396f9652b6ff72a70

                    и

                    d131dd02c5e6eec4693d9a0698aff95c
                    2fcab5O712467eab4004583eb8fb7f89
                    55ad340609f4b30283e4888325f1415a
                    085125e8f7cdc99fd91dbd7280373c5b
                    d8823e3156348f5bae6dacd436c919c6
                    dd53e23487da03fd02396306d248cda0
                    e99f33420f577ee8ce54b67080280d1e
                    c69821bcb6a8839396f965ab6ff72a70

                    Каждый из этих блоков даёт MD5-хеш, равный 79054025255fb1a26e4bc422aef54eb4.

                    Вероятность коллизии, конечно возрастает с ростом размера файла, но все-равно очень мала.
                      +1
                      Спасибо за пример, серьезно Вы первый кто ответил на мой вопрос про две строки!
                        0
                        Эти блоки никак не похожи на содержимое графических файлов.
                        Т.е. я о том, что произвольное содержимое (то, которое можно произвольно менять) ещё можно подогнать под коллизии. А вот содержимое, строящееся по определённым правилам, под коллизии подобрать гораздо сложней.
                          0
                          > А вот содержимое, строящееся по определённым правилам, под коллизии подобрать гораздо сложней.

                          Не уверен по поводу графических файлов, но однажды, при написании парсера (краулера/спайдера/лоадера/как кому нравится), столкнулся с интересной вещью… Для начала маленькое вступление: надо было вытянуть инфу с сайта имевшего следующую структуру: список букв, на странице буквы пагинатор, на кадой букво-pageN список линков на продукты. Вроде всё просто и очевидно…
                          Но оказалось, что после запуска скрипта он глючит на 3ей ссылке. Что я уже только и не делал — безрезультатно.
                          Когда же я узнал истинную причину проблем — чуть не охренел. Для каждой ссылки считался хеш, чтобы в дальнейшем было проще отслеживать обработанные и необработанные ссылки. Так вот, оказалось, что у двух идущих подряд ссылок был одинаковый хеш 0_о
                          Ссылки имели вид host.com/a/product1.html, host.com/a/product2.html — различия только в последней части…
                          Вот такие вот случаются казусы :)
                            0
                            Хэш был MD5? Если да, то будьте добры строки в студию.

                            Это будет, вероятно, первый зафиксированный в истории случай коллизии между двумя _осмысленными_ сообщениями.
                              0
                              к сожалению источников не осталось, так как дело было более года назад. хеш был crc32, если мне не изменяет память. но комментом я не хотел сказать о ущербности md5 или отсутствия таковой :)
                              всяко бывает…
                                0
                                А, ну CRC32 — это не интересно… на это считай что каждый напарывался…
                              0
                              если честно, не верится :) изменения даже в один бит меняют хеш до неузнаваемости.
                              наверно, все же глюк был в другом.
                                0
                                нет, проблемы была именно в этом: не предусмотрел возможность одинакового хеша
                          0
                          Я когда-то тоже сделал файлохранлище. И вот что у меня есть сказать по этому поводу:
                          1. Имена файлов.
                          Можно хранить файл под именем, равным его идентификатору в базе, а браузеру выдавать имя файла из базы. Тогда и коллизий не будет, и проблем с русскоязычными названиями.
                          Единственный довольно большой минус — приходится отдавать файл скриптом.
                          2. Если пользователь загружает файл, хеш которого уже есть в базе (т.е. по сути дубликат), можно не хранить его, а сделать жёсткую ссылку на файл с таким же хешем. Тогда фактически будут храниться два файла, занимающих место одного.

                          А вот проблема, с которой я не справился (забил на тот момент) — в разделе кончились ноды. Пересоздавать раздел с другими настройками не было возможности, т.к. некуда было забекапить столько данных. ФС UFS.
                            0
                            использую совершенно лишенную смысла микрооптимизацию для экономии 7 байт трафика при запросе браузера и 7 байт в базе:
                            $hash = base_convert($md5, 16, 36);
                              0
                              Учите русский язык, потом уже занимайтесь другими языками (php):
                              > файл храниться
                              сиди и гадай пропущено тут «должен» или человек не знает банальных правил
                                –1
                                Вот сиди и гадай, читал человек текст или зацепил два слова…
                                В приведенном Вами примере, идет перечисление ошибок, допускаемых разработчиками. Поэтому слово «должен» в данном контексте неуместно так как ошибка НЕ ДОЛЖНА быть повторена.
                                Если Вам интересна точная мнемоническая расшифровка фразы «файл храниться» в контексте первых двух пунктов списка ошибок, то в данном случае следует читать следующим образом:
                                — «Некоторые разработчики в своих проектах хранят файл»…
                                  0
                                  Ну что Вы сразу накинулись на человека? Ошибка ведь действительно есть, человек просто таким образом намекнул почему она его «задела». Ошибок в тексте вообще полно, но мы-то тут не для этого «собрались» :)
                                  P.S. Хотя тексты без ошибок читаются лучше...
                                    0
                                    он имел ввиду, что «ь» лишний.
                                  0
                                  Бывают еще любители фаилы в базу писать. Сам пока не встречал случая где это было действительно необходимо.
                                    0
                                    Вы знаете я тоже не встречал… При работе с базой данных свои ошибки…
                                    0
                                    Мне другое интересно. ВК и фб используют хранения в одном большом файле и потом в памяти сдвигают. Как такое сделать и как разносить тогда файлы по серверам? А вообще по названию я делаю так: md5($file_name.uniqid()); и потом храню путь в бд.
                                      0
                                      Да, кстати, самое интересное… хотя для хранения файлов и используют файловую систему XFS, но тем не менее все данные хранятся в одном большом файле, открытом на чтение, и каждый новый загружаемый контент дописывается в конец этого большого файла. Индекс, по которому искать контент, хранится в оперативке, и ведется некий бинарный лог для восстановления индекса, в случае непредвиденной перезагрузки сервера. Как говорят разработчики Вконтакте, при обычном (стандартном хранении данных, как мы привыкли — по папкам и подпапкам) FS при прямом доступе к файлам делает кучу ненужных операций, что может оказаться весьма нежелательным и плохо сказаться не производительности. Так же проскользнула интересная мысль — для обработки документов использовать черный список (запрещенных типов), вместо белого (разрешенных).


                                      Я так понимаю одним php тут явно не обойтись.
                                      Скорее всего написали на C++
                                      Распределение по серверам думаю сделали по обьему данных.
                                      По любому вначале проверяют свободное место и обьем загружаемых данных.
                                        0
                                        По моему лучше имя файла в хранилище делать от содержимого, а не от имени… Так избежишь дублированной загрузки файлов.
                                        А хранить в одном большом файле, идея прекрасная но не для php и тут будет огромная проблема с удалением (будет оставаться пустое место в файлах) нужен хороший механизм оптимизации.
                                          0
                                          maxxxixxxx,
                                          Я свой стартап буду писать на с++, поскольку будет очень високие нагрузки(ну, кроме этого конечно настроить мастер-слейв и т.д).
                                          Нет, мне другое, вот они распределяют по серверам, но как они розпределяют если всё в одном файле.
                                          kamaikin, а как тогда удалят фотографию, если юзер захотел?
                                          А вообще оптимизировать надо, конечно. А для проекта на с++, как будет, если для php плохо?
                                            0
                                            Я бы удалял следующим образом: Когда удаляет пользователь то файл помечается как удаленный, и после этого раз в сутки (раз в час, раз в неделю) просто пересобирал файл уже без «ненужной» информации.
                                            Ну для PHP не все так плохо) В отличии от С++ для пыхи есть отработанные решения под высокую нагрузку имменно для веба.

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

                                        Самое читаемое