Как стать автором
Обновить

Горизонтальное масштабирование. Что, зачем, когда и как?

Время на прочтение17 мин
Количество просмотров123K
Всего голосов 52: ↑47 и ↓5+42
Комментарии17
1

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

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

Ага, у якобы подкованных программистов вечно все тупит. :)
Так а че я не так написал?

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

Выходит дороже.

(Видимо я раскрыл чей-то секрет :) )

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

Подозреваю, что в общем виде нет.

Как быть с Foreign ключами при шардинге MySQL?

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

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

Разделение монолита на части не обязательно будет способствовать
масштабированию.
1) И это связано с накладными расходами на взаимодействие между
получившимися частями. Чем интенсивнее взаимодействие, объём
трафика, количество запросов, тем медленнее будет работать
система после разделения на части. Отчасти это зависит от
архитектуры, которая получится в результате. Тесно связанные
между собой части будет сложнее разделить на отдельные сервисы.
2) Так же это связано с потерей возможности низкоуровневых
оптимизаций. В монолитном решении можно сделать более эффективные
оптимизации. Например можно сделать JOIN двух больших mysql таблиц,
вместо того, чтобы делать несколько запросов.
3) Накладные расходы на межсерверное взаимодействие. Если данные
лежат на одном сервере, или вообще в оперативной памяти одного и
того же процесса, то получить такие данные можно быстрее, чем из
оперативной памяти других серверов, к которым нужно ещё сделать
сетевые запросы.

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

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

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

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

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

Например если взять сервис, который работает с двумя большими
таблицами MYSQL и использует JOIN, например модуль комментариев
к статье на сайте с возможностью лайка и дизлайка каждого комментария,
то разделив этот сервис на два (один работает непосредственно
с комментариями, а другой обрабатывает лайки и дизлайки), можно
каждый из них поместить в своё собственное, более подходящее под
нагрузку хранилище, например комментарии положить в nosql mongodb
какой-нибудь, а комментарии в tarantool, прикинув по нагрузке,
что лайкают и дизлайкают чаще, чем пишут и редактируют комментарии.
Прошу прощения за опечатки, возможность редактирования пропала раньше, чем я успел дочитать :(
В следующий раз будут готовить и проверять текст комментария отдельно до публикации.

> а комментарии в tarantool
читать «а лайки — в tarantool»

Формальной не встречал.


Но сильно может помочь граф процессов.
Это если с этапа проектирования или рефакторинга.


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


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

Благодарю за статью, так по полочкам разложили. Хорошо её брать за основу и дальше углубляться.
Александр, вопрос не по теме.
Где можно почитать про такую как у вас на Stay.com реализацию карты с ограничением скроллинга в рамках города? Или это стандартная возможность Openstreetmaps?

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

Высоконагруженные и долгозагружаемые — это разные вещи, не так ли? Можно заглавное фото поменьше размером? А то фотография весом в 2,5 Мб. в посте про хайлоад-системы как-то не в тему.
П.С. Иногда хочется почитать и с мобильного.
Пофиксил.
А для мониторинга еще можно использовать стэк ELK
При умелой настройке тоже может быть полезным для выявления мест нуждающихся в масштабировании.
Отчего такое пренебрежение HAProxy? Каждый инструмент хорош по-своему.

Недавно потребовалось «отбалансировать» TCP (суть — несколько линков к офису и они ненадежны), решил попробовать nginx stream модуль. Но возник конфликт интересов — из-за продвижения Nginx Plus из этого модуля (и не только из него) выкинули health check… По итогу Nginx слишком долго переключался между линками, иногда вообще не переключался, так и ждал когда же ответит сервис. Тюнинг таймаутов, весов и прочего не помогал.

Поставил HAProxy и влет поднял балансировку — переключения происходят быстро, встроенный health check работает надежно… Вот тебе и nginx (при этом я его большой фанат — начал применять очень и очень давно, много иностранцев подсадил на него...).

Это частный случай, для веб-балансировки он (nginx) в целом работает стабильно. Плюс когда требуется по вирт.хостам раскидывать и т.п., тут nginx намного удобнее. Правда и тут можно HAProxy поставить — зависит от задач.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий