Pull to refresh

Comments 45

UFO just landed and posted this here
Minio — под свободной лицензией, за физическое место придётся платить в любом случае.
~~ entity_type char(32) not null default '', — Тип сущности~~

Можно было обойтись 'small int'
Вообще все переделать.


  • entity_type char(32) not null default '', — Тип сущности
  • mime char(32) not null default '', — MIME-тип
  • md5 char(32) not null default '', — MD5
  • sha1 char(40) not null default '', — SHA1
  • file char(64) not null default '', — Физическое расположение

Это ад и содомия. От такого вытекают глаза :)




Нужно:
  • Вынести в отдельные таблицы
    • entity_type
    • mime
    • url
    • file
    • md5
    • sha1
  • связать все по айди.

Иначе:
То есть берём идентификатор файла в шестнадцатеричном представлении, разбиваем оное на чанки по два символа.

Решение отличное, но при fixed rows у вас ~495 байт на запись, или ~246-495 в зависимости от заполненности varchar, когда у вас есть шанс иметь число цифровую главную табличку и пачку привязанных к ней. Удобство страдает только в самом начале (приходится более сложные sql писать), но эффективность и выдаваемый перфоманс максимальны.


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


Вопрос: зачем вам и md5 и sha1?


На md5 коллизии не новы, на sha1 гугл давно предоставил 2 pdf с одним хешем. Используйте sha256, и место в сумме съэкономите.


upd: забыл добавить про md5 & sha1.

Тут всё дело в том, что решение берётся из моей CMS, являющейся в силу своей архитектуры слабосвязанным набором данных. Целостность на уровне внешних ключей поддерживается лишь в рамках одного модуля или его явной связи с другими модулями. В реальной системе entity_type по факту означает модель данных. Сама модель может быть как реальной, так и мета-значением. Это во-первых.

Во-вторых, MIME-типы. Если следовать Вашей логике, то нужно делать системный справочник. А в этом справочнике нужно предусматривать все типы по сути. А их десятки. Если не сотни уже. Если мы будем грузить только изображения, то большая часть справочника будет тупо не задействована. Плюс возрастают накладные расходы при выборках по MIME-типу, ибо в моём случае просто индекс, а в Вашем придётся делать JOIN к таблице MIME-типов. К слову сказать то же касается и таблицы типов сущности.

Ну и в-третьих. Зачем выносить в отдельные таблицы URL, MD5 и SHA1 я вообще ума не приложу. А физическое расположение файла нужно исключительно на тот случай, если будет изменён алгоритм формирования физического имени файла. У меня в первоначальной версии было md5(последний_байт + URL).

В сухом остатке из Ваших тезисов имеет смысл задуматься только над entity_type и алгоритмами хэширования. Кои, кстати, в данном случае в принципе имеют чисто декоративное значение :)
Два запроса лучше одного с JOIN. Тем более на больших данных.
В реальной системе entity_type по факту означает модель данных

У вас там char, который гарантированно ест свои байты в любом случае. Его надо выносить в отдельный справочник (связанную таблицу).


Во-вторых, MIME-типы. Если следовать Вашей логике, то нужно делать системный справочник.

Конечно.


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

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


а в Вашем придётся делать JOIN к таблице MIME-типов

Я сам ранее был не сторонником JOIN'ов, чисто в визуальном плане + когда данные есть гарантированно, и не будет null, то и обычный запрос к двум таблицам, саб и join вернут одинаковый результат (хотя оптимизатор может переделать join в саб, а скорее всего сам саб в join, об этом есть тонны тасков про перфоманс в гугле).
Варианты:
1)


select d.*, t.mimeDesc from tdata d, tmimes t where 1=1 and t.id=d.mimeId;

2)


select d.*, (select t.mimeDesc from tmimes t where t.id=d.mimeId) as 'mimeDesc' from tdata d where 1=1;

3)


select d.*, t.mimeDesc from tdata d left join tmimes t on t.id=d.mimeId where 1=1;

Для случая, когда данные в tmimes есть всегда вам подойдет первый запрос. Он простой и очень быстрый.


Зачем выносить в отдельные таблицы URL, MD5 и SHA1 я вообще ума не приложу

Потому, что сие очень сильно ест размер таблицы, у вас там char, а записей хотите делать много. Далее — решили вы удалить md5, ибо больше не используете его на 245,874,492 записях… Грохнуть 8 байтовый столбик и табличку с md5 или 32 байтовой поле, как бы разный масштаб :)


В сухом остатке из Ваших тезисов имеет смысл задуматься только над entity_type и алгоритмами хэширования

Нет, стоит задуматся над переносом на 6 форму. От этого не уйти, рано или поздно оно прижмет и будет ныть и болеть. То, что я делал в стиле "удобно" сейчас переводится на 6-ую. Количество таблиц растет, глаза разбегаются, но софту то насрать. А когда будете вносить радикальные правки на большом объеме, то на 6й форме можно плакать от восторга, или ломать таблицы или базу на других формах.


На счет алгоритмов хеширование — на современном железе самый быстрый это sha1 (просто быстрый, так же и бесполезный). Если нужен алгоритм, который не sha1, это sha512:


type             16 bytes     64 bytes    256 bytes   1024 bytes   8192 bytes  16384 bytes
sha256           98888.60k   219086.70k   399258.79k   495975.42k   532501.85k   535702.19k
sha512           63937.97k   255262.34k   446925.14k   671571.97k   787614.38k   797944.49k```

Ибо sha512 расчитывается быстрее, чем 256, как только данные больше чем 64 байта (я его использую).

З.Ы. кстати, ключи можете делать `unsigned`, они же 1+, отрицательных нету.
Ну, второй пример, который вы нашли (и указали ссылку в начале своего текста) как бэ является де-факто решением для хранения всех файлов, с небольшим дополнением связей из конкретно вашей БД.

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

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

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

Лень делать скрины, но приведу вам стату: по запросу в гугле site: мой домен — ответ примерно такой: нашлось примерно 243 миллиона страниц.
В консоли гугла, средняя скорость загрузки страниц — 242 мc. Хостинг шаред (общий тысячи пользователей на одной машине) 300 рублей в месяц, плюс 50Гб места докупил, чтобы файлы влезали, нагрузки нет особой на хостинг от моего сайта этого.
Зачем вам вообще MIME-типы для картинок? Всё равно список расширений обычно фильтруется софтом, да и для фоток будет jpg или jpg? Вы же не будете фотки bmp хранить? gif и png тоже не особо подходят (ну для аватарок разве что), ну разве что новомодный webp, но вряд ли на сайте знакомств найдется, хотя бы один пользователь который зальёт свои фотки в webp.
Ну, a char(32) для mime, и еще и индекс по этому полю (вот интересно зачем он вообще) — это фэйл. Если так хотите хранить текстом MIME то для этого есть тип ENUM в MySQL.

Что касается хэшей, то я лично ничего не имею против md5 и sha1. Я не из тех кто кричит, караул в них нашли коллизии — значит их нельзя использовать. В любом хэше есть коллизии, так как такова природа самих кэшей, нельзя в 16-64 байта загнать мегабайт, чтобы не было коллизий. Потому, имхо, лучше использовать 2 хэша (чтобы было 2 разных алгоритма), что обычно и делается в программах бэкапа и дедупликации. Обычно используется быстрый хэш и медленный, но скажем так более стойкий.
Но у Вас проблема в том, что на этот более стойкий у вас тоже индекс. Зачем? Чтобы был?
Вы же картинки добавляете своим софтом, поэтому если нашли по md5, то проверили у найденных строк sha1. Нет смысла искать сразу по sha1, а потом по md5, так как если быстрый хэш не совпадает, то медленный уже даже можно не сравнивать.

Ну и конечно эта любовь к Bigint. К тому времени, как у Вас количество файлов превысит 4 млрд, вы уже не один раз переделаете и базу, и систему хранения, если вообще не закинете это дело. Особенно это касается полей по которым индексы.
Про Bigint для Size даже смешно, Вы что реально думаете, что файлы больше 4 гигов, будут заливаться тем же кодом, что и фотки/аватарки юзеров? Там своих костыликов припасено.

Ну и откройте для себя UNSIGNED для целочисленный полей.
mime type должен отдаваться сервером при отдаче файла, один раз его определить, записать в базу и потом отдавать из неё — неплохой вариант.

Список mime меняется, при каждом новом делать alter table?

bigint спорно, но вот unsigned лучше не использовать без очень-очень веских причин, особенно в случае если заведомо будут на клиентах языки его не поддерживающие, такие как PHP или JS.
и потом отдавать из неё — неплохой вариант

Что неплохо в том чтобы для трёх mime-типов по 10 символов (image/png, image/gif, image/jpg), держать в базе столбец CHAR(32) и еще и индекс по этому столбцу?
Список mime меняется, при каждом новом делать alter table?

И в чем проблема у Вас каждую неделю новый формат для изображений придумывают?
но вот unsigned лучше не использовать без очень-очень веских причин

Ага, особенно с UNSIGNED TINYINT ужасные проблемы.
Индекс-то вам зачем? Ну а определние типа по расширению — это огромная дыра в безопасности… Плюс вы забыли из популярных как минимум svg и bmp.

И с ним тоже, если у вас нет беззнаковых типов в ЯП/библиотеках

Году в 2008 вас бы похвалили за такой код.


Судя по оговоркам ("В качестве СУБД я использую расширение MySQLi", "законодательство, обязывающее хранить информацию о пользователях не менее полугода" в контексте обсуждения автоинкремента и пр.) — вы начинающий программист. Если поставите себе целью подтянуть код до современных стандартов, то через пару лет у вас получится то, что не стыдно показать широкой аудитории.


Сейчас же, уж извините, у вас старый добрый спгетти-код, даром что завернутый в класс. В одну кучу смешались SQL, HTTP, файловая система. Никакой абстракции, никакого разделения ответственности. Обработка ошибок где-то лишняя ('cannot make dir: ' ), где-то отсутствует совсем (mysqli), где-то однозначно вредная (echo $e->getMessage();).


В качестве работы над ошибками попробуйте сделать отдельные сервисы для работы с БД и HTTP. Для работы с таблицей файлов в БД также нужен будет отдельный класс. И ради бога, забудьте уже этот чудовищный mysql_query стайл. А то получается как в анекдоте — "ложечку вынул, а глаз все равно зажмуриваешь". Буковку i к вызовам функций приписал, а все остальное осталось как прежде.


В итоге вместо ручного колупания с SQL должно получиться что-то вроде


$this->repository->save($url, $meta, остальные параметры);

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

Простите, какой «mysql_query стайл»? MySQLi там используется в ООП-варианте. Там всё на ООП. Это раз. И два. Данный класс я выдернул из целостной системы, где и все сервисы существуют и обработка ошибок и вот это вот всё. Затем адаптировал для отдельного использования. Статья не о конкретном коде, а о самом принципе решения задачи. Код — лишь простейший пример реализации, являющий собой порядком урезанный код из реально работающей системы.

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


Добавления палочки со стрелочкой к вызову функции тоже недостаточно. Оно не делает ваш код объектно-ориентированным, а работу с БД менее унылой.


И ваша реакция на совершенно справедливое замечание о нормализации БД в комментарии выше — это тоже очень, очень печально.


Не стыдно чего-то не знать. Стыдно принимать в штыки критику и отказываться учиться.

А я и не воспринимаю критику в штыки. Я лишь уточняю, делаю пояснения. Поймите, уважаемый, я — не профессионал. Я — лишь любитель. И по сему имею право чихать с высокой колокольни и на стандарты кода (к слову код под PSR я причесал исключительно для Хабра), и на типовые решения, и на PROFIT-методики, и даже на традиции. Я многого не знаю, многое открываю для себя заново, порой только сейчас. Но я не скован рамками профессионализма ;-)

Ну вот, теперь по крайней мере честно.


Ваша бравада эмоционально оправдана, но со стороны смотрится жалко. Звучит это всё, как "Я не профессионал, я любитель. И поэтому я чихал на традиционные представления, что дважды два равно четыре. У меня будет 7!". И с таким отношением вы никогда не дойдете до квадратных уравнений.

Вы запрещаете физическое удаление файла («для соответствия законодательству»), но при этом разрешаете его модификацию без сохранения первоначальной версии. Где логика?
PROFIT!

0f/65/84/10/67/68/19/ff.file

У вас получилось 7 уровней вложенности папок. Это слишком много:

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

2. Большинство папок, кроме корневой, будет содержать ровно по одной записи. Это я вам вполне квалифицированно заявляю: у меня в одном из проектиков файлы разложены примерно так же, только структура двухуровневая (ab/cd/efgh1231231243232.file). И хотя файлов сложено уже несколько десятков тысяч, внутри папок второго уровня редко лежит больше одного-двух файлов.

Отсюда вывод: делать структуру папок с более чем двумя уровнями вложенности — нехорошее излишество.

Если уж вы интересуетесь производительностью файлового доступа, то неплохо было бы провести эксперименты, так сказать, «в натурных условиях». Создать структуру папок с определенной вложенностью, напихать в нее энцать миллионов файлов. Затем проверить производительность, вычитывая из нее случайные файлы. И так — для различных уровней вложенности и различных «наполненностей» папок (например, если резать не по два, а по три символа, то максимальное количество файлов в папке станет равно 4096). А если вы еще и на различных файловых системах эксперимент проведете, то вообще будет прекрасно — ведь, например, ReiserFS изначально разрабатывалась из расчета на быстрый поиск в папках. Думаю, результаты таких экспериментов были бы интересны многим завсегдатаям.
Благодарю за конструктивные предложения! Надо будет подумать над Вашими тезисами. Однако, с другой стороны перечитайте абзац про автоинкремент. Плюс для по-настоящему больших проектов становится актуальной кластеризация. А это уже совсем другая история.
Однако, с другой стороны перечитайте абзац про автоинкремент.

Перечитал, но не понимаю, что вы имели в виду. Если вы о том, что трех байт может не хватить для нумерации всех файлов — так я и не предлагаю укорачивать имя файла. Просто
0f/65/84/10/67/68/19/ff.file станет 0f/65/8410676819ff.file.
Если идентификатором файла будет некий хэш, а не просто порядковый номер, то файлы «размажутся» по подпапкам более-менее равномерно. В результате в каждой конечной папке будет всего несколько десятков файлов (пара сотен, если количество файлов перевалит за 16777216). Ну а если файлов станет больше, то у вас возникнут проблемы совсем другого масштаба (в частности, приходит на ум исчерпание инодов файловой системы).
Поддержу, не нужна такая глубокая вложенность. Из жизни: от хэша берутся два блока по 3 символа на каталоги, abc/def/filename. Занимая, на текущий момент, чуть больше 40 TB, эта структура содержит 3-5 файлов на каталог.
«Обратный индекс», то бишь ревизия всех файлов для сравнения фактического наличия с БД — занимает около 3х суток (zfs). Чем глубже — тем дольше. Ну и тюнинг файловой системы, конечно, не помешает.
berez делал по схеме, которую вы описали, включая:
Если идентификатором файла будет некий хэш, а не просто порядковый номер, то файлы «размажутся» по подпапкам более-менее равномерно.
спустя полгода вышли из строя два новых HDD-диска в зеркальном рейде, на них проводился бекап изображений с основного сервера с SSD-рейдом, с помощью rsync.
Логи atop были утеряны, а другой нагрузки кроме суточных бекапов не было, поэтому есть подозрения, что из-за «равномерного размазывания по подпапкам» rsync сканировал много лишнего и если бы я использовал вместо хеша инкремент, то старые папки бы не пересканировались лишний раз, потому что даты их изменения не менялись и диски бы прожили гораздо дольше.
Но это только предположение. Может здесь есть специалисты по rsync, которые в курсе как он работает «под капотом».
Во избежание подобных нюансов в будущем, к структуре с «равномерным размазыванием по подпапкам» был добавлен ещё один уровень папок «сегодняшняя дата», итого имеем:
2018-09-21/0f/65/8410676819ff.file
Данный подход позволяет создавать инкрементальные суточные бекапы без пересканирования всей структуры. Просто берётся папка за предыдущий день и создаётся архив или зеркалируется на другой сервер с помощью rsync.
UFO just landed and posted this here
Ну так-то да, у «размазывания» есть свои недостатки. :)
Кстати, если у вас в день было не очень много файлов (тыщ 50-100), то можно по идее один уровень вложенности папок удалить: 2018-09-21/0f/658410676819ff.file
В среднем это даст 200-300 файлов в папке, что вполне нормально.
Как уже сказали, это отличная технология для 2002 года. Но, во-первых, ничего не сказано про sendfile. Во-вторых, завтра у вас сдохнет диск со всем каталогом — что с репликацией? RAID? Ну ок, тогда сдохла вся машина целиком, выключилась и не грузится. Что тогда делать? В-третьих, средний размер перепакованного аватара — 650 байт, а размер кластера на диске — 4096 байт, видите подвох?

www.s3-client.com/s3-compatible-storage-solutions.html

У вас хранилище почти write only, т.е. писать с накоплением,… в чем то похожем я вообще не стал пользоваться файловой системой а реализовал примитивный формат хранения в блочном файле диска (точнее раздела но это уже не важно, хоть в файле пишите), где идентификатором было смещение на диске (точнее номер 4 килобайтового сектора) и размер файла, упакованные в 8 байт (6 байта номер сектора и 2 байта размер, вы можете сделать 5 и 3, если максимальный размер файла у вас другой, так же никто не мешает использовать больше числа), у меня файлы были маленькие, но никто не мешает и тут проявить смекалку, в зависимости от задач, размер файла можно вытягивать из содержимого (по типу файла), а размер задавать в секторах, заполнив остатки нулями.

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

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

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

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

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

КМК, 1000 файлов в каталоге это беда для fat32. Файловые системы из linux этим не страдают. Время открытия файла из ext4 сильно изменится. Правда, листинг такого каталога (для backup к примеру) будет делаться дорого.

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

Можно конечно выкрутиться. Точно помню, есть файловые системы, позволяющие вынести хранение именно данных файлов на отдельный носитель, а всю структуру каталогов, распределение файлов по диску и журналы — на другом, например маленьком и быстром — оперативная память или ssd, толи jfs толи xfs, когда то давно я с этим игрался.

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

Для такого хранилища файловая система избыточна. Раз все атрибуты файла хранятся в БД, а никакие свойства ФС не нужны, можно дописывать картинки в конец бинарного файла и запоминать в базе смещение. Нет проблем с инодами. С неиспользованным местом при хранении мелких файлов. С сохранением удаленных и перезалитых картинок. С дедупликацией… С backup… Удалять файлы навсегда трудно, ага.

Упсь… было уже, сорри.

1000 файлов в каталоге это беда для fat32. Файловые системы из linux этим не страдают
У меня как раз сейчас стоит задача положить около миллиона файлов в один каталог. (Зачем? Ответ — иначе придется сильно перепиливать legacy-код, а этого делать не хочется.)
Каталог планируется разместить на отдельном 10 терабайтном HDD на ext4, который через симлинк будет подключен к основной файловой системе.
Формально пишут, что число файлов в одном каталоге ext4 не ограничено. Утверждают что поиск файла в директории в ext4 идет по B-tree, т.е. вроде должен быть быстрым на большом количестве записей. Но все равно опасаюсь «подводных камней».
Подскажите, пожалуйста, на какие проблемы я могу нарваться?
Зависит от этого кода. Если внутри только fopen(filename) — скорее всего, ничего страшного. Если, конечно, правда то о чем пишут про B-tree.
Если же внутри есть opendir/readdir — будут тормоза.
Можно провести эксперимент. Создать текстовый файл в 1М рандомных имен файлов.
time for ((i=0; i<1000000; i++)); do dd if=/dev/urandom bs=512 count=1 2>/dev/null  | md5sum - | awk '{print $1}' >> list.txt; done

real	49m6.400s
user	6m24.928s
sys	9m55.281s

Создать 1М этих файлов (непустых, для чистоты эксперимента).
time while read l; do echo "$l$l" > dir/$l ; done < list.txt 

real	2m51.659s
user	0m35.826s
sys	0m32.230s

Посмотреть, сколько времени займет cat ${произвольный_файл_из_середины_списка}

time cat dir/5f818b958f8b4be383b13d70145ad671
5f818b958f8b4be383b13d70145ad6715f818b958f8b4be383b13d70145ad671

real	0m0.018s
user	0m0.000s
sys	0m0.000s


Создать новый файл в огромном каталоге
time touch dir/876685e36bf04e096b40ba987c843ff8_

real	0m0.291s
user	0m0.000s
sys	0m0.000s


Новый файл в пустом каталоге
time touch 876685e36bf04e096b40ba987c843ff8_

real	0m0.002s
user	0m0.000s
sys	0m0.000s


Листинг огромного каталога
time ls dir| wc -l
1000001

real	1m16.389s
user	0m8.033s
sys	0m1.572s
Благодарю! Я сам планировал провести эти эксперименты, написать тестовые скрипты. А вы все сделали за меня. Огромное спасибо!
Сейчас соберу сервер и погоняю ваши тесты на реальном железе, а потом и на реальном legacy-софте с реальными данными. Но думаю теперь уже вполне понятно чего можно ожидать.
Файловые системы из linux этим не страдают.

Страдают. Только это не так явно проявляется, как в винде, и на больших размерах каталогов.
А оплачивать вы будете?
Готовые решения завсегда по стоимости выше своего получаются, ведь там приходится платить не только затраты на собственно задачу но и налоги и маржу нескольких посредников.
Когда вы построите/найдете датацентр сравнимого качества и реализуете сервисы сравнимой надежности (гляньте просто для интереса, какова надежность S3) — тогда и можно будет сравнить стоимость.
Нет, высокая маржа на оплату сетевого трафика на столько высока, что во многих случаях использование s3 не оправдано.

Тем более в задачи не сказано о характере нагрузки, количестве пользователей и вообще необходимости онлайн, поэтому оценить различия в затратах не представляется возможности. А так да, существует ограниченный список задач, при которых s3 оправдан более чем.
Сравнивать цены нужно не после выбора самых надежных (как S3 с РКН дружит, кстати), а после отброса недостаточно надёжных. Если требования к надёжности на порядки ниже предоставляемых S3, а он на порядок дороже альтернатив, то вряд ли он будет хорошим выбором.
Был примерно подобный опыт, но использовали MongoId, на практике в одних папках лежало овер4к файлов, в других 2-3. Как тут с равномерностью? Пока новых задач не было под большое кол-во файлов, поэтому просто положил оригинальные файлы под primaryKey /files/000/000/001.jpg, при этом извне недоступны, дальше по запросу нарезается(?) раздается под кэшем со storage-(1-2-3).site.tld/access-key/$file_id/(full|120x90|...).ext. Таблица файлов естественно знает откуда раздается файл
Увы — ничто не ново под луной. )
Данный принцип хранения файлов используется в EMC Documentum со времен царя Гороха.
С другой стороны это говорит только о том, что мысль XanderBass ушла в правильном направлении. )
Идея с чанками хороша. Но что если фото хранить в БД как BLOB? БД умеет работать с файловой системой из коробки и думать о том как работать с файловой системой уже не нужно. И работа с данными упрощается.
Жирные файлы в БД в общем случае ужасная идея.
Во-первых, Вам для простой отдачи файла нужно лезть за ним в БД, это уже избыточно.
Во-вторых, Вы резко повышаете траффик между приложением и БД. Вместо прежних (допустим) 5 запросов вытаскивающих (допустим) 100кб данных Вы вдруг тянете 20 фоток по 500кб каждая — в 5 раз больше запросов и в 100 раз больше траффик. Это сильно сказывается даже если БД на том же сервере.
В-третьих, Вы тут же теряете возможность работы с файлами штатными средствами. Отдать через sendfile в nginx, запроксировать на отдельном серваке для статики, сделать выборочный бакап и т.д. — для всего этого вдруг будете начинать городить колхоз.

p.s.: vbulletin (форум) по умолчанию хранил (а может и хранит) аттачменты в файлах, простой перенос файлов из БД в ФС при достаточно большом форуме приводит к росту отзывчивости сайта и снижению нагрузки аж на 2 порядка, в зависимости от запущенности ситуации с этим.
1. По ключу = Url положить в редис
2. скриптом читать url, запрашивать редис, возвращать путь на диске(дисках/серверах/cdn), путь передать в X-Accel-Redirect
<?php
// Get requested file name
$path = получаем пути;
// лезем в мемкэш и получаем путь реальный

header("X-Accel-Redirect: /files/" . $pathReal);

?>


3. не забыть про nginx
        location / {
            rewrite ^/file/(.*) last;

            proxy_pass         http://127.0.0.1:8080/;
            proxy_redirect     off;
            proxy_set_header   Host             $host;
            proxy_set_header   X-Real-IP        $remote_addr;
            proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;

        }

        location /files {
            root /var/www/mountedstorage;
            internal;
        }



5. достичь просветления и переписать с php на lua и получить
Яростно плюсую за X-Accel-Redirect, делал так еще в далеком 2010.
Если уж делать контроль доступа и скрытие реальных путей, то быстрее от php получить редирект или 403/404 и отдать его nginx, чем отдавать файл из php.

По теме — чанки это хорошо. Но не по два символа.
В моем случае первый два это маппинг на 16х16 групп-серверов (если таковое нужно), запланированное горизонтальное масштабирование, и потом тупо id файла разбитый на чанки по целым тысячам
Например /DD/000/494/494486.jpeg, где 494486 и есть автоинкрементный id.
Sign up to leave a comment.

Articles