Ноя
19
2012

По следам HighLoad++ 2012 (хранение контента Вконтакте, NoSQL в Mamba, MySQL в Google)

По следам HighLoad++ 2012 (день первый, часть первая)Чуть менее месяца назад в Москве прошла конференция разработчиков высоконагруженных систем HighLoad++ 2012. Проходила она в два дня, на протяжении которых со своими докладами выступали ребята из таких крутых контор, как Google, Yandex, Twitter, Badoo, Mamba, Вконтакте, Одноклассники, Percona, NGINX, Sphinx Technologies, Oracle, Evernote, Parallels и др. Так сказать, по горячим следам решил написать серию статей — что довелось услышать и увидеть на конференции :) Так как информации накопилось довольно много, то решил разбить весь этот своеобразный отчет на 4ре части. В этой части — первая половина первого дня конференции и доклады от Вконтакте, Мамбы, Яндекса и Гугла.

Хранение и доставка контента (Вконтакте)

Конференция началось с доклада разработчика Вконтакте, Олега Илларионова, в котором он рассказывал, как они хранят и раздают контент пользователям. В начале доклада рассматривается трехэтапная эволюция системы хранения данных.

  1. этап: простая организация загрузки контента — все файлы грузятся на тот же сервер, где и отрабатывает код. Тут все просто, но имеются две проблемы — во-первых, это довольно небезопасно (как я понял, тут имеется ввиду, что в случае загрузки shell-а и тем самым получив доступ к серверу, злоумышленник фактически получает доступ к исполняемому коду), во-вторых в данном случае мы ограничиваемся лишь вертикальной масштабируемостью сервера.
  2. этап: выделяется группа серверов для хранения контента, первоначально по прежнему данные грузятся на сервер, где исполняется код, а потом переносятся на контентные сервера. Для каждого файла храним номер сервера, где он расположен. Но тут опять же имеется одна проблема — файлы грузятся через один сервер, что не особо масштабируемо.
  3. этап — усовершенствованный способ 2го этапа. На этапе загрузки выбирать контентный сервер, куда грузить. Тут необходимо решить вопрос, как выбрать такой сервер:
    1. загружать на последний добавленный сервер (не очень хорошо, т.к. быстро заканчивается место)
    2. загружать на самый свободный (опять же не очень хорошо, т.к. на сервер сразу сваливается весь траффик)
    3. загружать на случайный сервер (на котором не кончилось место — самый оптимальный вариант).
    Тут принцип какой — есть список доступных для загрузки серверов. Скрипт приложения случайным образом выбирает сервер, и шлет на него примитивный вопрос, сможет ли он принять данные или нет. Сервер отвечает либо (+), либо (-), когда для некоего сервера набралось достаточно количество (-) он удаляется из списка. Сами сервера для загрузки данных являются довольно обособленными ото всей системы, на них открыты лишь необходимые порты, и если злоумышленник чудом пробрался на такой сервер, то в принципе больше он ни к чему другому доступ не получит.

По следам HighLoad++ 2012 (день первый, часть первая)Также в докладе приводились примеры bottleneck-ов систем хранения и раздачи контента. Одним из таких является траффик — как далеко и по какому маршруту оптимальнее всего доставлять контент. Для решения этой проблемы Вконтакте поставили в Москве сервера для кэширования видео с серверов Спб. Ещё одним bottleneck-ом является HTTPS. Тут, как я понял, проблема следующего характера — ввиду большого распространения всяких телефонов/планшетов и с ростом публичных WiFi сетей, появляется угроза того, что злоумышленники путем сниффанья траффика такой открытой WiFi-сети могут перехватить сессионную куку и из той же самой сети спаммить вконтактовый аккаунт жертвы. Для установления полноценного HTTPS-соединения требуется, чтобы весь контент на странице отдавался по HTTPS (то есть все картники, JavaScript, CSS и т.п. иначе браузеры будут злостно ругаться, на неполноценный HTTPS). Таким образом весь контент с серверов тоже должен отдаваться по HTTPS, а так как таких серверов довольно много, и на каждый сервер сертификат не купишь — дорого, да и иметь большое количество сертификатов неудобно и небезопасно, то приходится иметь в запасе ряд проксирующих серверов с сертификатами (насколько я понял, у Вконтакте таких серверов 10).

Одно время Вконтакте использовали реализацию Peer-to-Peer на Flash-е при раздаче видео-контента, но сейчас отказались от неё вообще (то ли используют, но совсем по-минимуму, тут не помню). Если говорить подробнее о видео, то для сжатия используют самый обычный ffmpeg, сама обработка производится через очередь ввиду «тяжести» данной операции. Вообще, касательно всего хранимого контента, если оперировать цифрами, то Вконтакте загружено:

  • около 30 000 000 000 фотографий (количество загрузок более 17 000 000 в сутки)
  • аудио контента намного меньше и количество загрузок около 130 000 в сутки
  • загрузок видео примерно в 2.5 раза больше чем аудио — 320 000 видеозаписей в сутки

Для фильтрации нежелательного аудиоконтента изначально использовался алгоритм md5 (ну или что-то вроде того, очень простое), но т.к. посути при изменении в аудио одного байта результат md5 будет совсем другой, то сейчас ребята внедряют более сложный интеллекуальный алгоритм, реализованный ими самими на C++. Да, кстати, ссылка для скачивания видео/аудиоконтента (но не фото) формируется динамически исходя из IP-адреса пользователя. Так при удалении контента, сами данные конечно не удаляются во избежании лишней фрагментации, но динамические ссылки работать перестают.

Для сжатия изображений используется библиотека Graphics Magic, и предварительное уменьшение на Flash. В своём докладе Олег сильно акцентировал внимание на том, что при обработке изображений хорошим тоном является заранее делать много копий картинок разных размеров, даже если сейчас они не требуются. Так например если требуется картинка размеров X×Y, то заранее делать 0.5×X×Y и 2×X×Y (эти размеры могут пригодиться в дальнейшем при создании, например мобильной версии приложения).

Да, кстати, самое интересное… хотя для хранения файлов и используют файловую систему XFS, но тем не менее все данные хранятся в одном большом файле, открытом на чтение, и каждый новый загружаемый контент дописывается в конец этого большого файла. Индекс, по которому искать контент, хранится в оперативке, и ведется некий бинарный лог для восстановления индекса, в случае непредвиденной перезагрузки сервера. Как говорят разработчики Вконтакте, при обычном (стандартном хранении данных, как мы привыкли — по папкам и подпапкам) FS при прямом доступе к файлам делает кучу ненужных операций, что может оказаться весьма нежелательным и плохо сказаться не производительности. Так же проскользнула интересная мысль — для обработки документов использовать черный список (запрещенных типов), вместо белого (разрешенных).

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

Практические вопросы использовани NoSQL в высоконагруженном проекте (Mamba)

Следующий доклад был ребят из Мамбы. Проблема сводилась к тому, что при их 350 запросах в секунду к каждой БД MySQL (всего в кластере 45 серверов MySQL), 27% всех запросов — это работа со счетчиками, а именно чтение:

SELECT * FROM TABLE WHERE id=123

и обновление:

UPDATE TABLE SET msgs = msgs + 1, unread = unread + 1 WHERE id = 123

Счетчики обновляются при каждой отправке/прочтении сообщения. Чтобы соптимизировать эту часть запросов решили копать в сторону NoSQL (рассматривали варианты memcached, redis, memcachedb и TokyoTyrant). Остановились на memcachedb и TokyoTyrant (т.к. memcached не персистентный, а redis хоть и персистентный, но RAM only). Для тестирования использовали написанную на PHP утилиту Brutis (говорят что она достаточно гибкая, простая и в ней много настроек). Вообщем как тестировали — с 30ти серверов в 30 потоков с каждого 30 минут (200 байт примерный размер каждой записи). В результате memcachedb при частой записи сильно нагружал диск во время синка, и в такие моменты его скорость снижалась в 100 раз. Таки решили остановиться на TokyoTyrant. Однако тут тоже возникли проблемы, уже на тестах с отказоустойчивостью, т.к. при вырубании сервера возникали проблемы с целостностью данных — данные терялись. Кстати, тут стоит отметить хорошую рекомендацию для тестирования отказоустойчивости: хотите проверить отказоустойчивость — дергайте сервер из розетки.

Вообщем, пока заместо TokyoTyrant искали другое средство (рассматривали MongoDB, CouchDB, Cassandra, HBase), авторы вышеупомянутого NoSQL-хранилища выпустили новое своё детище KoytotTycoon. В отличие от своего прародителя KoytotTycoon обладает следующими преимуществами:

  • данные консистентны при падении сервера
  • скорость работы выше
  • HTTP протокол вместо memcache
  • расширенный интерфейс протокола (HTTP) — можно писать свои функции и плагины

По следам HighLoad++ 2012 (день первый, часть первая)Однако и тут снова возникли проблемы — при попытке записать 30 000 000 значений производительность резко упала до 250 op/s. Решили попробовать LevelDB от Google и остановились на нем. Алгоритм хранения данных следующий — данные хранятся в SSTable (Sorted String Table) в качестве пар ключ=>значение. Для быстрой записи используются так называемые MemTable (те же SSTable, но в оперативной памяти), при чтении сначала проверяются индексы MemTable, а потом SSTable. Все SStable представляют собой LSM-tree, и периодически происходит слияние веток, находящихся в памяти с ветками на диске. Так же пишется Write Ahead Log, на случай если отключится питание.

Вообщем, остановившись на LevelDB ребята реализовали репликацию мастер-слейв, организовали сетевое взаимодействие по протоколу JSON-RPC с возможностью асинхронных запросов и добились следующих результатов: 4700 get/s, 1600 update/s, 500 inc/s. размер базы в продакшене составляет 200 000 000 записей. Во время слияния поддеревьев среднее время ответа (GET) составляет от 0.5 мс до 1.4 мс. Утилизация дисков в этот момент поднимается до 25%, происходит раз в 10 минут и длится минут 5ть (как я понял железо в данном случае мониторили при помощи Zabbix). Замеры времени выполнения запросов к LevelDB производили из PHP через некую тулзу BTP собственного производства.

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

Что делать со своим первым миллиардом? (Yandex)

По следам HighLoad++ 2012 (день первый, часть первая)В общих словах говорили про распределенную систему хранения и обработки данных от Яндекса Elliptics и сопутствующие инструменты:

  • Grape (система конвейерной обработки событий в режиме реального времени)
  • Pohmelfs (кэш-согласованная распределенная файловая система)
  • Cocaine (облачная платформа, к которой в виде плагинов подключаются различные языки программирования)
  • и ряд других

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

MySQL в Google

Google использует MySQL на доброй половине проектов, среди которых такие крутые как Ads, Checkout и YouTube. Вы спросите, почему MySQL:

  • старый добрый SQL
  • реляционный и транзакционный (ACID-совместимый)
  • масштабируемый (репликация и шардинг)
  • достаточно надежный (достигается за счет собственных патчей и встроенной репликации)
  • API для всех языков, используемых в Google.
  • о-о-очень большая распространенность, и если чего-то не знаешь, всегда можно погуглить, и найдешь ответ (тут зал аплодировал :-) )

Используют достаточно давно (как минимум с 2003 года), в продакшене версия 5.1 с патчами собственного производства.
Но тем не менее в MySQL не все так хорошо, как кажется с первого взгляда:

  • плохо подходит для хранения неструктурированных типов данных
  • отсутствует master-master репликация
  • однопоточная репликация

По следам HighLoad++ 2012 (день первый, часть первая)Google добились реализации схемы с развертыванием MySQL на тысячах машин, с автоматизацией выбора мастера, изменения схемы, управления правами и балансировки нагрузки. В качестве репликации используется master-slave репликация, при этом были рассмотрены возможные варианты данного типа репликации: синхронная (дожидаемся ответа об удачной записи со всех slave-ов), асинхронная (не дожидаемся ответа вообще) и полу-синхронная (дожидаемся ответа только от одного slave-а). В Google используется последний тип репликации (то есть задержка при репликации возникает только из-за одного slave-а).

В качестве файловой системы для MySQL используют ext3. Вообщем-то у Google все устроено относительно несложно — есть набор шард, в рамках шарда — один мастер и куча реплик, и есть некий процесс decider, который следит за состоянием процессов mysqld в кластере и выбирает нового мастера в случае падения старого. При падении мастера decider старается выбрать самый свежий слейв (тут, кстати, используется патч их собственного производства — Group IDs, который ведет глобальный счетчик транзакций по кластеру, и при выборе нового мастера decider использует именно этот механизм). При этом, чтобы оперативно выбрать новый master, на запросы вводятся некоторые ограничения: транзакция на запись не может длиться дольше 30 секунд; если в приложении запись fail-тся, то это считается нормальным, и приложению нужно попробовать провести запрос на запись ещё раз (то есть на стороне приложения по некоему регламенту должен быть предусмотрен такой поворот событий). Таким образом задержка выбора нового мастера составляет 10-15 секунд.

Обновление схемы данных производится примерно так: берут реплику, отключают её от репликации, изменяют схему, делают бэкап, поднимают из бэкапа остальные реплики (в коде приложения хотя бы некоторое время должна поддерживаться обратная совместимость SQL команд). Помимо своих патчей, также используют туллы собственного производства, например google-mysql-tools (написана на python, позволяет, например, выполнять параллельные мультишардовые запросы). В дальнейшем планируется переезд на MySQL 5.5 или же какую-то альтернативу мускулу.

Комментарии (3)

  • Привет! Много полезного, но мне особенно понравилась про MySQL. А какую СУБД бы выбрал ты, для этих целей на месте гугла? :)

    • Олег, привет!
      Да MySQL бы и выбрал бы, так как есть опыт «приготовления» :) вообще мне кажется вопрос выбора базы несколько холиварный. В духе, что лучше — PostgreSQL или MySQL. У каждой есть свои преимущества и свои недостатки. Но это все не важно, главное уметь их «готовить» :)

      • Я бы тоже выбрал её, года три назад я неделю изучал работу с разными СУБД и пришёл к выводу, что все примерно равносильны в рамках наших потребностей, поэтому лучше брать ту что умеем готовить, как ты уже сказал :)

Оставить комментарий на Str@nnik

CAPTCHA image


Поля, отмеченные * обязательны для заполнения


XHTML: Вы можете использовать следующие теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">