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

Источники информации

Как и предыдущий пост "Архитектура Google", этот тоже является переводом статьи от Todd'а Hoff'а. Возможно читателям Google был более интересен, но подход Flickr к масштабируемости тоже более чем заслуживает внимания. Далее привожу источники информации из оригинальной статьи:

Платформа

  • PHP
  • MySQL
  • Сегментирование (прим.: разбиение системы на части, обслуживающие каждая свою группу пользователей; называть можно было по-разному, но давайте остановимся на этом варианте перевода слова "Shards")
  • Memcached для кэширования
  • Squid в качестве обратной-прокси для html и изображений
  • Linux (RedHat)
  • Smarty в роли шаблонизатора
  • Perl
  • PEAR для парсинга e-mail и XML
  • ImageMagick для обработки изображений
  • Java для узлового сервиса
  • Apache
  • SystemImager для развертывания систем
  • Ganglia для мониторинга распределенных систем
  • Subcon хранит важные системные конфигурационные файлы в SVN-репозитории для легкого развертывания на машины в кластере.
  • Cvsup для распространения и обновления коллекций файлов по сети

Статистика

  • Более четырех миллиардов запросов в день
  • Примерно 35 миллионов фотографий в кэше Squid
  • Около двух миллионов фотографий в оперативной памяти Squid
  • Всего приблизительно 470 миллионов изображений, каждое представлено в 4 или 5 размерах
  • 38 тысяч запросов к memcached (12 миллионов объектов)
  • 2 петабайта дискового пространства
  • Более 400000 фотографий добавляются ежедневно

Архитектура

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

  • Два ServerIron
  • Squid кэши
  • Системы хранения NetApp
  • Серверы PHP приложений
  • Менеджер хранения данных
  • Master-master сегменты
  • Центральная база данных, структурированная по принципу Dual Tree
  • Memcached кластер
  • Поисковая система

Хранение данных

  • Структура Dual Tree является индивидуальным набором модификаций для MySQL, позволяющим масштабировать систему путем добавления новых мастер-серверов без использования кольцевой архитектуры. Эта система позволяет экономить на масштабировании, так как варианты мастер-мастер требовали бы удвоенных вложений в оборудование.
  • Центральная база данных включает в себя таблицу пользователей, состоящую из основных ключей пользователей (несколько уникальных идентификационных номеров) и указатель на сегмент, на котором может быть найдена остальная информация о конкретном пользователе.
  • Использование выделенных серверов для статического контента
  • Все, за исключением фотографий, хранится в базе данных
  • Отсутствие состояний заключается в том, что в случае необходимости они имеют возможность передать пользователей от сервера к серверу, что стало намного проще для них после создания своего API
  • В основе масштабируемости лежит репликация, но этот факт помогает лишь при обработке операций чтения
  • Для поиска по определенной части базы данных создается отдельная копия этого фрагмента
  • Использования горизонтального масштабирования для того чтобы можно было проще добавлять новые машины в систему
  • Обработка изображений, полученных от пользователей по электронной почте, происходит с помощью PHP
  • Раньше система страдала от задержек связанных с организацией по принципу мастер-слуга. При слишком большой нагрузке они имели одну точку, которая теоретически могла дать сбой.
  • Им было необходимо иметь возможность проводить технические работы во время непрерывной работы сайта, не прекращая его функционирование.
  • Были проведены отличные работы по планированию распределения дискового пространства, более подробную информацию можно найти по ссылкам в разделе "Источники информации".
  • Для обеспечения возможности масштабирования в будущем, они пошли по федеративному пути развития:
    • Сегменты системы: Мои данные хранятся на моем сегменте, но запись о Вашем комментарии хранится на Вашем сегменте.
    • Глобальное кольцо: Принцип работы схож с DNS, Вам необходимо знать куда Вы хотите пойти и кто контролирует то место, куда Вы собираетесь пойти.
    • Логика на PHP устанавливает соединение с сегментом и поддерживает целостность данных (10 строк кода с комментариями!)
  • Сегменты:
    • Срез основной базы данных
    • Активная репликация по принципу мастер-мастер: имеет несколько недостатков в MySQL 4.1. Автоматическое инкрементирование идентификационных номеров используется для поддержания системы в режиме одновременной активности обоих серверов в паре
    • Привязывание новых учетных записей к сегментам системы происходит случайным образом
    • Миграция пользователей проводится время от времени для того, чтобы избавиться от проблем, связанных с излишне активными пользователями. Необходима сбалансированность в этом процессе, особенно в случаях с большим количеством фотографий… 192 тысячи фотографий, 700 тысяч тэгов, может занять несколько минут. Миграция выполняется вручную.
  • Нажатие на Favorite:
    • Получается информация об учетной записи владельца из кэша для того, чтобы узнать к какому сегменту он привязан (допустим на shard-5)
    • Получается информация о моей учетной записи из кэша, более конкретно - мой сегмент (например shard-13)
    • Начинается "распределенная транзакция" для определения ответов на вопросы: Кто добавил эту фотографию в избранное? Как изменился список избранных фотографий?
  • Подобные вопросы могут задаваться любому сегменту, информация на них абсолютно избыточна.
  • Для избавления от задержек, связанных с репликацией...
    • при каждой загрузке страницы, пользователю предоставляется список серверов
    • если сервер не в состоянии ответить на запрос, запрос переходит к следующему серверу в списке; если список кончился - выводится сообщение об ошибке. При этом не используются постоянные соединения, каждый раз создаются и разрываются новые соединения.
  • Запросы на чтение и запись от каждого пользователя ограничиваются рамками одного сегмента. Задержки репликации исчезают из поля зрения пользователей.
  • Каждый сервер в рамках одного сегмента в обычном состоянии нагружен ровно на половину. Выключите половину серверов в каждом сегменте и система продолжит функционировать без изменений. Это значит, что один сервер внутри сегмента может взять на себя всю нагрузку второго, в то время как второй сервер может по каким либо причинам быть отключен от системы, например для проведения технических работ. Обновление оборудования производится очень просто: отключается половина сегмента, она же обновляется, подключается обратно, процесс повторяется для оставшейся половины.
  • Периоды пиковой нагрузки также нарушают правило 50% нагрузки. В такие моменты система получает 6-7 тысяч запросов в секунду, в то время как на данный момент система может работать на пятидесятипроцентном уровне нагрузки только при четырех тысячах запросов в секунду.
  • В среднем при загрузке одной страницы выполняется 27-35 SQL-запросов. Списки избранных фотографий обрабатываются в реальном времени, ровно как и доступ через API к базе данных. Все требования к нагрузке в реальном времени выполняются без каких-либо недостатков.
  • Более 36 тысяч запросов в секунду может выполняться не выходя за рамки возможностей системы, даже при резком росте трафика.
  • Каждый сегмент содержит данные о более чем 400 тысячах пользователей.
  • Многие данные хранятся в двух местах одновременно. Например, комментарий является частью между комментатором и автором комментируемого контента. Где его хранить? Как насчет обоих мест? Транзакции используются для предотвращения рассинхронизации данных: открывается первая транзакция, выполняется запись, открывается вторая транзакция, выполняется запись, подтверждается первая транзакция если все нормально, после чего вторая подтверждается только в случае если первая прошла успешно.

Поиск

  • Используется два варианта поиска: поиск в рамках сегмента, поддерживающий до 35 тысяч запросов в секунду, а также проприетарный веб-поиск от Yahoo!
  • В 90% случаев используется система от Yahoo!, за исключением поиска по тэгу фотографий одного пользователя и массовых изменений тэгов.
  • Эту систему стоит рассматривать как аналог Lucene.

Оборудование

  • EMT64 под управлением RHEL 4 с 16 Gb оперативной памяти.
  • 6 жестких дисков с 15000rpm, объединены в RAID-10.
  • Размер для пользовательских метаданных достигает 12 терабайт (это не включает фотографии, для них цифры существенно больше).
  • Используются 2U корпуса.

Резервное копирование данных

  • ibbackup выполняется регулярно посредством cron daemon'а, на каждом сегменте настроен на разное время.
  • Каждую ночь делается снимок со всего кластера баз данных.
  • Запись или удаление нескольких больших файлов с резервными копиями одновременно на реплицирующую систему хранения может сильно сократить производительность системы вцелом на последующие несколько часов из-за процесса репликации. Выполнение этого на активно работающей системе хранения фотографий было бы не самой лучшей идеей.
  • Содержание нескольких резервных копий всех Ваших данных требует существенных материальных затрат, но оно того стоит. Особенно это актуально для тех ситуаций, когда Вы понимаете, что что-то пошло не так только спустя несколько дней после того как это случилось, в таких случаях неплохо иметь, например, резервные копии 1, 3, 10 и 30-дневной давности.
  • Фотографии хранятся в системе хранения данных. После загрузки изображения система выдает различные его размеры, на чем ее работа заканчивается. Метаданные и ссылки на файловые системы, где расположены фотографии, хранятся в базе данных.
  • Агрегация данных проходит очень быстро, так как она ограничена пределами сегмента.
  • max_connections = 400 соединений на каждый сегмент, неплохой запас. Значение для кэша потоков установлено равным 45, так как не бывает ситуаций когда более 45 пользователей одновременно выполняют какие-либо действия с одним конкретным сегментом.

Тэги

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

Направления для развития

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

Подводим итоги

  • Старайтесь думать о своем приложении как о чем-то большем, чем просто веб-приложении, тогда у Вас возможно появятся поддержка различных API, RSS и Atom ленты и многие другие возможности.
  • Отсутствие состояний системы позволяет более легко выполнять модернизации не моргнув и глазом.
  • Реструктуризация базы данных - не самое лучшее занятие.
  • Планирование нагрузок должно проводиться уже на ранних этапах развития проекта
  • Начинайте медленно. Не покупайте сразу много оборудования просто из-за того, что Вы рады/боитесь, что ваш сайт взорвется.
  • Измеряйте реально, планирование нагрузок должно базироваться на реальных вещах, а не абстрактных.
  • Внедряйте ведение логов и индивидуальные измерения для оценки реальных показателей на основе серверной статистики, статистика использования не менее важна чем серверная.
  • Кэширование и оперативная память может стать ответом на все вопросы.
  • Создавайте четкие уровни абстракции между работой базы данных, бизнес-логикой, логикой страниц, разметкой страниц и презентационным уровнем. Это позволяет ускорить циклы итеративной разработки.
  • Разделение приложения на уровни позволяет каждому заниматься своим делом: разработчики могут строить логику страниц, в то время как дизайнеры работают с удобством работы для пользователей.
  • Делайте релизы как можно чаще, пускай даже это будет происходить каждые полчаса.
  • Забудьте о всех небольших эффективных вещах, предварительная оптимизация является корнем всего зла в примерно 97% всех случаев.
  • Тестируйте в работе. Постройте архитектурные механизмы (флаги конфигурации, балансировку нагрузки, и так далее), которые позволят Вам разворачивать новое оборудование в (и из) работу.
  • Забудьте об искусственных тестах, они годятся только для получения общего представления о нагрузках, но не для планирования. Искуственные тесты дают искусственные результаты, для настоящих тестов все же стоит пользоваться реальным временем выполнения задач.
  • Найдите максимальное значения для всех показателей:
    • Какой максимум чего-то, что может выполнять каждый сервер?
    • Как близко параметр находится к максимуму и каковы тенденции?
    • MySQL (дисковый ввод/вывод?)
    • Squid (дисковый ввод/вывод? или процессорное время?)
    • Memcached (процессорное время? или пропускная способность?)
  • Старайтесь учесть особенности использования Вашего приложения.
    • Возможен ли резкий рост нагрузки, связанный с каким-либо событием? Например: какое-либо бедствие, или может быть новость?
    • Flickr получает на 20-40% больше новых фотографий в первый рабочий день нового года, чем в любой пик в предыдущем году.
    • По воскресеньям нагрузка в среднем на 40-50% выше, чем в любой другой день недели.
  • Учтите возможность экспоненциального роста. Больше пользователей означает больше контента, больше контента означает больше соединений, больше соединений означает более активное использование.
  • Планируйте возможные варианты управления работой системы в периоды пиковых нагрузок.