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

АЛЕКСАНДР КАЛЕНДАРЕВ , РБК Медиа, программист, [email protected]


Проблемы и пути решения

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

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

Уточним терминологию:

  • Производительность (performance) – способность приложения отвечать таким требованиям, как максимальное время реакции, пропускная способность.
  • Пропускная способность (capacity) – максимальная возможность приложения пропустить через себя определенное количество запросов в единицу времени или держать определенное число пользовательских сессий.
  • Масштабируемость (scalability) – это характеристика приложения, показывающая его способность сохранять производительность при увеличении пропускной способности. В свою очередь, масштабирование – это процесс обеспечения роста системы. Масштабирование может быть вертикальным или горизонтальным.
  • Вертикальное масштабирование – это увеличение производительности за счет наращивания мощности железа, объема оперативной памяти и т.д. Рано или поздно вертикальное масштабирование упрется в верхний предел.
  • Горизонтальное масштабирование – это увеличение производительности за счет разделения данных на множество серверов.

Функциональное разделение данных

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

Масштабирование с использованием репликации

Самый простой способ масштабирования, который часто используется для небольших и средних проектов, – использование репликации. Репликация – это механизм синхронизации нескольких копий объекта, таблиц базы данных (см. рис. 2). Master-slave-репликация – это синхронизация данных с основного master-сервера к подчиненным slave-серверам.

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

Множество БД имеет встроенную репликацию, или, как говорят, «решение из коробки». Например, PostgreSQL-репликация может осуществляться следующими утилитами:

  • Slony-I – асинхронная (master to multiple slaves) репликация;
  • pgpool-I/II – синхронный мультимастер репликации;
  • Pgcluster – синхронный мультимастер репликации;
  • Bucardo;
  • Londiste;
  • RubyRep.
  • начиная с версии 9.0, встроенная потоковая репликация.

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

Статью целиком читайте в журнале «Системный администратор», №10 за 2014 г. на страницах 54-62.

PDF-версию данного номера можно приобрести в нашем магазине .


Вконтакте

Масштабирование приложений ASP.NET

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

Далее вы развертываете приложение, вводите его в эксплуатацию и оно работает замечательно в течение нескольких первых недель, но с увеличением количества пользователей увеличивается и количество запросов, которые требуется обработать вашему серверу, и вдруг, ваш сервер начинает захлебываться. Сначала это может проявляться в увеличении времени обработки запросов, затем рабочий процесс начинает использовать все больше памяти и вычислительных ресурсов и в конечном итоге веб сервер просто перестает успевать обрабатывать все запросы и в файлах журналов начинают все чаще появляться сообщения HTTP 500 («Internal Server Error»).

Что случилось? Может быть снова заняться оптимизацией приложения? Однако, с увеличением количества пользователей ситуация повторится. Может быть увеличить объем памяти или добавить процессоры? Однако подобное расширение возможностей единственного компьютера имеет свои пределы. Пришло время признать тот факт, что вам необходимы дополнительные серверы.

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

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

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

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

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

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

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

Механизмы масштабирования в ASP.NET

Горизонтальное масштабирование требует хранения информации о состоянии за пределами процессов. В ASP.NET имеется два механизма, обеспечивающих такой способ хранения данных:

Служба управления состоянием (State Service)

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

SQL Server

ASP.NET поддерживает возможность хранения информации о состоянии в базе данных SQL Server. Этот механизм не только поддерживает те же возможности, что и служба управления состоянием, но также обеспечивает долговременное хранение данных, поэтому, даже если на веб-серверах и на сервере с базой данных SQL Server случится аварийная ситуация, информация о состоянии сохранится.

Для нужд кеширования в большинстве случаев можно с успехом использовать один из механизмов распределенного кеширования, таких как Microsoft AppFabric Cache , NCache или Memcached , последний из которых является открытой реализацией распределенного кеша.

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

Некоторые распределенные кеши, такие как AppFabric Cache и Memcached, также имеют собственные реализации службы управления состоянием и провайдеров кеша для ASP.NET.

Ловушки горизонтального масштабирования

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

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

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

Управлять генерацией этих ключей в ASP.NET можно путем настройки параметров в разделе machineKey, в файле web.config. Когда веб-приложение выполняется на нескольких серверах, вам необходимо настроить все серверы так, чтобы они использовали один и тот же предварительно сгенерированный ключ.

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

Более полную информацию о создании уникальных ключей и включения их в настройки приложений можно получить в базе знаний Microsoft Knowledge Base . За дополнительной информацией о создании RSA-контейнера ключей обращайтесь к статье «Импорт и экспорт защищенных контейнеров ключей RSA для конфигурации» на сайте MSDN.

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

Приступим?

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

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

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

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

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

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

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

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

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

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

  • поддержка многопроцессорной обработки;
  • гибкость архитектуры.

Многопроцессорные системы

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

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

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

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

Гибкость архитектуры

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

Обычно узким местом является невозможность динамической подстройки характеристик программ сервера баз данных. Способность на ходу определять такие параметры, как объем потребляемой памяти, число занятых процессоров, количество параллельных потоков выполнения заданий (будь то настоящие потоки (threads), процессы операционной системы или виртуальные процессоры) и количество фрагментов таблиц и индексов баз данных, а также их распределение по физическим дискам БЕЗ останова и перезапуска системы является требованием, вытекающим из сути современных приложений. В идеальном варианте каждый из этих параметров можно было бы изменить динамически в заданных для конкретного пользователя пределах.

9 июля 2015 в 09:10

Горизонтальное масштабирование серверов баз данных для OLTP-систем, или что есть на рынке

  • Администрирование баз данных ,
  • Серверная оптимизация

Как правило, в крупных и средних компаниях существуют высоконагруженные транзакционные информационные системы, которые являются важнейшей составляющей бизнеса, их называют OLTP-системами. С ростом бизнеса нагрузка увеличивается очень быстро, поэтому задача увеличения производительности имеющихся ресурсов под серверы баз данных, стоит очень остро. Зачастую для решения задачи увеличения производительности серверов баз данных приобретается более мощное оборудования (так называемое «вертикальное» масштабирование), но этот способ имеет очень существенный минус: компания рано или поздно купит сервер баз данных максимальной производительности по приемлемой цене, и что делать дальше? Дальше перспективы для бизнеса могут быть не такие радужные – во многих случаях речь идет об ухудшении репутации компании, невозможности обслужить клиентов в моменты повышенного спроса, значительной потере прибыли.

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

Многие ИТ-компании в России и мире занимаются разработкой подобных решений, ниже я попытаюсь рассказать о них более подробно.

Первое решение - Oracle RAC (Real Application Cluster - появилось еще в далеком 2001 году в версии 9i для повышения доступности и производительности в высоконагруженных системах на базе СУБД Oracle. Оно позволяет распределить нагрузку на высоконагруженную базу данных между серверами БД и тем самым увеличить возможности OLTP-системы по беспроблемному росту информационных потоков. Для получения более подробной информации можно обратиться к документации или книгам издательства Oracle Press. Поэтому остановлюсь на некоторых моментах, интересных с точки зрения принципа работы.

Т.к. в Oracle RAC реализована архитектура Shared-everything (со всеми присущими ей преимуществами и недостатками), то для каждого сервера в Oracle RAC существует свой кэш, в который попадают данные SQL запросов, выполненных на нём. Также существует глобальный кэш кластера, реализованный с помощью технологи Cache Fusion, который синхронизируется с локальными кэшами серверов по данным. Особую роль в координации ресурсов кластера и объединения кэша играет структура данных Global Resource Directory, в которой фиксируется на каком сервере, какие данные и по каким объектам актуальны; какой режим блокировок для объекта на экземпляре. Вся эта информация помогает принять решение, на какой сервер с точки зрения производительности лучше отправить запрос SQL, так как в случае неправильного решения время запроса SQL увеличится за счет времени на синхронизацию данных между кэшами.

Важная особенность такого подхода к распределению нагрузки между серверами БД - необходимость учета «разнообразия» траффика SQL от OLTP-системы. В случаях, когда запросы SQL извлекают данные из многих таблиц одновременно, и интенсивность изменения в этих таблицах большая, возможна потеря времени на синхронизацию данных кэша между различными серверами кластера (именно по этой причине нужен быстрый и надежный interconnect между серверами). Это, в свою очередь, может привести к ухудшению отклика OLTP-системы, и преимущества от использования Oracle RAC могут быть полностью нивелированы.

Плюсы:

  • Active/Active кластер
  • Балансировка нагрузки
  • Масштабирование с увеличением производительности, но и увеличением доступности
  • Практически линейное увеличение производительности при добавлении новых узлов в кластер
  • «Прозрачное» для приложений масштабирование

Минусы:

  • Работает только с СУБД Oracle
  • Для работы желателен высокопроизводительный interconnect с низкими задержками
  • СХД может быть единой точкой отказа. Для обеспечения высокого уровня отказоустойчивости RAC нужно комбинировать со standby или зеркалированием СХД.

Второе решение - Citrix NetScaler – реализует горизонтальное масштабирование серверов БД для OLTP-систем на базе MS SQL Server и MySQL иначе, чем Oracle RAC. С техническими особенностями можно ознакомиться, пройдя по ссылке .

Если в Oracle RAC серверы баз данных синхронизируются автоматически, то Citrix NetScaler для синхронизации должен использовать сторонние технологии: AlwaysOn от Microsoft, MySQL replication. Само же решение Citrix NetScaler является прокси-сервером между уровнем приложения (сервер приложения, web-сервер) и серверами баз данных, таким образом все запросы SQL к серверу БД проходят через него.

По спецификации решение умеет распознавать сигнатуру запросов SQL (на чтение или запись данных) и перенаправлять их на нужные (определенные настройками) сервера в кластере. Задержка на обработку запроса SQL прокси-сервером минимальна, поэтому отклик OLTP-системы не должен ухудшиться после внедрения. Несмотря на этот плюс, возможности для балансировки нагрузки от запросов SQL также зависят от особенностей траффика OLTP-системы. Во многих OLTP-системах измененные данные в транзакции сразу считываются следующим запросом SQL для дальнейшей работы. Учитывая особенности такой технологии, как например MS AlwaysOn, данные на дополнительных серверах отстают от основного на некоторое время (в синхронном и асинхронном режиме). Без учета этого факта приложение и пользователь могут получить ситуацию, при которой добавленные данные будут отсутствовать в выборке следующего запроса SQL. Как правило, технологию Citrix NetScaler рекомендуют использовать не в автоматическом режиме, а в ручном, поэтому сфера ее применения ограничивается несложными запросами к БД в веб-приложениях.

Третья технология - Softpoint Data Cluster – российская разработка, которая схожа с двумя предыдущими, при этом в ряде моментов более применима к практическим задачам по «горизонтальному» масштабированию серверов баз данных для OLTP- систем. Более подробную информацию о продукте можно найти на сайте вендора .

Технология на первый взгляд похожа на Citrix NetScaler, так как представляет собой прокси-сервер между уровнем приложения и уровнем базы данных, а также тесно интегрирована с технологиями синхронизации БД (например, MS AlwaysOn), но в отличие от Citrix NetScaler отслеживает рассинхронизации серверов БД в кластере и полностью гарантирует непротиворечивость данных в выборках, где бы на серверах ни выполнялся запрос SQL. Эта особенность позволяет без адаптации к трафику приложения обеспечить автоматическую балансировку нагрузки.

Также технология обеспечивает синхронизацию временных таблиц между серверами в кластере, что очень важно для более качественной балансировки, в том числе запросов SQL с использованием временных таблиц. Важным преимуществом использования Softpoint Data Cluster является возможность ознакомиться с примерами внедрений для



Есть вопросы?

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: