title: Алекс Сюй, «System Design. Подготовка к сложному интервью» (Vol. 1, рус. изд.) — глава 1: Масштабирование от нуля до миллионов пользователей source: materials/System Design. Подготовка к сложному интервью.pdf, стр. 9–39 конспект: TASK-45.38, извлечено pymupdf 2026-09-17; ниже полный «грязный» текст главы + выжимка статус: выжимка и перенос в knowledge-base.md / methodology.md / classic-designs.md — см. _map.md
Глава 1. Масштабирование от нуля до миллионов пользователей
Выжимка
Лестница эволюции архитектуры «от одного сервера к миллионам пользователей» — проговаривать как последовательность «боль → шаг»:
- Один сервер (web + БД на одной машине) — точка старта.
- Разделение web и БД — теперь масштабируются независимо.
- Кэш на уровне приложения (помнить запросы; ключ — запрос, значение — ответ; вытеснение LRU + refresh policy).
- LB + горизонтальное масштабирование web-tier (пользователи → LB → пул серверов без состояния).
- Репликация БД master–slave (один ведущий на запись, ведомые на чтение; проблема: если slaves отстают, читаем устаревшее).
- Отдельный кэш-слой (Redis/Memcached) — cache-aside + TTL.
- CDN для статики/медиа — ближе к пользователю, разгрузка центра.
- Stateless-серверы — сессии/состояние наружу (в shared-хранилище), иначе горизонтальный масштаб и failover не работают.
- Несколько ДЦ (GeoDNS/маршрутизация к ближайшему) — плата: трафик между ДЦ, консистентность данных, тестирование.
- Очередь сообщений — асинхронная развязка компонентов (писатель/читатель не знают друг о друге).
Дополнительные темы главы: tiers (web/БД), вертикальное vs горизонтальное масштабирование, SecShell/безопасность касаются вскользь. Месседж для секции: система растёт итеративно, каждый компонент — ответ на конкретную боль, а не «сразу микросервисы на Kafka». Перенос: лестница уже отражена в KB §6 («до шардирования»); очередь — KB §8; сводка — KB §14.1.
Полный текст (грязная выгрузка)
1 МАСШТАБИРОВАНИЕ ОТ НУЛЯ ДО МИЛЛИОНОВ ПОЛЬЗОВАТЕЛЕЙ Проектирование системы с поддержкой миллионов пользователей — не простая задача. Это процесс, требующий непрерывного совершенствова ния и бесконечного улучшения. В этой главе мы создадим систему, кото рая работает в однопользовательском режиме, и постепенно расширим ее для обслуживания миллионов пользователей. Прочитав эту главу, вы освоите несколько методик, которые помогут вам справиться с вопросами на интервью по проектированию ИТ-систем. КОНФИГУРАЦИЯ ИЗ ОДНОГО СЕРВЕРА Путь в тысячу ли начинается с первого шага. То же самое относится и к созданию сложной системы. Начнем с чего-то простого и разместим все на одном сервере. На рис. 1.1 показана конфигурация одного сервера, на котором запускаются все компоненты: веб-приложение, база данных, кэш и т. д. Исследование потока запросов и источника трафика поможет разо браться в этой конфигурации. Давайте сначала взглянем на поток за просов (рис. 1.2). 1. Пользователи обращаются к веб-сайтам по их доменным именам, таким как api.mysite.com. Обычно система доменных имен (Domain Name System, DNS) представляет собой сторонний платный сервис, размещенный за пределами наших серверов. 2. Адрес интернет-протокола (Internet Protocol, IP) возвращается браузеру или мобильному приложению. В этом примере он имеет вид 15.125.23.214.
10 ГЛАВА 1 3. После получения IP-адреса вашему веб-браузеру напрямую от правляются запросы протокола передачи гипертекста (Hypertext Transfer Protocol, HTTP) [1]. Пользователь Веб-браузер Мобильное приложение Веб-сервер IP-адрес api.mysite.com api.mysite.com www.mysite.com Рис. 1.1 Пользователь Веб-браузер Мобильное приложение Веб-сервер 15.125.23.214 api.mysite.com HTML-страница 15.125.23.214 1 2 3 4 Рис. 1.2
Масштабирование от нуля до миллионов пользователей 11 4. Веб-сервер возвращает HTML-страницы или JSON-ответы для рендеринга. Теперь давайте исследуем источник трафика. Трафик, который по лучает ваш веб-сервер, приходит из двух мест: из веб- и мобильного приложения.
Веб-приложение использует сочетание серверных и клиентских языков. Первые (Java, Python и т. д.) предназначены для реали зации бизнес-логики, системы хранения и т. п., а вторые (HTML и JavaScript) — для предоставления информации.
Мобильное приложение использует протокол HTTP для взаимо действия с веб-сервером. Для передачи данных зачастую применя ется формат API-ответов JSON (JavaScript Object Notation — «пред ставление объектов JavaScript»), отличающийся своей простотой. Пример API-ответа в формате JSON показан ниже: GET /users/12 – Retrieve user object for id = 12 { "id": 12, "firstName": "John", "lastName": "Smith", "address":{ "streetAddress": "21 2nd Street", "city": "New York", "state": "NY", "postalCode": 10021 } "phoneNumbers": [ "212 555-1234", "646 555-4567" ] } БАЗА ДАННЫХ С увеличением количества пользователей одного сервера рано или поздно перестанет хватать, и нам понадобится несколько серверов: один для веб-/ мобильного трафика, а другой — для базы данных (рис. 1.3). Разделение системы на веб-уровень и уровень данных позволяет масштабировать эти компоненты независимо друг от друга.
12 ГЛАВА 1 Пользователь Веб-браузер Мобильное приложение Веб-сервер IP-адрес www.mysite.com База данных чтение/запись/обновление Возврат данных www.mysite.com api.mysite.com БД Рис. 1.3 Какую базу данных выбрать? Базы данных (БД) бывают как реляционными (что является традици онным решением), так и нереляционными. Давайте посмотрим, чем они отличаются. Реляционные БД также называют системами управления реляционны ми базами данных (СУРБД). К самым популярным относятся MySQL, Oracle, PostgreSQL и т. д. Реляционные БД предоставляют и хранят дан ные в таблицах и строках. С помощью SQL можно выполнять операции объединения между различными таблицами базы данных. Нереляционные БД также называют NoSQL. Популярностью пользуются CouchDB, Neo4j, Cassandra, HBase, Amazon DynamoDB и т. д. [2]. Эти базы данных делятся на четыре категории: хранилища «ключ–значение», графовые, столбцовые и документные. Нереляционные БД обычно не поддерживают операции соединения. Большинство разработчиков предпочитает реляционные базы данных, поскольку они применяются уже на протяжении более 40 лет и хорошо себя зарекомендовали. Но если они не подходят для ваших конкретных
Масштабирование от нуля до миллионов пользователей 13 задач, стоит непременно обратить внимание на другие варианты. Нере ляционные БД могут быть подходящим решением, если:
ваше приложение нуждается в крайне низкой латентности;
ваши данные не структурированы или не имеют никаких реляци онных связей;
вам нужно лишь сериализовать и десериализовать свои данные (JSON, XML, YAML и т. д.);
вам нужно хранить огромные объемы данных. ВЕРТИКАЛЬНОЕ И ГОРИЗОНТАЛЬНОЕ МАСШТАБИРОВАНИЕ Вертикальное масштабирование, известное как наращивание, — это про цесс повышения мощности ваших серверов (процессоров, памяти и т. д.). Горизонтальное масштабирование, которое еще называют расширением, заключается в добавлении новых серверов в пул ресурсов. Вертикальное масштабирование отлично подходит для задач с неболь шим трафиком. Его главным преимуществом является простота. К со жалению, у него есть ряд серьезных ограничений.
Вертикальное масштабирование имеет жесткий лимит. Ресурсы отдельно взятого сервера нельзя увеличивать бесконечно.
Вертикальное масштабирование не предусматривает отказоустой чивость и резервирование избыточных ресурсов. Если один из серверов выйдет из строя, веб-сайт/приложение станет полностью недоступным. Из-за этих ограничений для крупномасштабных приложений лучше под ходит горизонтальное масштабирование. В предыдущей конфигурации пользователи подключались к веб-серверу напрямую. Если веб-сервер выйдет из строя, они потеряют доступ к веб- сайту. Если же к веб-серверу одновременно обратится большое количество пользователей и нагрузит его до предела, в результате, как правило, от веты будут приходить медленно либо к серверу вовсе станет невозмож но подключиться. Для решения этих проблем больше всего подходит балансировщик нагрузки.
14 ГЛАВА 1 БАЛАНСИРОВЩИК НАГРУЗКИ Балансировщик нагрузки равномерно распределяет входящий трафик между веб-серверами, которые указаны в его списке. На рис. 1.4 показано, как это работает. Пользователь Веб-браузер Мобильное приложение Сервер 1 Балансировщик нагрузки Сервер 2 Домен IP-адрес mywebsite.com 88.88.88.1 Внешний IP: 88.88.88.1 Внутренний IP: 10.0.0.2 Внутренний IP: 10.0.0.1 Рис. 1.4 Как видно на рис. 1.4, пользователи напрямую подключаются к внешнему IP-адресу балансировщика нагрузки. В этой конфигурации клиенты боль ше не имеют прямого доступа к веб-серверам. По соображениям безопас ности для взаимодействия между серверами используются внутренние IP-адреса. Внутренний IP доступен только для серверов из той же сети, но не виден из интернета. Балансировщик нагрузки взаимодействует с веб-серверами с помощью внутренних IP-адресов.
Масштабирование от нуля до миллионов пользователей 15 На рис. 1.4 показано, как за счет добавления балансировщика нагрузки и второго сервера нам удалось решить проблему с отсутствием отказо устойчивости и улучшить доступность веб-уровня. Подробности объ ясняются ниже.
Если сервер 1 выходит из строя, весь трафик перенаправляется к серверу 2. Благодаря этому веб-сайт остается доступным. Чтобы сбалансировать нагрузку, мы добавим в пул серверов новый ис правный веб-сервер.
Если посещаемость веб-сайта стремительно растет и для обслужи вания трафика не хватает двух серверов, балансировщик нагрузки может изящно справиться с этой проблемой. Для этого достаточно расширить пул серверов, и балансировщик начнет автоматически передавать запросы новым веб-серверам. Веб-уровень выглядит хорошо, но что насчет уровня данных? Текущая конфигурация предусматривает лишь одну БД, что исключает поддержку отказоустойчивости и резервирования. Для решения этих проблем обыч но применяют репликацию. Давайте посмотрим, что это такое. РЕПЛИКАЦИЯ БАЗЫ ДАННЫХ Цитата из английской Википедии: «Репликация баз данных может ис пользоваться во многих СУБД, обычно в режиме “ведущий–ведомый”, где роль ведущего сервера играет оригинал (master), а его копии являются ведомыми (slave)» [3]. Ведущая база данных обычно поддерживает только операции записи. Ведомые БД получают от ведущей копии ее содержимого и поддерживают только операции чтения. Все команды для модификации данных, такие как вставка, удаление или обновление, должны направляться ведущей базе данных. В большинстве приложений чтение происходит намного чаще, чем запись, поэтому ведомых БД обычно больше, чем ведущих. На рис. 1.5 показана ведущая база данных с несколькими ведомыми. Преимущества репликации базы данных:
Повышенная производительность. В модели «ведущий–ведомый» все операции записи и обновления происходят на ведущих узлах, а операции чтения распределяются между ведомыми. Это улучшает
16 ГЛАВА 1 производительность, увеличивая количество запросов, которые можно обрабатывать параллельно.
Надежность. Если один из ваших серверов с базой данных слома ется из-за стихийного бедствия, такого как тайфун или землетрясе ние, данные не будут утеряны. Вам не нужно беспокоиться о потере данных, так как они реплицируются по разным местам.
Высокая доступность. За счет репликации данных по разным ме стам ваш веб-сайт будет продолжать работать, даже если одна из БД выйдет из строя, поскольку у вас по-прежнему будет доступ к данным, размещенным на другом сервере. Веб-серверы Ведущая БД Репликация БД Репликация БД Репликация БД Ведомая БД1 Ведомая БД2 Ведомая БД3 запись чтение чтение чтение БД БД БД БД Рис. 1.5
Масштабирование от нуля до миллионов пользователей 17 В предыдущем разделе мы обсудили то, как балансировщик нагрузки улучшает доступность системы. Зададим здесь тот же вопрос: что, если одна из БД перестанет работать? Архитектурная конфигурация, пред ставленная на рис. 1.5, может справиться с этой ситуацией:
Если имеется лишь одна ведомая база данных и она выходит из строя, операции чтения будут временно перенаправлены к ведущей БД. Сразу после выявления проблемы новая ведомая БД заменит старую. Если ведомых БД несколько, операции чтения перена правляются к другим исправным экземплярам. Новый сервер базы данных заменит старый.
Если ведущая база данных выйдет из строя, ее место займет одна из ведомых. Все операции будут временно выполняться на серве ре новой ведущей БД. Новая ведомая БД, предназначенная для репликации данных, немедленно заменит старую. В промышлен ных системах переквалификация ведомой БД в ведущую требует дополнительных усилий, так как ее содержимое может быть не актуальным. Недостающие данные придется обновить с помощью скриптов восстановления. В качестве решения можно использовать и другие методы, включая конфигурации с несколькими ведущими узлами и циклическую репликацию, но они более сложные, поэтому мы не станем рассматривать их в этой книге. Если вам интересна эта тема, обратитесь к справочным материалам [4] [5]. На рис. 1.6 показана архитектура системы после добавления балансиров щика нагрузки и репликации базы данных. Рассмотрим эту конфигурацию.
Пользователь получает из DNS IP-адрес балансировщика нагрузки.
Пользователь подключается к балансировщику нагрузки с помо щью этого IP-адреса.
HTTP-запрос направляется либо к серверу 1, либо к серверу 2.
Веб-сервер считывает пользовательские данные из ведомой БД.
Веб-сервер направляет любые операции по изменению данных ведущей БД, включая чтение, обновление и удаление. Итак, мы как следует разобрались в веб-уровне и уровне данных. Теперь пришло время улучшить время загрузки/ответа. Для этого можно до
18 ГЛАВА 1 бавить слой кэша и разместить статические ресурсы (JavaScript/CSS/ изображения/видеофайлы) в сети доставки содержимого (content delivery network, CDN). Пользователь Веб-браузер Мобильное приложение IP-адрес www.mysite.com Балансировщик нагрузки Веб-уровень Сервер 1 Сервер 2 Репликация Ведущая БД Ведомая БД www.mysite.com api.mysite.com Уровень данных Запись Запись Чтение Чтение БД БД Рис. 1.6 КЭШ Кэш — это участок памяти, в который временно записываются резуль таты ресурсоемких ответов или данных, к которым часто обращаются. Это позволяет ускорить обслуживание последующих запросов. Как про
Масштабирование от нуля до миллионов пользователей 19 иллюстрировано на рис. 1.6, при каждой загрузке новой веб-страницы выполняется один или несколько запросов к БД для извлечения данных. Многократное обращение к базе данных существенно влияет на произ водительность. Кэш может смягчить эту проблему. Уровень кэша Уровень кэша — это слой временного хранилища данных, который по сво ей скорости работы намного опережает БД. К преимуществам отдельного уровня кэша можно отнести улучшение производительности системы, возможность снизить нагрузку на базу данных и масштабировать этот уровень независимо от других. На рис. 1.7 показан пример конфигурации сервера кэширования. 1. Если данные есть в кэше, читаем их из кэша Веб-сервер Кэш КЭШ БД 2.2. Возвращаем данные веб-серверу 2.1. Если данных нет в кэше, сохраняем их в кэш База данных Рис. 1.7 Получив запрос, веб-сервер сначала проверяет наличие ответа в кэше. Если ответ есть, данные возвращаются клиенту. Если нет, то веб-сервер обращается к базе данных, сохраняет ответ в кэше и пересылает его об ратно клиенту. Эта стратегия называется кэшем сквозного чтения. В за висимости от типа и размера данных, а также от того, как к ним обычно обращаются, можно использовать и другие подходы. В предыдущем исследовании объясняется принцип работы разных стратегий кэширо вания [6]. Взаимодействовать с серверами кэширования просто, так как большин ство из них предоставляют API-интерфейсы для распространенных язы ков программирования. В следующем фрагменте кода показан типичный пример использования API-интерфейса Memcached: SECONDS=1 cache.set('myKey', 'hi there', 3600 * SECONDS) cache.get('myKey')
20 ГЛАВА 1 Некоторые аспекты использования кэша Вот несколько соображений касательно использования систем кеширо вания.
Определитесь с тем, когда будет использоваться кэш. Это лучше делать в ситуациях, когда чтение данных происходит часто, а из менение — редко. Поскольку кэшированные данные хранятся в энергозависимой памяти, сервер кеширования не подходит для постоянного хранения. Например, если он перезапустится, все дан ные, хранившиеся в памяти, будут утрачены. В связи с этим данные необходимо записывать в постоянные хранилища.
Выбор срока действия. Рекомендуется реализовать механизм, ограничивающий срок действия кэша. Просроченные данные не медленно удаляются. Если такого механизма нет, данные будут храниться в памяти постоянно. Срок действия лучше не делать слишком коротким, иначе система будет слишком часто обнов лять данные, загружая их из БД. С другой стороны, из-за слишком длинного срока действия данные могут оказаться неактуальными.
Согласованность. Это подразумевает синхронизацию данных в хра нилище и кэше. Несогласованность может возникнуть из-за того, что операции изменения данных в хранилище и кэше выполняются не за одну транзакцию. При масштабировании системы в пределах нескольких регионов может быть непросто поддерживать согласо ванность. Подробнее об этом можно почитать в документе Scaling Memcache at Facebook, опубликованном Facebook [7].
Предотвращение сбоев. Наличие лишь одного сервера кэширования может оказаться потенциальной единой точкой отказа (single point of failure, SPOF), которая, согласно английской Википедии, имеет следующее определение: «Единая точка отказа — это компонент, выход из строя которого приводит к прекращению работы всей системы» [8]. В связи с этим, чтобы избежать SPOF, рекомендует ся использовать несколько серверов кэширования, размещенных в разных центрах обработки данных (ЦОД). А еще можно выделить какой-нибудь дополнительный объем памяти: это создаст буфер на случай, если память начнет использоваться более активно.
Политика вытеснения. Когда кэш полностью заполнен, любой запрос на добавление новых элементов может привести к удале
Масштабирование от нуля до миллионов пользователей 21 нию существующих. Это называют вытеснением кэша. Самой по пулярной политикой считается вытеснение давно неиспользуемых данных (least-recently-used, LRU). Для разных ситуаций могут также подойти вытеснение наименее часто используемых данных (least-frequently-used, LFU) или метод «первым пришел, первым ушел» (FIFO, first-in-first-out). Пользователь А Пользователь Б Пользователь В Единая точка отказа Один сервер Рис. 1.8 СЕТЬ ДОСТАВКИ СОДЕРЖИМОГО (CDN) CDN — это сеть географически распределенных серверов, которая исполь зуется для доставки статического содержимого. Серверы CDN кэшируют такие статические файлы, как изображения, видео, CSS, JavaScript и т. д. Кэширование динамического содержимого — идея относительно новая. Здесь мы не будем углубляться в детали. Ограничимся лишь следующим: такой способ позволяет записывать в кэш HTML-страницы в зависимости от пути, параметров, cookie-файлов и заголовков запроса. Подробнее об этом можно почитать в статье из списка дополнительной литературы [9]. В этой книге мы сосредоточимся на использовании CDN для кэширова ния статического содержимого. Вот общий принцип работы CDN: когда пользователь посещает веб-сайт, ближайший к нему сервер CDN доставляет статическое содержимое.
22 ГЛАВА 1 Очевидно, что чем дальше от серверов CDN находятся пользователи, тем медленнее загружается веб-сайт. Например, если серверы CDN рас положены в Сан-Франциско, пользователь из Лос-Анджелеса получит содержимое быстрее, чем пользователь из Европы. На рис. 1.9 проиллю стрировано, как CDN может уменьшать время загрузки. Клиент Исходный источник CDN 120 мс Клиент 30 мс Рис. 1.9 Принцип работы CDN продемонстрирован на рис. 1.10. 1. Пользователь A пытается получить image.png с помощью URL- адреса изображения. Домен этого URL-адреса предоставляется провайдером CDN. Ниже показано, как могут выглядеть URL- адреса изображений, на примере CDN от Amazon и Akami: https://mysite.cloudfront.net/logo.jpg https://mysite.akamai.com/image-manager/img/logo.jpg 2. Если в кэше сервера CDN нет image.png, он запрашивает этот файл из оригинального источника, например веб-сервера или онлайн- хранилища вроде Amazon S3. 3. Источник возвращает серверу CDN файл image.png вместе с до полнительным HTTP-заголовком TTL (Time-to-Live — «время жизни»), который определяет, как долго изображение будет на ходиться в кэше. 4. CDN кэширует изображение и возвращает его пользователю A. Оно остается в кэше CDN, пока не истечет срок TTL.
Масштабирование от нуля до миллионов пользователей 23 Пользователь A 1. Получаем image.png 4. Возвращаем image.png Сервер CDN 2. Если image.png нет в CDN, берем его на сервере 3. Сохраняем image.png в CDN 5. Получаем image.png 6. Возвращаем image.png Пользователь Б Рис. 1.10 5. Пользователь Б отправляет запрос на получение того же файла. 6. Если срок TTL еще не истек, изображение возвращается из кэша. Нюансы использования CDN
Стоимость. Серверы CDN предоставляются сторонними компа ниями, а перемещение данных в CDN и из CDN стоит денег. Кэ ширование нечасто используемых ресурсов не даст существенных преимуществ, поэтому из CDN их лучше убрать.
Подбор подходящего срока годности кэша. Для содержимого, которое зависит от времени, необходимо предусмотреть срок год ности кэша. Он должен быть не слишком длинным, но и не слиш ком коротким. В первом случае содержимое может потерять свою актуальность, а во втором — привести к повторной перезагрузке содержимого с исходных серверов в CDN.
Возможность сбоев. Вы должны подумать о том, как ваши веб- сайты/приложения будут справляться с недоступностью CDN. Если CDN временно выходит из строя, у клиента должна быть возможность обнаружить эту проблему и запросить ресурсы из исходного источника.
Аннулирование файлов. Файлы можно удалять из CDN до истече ния их срока годности одним из следующих способов: аннулировать объект CDN с помощью API-интерфейсов, предо ставляемых поставщиками CDN;
24 ГЛАВА 1 использовать версионирование, чтобы возвращать разные вер сии объектов. Для этого к URL-адресу можно добавить параметр с номером версии. Например, версия 2 может быть представлена строкой запроса: image.png?v=2. На рис. 1.11 показана конфигурация после добавления CDN и кэша. 1. Статические ресурсы (JS, CSS, изображения и т. д.) больше не раз даются веб-серверами. Для повышения производительности они извлекаются из CDN. 2. Нагрузка на базу данных снижается за счет кэширования. Пользователь Веб-браузер Мобильное приложение CDN Балансировщик нагрузки Веб-уровень Сервер 1 Сервер 2 Репликация Ведущая БД Ведомая БД www.mysite.com api.mysite.com Уровень данных Кэш 2 1 КЭШ БД БД Рис. 1.11
Масштабирование от нуля до миллионов пользователей 25 ВЕБ-УРОВЕНЬ БЕЗ СОХРАНЕНИЯ СОСТОЯНИЯ Пришло время поговорить о горизонтальном масштабировании веб- уровня. Для этого нужно вынести из него состояние (например, инфор мацию о пользовательских сеансах). Данные сеансов рекомендуется записывать в постоянные хранилища, такие как реляционные БД или NoSQL. Каждый веб-сервер в кластере может запросить состояние из базы данных. Таким образом получается веб-уровень без сохранения состояния. Архитектура с сохранением состояния От того, хранит сервер состояние или нет, зависит, будет ли он «помнить» данные клиента (состояние) между разными запросами. На рис. 1.12 показан пример архитектуры с сохранением состояния. • Данные сеанса для пользователя A • Изображение в профиле пользователя A Пользователь A Пользователь Б Пользователь В http-запрос http-запрос http-запрос Cервер 1 Cервер 2 Cервер 3 • Данные сеанса для пользователя Б • Изображение в профиле пользователя Б • Данные сеанса для пользователя В • Изображение в профиле пользователя В Рис. 1.12 На рис. 1.12 данные сеанса и изображение в профиле пользователя A хранятся на сервере 1. Чтобы аутентифицировать пользователя A, HTTP-
26 ГЛАВА 1 запрос должен быть направлен к этому серверу. Если отправить этот запрос, к примеру, серверу 2, аутентификация не пройдет, так как на втором сервере нет данных соответствующего сеанса. Точно так же все HTTP-запросы пользователя Б должны направляться к серверу 2, а за просы пользователя В — к серверу 3. Проблема в том, что каждый запрос с отдельно взятого клиента необ ходимо оправлять на соответствующий сервер. В большинстве балан сировщиков нагрузки для этого предусмотрены липкие сеансы [10], но такой подход увеличивает накладные расходы. Из-за него добавление и удаление серверов дается с трудом. Также возникают проблемы, если сервер выходит из строя. Архитектура без сохранения состояния На рис. 1.13 показана архитектура без сохранения состояния. Пользователь A Пользователь Б Пользователь В http-запрос http-запрос http-запрос Веб-серверы извлечение состояния Разделяемое хранилище Рис. 1.13
Масштабирование от нуля до миллионов пользователей 27 В этой не хранящей состояние архитектуре пользовательские HTTP- запросы могут быть направлены любым веб-серверам, которые извлекают данные о состоянии из общего хранилища. Хранилище отделено от веб- серверов. Отсутствие состояния делает систему более простой, надежной и масштабируемой. На рис. 1.14 показана обновленная конфигурация с веб-уровнем, не хранящим состояние. Пользователь Веб-браузер CDN Балансировщик нагрузки Автомасштабирование Сервер 1 Сервер 2 www.mysite.com api.mysite.com Мобильное приложение Сервер 4 Сервер 3 Ведомая БД Ведущая БД Ведомая БД Кэш NoSQL БД БД БД КЭШ Репликация Репликация 1 Рис. 1.14 На рис. 1.14 данные сеанса вынесены из веб-уровня и теперь находятся в постоянном хранилище, роль которого могут играть реляционные базы данных: Memcached/Redis, NoSQL и т. д. Здесь хранилище NoSQL выбрано в связи с простотой его масштабирования. Автомасштабирование означает, что добавление и удаление веб-серверов происходит автоматически в за
28 ГЛАВА 1
висимости от объемов трафика. После того как данные о состоянии вы
несены в отдельное хранилище, автомасштабирование веб-уровня легко
достигается за счет добавления и удаления серверов с учетом нагрузки.
Ваш веб-сайт стремительно развивается, привлекая множество пользовате
лей со всего мира. Для улучшения доступности и UX в различных регионах
крайне необходима поддержка нескольких центров обработки данных.
ЦЕНТРЫ ОБРАБОТКИ ДАННЫХ
На рис. 1.15 показана демонстрационная конфигурация с двумя центрами
обработки данных (ЦОД). В нормальных условиях пользователи, скажем,
Веб-браузер
Пользователь
Мобильное
приложение
www.mysite.com
api.mysite.com
Балансировщик нагрузки
геомаршрутизация
CDN
Веб-серверы
Кэш
Базы
данных
ЦОД1...
Веб-серверы
Кэш
Базы
данных
ЦОД2...
NoSQL
геомаршрутизация
КЭШ
КЭШ
КЭШ
КЭШ
КЭШ
КЭШ
Рис. 1.15
Масштабирование от нуля до миллионов пользователей
29
из США с помощью geoDNS направляются к ближайшему центру об
работки данных с разделением трафика между регионами US-East и US-
West в пропорции x % к (100 – x) %. Это называется географической
маршрутизацией. geoDNS — это сервис, который сопоставляет доменные
имена с IP-адресами в зависимости от местонахождения пользователя.
В случае любого серьезного нарушения работы одного из центров об
работки данных мы перенаправляем весь трафик к исправному ЦОД.
На рис. 1.16 ЦОД2 (US-West) недоступен, поэтому 100 % трафика на
правляется к ЦОД1 (US-East).
Пользователь
Веб-серверы
Кэш
Базы
данных
ЦОД1...
Веб-серверы
Кэш
Базы
данных
ЦОД2...
NoSQL
Веб-серверы
Кэш
Базы
данных
ЦОД2...
КЭШ
КЭШ
КЭШ
КЭШ
КЭШ
КЭШ
Веб-браузер
Мобильное
приложение
www.mysite.com
api.mysite.com
Балансировщик нагрузки
100% трафика
CDN
×
Рис. 1.16
Для реализации архитектуры с несколькими центрами обработки данных
необходимо решить несколько технических вопросов.
30 ГЛАВА 1
Перенаправление трафика. Необходимы эффективные инстру менты для направления трафика к подходящему ЦОД. GeoDNS позволяет выбирать центр обработки данных, который находится ближе всего к пользователю.
Синхронизация данных. Пользователи могут работать с разными локальными базами данных и кэшами в зависимости от региона. В случае сбоя трафик может быть перенаправлен к ЦОД, в котором нет запрашиваемых данных. Распространенным решением является репликация данных между несколькими ЦОД. В одном из иссле дований показано, как Netflix реализует асинхронную репликацию между разными центрами обработки данных [11].
Тесты и развертывание. В конфигурации с несколькими ЦОД тестирование веб-сайта/приложения необходимо проводить в раз ных местах. Автоматические средства развертывания незаменимы в поддержании согласованности всех ЦОД [11]. Чтобы еще сильнее улучшить масштабируемость нашей системы, мы должны разделить ее компоненты; это позволит масштабировать их не зависимо друг от друга. Во многих реальных распределенных системах для решения этой задачи используют очереди сообщений. ОЧЕРЕДЬ СООБЩЕНИЙ Очередь сообщений — это устойчивый компонент, который загружается в память и поддерживает асинхронное взаимодействие. Он служит бу фером и распределяет асинхронные запросы. Очередь сообщений имеет простую базовую архитектуру. Сервисы ввода, так называемые произво дители/издатели, создают сообщения и публикуют их в очереди. Другие сервисы или серверы, которые называют потребителями/подписчиками, подключаются к очереди и выполняют действия, определенные в сообще ниях. Эта модель показана на рис. 1.17. Благодаря разделению очередь сообщений является предпочтительной архитектурой для создания масштабируемых и надежных приложений. Производитель может публиковать сообщения в очереди, даже если потребитель не в состоянии их обработать. И наоборот — потребитель может считывать сообщения из очереди, даже если производитель не доступен.
Масштабирование от нуля до миллионов пользователей 31 публикация потребление подписка Производитель Потребитель Очередь сообщений Рис. 1.17 Рассмотрим следующий сценарий: ваше приложение поддерживает функции редактирования фотографий, такие как обрезка, повышение четкости, размытие и т. д. Для выполнения этих операций нужно какое- то время. На рис. 1.18 показано, как веб-серверы публикуют в очереди сообщений задания по обработке фотографий. Рабочие узлы достают задания из очереди и выполняют асинхронную обработку. Производи тель и потребитель могут масштабироваться независимо друг от друга. Когда очередь становится слишком большой, для сокращения времени обработки добавляются новые рабочие узлы. Если же очередь в основном пустует, количество рабочих узлов можно уменьшить. публикация Потребитель Очередь для обработки фото Производитель Рабочие узлы для обработки фотографий потребление Веб-серверы Рис. 1.18 ЛОГИРОВАНИЕ, МЕТРИКИ, АВТОМАТИЗАЦИЯ Если вы имеете дело с небольшим веб-сайтом на базе нескольких серве ров, то без поддержки логирования, метрик и автоматизации в принципе можно обойтись. Если же ваш сайт дорос до обслуживания крупной компании, эти инструменты являются незаменимыми. Логирование. Мониторинг логов играет важную роль, помогая выявлять в системе ошибки и проблемы. Логи можно отслеживать на каждом от
32 ГЛАВА 1 дельном сервере, но есть также инструменты, позволяющие собирать их в централизованном сервисе ради удобства поиска и просмотра. Метрики. Сбор разного рода метрик помогает лучше понять предметную область и оценить работоспособность системы. Вам может пригодиться что-то из следующего:
метрики уровня сервера: процессор, память, дисковый ввод/вывод и т. д.;
агрегированные метрики: производительность всего уровня базы данных, уровня кэша и т. д.;
ключевые бизнес-метрики: суточное количество активных пользо вателей, удержание, доход и т. д. Автоматизация. Когда система становится большой и сложной, для по вышения продуктивности необходимо разработать новые или исполь зовать уже готовые средства автоматизации. Рекомендуется применять непрерывную интеграцию — это когда каждая фиксация кода автома тически проверяется, что помогает обнаруживать проблемы на ранних этапах. Кроме того, автоматизация процессов сборки, тестирования, развертывания и других может существенно повысить продуктивность разработчиков. Добавление очередей сообщений и других инструментов На рис. 1.19 показана обновленная архитектура. Ради экономии бумаги на этой схеме изображен лишь один центр обработки данных. 1. Архитектура включает в себя очередь сообщений, которая помо гает сделать систему менее связанной и более устойчивой к от казам. 2. Также добавлены средства логирования, автоматизации и сбора метрик. Объемы данных растут ежедневно, что увеличивает нагрузку на БД. При шло время масштабировать уровень данных.
Масштабирование от нуля до миллионов пользователей
33
NoSQL
Кэш
База
данных
Рабочие
узлы
Очередь сообщений
Логирование
Автоматизация
Мониторинг
Метрики
Инструменты
1
Пользователь
Веб-серверы
ЦОД1
Веб-браузер
Мобильное
приложение
www.mysite.com
api.mysite.com
Балансировщик нагрузки
CDN
2
КЭШ
КЭШ
КЭШ
Рис. 1.19
МАСШТАБИРОВАНИЕ БАЗЫ ДАННЫХ
Есть два общих подхода к масштабированию баз данных: вертикальный
и горизонтальный.
34 ГЛАВА 1 Вертикальное масштабирование Вертикальное масштабирование (или наращивание) подразумевает по вышение производительности существующего компьютера за счет до бавления ресурсов процессора, памяти, диска и т. д. Серверы баз данных бывают довольно мощными. Сервис Amazon RDS (Relational Database Service — «сервис реляционных баз данных») [12] предлагает серверы БД с 24 Тб оперативной памяти. Они позволяют хранить и обрабатывать множество информации. В 2013 году на сайт stackoverflow.com ежедневно заходило больше 10 миллионов уникальных пользователей, но в то время у него была всего одна ведущая база данных [13]. При этом у вертикаль ного масштабирования есть серьезные недостатки.
Вы можете добавлять к своему серверу дополнительные ресурсы процессора, памяти и т. д., но аппаратные ограничения игнориро вать не получится. Если у вас много пользователей, одного сервера будет недостаточно.
Повышенный риск возникновения единой точки отказа.
Вертикальное масштабирование имеет высокую общую стоимость. Мощные серверы очень дорогие. Горизонтальное масштабирование Горизонтальное масштабирование (или расширение) заключается в до бавлении новых серверов. Сравнение вертикального и горизонтального масштабирования представлено на рис. 1.20. Шардинг позволяет разделить крупные наборы данных на более мелкие и простые в использовании части, которые называют шардами. Все шарды имеют одну и ту же схему, но каждый из них хранит уникальные данные. Пример сегментированных баз данных показан на рис. 1.21. Сервер БД для сохранения пользовательской информации выбирается на основе ID пользователя. При каждом обращении к данным используется функция хеширования, которая находит подходящий шард. В нашем примере функция хеширования имеет вид user_id % 4. Если результат равен 0, для хранения и извлечения данных используется сегмент 0. Если резуль тат равен 1, выбирается сегмент 1. Та же логика распространяется и на остальные сегменты.
Масштабирование от нуля до миллионов пользователей 35 (увеличение ресурсов процессора, памяти, диска и т. д.) VS Вертикальное масштабирование (добавление новых серверов) Горизонтальное масштабирование Рис. 1.20 0 1 2 3 user_id % 4 Рис. 1.21 На рис. 1.22 показана таблица с пользовательской информацией, храня щаяся в сегментированных базах данных. При реализации стратегии сегментирования самый важный фактор — это выбор ключа. Ключ шардинга (или ключ раздела) состоит из одного или нескольких столбцов, на основе которых происходит распределение данных. Как видно на рис. 1.22, ключом выступает user_id. Ключ по
36 ГЛАВА 1 зволяет эффективно извлекать и изменять данные, направляя запросы к подходящей БД. При выборе ключа шардинга один из важнейших критериев — возможность равномерного распределения данных. Пользователи user_id 0 4 8 12 ... Шард 0 Пользователи user_id Шард 1 Пользователи user_id Шард 2 Пользователи user_id Шард 3 1 5 9 13 ... 2 6 10 14 ... 3 7 11 15 ... ... ... ... ... Рис. 1.22 Шардинг отлично подходит для масштабирования баз данных, но это далеко не идеальное решение. Оно усложняет систему и создает допол нительные трудности.
Повторное сегментирование данных. Это может понадобиться, когда 1) отдельный шард полностью заполняется из-за стремитель ного развития системы или 2) некоторые шарды заполняются бы стрее других из-за неравномерного распределения данных. В такой ситуации необходимо обновить функцию сегментирования и пере местить имеющиеся данные. Для решения этой проблемы зачастую применяют согласованное хеширование, которое описано в главе 5.
Масштабирование от нуля до миллионов пользователей 37
Проблема знаменитостей. Слишком частое обращение к опреде ленному шарду может вызвать перегрузку сервера. Представьте, что информация о Кэтти Перри, Джастине Бибере и Леди Гаге очутилась в одном и том же сегменте. Если речь идет о социальных приложениях, этот сегмент будет перегружен операциями чтения. Для решения этой проблемы, возможно, придется выделить по от дельному шарду для каждой знаменитости. Может случиться так, что каждый сегмент потребует дальнейшего разделения.
Соединение и денормализация. После сегментирования базы данных между несколькими серверами становится сложно вы полнять операции соединения, охватывающие несколько шардов. Распространенное решение состоит в денормализации базы данных таким образом, чтобы запросы могли выполняться в рамках одной таблицы. На рис. 1.23 база данных сегментирована, чтобы справиться с растущими объемами трафика. Вместе с тем некоторые нереляционные функции перенесены в хранилище NoSQL, чтобы снизить нагрузки на БД. Вы можете ознакомиться со статьей, в которой описано множество примеров применения NoSQL [14]. МИЛЛИОНЫ ПОЛЬЗОВАТЕЛЕЙ Масштабирование системы — это пошаговый процесс. Вещи, описанные в этой главе, могут здорово вам помочь. Но если ваша аудитория поль зователей куда больше нескольких миллионов, вам могут понадобиться тонкие оптимизации и новые стратегии. Например, вам, возможно, при дется оптимизировать свою систему и разбить ее на еще более мелкие сервисы. Все методики, изложенные в этой главе, должны послужить хорошей основой в борьбе с новыми трудностями. В завершение перечислим шаги, которые предпринимаются в ходе масштабирования системы для поддержки миллионов пользователей:
веб-уровень не должен хранить состояния;
резервирование должно быть предусмотрено на каждом уровне;
кэширование данных следует проводить как можно более активно;
38 ГЛАВА 1
Шард 1
Шард ...
Шард 2
NoSQL
Кэш
База данных
Рабочие
узлы
Очередь сообщений
Логирование
Автоматизация
Мониторинг
Метрики
Инструменты
1
Пользователь
Веб-серверы
ЦОД1
Веб-браузер
Мобильное
приложение
www.mysite.com
api.mysite.com
Балансировщик нагрузки
CDN
2
КЭШ
КЭШ
КЭШ
Рис. 1.23
система должна поддерживать больше одного центра обработки данных;
статические ресурсы нужно хранить в CDN;
для масштабирования данных следует применять шардинг;
уровни должны быть разделены на отдельные сервисы;
Масштабирование от нуля до миллионов пользователей 39
необходимо выполнять мониторинг системы и использовать сред ства автоматизации. Поздравляем, вы проделали длинный путь и можете собой гордиться. Отличная работа! СПРАВОЧНЫЕ МАТЕРИАЛЫ [1] Протокол передачи гипертекста: https://ru.wikipedia.org/wiki/HTTP [2] Should you go Beyond Relational Databases?: https://blog.teamtreehouse.com/ should-you-go-beyond-relational-databases [3] Репликация: https://ru.wikipedia.org/wiki/Репликация_(вычислительная_тех ника) [4] Репликация с несколькими ведущими серверами: https://en.wikipedia.org/ wiki/Multi-master_replication [5] NDB Cluster Replication: Multi-Master and Circular Replication: https:// dev.mysql.com/doc/refman/5.7/en/mysql-cluster-replicationmulti-master.html [6] Caching Strategies and How to Choose the Right One: https://codeahoy. com/2017/08/11/caching-strategies-and-how-tochoose-the-right-one/ [7] R. Nishtala, «Facebook, Scaling Memcache at», 10th USENIX Symposium on Networked Systems Design and Implementation (NSDI ’13). [8] Единая точка отказа (англ.): https://en.wikipedia.org/wiki/Single_point_ of_failure [9] Доставка динамического контента с Amazon CloudFront: https://aws. amazon.com/ru/cloudfront/dynamic-content/ [10] Configure Sticky Sessions for Your Classic Load Balancer: https://docs.aws. amazon.com/elasticloadbalancing/latest/classic/elbsticky-sessions.html [11] Active-Active for Multi-Regional Resiliency: https://netflixtechblog.com/ active-active-for-multi-regional-resiliencyc47719f6685b [12] Инстансы Amazon EC2 High Memory: https://aws.amazon.com/ru/ec2/ instance-types/high-memory/ [13] What it takes to run Stack Overflow: http://nickcraver.com/blog/2013/11/22/ what-it-takes-to-runstack-overflow [14] What The Heck Are You Actually Using NoSQL For: http://highscalability. com/blog/2010/12/6/what-the-heck-are-youactually-using-nosql-for.html