Connect.ua - история роста

Connect.ua - это первый украинский социальный сервис. За два года проект вырос в достаточно крупный, а следовательно имеет свою собственную историю масштабирования и роста.
Во время роста мы перепробовали большое количество технологий и подходов, которыми я и хочу поделиться в этой статье.
Наши показатели и цифры
Некоторые цифры проекта
- 720 тыс. пользователей
- 2 миллиона фотографий
- 400 тысяч видео роликов
- 20 миллионов личных сообщений
- 50 миллионов оценок в ЖЖОТ
- 25 миллионов событий в ленте новостей
- 40 миллионов отправленных “Подмигнуть”
Технические показатели
- 500 запросов в секунду на MySQL
- 300 активных соединений на фронте + 200 активных соединений на медиа серверах (HTTP)
- 1.5 тысячи запросов в секунду на каждом из бекендов
- 8 миллионов запросов к бекендам в день
- 7 Тб медиа (видео + аудио + фото)
- 3 сервера баз данных
- 4 бекенд сервера
- 1 фротенд
- 4 медиа сервера
- 2 сервера транскодирования
- 20 Гб памяти под Memcached
Технологии
Технологий используется огромное множество, причем динамика использования новых технологий очень высокая. За год на проекте появилось множество улучшений, связанных с новыми техническими решениями.
- MySQL
- Sphinxsearch
- Memcached
- MemcacheDB
- PHP
- PHP-FPM
- Eaccelerator
- Nginx
- Imagemagick
- FFmpeg
- Mencoder
- Flvtools
- Varnish
- GFS
- Munin
- Nagios
- Xhprof
- Minify (библиотека сжатия статики)
- Linux shell
Далее наш опыт, архитектура, решения, ошибки и выводы:
Мониторинг и статистика
Как только появляются первые признаки тормозов или отказов на сайте, первым делом устанавливайте систему статистики. Мы поставили Munin и очень довольны. Множество плагинов, практически для любых технических решений. Очень просто писать свои плагины на любом интерпретируемом языке (мы пишем на PHP).
Для мониторинга используем Nagios.
Система статистики не только помагает обнаруживать проблемы, но и прогнозировать расширение.
Оборудование
Мы используем подход горизонтального расширения, а не вертикального. Поэтому мы делаем выбор в сторону недорого оборудования. Что это дает:
- Избавляет от SPOF (единых узлов падения)
- Покупая дорогое и мощное оборудование, Вам нужно больше платить и за все остальное (коммутаторы, сеть, поддержка и т.п.)
- Стоимость вертикального роста имеет экспоненциальный характер
- В любом случае вертикальный рост ограничен
СУБД: не в MySQL проблема, проблема в эффективности
Для начала, мы отказались от использования классической схемы репликации “запись на мастер” + “чтение с реплики”. При асинхронной репликации время отставания реплики неконтролируемо, что очень усложняет логику приложения. На реплику идут только агрегатные запросы, которые не чувствительны к небольшим задержкам (лучшее видео и т.п.).
В качестве архитектурного решения мы используем федерацию (вертикальное разделение СУБД по таблицам). Часть таблиц работает на одном сервере, часть на другом. Необходимость федерации появилась, когда некоторые особо большие таблицы препятствовали эффективной работе буфера MySQL.
Движки таблиц - InnoDB для всех таблиц, кроме случаев с полностью статическими таблицами (города, страны и т.п.) - для них MyISAM.
Для полнотекстового поиска используем Sphinx. Работает по схеме дельта индексации. Самая тяжелая часть - полная переиндексация, поэтому запускаем ее раз в неделю. Планируем внедрить объединения индексов (index merging).
Наиболее тяжелые функциональные части для СУБД - это личные сообщения и лента новостей. Ленту новостей недавно перевели на MemcacheDB. Стратегию выбрали масштабируемую, у каждого пользователя есть свой личный список событий, который обновляется, когда генерируется новое событие. Это позволит очень легко масштабировать этот функционал.
Что мы делали для оптимизации нагрузки
- Кеширование (подробнее - ниже)
- Тюнинг настроек сервера MySQL - никогда не используйте настройки по умолчанию
- Оптимизировали (и все еще продолжаем) запросы. EXPLAIN + maatkit - это наш набор оптимизатора.
- Тяжелые запросы зачастую можно упростить на порядки выкинув малозначимые части логики
- Для особо тяжелых и не поддающихся ускорению запросов есть слейв
- Денормализация + индексация - постоянные задачи
Кеширование: кешируйте все и еще немножко
Кеширование - первое, что мы начали делать для оптимизации приложения. Memcached выбрали по нескольким причинам: простой и эффективный, поддержка распределенного кеширования, встроенный алгоритм консистентного хеширования (необходимо при добавлении новых серверов), поддержка сессий PHP.
Сейчас хитрейт доходит до 95%, показатели вынужденного вытеснения из памяти стремятся к нулю.
Стратегию кеширования используем довольно простую. Для списков кешируем только первичные ключи. Для тяжелых запросов используем дубликаты с разным временем жизни. Ввиду большого количества персональных выборок, пришлось реализовать собственный механизм пространств имен для того, чтобы иметь возможность обновлять несколько объектов одновременно.
Система кеширования носит двухслойных характер: кеш приложения => memcached.
Клиентская оптимизация
Клиентская оптимизация - это второе, что мы начали делать для оптимизации производительности. Клиентская оптимизация позволила увеличить скорость загрузки страниц в несколько раз. После базовой оптимизации клиентской части, удалось повысить показатель просмотров страниц в полтора раза. Кроме всего прочего клиентская оптимизация позволяет сэкономить канал.
Что мы делали:
- Gzip всего
- Склеивание и сжатие статики
- Вынос статики на другой домен
- Установка необходимых заголовков для кеширования на клиентах
Сейчас наиболее медленное место в клиентской части - это система управления банерными показами.
Медиа
Наиболее ресурсоемкой частью проекта является то, что относится к медиа. Поэтому все медиа ресурсы изолированы от основной части системы. В качестве стратегии роста используем разделение по серверам. Каждый сервер работает независимо от других, что делает всю систему устойчивой к сбоям.
PHP
С PHP было меньше всего проблем. Единственным шагом, который привел к ощутимому росту скорости работы, стала установка eAccelerator’a. Большинство изменений в коде были связаны с архитектурными изменениями.
Для профилирования используем XhProf - хорошо подходит для работы в продуктивных условиях.
Что мы планируем
- Переводить часть логики с MySQL на key=value базы данных
- Внедрить систему очередей сообщений
- Оптимизировать клиентскую часть дальше
- Искать затычки и исправлять их
В качестве итога
После проделанной работы мы пришли к нескольким простым правилами, которыми пользуемся и сейчас:
- Если решение работает у кого-то, это еще не значит, что оно будет работать для Вас
- Во многих случаях приходиться экспериментировать. Нужно позаботиться о том, чтобы небольшие изменения в системе не вызывали огромных трудностей
- Думайте о росте заранее, но не решайте не существующих проблем
- Многие технологии умеют гораздо больше, чем кажется - гораздо больше
- Творчество Ваш самый необходимый инструмент
- Не делайте того, о чем ничего не знаете
Надеюсь будет полезно!


А кто может детально объяснить про “федерацию (вертикальное разделение СУБД по таблицам)”
И в чем же плюс “key=value базы данных”
нашел статью - http://highload.com.ua/index.php/2009/04/27/keyvalue-%D0%BA%D0%BB%D1%8E%D1%87%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B5-%D0%B1%D0%B0%D0%B7%D1%8B-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85/ , но на многие вопросы так и не был дан ответ.
круто. Масштабы впечатляют.
А сколько человек работает над оптимизацией?
@Max
1
@IgorN
Игорь,
Федерация - это очень простой процесс. Вы просто берете несколько таблиц и выносите на другой сервер БД. Самая сложная часть при этом, это вовсе не перенос, а выделение группы таблиц для переноса.
По поводу key=value, не уследил, комментарии напишу в ближайшее время.
Еще вопрос, а на стороне приложения надо, что то менять? Или все разруливается на уровне БД?
@IgorN
Нет федерация - это уровень приложения, базы данных ничего друг про друга не знают. Это не кластерное решение (а кластер дороже и хуже в плане устойчивости). Т.е. всю логику нужно встраивать в само приложение. Обычно это делается маркированием серверов, например “user-db”, “photo-db” и т.п. А при описании моделей необходимо указывать конкретное соединение, с которым эта модель работает. Это не такой уж и трудоемкий процесс, если в приложении существует грамотная прослойка для работы с СУБД.
Спасибо за ответ.
Привет
Производилась ли какаято оптимизация для конвертации видео? Были ли проблемы с ffmpeg в этом ключе? Кстати, для чего mencoder используется, как я понимаю у него с ffmpeg похожие функции (хотя я сам не юзал mencoder)
P.S. почини валидацию формы с комментариями (на этом блоге), два раза пришлось перенабивать все поля
@Ander
Система транскодирования у нас кастомная, которая использует и mencoder и ffmpeg. Вся оптимизация представляла из себя подстройку параметров транскодирования, для получения лучшего варианта “скорость/качество”.
Расскажите, пожалуйста, о медиа серверах. Какие решения используются для отказоустойчивости, репликации, надежности хранения данных?
Интересно и полезно конечно, но статья было бы более полной, если бы вы:
1. Указали в какой конфигурации сеть стартанула и, возможно, написали кратко о ее развитии(самые важные события).
2. Расписали в нескольких словах пункт “Думайте о росте заранее, но не решайте не существующих проблем”, дали бы несколько рекомендаций.
@Олег
Олег,
В отношении медиа серверов в недалеком прошлом пришли к наиболее эффективному решению в плане организации подсистемы хранения файлов: разделение по серверам с дублированием (переход на нее в процессе).
Немного детальнее:
Каждый файл (видео ролик) хранится на двух серверах (которые выбераются из пула на момент загрузки). Этим обеспечивается отказоустойчивость (вероятность выхода из строя двух серверов очень мала).
Каждый медиа сервер (нода) ничего не знает про другие сервера и представляет из себя автономный узел отдачи медиа. Такое програмное дублирование позволяет отказаться от дорогих (или вообще от любых) рейд контроллеров и зеркалирования данных. Еще одно преимущество этой системы - это масштабируемость (главное успевать вовремя вставлять новое железо) и отсутствие SPOF (никакие централизованые балансировщики не нужны).
@Алексей
Алексей, спасибо за вопросы.
1. Слишком много всего рассказывать, возможно у Вас есть какие-то специфические вопросы?
2. “Думайте о росте заранее”. Это задача для архитекторов проекта. В двух словах это означает, что систему нужно заранее проектировать масштабируемой (если вообще это актуально), но не заниматься ее масштабированием до появления такой потребности. Допустим у Вас в системе есть таблица авторизации. Нужно задать себе вопрос, что будет, если пользователей станет 100 миллионов - тогда мы сделаем “это и это” (например, шардинг). Так вот, делать этого заранее не нужно, но необходимо обеспечить условия, чтобы сделать это можно было максимально просто (например, вынести всю логику работы с авторизацией в одну компоненту, и иметь единый и удобный центр управления этой логикой).
Очень интересно. Поддерживаю вопрос про то, как всё начиналось, какие ресурсы были выделены изначально и как соответствовали выделяемые ресурсы посещаемости: график был бы познавателен, в том числе и для приблизительного прогнозирования.
Пы.Сы. Зашёл на сайт и попал в карусель редиректов. Вот что делает одно из популярнейших расширений для FF NoScript %)
@Den Golotyuk
Да, это сейчас достаточно популярная модель, благодаря которой можно получить надежность на недорогом железе.
А есть ли подсистема автоматической репликации, когда один из медиа-серверов выходит из строя? И каким образом вы даете ссылки на медиа файлы? Это прямые ссылки на файлы одного из медиа серверов? Или, все таки, ссылки проходят через промежуточный сервер, который уже перенаправляет на нужный?
@Олег
Системы репликации нет, и она в этом случае не актуальна. По скольку каждый файл хранится на двух серверах, то при выходе одного сервера из строя, все ролики будут доступны с другого сервера.
По поводу узла отдачи думаем. С одной стороны он не нужен, с другой, если выходит из строя один сервера, нужна система переключения на другой. Пока склоняемся в сторону того, что такая система будет реализована на уровне приложения (при генерации прямой ссылки).
@Den Golotyuk
В первом пункте я имел ввиду конфигурацию железа, на котором изначально запускалась сеть, и какие технологий из вышеприведенного списка применялись в первых её версиях.
@Алексей
Конфигурация серверов изначально имела довольно простой вид:
- 1 Сервер БД
- 1 Бекенд
- 1 Фронтенд
- 1 Хранилище
В начальном варианте медиа отсутствовало как сервис. Никакого кеширования тоже небыло.
> 1.5 тысячи запросов в секунду на каждом из бекендов
> 8 миллионов запросов к бекендам в день
Что-то у меня цифры не сходятся.
@ygrek
а как Вы считали?
@IgorN
по поводу федерации, есть 2 решения:
1) на уровне БД
на главном сервере(к которому идут коннекты) нужно создать таблицы с движком Federated - коннект к другой базе с одноименными названиями
плюсы: приложение остается без изменений
из минусов: запрос к федерейту не кешируется; цепочка передачи данных увеличивается еще на один сервер; JOIN юзать крайне нежелательно
2) на уровне приложения
для каждой таблицы определен сервер на котором она находится и во время запроса нужно определять куда коннектится.
плюсы: нет зависимости баз друг от друга; передача данных происходит быстрее;
из минусов: нужно следить за коннектами и и менеджерить запросы; нельзя сделать JOIN
8_000_000/(24*60*60) = 92
@ygrek
Первый показатель - пики, второй показатель - сумма за день.
в свете больших нагрузок не подумываете ли об облаке на базе Eucaliptus?
@Masterkey
Нет, но спасибо, детально присмотримся к решению!
Хочется задать дерзкий вопрос!
(может даже и глупый одновременно :))
Можно ли что-то подобное на Zend Framework?
На сколько реально?
В чем могут быть сложности?
Спасибо.
Можно подобное сделать на чем угодно, в т.ч. и на ZF. Главное условие - Вы должны хорошо понимать ту технологию, на основе которой Вы собираетесь это делать.
Реально сделать на 100%, сложности будут, но с ZF не связанные (связанные с ZF могут быть, если эта платформа не очень хорошо знакома).