Как сделать так, чтобы мои приложения хорошо масштабировались?

В общем, какие проектные решения помогают хорошо масштабировать приложение?

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

Ответы на данный момент
1) Определите масштабирование. Вам нужно масштабироваться для большого количества пользователей, трафика, объектов в виртуальной среде?
2) Посмотрите на свои алгоритмы. Будет ли объем работы, которую они выполняют, масштабироваться линейно с фактическим объемом работы, т. е. с количеством элементов, которые необходимо выполнить в цикле, количеством пользователей и т. д.?
3) Посмотрите на свое оборудование. Разработано ли ваше приложение таким образом, чтобы вы могли запускать его на нескольких машинах, если одна из них не справляется?

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


person Community    schedule 03.09.2008    source источник


Ответы (7)


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

Сначала создавайте максимально простой код, затем профилируйте систему и оптимизируйте только тогда, когда есть очевидная проблема с производительностью.

Часто цифры профилирования вашего кода нелогичны; узкие места, как правило, находятся в модулях, которые, по вашему мнению, не будут медленными. Данные — это главное, когда дело доходит до оптимизации. Если вы оптимизируете части, которые, по вашему мнению, будут медленными, вы часто оптимизируете не те вещи.

person Simon Johnson    schedule 03.09.2008

Итак, вы нашли ключевой момент в использовании «большой нотации O». Это одно измерение, которое, безусловно, может укусить вас сзади, если вы не обращаете внимания. В игре есть и другие измерения, которые некоторые люди не видят сквозь очки с большой буквой «О» (но если вы присмотритесь, они действительно есть).

Простым примером такого измерения является соединение с базой данных. Существуют «лучшие методы» построения, скажем, левого внутреннего соединения, которые помогут сделать SQL более эффективным. Если вы разберете реляционное исчисление или даже посмотрите на план объяснения (Oracle), вы можете легко увидеть, какие индексы используются в каком порядке и происходят ли какие-либо сканирования таблиц или вложенные операции.

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

browser -> web server -> app server -> DB -> app server -> XSLT -> web server -> browser JS engine execution & rendering

У вас должен быть какой-то метод измерения производительности (время отклика, пропускная способность, измеренная в единицах времени и т. д.) в каждой из этих отдельных областей, а не только на уровне устройства и ОС (ЦП, память, дисковый ввод-вывод, и т. д.), но специфичные для каждой службы уровня. Итак, на веб-сервере вам нужно знать все счетчики используемого вами веб-сервера. На уровне приложений вам понадобится это, а также видимость любой виртуальной машины, которую вы используете (jvm, clr, что угодно). Большинство OR-сопоставителей проявляются внутри виртуальной машины, поэтому убедитесь, что вы обращаете внимание на все особенности, если они видны вам на этом уровне. Внутри БД вам нужно знать все, что выполняется, и все конкретные параметры настройки для вашей разновидности БД. Если у вас есть большие деньги, BMC Patrol — довольно хорошая ставка для большинства из них (с соответствующими модулями знаний (KM)). В более дешевом конце вы, безусловно, можете кататься самостоятельно, но ваш пробег будет варьироваться в зависимости от вашей глубины знаний.

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

Итак, учитывая эту проблемную область, какие решения вы могли бы принять, чтобы помочь с масштабируемостью?

  1. Обработка соединения. Это также связано с управлением сеансом и аутентификацией. Это должно быть максимально чистым и легким без ущерба для безопасности. Метрика — максимальное количество подключений в единицу времени.

  2. Отработка отказа сеанса на каждом уровне. Необходимо или нет? Мы предполагаем, что каждый уровень будет представлять собой кластер ящиков по горизонтали под неким механизмом балансировки нагрузки. Балансировка нагрузки обычно очень легкая, но некоторые реализации аварийного переключения сеанса могут быть тяжелее, чем хотелось бы. Кроме того, если вы работаете с закрепленными сеансами, это может более глубоко повлиять на ваши параметры в архитектуре. Вы также должны решить, привязывать ли веб-сервер к конкретному серверу приложений или нет. В мире удаленного взаимодействия .NET их, вероятно, проще связать вместе. Если вы используете стек Microsoft, может быть более масштабируемым сделать двухуровневый (пропустить удаленное взаимодействие), но вам придется пойти на существенный компромисс безопасности. Что касается Java, я всегда видел как минимум 3 уровня. Нет причин делать это иначе.

  3. Иерархия объектов. Внутри приложения вам нужна максимально чистая и легкая структура объектов. Приносите данные, которые вам нужны, только тогда, когда они вам нужны. Злобно отсекайте любое ненужное или лишнее получение данных.

  4. ИЛИ неэффективность картографа. Существует несоответствие импеданса между объектным дизайном и реляционным дизайном. Конструкция «многие ко многим» в РСУБД находится в прямом конфликте с иерархиями объектов (человек.адрес против местоположения.резидент). Чем сложнее ваши структуры данных, тем менее эффективным будет ваш преобразователь OR. В какой-то момент вам, возможно, придется отказаться от приманки в разовой ситуации и применить более... ну... примитивный подход к доступу к данным (хранимая процедура + уровень доступа к данным), чтобы выжать больше производительности или масштабируемости из конкретного уродливый модуль. Поймите связанные с этим затраты и примите осознанное решение.

  5. XSL-преобразования. XML — прекрасный, нормализованный механизм для передачи данных, но он может быть очень производительным псом! В зависимости от того, сколько данных вы носите с собой, какой парсер вы выберете и насколько сложна ваша структура, вы можете легко загнать себя в очень темный угол с помощью XSLT. Да, с академической точки зрения это блестяще чистый способ создания уровня представления, но в реальном мире могут возникнуть катастрофические проблемы с производительностью, если вы не уделите этому особого внимания. Я видел систему, потребляющую более 30% времени транзакций только в XSLT. Некрасиво, если вы пытаетесь увеличить базу пользователей в 4 раза, не покупая дополнительные коробки.

  6. Можете ли вы купить выход из затора масштабируемости? Абсолютно. Я наблюдал, как это происходит больше раз, чем я хотел бы признать. Закон Мура (как вы уже упомянули) действует и сегодня. На всякий случай имейте при себе немного наличных.

  7. Кэширование — отличный инструмент для снижения нагрузки на движок (удобным побочным эффектом является увеличение скорости и пропускной способности). Однако за это приходится платить с точки зрения объема памяти и сложности при аннулировании кеша, когда он устарел. Мое решение состояло бы в том, чтобы начать с полной очистки и постепенно добавлять кеширование только там, где вы решите, что это полезно для вас. Слишком часто сложности недооцениваются, и то, что начиналось как способ решения проблем с производительностью, в итоге приводит к функциональным проблемам. Кроме того, вернемся к комментарию об использовании данных. Если вы каждую минуту создаете объекты на гигабайты, не имеет значения, кэшируете вы их или нет. Вы быстро исчерпаете свой объем памяти, а сборка мусора испортит вам день. Таким образом, я думаю, вывод состоит в том, чтобы убедиться, что вы точно понимаете, что происходит внутри вашей виртуальной машины (создание объекта, уничтожение, GC и т. д.), чтобы вы могли принимать наилучшие возможные решения.

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

person Ed Lucas    schedule 16.09.2008

Есть блог под названием High Scalibility, в котором содержится много информации по этой теме. Некоторые полезные вещи.

person Malik Daud Ahmad Khokhar    schedule 03.09.2008

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

Решите, что на самом деле означает масштабирование для вашего проекта. Является ли бесконечное количество пользователей, может ли он справиться с косой чертой на веб-сайте, это циклы разработки?

Используйте это, чтобы сосредоточить свои усилия по развитию

person svrist    schedule 03.09.2008

Джефф и Джоэл обсуждают масштабирование в подкасте Stack Overflow #19.

person GateKiller    schedule 03.09.2008

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

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

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

person Tim Howland    schedule 03.09.2008
comment
Извините, я категорически не согласен с этим. Часто к тому времени, когда масштабируемость становится проблемой, единственное реальное решение — переписать систему почти с нуля, потому что необходимые изменения часто всеобъемлющие и глубокие. - person RickNZ; 29.12.2009
comment
Преждевременная оптимизация — корень всех зол. - person Tim Howland; 29.12.2009

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

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

Один из способов приблизиться к этому:

    for each car {
       determine my position;  
       for each car {  
         add my position to this car's map;  
       }
    }

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

Но есть проблема с масштабируемостью. Когда есть 2 машины, эта стратегия требует 4 шагов «добавить мою позицию»; когда есть 3 машины, это занимает 9 шагов. Для каждого "обновления позиции" вам необходимо просмотреть весь список автомобилей, и для каждого автомобиля необходимо обновить свое местоположение.

Не обращая внимания на то, сколько других действий необходимо выполнить с каждым автомобилем (например, для вычисления положения отдельного автомобиля может потребоваться фиксированное количество шагов), для N автомобилей требуется N2 "посещение автомобилей" для запуска этого алгоритма. Это не проблема, когда у вас 5 машин и 25 ступенек. Но по мере того, как вы будете добавлять машины, вы увидите, что система захлебывается. 100 автомобилей пройдут 10 000 шагов, а 101 автомобиль пройдёт 10 201 шаг!

Лучшим подходом было бы отменить вложенность циклов for.

    for each car {  
      add my position to a list;  
    }  
    for each car {    
      give me an updated copy of the master list;  
    }

В этой стратегии количество шагов кратно N, а не N2. Поэтому 100 машин требуют в 100 раз больше работы, чем 1 машина, а НЕ 10 000 раз.

Эта концепция иногда выражается в «обозначении большого O» — необходимое количество шагов равно «большой O из N» или «большой O из N2».

Обратите внимание, что эта концепция связана только с масштабируемостью, а не с оптимизацией количества шагов для каждого автомобиля. Здесь нам все равно, 5 шагов или 50 шагов на машину — главное, чтобы N машин делали (X * N) шагов, а не (X * N2).

person Nathan Long    schedule 03.09.2008