Тарифы Услуги Сим-карты

Горизонтальное масштабирование PHP-приложений. Масштабируемость системы

Итак вы сделали сайт. Всегда интересно и волнительно наблюдать как счетчик посещений медленно, но верно ползет вверх, с каждым днем показывая все лучшие результаты. Но однажды, когда вы этого не ждете, кто-то запостит ссылку на ваш ресурс на каком-нибудь Reddit или Hacker News (или на Хабре - прим. пер.), и ваш сервер ляжет.

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

Немного про оптимизацию

Основные советы всем известны: обновитесь до последней версии PHP (в 5.5 теперь встроен OpCache), разберитесь с индексами в базе данных, кэшируйте статику (редко изменяемые страницы, такие как “О нас”, “FAQ” и т.д.).

Также стоит упомянуть об одном особом аспекте оптимизации - обслуживании статического контента не-Apache сервером, таким как, например, Nginx, Настройте Nginx на обработку всего статического контента (*.jpg, *.png, *.mp4, *.html…), а файлы требующие серверной обработки пусть отсылает тяжелому Apache. Это называется reverse proxy .

Масштабирование

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

Вертикальное масштабирование.

Представьте себе сервер, обслуживающий веб-приложение. У него 4ГБ RAM, i5 процессор и 1ТБ HDD. Он отлично выполняет свои функции, но, что бы лучше справляться с более высоким трафиком, вы решаете увеличить RAM до 16ГБ, поставить процессор i7, и раскошелиться на SSD диск. Теперь сервер гораздо мощнее, и справляется с высокими нагрузками. Это и есть вертикальное масштабирование.

Горизонтальное масштабирование.

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

Есть два типа балансировщиков - аппаратные и программные. Программный - устанавливается на обычный сервер и принимает весь трафик, передавая его соответствующим обработчикам. Таким балансировщиком может быть, например, Nginx. В разделе “Оптимизация” он перехватывал все запросы на статические файлы, и обслуживал эти запросы сам, не обременяя Apache. Другое популярное ПО для реализации балансировки нагрузки - Squid . Лично я всегда использую именно его, т.к. он предоставляет отличный дружественный интерфейс, для контроля за самыми глубокими аспектами балансировки.

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

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

Постоянное соединение

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

Обмен локальными данными.

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

Использование БД для хранения сессий

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

Распределенная файловая система

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

Memcached

Вы также можете использовать memcached для хранения данных сессий в RAM. Однако это не безопасно, ибо данные в memcached перезаписываются, если заканчивается свободное место. Вы, наверное, задаетесь вопросом, разве RAM не разделен по машинам? Как он применяется на весь кластер? Memcached имеет возможность объединять доступную на разных машинах RAM в один пул .

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

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

Session.save_handler = memcache session.save_path = "tcp://path.to.memcached.server:port"

Redis кластер

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

Другие решения

Итого

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

Надеюсь этот небольшой гайд поможет вам выбрать подход к масштабированию для вашего проекта.

Во второй части статьи мы поговорим о масштабировании базы данных .

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

Приступим?

Для начала имеет смысл определиться с тем, о чем мы вообще будем говорить. В данном контексте перед веб-приложением ставятся три основные цели:

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

Основной темой разговора будет, как не трудно догадаться, масштабируемость, но и остальные цели не думаю, что останутся в стороне. Сразу хочется сказать пару слов про доступность, чтобы не возвращаться к этому позднее, подразумевая как "само собой разумеется": любой сайт так или иначе стремится к тому, чтобы функционировать максимально стабильно, то есть быть доступным абсолютно всем своим потенциальным посетителям в абсолютно каждый момент времени, но порой случаются всякие непредвиденные ситуации, которые могут стать причиной временной недоступности. Для минимизации потенциального ущерба доступности приложения необходимо избегать наличия компонентов в системе, потенциальный сбой в которых привел бы к недоступности какой-либо функциональности или данных (или хотябы сайта в целом). Таким образом каждый сервер или любой другой компонент системы должен иметь хотябы одного дублера (не важно в каком режиме они будут работать: параллельно или один "подстраховывает" другой, находясь при этом в пассивном режиме), а данные должны быть реплицированы как минимум в двух экземплярах (причем желательно не на уровне RAID, а на разных физических машинах). Хранение нескольких резервных копий данных где-то отдельно от основной системы (например на специальных сервисах или на отдельном кластере) также поможет избежать многих проблем, если что-то пойдет не так. Не стоит забывать и о финансовой стороне вопроса: подстраховка на случай сбоев требует дополнительных существенных вложений в оборудование, которые имеет смысл стараться минимизировать.

Масштабируемость принято разделять на два направления:

Вертикальная масштабируемость Увеличение производительности каждого компонента системы c целью повышения общей производительности. Горизонтальная масштабируемость Разбиение системы на более мелкие структурные компоненты и разнесение их по отдельным физическим машинам (или их группам) и/или увеличение количества серверов параллельно выполняющих одну и ту же функцию.

Так или иначе, при разработке стратегии роста системы приходится искать компромис между ценой, временем разработки, итоговой производительность, стабильностью и еще массой других критериев. С финансовой точки зрения вертикальная масштабируемость является далеко не самым привлекательным решением, ведь цены на сервера с большим количеством процессоров всегда растут практически экспоненциально относительно количества процессоров. Именно по-этому наиболее интересен горизонтальный подход, так как именно он используется в большинстве случаев. Но и вертикальная масштабируемость порой имеет право на существование, особенно в ситуациях, когда основную роль играет время и скорость решения задачи, а не финансовый вопрос: ведь купить БОЛЬШОЙ сервер существенно быстрее, чем практически заново разрабатывать приложения, адаптируя его к работе на большом количестве параллельно работающих серверов.

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

Серверы приложений

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

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

Балансировка нагрузки

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

Оборудование Сетевое оборудование, позволяющее распределять нагрузку между несколькими серверами, обычно стоит достаточно внушительные суммы, но среди прочих вариантов обычно именно этот подход предлагает наивысшую производительность и стабильность (в основном благодаря качеству, плюс такое оборудование иногда поставляется парами, работающими по принципу ). В этой индустрии достаточно много серьезных брендов, предлагающих свои решения - есть из чего выбрать: Cisco , Foundry , NetScalar и многие другие. Программное обеспечение В этой области еще большее разнообразие возможных вариантов. Получить программно производительность сопоставимую с аппаратными решениями не так-то просто, да и HeartBeat придется обеспечивать программно, но зато оборудование для функционирования такого решения представляет собой обычный сервер (возможно не один). Таких программных продуктов достаточно много, обычно они представляют собой просто HTTP-серверы, перенаправляющие запросы своим коллегам на других серверах вместо отправки напрямую на обработку интерпретатору языка программирования. Для примера можно упомянуть, скажем, с mod_proxy . Помимо этого имеют место более экзотические варианты, основанные на DNS, то есть в процессе определения клиентом IP-адреса сервера с необходимым ему интернет-ресурсов адрес выдается с учетом нагрузки на доступные сервера, а также некоторых географических соображений.

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

Ресурсоемкие вычисления

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

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

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

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

Сессии

Практически все веб-приложения каким-либо образом взаимодействуют со своими посетителями и в подавляющем большинстве случаев в них присутствует необходимость отслеживать перемещения пользователей по страницам сайта. Для решения этой задачи обычно используется механизм сессий , который заключается в присвоении каждому посетителю уникального идентификационного номера, который ему передается для хранения в cookies или, в случае их отсутствия, для постоянного "таскания" за собой через GET. Получив от пользователя некий ID вместе с очередным HTTP-запросом сервер может посмотреть в список уже выданных номеров и однозначно определить кто его отправил. С каждым ID может ассоциироваться некий набор данных, который веб-приложение может использовать по своему усмотрению, эти данные обычно по-умолчанию хранятся в файле во временной директории на сервере.

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

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

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

В качестве альтернативы сессиям иногда используют похожие по предназначению механизмы, построенные на cookies, то есть все необходимые приложению данные о пользователе хранятся на клиентской стороне (вероятно в зашифрованном виде) и запрашиваются по мере необходимости. Но помимо очевидных преимуществ, связанных с отсутствием необходимости хранить лишние данные на сервере, возникает ряд проблем с безопасностью. Данные, хранимые на стороне клиента даже в зашифрованном виде, представляют собой потенциальную угрозу для функционирования многих приложений, так как любой желающий может попытаться модифицировать их в своих интересах или с целью навредить приложению. Такой подход хорош только если есть уверенность, что абсолютно любые манипуляции с хранимые у пользователей данными безопасны. Но можно ли быть уверенными на 100%?

Статический контент

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

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

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

Альтернативой этим подходам выступает использование так называемых Content Delievery Network - внешних сервисов, обеспечивающих доступность Вашего контента пользователям за определенное материальное вознаграждение сервису. Преимущество очевидно - нет необходимости организовывать собственную инфраструктуру для решения этой задачи, но зато появляется другая дополнительная статья расходов. Список таких сервисов приводить не буду, если кому-нибудь понадобится - найти будет не трудно.

Кэширование

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

СУБД Практически все современные СУБД предоставляют встроенные механизмы для кэширования результатов определенных запросов. Этот метод достаточно эффективен, если Ваша система регулярно делает одни и те же выборки данных, но также имеет ряд недостатков, основными из которых является инвалидация кэша всей таблицы при малейшем ее изменении, а также локальное расположение кэша, что неэффективно при наличии нескольких серверов в системе хранения данных. Приложение На уровне приложений обычно производится кэширование объектов любого языка программирования. Этот метод позволяет вовсе избежать существенной части запросов к СУБД, сильно снижая нагрузку на нее. Как и сами приложения такой кэш должен быть независим от конкретного запроса и сервера, на котором он выполняется, то есть быть доступным всем серверам приложений одновременно, а еще лучше - быть распределенным по нескольким машинам для более эффективной утилизации оперативной памяти. Лидером в этом аспекте кэширования по праву можно назвать , о котором я в свое время уже успел . HTTP-сервер Многие веб-серверы имеют модули для кэширования как статического контента, так и результатов работы скриптов. Если страница редко обновляется, то использование этого метода позволяет без каких-либо видимых для пользователя изменений избегать генерации страницы в ответ на достаточно большую часть запросов. Reverse proxy Поставив между пользователем и веб-сервером прозрачный прокси-сервер, можно выдавать пользователю данные из кэша прокси (который может быть как в оперативной памяти, так и дисковым), не доводя запросы даже до HTTP-серверов. В большинстве случаев этот подход актуален только для статического контента, в основном разных форм медиа-данных: изображений, видео и тому подобного. Это позволяет веб-серверам сосредоточиться только на работе с самими страницами.

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

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

Базы данных

На закуску я оставил самое интересное, ведь этот неотъемлемый компонент любого веб-приложения вызывает больше проблем при росте нагрузок, чем все остальные вместе взятые. Порой даже может показаться, что стоит вообще отказаться от горизонтального масштабирования системы хранения данных в пользу вертикального - просто купить тот самый БОЛЬШОЙ сервер за шести- или семизначную сумму не-рублей и не забивать себе голову лишними проблемами.

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

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

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

Временным решением этой проблемы, возможно, может стать замена master-сервера на более производительный, но так или иначе не выйдет бесконечно откладывать переход на следующий "уровень" развития системы хранения данных: "sharding" , которому я совсем недавно посвятил . Так что позволю себе остановиться на нем лишь вкратце: идея заключается в том, чтобы разделить все данные на части по какому-либо признаку и хранить каждую часть на отдельном сервере или кластере, такую часть данных в совокупности с системой хранения данных, в которой она находится, и называют сегментом или shard ’ом. Такой подход позволяет избежать издержек, связанных с реплицированием данных (или сократить их во много раз), а значит и существенно увеличить общую производительность системы хранения данных. Но, к сожалению, переход к этой схеме организации данных требует массу издержек другого рода. Так как готового решения для ее реализации не существует, приходится модифицировать логику приложения или добавлять дополнительную "прослойку" между приложением и СУБД, причем все это чаще всего реализуется силами разработчиков проекта. Готовые продукты способны лишь облегчить их работу, предоставив некий каркас для построения основной архитектуры системы хранения данных и ее взаимодействия с остальными компонентами приложения.

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

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

В этом разделе стоит упомянуть еще один, более специфический, тип интернет-проектов. Такие проекты оперируют данными, не имеющими четко формализованную структуру, в таких ситуациях использование реляционных СУБД в качестве хранилища данных, мягко говоря, нецелесообразно. В этих случаях обычно используют менее строгие базы данных, с более примитивной функциональностью в плане обработки данных, но зато они способны обрабатывать огромные объемы информации не придираясь к его качеству и соответствию формату. В качестве основы для такого хранилища данных может служить кластерная файловая система, а для анализа же данных в таком случае используется механизм под названием , принцип его работы я расскажу лишь вкратце, так как в полном своем масштабе он несколько выходит за рамки данного повествования.

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

Map Основной целью данного этапа является представление произвольных входных данных в виде промежуточных пар ключ-значение, имеющих определенный смысл и формально оформленных. Результаты подвергаются сортировке и группированию по ключу, а после чего передаются на следующий этап. Reduce Полученные после map значения используются для финального вычисления требуемых итоговых данных.

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

Примером готового каркаса для реализации работы с данными по такому принципу служит opensource проект Apache Foundation под названием , о котором я уже неоднократно рассказывал ранее, да и написал в свое время.

Вместо заключения

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

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

) Здравствуйте! Я Александр Макаров, и вы можете меня знать по фреймворку «Yii» — я один из его разработчиков. У меня также есть full-time работа — и это уже не стартап — Stay.com, который занимается путешествиями.

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

Что такое масштабирование, вообще? Это возможность увеличить производительность проекта за минимальное время путем добавления ресурсов.

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

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

Самый классный вопрос, который задают, — а зачем оно надо, если у меня все и на одном сервере прекрасно работает? На самом-то деле, надо проверить, что будет. Т.е., сейчас оно работает, но что будет потом? Есть две замечательные утилиты — ab и siege, которые как бы нагоняют тучу пользователей конкурента, которые начинают долбить сервер, пытаются запросить странички, послать какие-то запросы. Вы должны указать, что им делать, а утилиты формируют такие вот отчеты:

Главные два параметра: n — количество запросов, которые надо сделать, с — количество одновременных запросов. Таким образом они проверяют конкурентность.

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

Есть еще один параметр — Response time — время ответа, за которое в среднем сервер отдал страничку. Оно бывает разное, но известно, что около 300 мс — это норма, а что выше — уже не очень хорошо, потому что эти 300 мс отрабатывает сервер, к этому прибавляются еще 300-600 мс, которые отрабатывает клиент, т.е. пока все загрузится — стили, картинки и остальное — тоже проходит время.

Бывает, что на самом деле пока и не надо заботиться о масштабировании — идем на сервер, обновляем PHP, получаем 40% прироста производительности и все круто. Далее настраиваем Opcache, тюним его. Opcache, кстати, тюнится так же, как и APC, скриптом, который можно найти в репозитории у Расмуса Лердорфа и который показывает хиты и мисы, где хиты — это сколько раз PHP пошел в кэш, а мисы — сколько раз он пошел в файловую систему доставать файлики. Если прогнать весь сайт, либо запустить туда какой-то краулер по ссылкам, либо вручную потыкать, то у нас будет статистика по этим хитам и мисам. Если хитов 100%, а мисов — 0%, значит, все нормально, а если есть мисы, то надо выделить больше памяти, чтобы весь наш код влез в Opcache. Это частая ошибка, которую допускают — вроде Opcache есть, но что-то не работает…

Еще часто начинают масштабировать, но не смотрят, вообще, из-за чего все работает медленно. Чаще всего лезем в базу, смотрим — индексов нет, ставим индексы — все сразу залетало, еще на 2 года хватит, красота!

Ну, еще надо включить кэш, заменить apache на nginx и php-fpm, чтобы сэкономить память. Будет все классно.

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

Как, вообще, понять, в чем проблема? Либо у вас уже настал highload, а это не обязательно какое-то бешеное число запросов и т.д., это, когда у вас проект не справляется с нагрузкой, и тривиальными способами это уже не решается. Надо расти либо вширь, либо вверх. Надо что-то делать и, скорее всего, на это мало времени, что-то надо придумывать.

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

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

На что нужно обращать внимание прямо сейчас при мониторинге? Это:

  1. доступность, т.е. жив сервер, вообще, или нет;
  2. нехватка ресурсов диска, процессора и т.д.;
  3. ошибки.
Как это все мониторить?

Вот список замечательных инструментов, которые позволяют мониторить ресурсы и показывать результаты в очень удобном виде:

Этот доклад - расшифровка одного из лучших выступлений на обучающей конференции разработчиков высоконагруженных систем за 2015 год.

Старьё! - скажите вы.
- Вечные ценности! - ответим мы.

  • highload junior
  • Добавить метки

    С ростом популярности web-приложения его поддержка неизбежно начинает требовать всё больших и больших ресурсов. Первое время с нагрузкой можно (и, несомненно, нужно) бороться путём оптимизации алгоритмов и/или архитектуры самого приложения. Однако, что делать, если всё, что можно было оптимизировать, уже оптимизировано, а приложение всё равно не справляется с нагрузкой?

    Оптимизация

    Первым делом стоит сесть и подумать, а всё ли вам уже удалось оптимизировать:
    • оптимальны ли запросы к БД (анализ EXPLAIN, использование индексов)?
    • правильно ли хранятся данные (SQL vs NoSQL)?
    • используется ли кеширование?
    • нет ли излишних запросов к ФС или БД?
    • оптимальны ли алгоритмы обработки данных?
    • оптимальны ли настройки окружения: Apache/Nginx, MySQL/PostgreSQL, PHP/Python?
    О каждом из этих пунктов можно написать отдельную статью, так что детальное их рассмотрение в рамках данной статьи явно избыточно. Важно лишь понимать, что перед тем как приступить к масштабированию приложения, крайне желательно максимально оптимизировать его работу – ведь возможно тогда никакого масштабирования и не потребуется.

    Масштабирование

    И так, допустим, что оптимизация уже проведена, но приложение всё равно не справляется с нагрузкой. В таком случае решением проблемы, очевидно, может послужить разнесение его по нескольким хостам, с целью увеличения общей производительности приложения за счёт увеличения доступных ресурсов. Такой подход имеет официальное название – «масштабирование» (scale) приложения. Точнее говоря, под «масштабируемостью » (scalability) называется возможность системы увеличивать свою производительность при увеличении количества выделяемых ей ресурсов. Различают два способа масштабирования: вертикальное и горизонтальное. Вертикальное масштабирование подразумевает увеличение производительности приложения при добавлении ресурсов (процессора, памяти, диска) в рамках одного узла (хоста). Горизонтальное масштабирование характерно для распределённых приложений и подразумевает рост производительности приложения при добавлении ещё одного узла (хоста).

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

    Архитектура приложения

    Большинство web-приложений априори являются распределёнными, так как в их архитектуре можно выделить минимум три слоя: web-сервер, бизнес-логика (приложение), данные (БД, статика).

    Каждый их этих слоёв может быть масштабирован. Поэтому если в вашей системе приложение и БД живут на одном хосте – первым шагом, несомненно, должно стать разнесение их по разным хостам.

    Узкое место

    Приступая к масштабированию системы, первым делом стоит определить, какой из слоёв является «узким местом» - то есть работает медленнее остальной системы. Для начала можно воспользоваться банальными утилитами типа top (htop) для оценки потребления процессора/памяти и df, iostat для оценки потребления диска. Однако, желательно выделить отдельный хост, с эмуляцией боевой нагрузки (c помощью или JMeter), на котором можно будет профилировать работу приложения с помощью таких утилит как xdebug , и так далее. Для выявления узких запросов к БД можно воспользоваться утилитами типа pgFouine (понятно, что делать это лучше на основе логов с боевого сервера).

    Обычно всё зависит от архитектуры приложения, но наиболее вероятными кандидатами на «узкое место» в общем случае являются БД и код. Если ваше приложение работает с большим объёмом пользовательских данных, то «узким местом», соответственно, скорее всего будет хранение статики.

    Масштабирование БД

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

    Снизить нагрузку на БД можно разнеся её на несколько хостов. При этом остро встаёт проблема синхронизации между ними, решить которую можно путём реализации схемы master/slave с синхронной или асинхронной репликацией. В случае с PostgreSQL реализовать синхронную репликацию можно с помощью Slony-I , асинхронную – PgPool-II или WAL (9.0). Решить проблему разделения запросов чтения и записи, а так же балансировки нагрузку между имеющимися slave’ами, можно с помощью настройки специального слоя доступа к БД (PgPool-II).

    Проблему хранения большого объёма данных в случае использования реляционных СУБД можно решить с помощью механизма партицирования (“partitioning” в PostgreSQL), либо разворачивая БД на распределённых ФС типа Hadoop DFS .

    Однако, для хранения больших объёмов данных лучшим решением будет «шардинг » (sharding) данных, который является встроенным преимуществом большинства NoSQL БД (например, MongoDB).

    Кроме того, NoSQL БД в общем работают быстрее своих SQL-братьев за счёт отсутствия overhead’а на разбор/оптимизацию запроса, проверки целостности структуры данных и т.д. Тема сравнения реляционных и NoSQL БД так же довольно обширна и заслуживает .

    Отдельно стоит отметить опыт Facebook, который используют MySQL без JOIN-выборок. Такая стратегия позволяет им значительно легче масштабировать БД, перенося при этом нагрузку с БД на код, который, как будет описано ниже, масштабируется проще БД.

    Масштабирование кода

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

    Далее необходимо настроить балансировку нагрузки/запросов между этими хостами. Сделать это можно как на уровне TCP (haproxy), так и на HTTP (nginx) или DNS .

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

    Файлы статики можно смонтировать с некого общего файлового хранилища по NFS /CIFS или использовать распределённую ФС (HDFS , GlusterFS , Ceph).

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

    Отдельно стоит отметить проблему деплоймента на несколько хостов. Как сделать так, что бы пользователь, нажимая «Обновить», не видел разные версии приложения? Самым простым решением, на мой взгляд, будет исключение из конфига балансировщика нагрузки (web-сервера) не обновлённых хостов, и последовательного их включения по мере обновления. Так же можно привязать пользователей к конкретным хостам по cookie или IP. Если же обновление требует значимых изменений в БД, проще всего, вообще временно закрыть проект.

    Масштабирование ФС

    При необходимости хранения большого объёма статики можно выделить две проблемы: нехватка места и скорость доступа к данным. Как уже было написано выше, проблему с нехваткой места можно решить как минимум тремя путями: распределённая ФС, хранение данных в БД с поддержкой шардинга и организация шардинга «вручную» на уровне кода.

    При этом стоит понимать, что раздача статики тоже не самая простая задача, когда речь идёт о высоких нагрузках. Поэтому в вполне резонно иметь множество серверов предназначенных для раздачи статики. При этом, если мы имеем общее хранилище данных (распределённая ФС или БД), при сохранении файла мы можем сохранять его имя без учёта хоста, а имя хоста подставлять случайным образом при формировании страницы (случайным образом балансирую нагрузку между web-серверами, раздающими статику). В случае, когда шардинг реализуется вручную (то есть, за выбор хоста, на который будут залиты данные, отвечает логика в коде), информация о хосте заливки должна либо вычисляться на основе самого файла, либо генерироваться на основании третьих данных (информация о пользователе, количестве места на дисках-хранилищах) и сохраняться вместе с именем файла в БД.

    Мониторинг

    Понятно, что большая и сложная система требует постоянного мониторинга. Решение, на мой взгляд, тут стандартное – zabbix, который следит за нагрузкой/работой узлов системы и monit для демонов для подстраховки.

    Заключение

    Выше кратко рассмотрено множество вариантов решений проблем масштабирования web-приложения. Каждый из них обладает своими достоинствами и недостатками. Не существует некоторого рецепта, как сделать всё хорошо и сразу – для каждой задачи найдётся множество решений со своими плюсами и минусами. Какой из них выбрать – решать вам.

    С ростом популярности web-приложения его поддержка неизбежно начинает требовать всё больших и больших ресурсов. Первое время с нагрузкой можно (и, несомненно, нужно) бороться путём оптимизации алгоритмов и/или архитектуры самого приложения. Однако, что делать, если всё, что можно было оптимизировать, уже оптимизировано, а приложение всё равно не справляется с нагрузкой?

    Оптимизация

    Первым делом стоит сесть и подумать, а всё ли вам уже удалось оптимизировать:
    • оптимальны ли запросы к БД (анализ EXPLAIN, использование индексов)?
    • правильно ли хранятся данные (SQL vs NoSQL)?
    • используется ли кеширование?
    • нет ли излишних запросов к ФС или БД?
    • оптимальны ли алгоритмы обработки данных?
    • оптимальны ли настройки окружения: Apache/Nginx, MySQL/PostgreSQL, PHP/Python?
    О каждом из этих пунктов можно написать отдельную статью, так что детальное их рассмотрение в рамках данной статьи явно избыточно. Важно лишь понимать, что перед тем как приступить к масштабированию приложения, крайне желательно максимально оптимизировать его работу – ведь возможно тогда никакого масштабирования и не потребуется.

    Масштабирование

    И так, допустим, что оптимизация уже проведена, но приложение всё равно не справляется с нагрузкой. В таком случае решением проблемы, очевидно, может послужить разнесение его по нескольким хостам, с целью увеличения общей производительности приложения за счёт увеличения доступных ресурсов. Такой подход имеет официальное название – «масштабирование» (scale) приложения. Точнее говоря, под «масштабируемостью » (scalability) называется возможность системы увеличивать свою производительность при увеличении количества выделяемых ей ресурсов. Различают два способа масштабирования: вертикальное и горизонтальное. Вертикальное масштабирование подразумевает увеличение производительности приложения при добавлении ресурсов (процессора, памяти, диска) в рамках одного узла (хоста). Горизонтальное масштабирование характерно для распределённых приложений и подразумевает рост производительности приложения при добавлении ещё одного узла (хоста).

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

    Архитектура приложения

    Большинство web-приложений априори являются распределёнными, так как в их архитектуре можно выделить минимум три слоя: web-сервер, бизнес-логика (приложение), данные (БД, статика).

    Каждый их этих слоёв может быть масштабирован. Поэтому если в вашей системе приложение и БД живут на одном хосте – первым шагом, несомненно, должно стать разнесение их по разным хостам.

    Узкое место

    Приступая к масштабированию системы, первым делом стоит определить, какой из слоёв является «узким местом» - то есть работает медленнее остальной системы. Для начала можно воспользоваться банальными утилитами типа top (htop) для оценки потребления процессора/памяти и df, iostat для оценки потребления диска. Однако, желательно выделить отдельный хост, с эмуляцией боевой нагрузки (c помощью или JMeter), на котором можно будет профилировать работу приложения с помощью таких утилит как xdebug , и так далее. Для выявления узких запросов к БД можно воспользоваться утилитами типа pgFouine (понятно, что делать это лучше на основе логов с боевого сервера).

    Обычно всё зависит от архитектуры приложения, но наиболее вероятными кандидатами на «узкое место» в общем случае являются БД и код. Если ваше приложение работает с большим объёмом пользовательских данных, то «узким местом», соответственно, скорее всего будет хранение статики.

    Масштабирование БД

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

    Снизить нагрузку на БД можно разнеся её на несколько хостов. При этом остро встаёт проблема синхронизации между ними, решить которую можно путём реализации схемы master/slave с синхронной или асинхронной репликацией. В случае с PostgreSQL реализовать синхронную репликацию можно с помощью Slony-I , асинхронную – PgPool-II или WAL (9.0). Решить проблему разделения запросов чтения и записи, а так же балансировки нагрузку между имеющимися slave’ами, можно с помощью настройки специального слоя доступа к БД (PgPool-II).

    Проблему хранения большого объёма данных в случае использования реляционных СУБД можно решить с помощью механизма партицирования (“partitioning” в PostgreSQL), либо разворачивая БД на распределённых ФС типа Hadoop DFS .

    Однако, для хранения больших объёмов данных лучшим решением будет «шардинг » (sharding) данных, который является встроенным преимуществом большинства NoSQL БД (например, MongoDB).

    Кроме того, NoSQL БД в общем работают быстрее своих SQL-братьев за счёт отсутствия overhead’а на разбор/оптимизацию запроса, проверки целостности структуры данных и т.д. Тема сравнения реляционных и NoSQL БД так же довольно обширна и заслуживает отдельной статьи .

    Отдельно стоит отметить опыт Facebook, который используют MySQL без JOIN-выборок. Такая стратегия позволяет им значительно легче масштабировать БД, перенося при этом нагрузку с БД на код, который, как будет описано ниже, масштабируется проще БД.

    Масштабирование кода

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

    Далее необходимо настроить балансировку нагрузки/запросов между этими хостами. Сделать это можно как на уровне TCP (haproxy), так и на HTTP (nginx) или DNS .

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

    Файлы статики можно смонтировать с некого общего файлового хранилища по NFS /CIFS или использовать распределённую ФС (HDFS , GlusterFS , Ceph).

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

    Отдельно стоит отметить проблему деплоймента на несколько хостов. Как сделать так, что бы пользователь, нажимая «Обновить», не видел разные версии приложения? Самым простым решением, на мой взгляд, будет исключение из конфига балансировщика нагрузки (web-сервера) не обновлённых хостов, и последовательного их включения по мере обновления. Так же можно привязать пользователей к конкретным хостам по cookie или IP. Если же обновление требует значимых изменений в БД, проще всего, вообще временно закрыть проект.

    Масштабирование ФС

    При необходимости хранения большого объёма статики можно выделить две проблемы: нехватка места и скорость доступа к данным. Как уже было написано выше, проблему с нехваткой места можно решить как минимум тремя путями: распределённая ФС, хранение данных в БД с поддержкой шардинга и организация шардинга «вручную» на уровне кода.

    При этом стоит понимать, что раздача статики тоже не самая простая задача, когда речь идёт о высоких нагрузках. Поэтому в вполне резонно иметь множество серверов предназначенных для раздачи статики. При этом, если мы имеем общее хранилище данных (распределённая ФС или БД), при сохранении файла мы можем сохранять его имя без учёта хоста, а имя хоста подставлять случайным образом при формировании страницы (случайным образом балансирую нагрузку между web-серверами, раздающими статику). В случае, когда шардинг реализуется вручную (то есть, за выбор хоста, на который будут залиты данные, отвечает логика в коде), информация о хосте заливки должна либо вычисляться на основе самого файла, либо генерироваться на основании третьих данных (информация о пользователе, количестве места на дисках-хранилищах) и сохраняться вместе с именем файла в БД.

    Мониторинг

    Понятно, что большая и сложная система требует постоянного мониторинга. Решение, на мой взгляд, тут стандартное – zabbix, который следит за нагрузкой/работой узлов системы и monit для демонов для подстраховки.

    Заключение

    Выше кратко рассмотрено множество вариантов решений проблем масштабирования web-приложения. Каждый из них обладает своими достоинствами и недостатками. Не существует некоторого рецепта, как сделать всё хорошо и сразу – для каждой задачи найдётся множество решений со своими плюсами и минусами. Какой из них выбрать – решать вам.