Как лучше хранить огромное число небольших файлов?
На сайте из LaTeX выражений генерируются png картинки с формулами. Они небольшие, но их очень много. Чтоб часто их не генерировать, они кэшируются. Пока картинок не очень много - видимых проблем нет. Но я уже задумываюсь, что будет дальше. Как лучше хранить огромное число небольших файлов? Хранить ли их в одной папке, или делать дерево вложенных папок такого вида /x1/x2/x3/.../xn/filename, где символы x1,...,xn пробегают, например, все значения от 0 до F. Если лучше хранить в дереве папок, то какая глубина вложенности оптимальная?
Система Debian, файлы считываются и отдаются с помощью PHP-скрипта.
Классический вариант - считать hash (md5, sha1, или sha256) для каждого файла при добавлении, в базе данных хранить хэш - имя фала - размер (опционально), а для записи на диск использовать следующий путь:
./e3/b0/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
первый уровень вложенности - первые два символа хэша в шестнадцатеричном представлении, второй уровень - третий и четвертый символ, имя файла - хэш; количество уровней вложенности можно увеличить, если файлов очень много.
В качестве альтернативного варианта рассмотрите возможность хранения этих объектов в базе данных. Обычно это не лучшая идея, но в вашем случае такой вариант может оказаться предпочтительным. Если на каждой странице содержится множество маленьких объектов, можно уменьшить количество запросов к серверу упаковывая их все в один ajax ответ на стороне сервера (получая содержимое из базы данных), и распаковывая с помощью javascript на клиенте. В некоторых случаях это может приводить к увеличению быстродействия (необходимо проверять на реальных данных).
Да, именно так я сейчас и делаю. Только сейчас у меня два уровня папок с именами из одного шестнадцатеричного символа, то есть полные имена файлов такого вида: /e/3/c44298fc1c149afbf4c89.
А как лучше: 4 уровня папок с именами по 1 символу, или два уровня с именами по 2?
Так
/e/3/c/4/4298fc1c149afbf4c899
или так
/e3/c4/4298fc1c149afbf4c899?
В обоих случаях конечное число папок будет одинаковым 16^4.
Один шестнадцатеричный символ - это нибл (полубайт), всего 16 возможных значений. Два символа - байт, 256 значений. С 256 объектами в каталоге прекрасно справится любая файловая система, нет никакого смысла сокращать их максимальное количество до 16, поэтому в своих проектах всегда применяю второй вариант.
habrastorage, кстати, использует трех-символьные имена подкаталогов (например, https://hsto.org/files/163/b9a/2a7/163b9a2a7dcf480... - это перебор. Больше 2 тысяч объектов в одном каталоге размещать не стоит, а 2^4^3 - это 4096. Файловые менеджеры при отображении содержимого каталогов всегда используют тот или иной тип сортировки (по имени, по дате, и т.д.), и если объектов слишком много, работать становится некомфортно.
Еще один момент, о котором необходимо позаботиться: если файлы могут быть загружены на сайт пользователями, то для предотвращения XSS уязвимостей размещать их нужно на отдельном домене.
neatsoft: А не много ли места будут занимать это дерево папок из 256+256*256 папок. Еще есть такой нюанс. Это хранилище с файлами нужно периодически очищать от неиспользуемых файлов. При каждом обращении к файлу обновляется дата его редактирования. php-скрипт по расписанию должен каждые сутки рекурсивно обходить все папки и удалять файлы по этой дате. Боюсь, будет тяжело за раз обходить все дерево.
256 каталогов в ext4 занимают 256*4094=1MiB, 256+256*256 каталогов - 257MiB.
Кстати, папки (folders) - это в винде, в линуксе - каталоги (directories) :)
Никаких сложностей в последовательном обходе 64K каталогов я не вижу, но гораздо лучше монтировать файловую систему с параметром noatime, а всю логику, включая контроль дат, возлагать на бэкэнд и базу данных. И nginx и apache позволяют контролировать нативную отдачу фалов из бэкэнда с помощью специальных заголовков - X-Accel-Redirect и X-Sendfile. Помимо контроля дат и прав доступа, использование этого механизма также позволит отдавать фалы используя осмысленные имена (например, /e3/b0/e3b0..b855/image.png вместо /e3/b0/e3b0..b855), и устанавливая правильные заголовки (например, Content-Disposition: attachment; filename="image.png").
Если лучше хранить в дереве папок, то какая глубина вложенности оптимальная?
Цель - распределить файлы так, чтобы в любом каталоге находилось не более тысячи (максимум - несколько тысяч) файлов. То есть, до нескольких миллионов файлов - двух уровней иерархии достаточно.