HighLoad 2012

в 10:52, , рубрики: высокая производительность, метки:

На днях в Москве прошла «конференция разработчиков высоконагруженных систем» HighLoad++, участником которой мне посчастливилось стать. Ниже я хочу кратко пройтись по докладам, которые посетил в рамках конференции, выделив в них интересные на мой взгляд моменты.

Сразу предупрежу, что какие-то вещи я мог понять неправильно, какие-то переврать. Если для вас это важно — не читайте этот пост, а приходите на следующую конференцию лично! :)

День 1

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

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

Изначально все файлы (фотографии) хранились на том же сервере, что и код — в папке upload. Этот подход прост, но имеет два изъяна: безопасность (заливка кода, XSS) и масштабируемость.

Далее ребята озадачились масштабируемостью, организовав следующую схему: файлы заливаются на основной сервер,
а потом переносятся на вспомогательные (предназначенные специально для пользовательских данных).
Каждому пользователю всегда соответствует один и тот же вспомогательный сервер, определяемый по id пользователя.
Однако, у этого подхода есть bottle neck — основной сервер.

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

  • добавляем на последний добавленный в систему сервер (не подходит, так как на сервере быстро кончится место);
  • на самый свободный сервер (не подходит, так как на сервер сваливается весь трафик);
  • на случайны свободный сервер (лучший);

Поиск и устранение узких мест. Обычно самое узкое место одно — остальное узлы системы по сравнению с ним работают на порядок лучше. Bottle neck'и при хранении большого объёма пользовательских данных могут быть следующие:

  • резервирование данных (используется RAID, если выгорает весь массив из 2х дисков — файлы пользователя теряются безвозвратно);
  • кеширование (60% контента достаётся из кеша на клиенте);
  • трафик (решением является CDN, особенно для видео: у ВКонтакте в Москве стоят сервера, которые кешируют видео с дата-центра в Спб);

В качестве файловой системы используется XFS. Однако пользовательские файлы хранятся не в файловой системе, а в одном большом файле. Файл всегда открыт для чтения, индекс файла хранится в оперативке. Дальнейшее развитие данной технологии ребята видят в полном абстрагировании от физических носителей.

В виду того, что широко распространения WiFi ВКонтакте переходит на HTTPS. Так как серверов с контентом огромное количество — на каждый из них сертификат не купишь (слишком дорого). Поэтому у ВКонтакте стоит несколько серверов, которые проксируют контент через HTTPS.

Что каcается картинок:

  • всего более 30 000 000 000, в сутки добавляется 17 000 000;
  • изображения предварительно сжимаются на стороне клиента (Flash);
  • используется Graphics Magic;
  • при сохранении фотографии нарезаются сразу для всех необходимых разрешений;

Что касается аудио:

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

Что касается видео:

  • в сутки добавляется 320К;
  • используется отложенная обработка видео через очередь — слишком дорого обрабатывать видео онлайн;
  • для обработки видео используется ffmpeg;
  • видео дублируется при повторной загрузке другими пользователями — это не проблема;
  • одно время у ВКонтакте был P2P для видео на flash (wat!?), сейчас уже справляются без него;

Вообще у меня сложилось ощущение, что контакт устроен относительно несложно. Несложно, но эффективно.

NoSQL в высоконагруженном проекте

Доклад о том, как NoSQL используется в Mamba. Особенности рассматриваемой системы заключаются в том, что более 30% выполняемых запросов — инкремент и чтение счётчиков.

Начинали поиск подходящего хранилища для данных как и полагается с Memcache. Не устроило отсутствие персистентности и RAM ONLY. Попробовали Radis — RAM ONLY.
Кроме того при большой нагрузке производительность Memcached падает в 100 раз (Для тестирования нагрузки использовали инcтрумент Brutis).

Долго ли, коротко ли, ребята пришли к использованию TokyoTyrant. Однако, у него внезапно обнаружились проблемы с целостностью БД при выключении сервера из розетки :) Решили их новой разработкой от автора TokyoTyrant — KoytotTycoon. Однако, записать в базу 30М записей не удалось в виду архитектурных ограничений.

Поэтому ребята пошли в сторону LevelDB от Google. В этой БД используется технология LSM-tree. Данные хранятся в SSTable-файлах: отсортированные неизменяемые пары ключ-значение.
Пишутся данные в аналогичную (но уже изменяемую) структуру в оперативной памяти. Время от времени происходит слияние поддеревьев из памяти на диск.
Что бы не отхватить проблем при внезапном отключении питания — пишется Write Ahead Log.

Ребята ещё раз протестировали и получили, что в большинстве случаев библиотека LevelDB на порядок выигрывает у всех используемых ранее вариантов. Сейчас они держат 4700 get/sec и 1600 update/sec при 200М записей в БД.

MVCC unmasked

MultiVersion Concurency Control — механизм, позволяющий reader'ам не блокировать write'ов, а write'ам reader'ов. В общем — значительно уменьшает количество блокировок в БД. Присутствует в Oracle, Mysql InnoDB, PostgreSQl и некоторых других.

Каждая запись в таблицах MVCC систем имеет два атрибута: creaton (xmin) — номер транзакции, в которой запись была создана, expiration (xmax) — номер транзакции, в которой запись была удалена.

    INSERT xmin 40, xmax Null
    DELETE xmin 40, xmax 47 

    UPDATE xmin 64, xmax 78 / xmin 78, xmax NUll

При выполнении каждого statement'а создаётся MVCC snapshot, который определяет, какие данные в БД видимы/доступны statement'у.
Алгоритм определения видимости строки следующий:

  • получаем номер последней завершенной транзакции (current);
  • считаем видимыми те записи, у которых:
    • xmin < current номера;
    • они не удалены (xmax = null), либо транзакция, в которой они удалены, не закомичена;

Поля xmin и xmax присутствуют в каждой таблице, но скрыты по-умолчанию. Однако, их можно всегда указать в выборке явно: SELECT xmin, xmax, * FROM test;

Получить id текущей транзакции можно с помощью запроса SELECT txid_current(). Данный запрос выбирает данные из таблицы pg_clog. Важно понимать, что откат транзакции — это просто установка соответствующего маркера для записи о транзакции в этой таблице. Ни какие данные при этом не удаляются. Транзакция просто помечается как откаченная.

Не MVCC CУБД требуют непосредственного изменения данных при изменении или удалении записей. MVCC СУБД лишены этой проблемы — все неактуальные данные чистятся отложено.
Тут хочется добавить от себя, что отложенная чистка (так называемый VACUUM) — не так совершенен, как хотелось бы… но это тема для отдельного разговора.

Кстати, вот сайт докладчика, на котором он обещал много интересных статей.

MySQL в Google

Пожалуй, самое большое разочарование среди докладов. Если коротко, то суть его сводится к одному тезису: «Да, у нас есть слегка пропатченный нами MySQL».
Может у меня завышенные требования, но я ожидал от Корпорации Добра чего-то более интересного.

Крупнейшие проекты: Ads, Checkout и, конечно же, YouTube.

Для дата центров используется не самое дорогое железо, а самое «продвинутое». Комплектующие (диски) приходят в негодность очень часто, но легко и дешево могут быть заменены.

MySQL используется в виде кластера. Для каждой шарды есть отдельный отдельный процесс (decider), который отвечает за выбор нового мастера в случае падения старого.
Heartbeat осуществляется простым скриптом, который пишет некоторые данные в мастера и проверяет их наличие на репликах.

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

Поддержка SPDY в NginX

SPDY — бинарный протокол поверх TCP/TLS соединения. Является транспортом для HTTP. Разработан Google.

Основные фичи:

  • мультиплексирование (несколько запросов за одно соединение, как от сервера к клиенту, так и наоборот);
  • сжатие заголовков (zlib, deflate);
  • flow control (окно для tcp-соединения);
  • server push («заливка» данных в браузер, инициированная сервером);

Из плюсов: встроенный HTTPS (за счёт TLS), хорошая работа при большом количестве картинок (за счёт мультиплексирования);
Из минусов: работает с одним доменом, как следствие — не поддерживает CDN;

По исследованиям (боевым) WordPress'a: SPDY быстрее HTTPS, но медленнее обычного HTTP.

SQL Tricks

Доклад представлял собой список интересных, но не всем знакомым фич SQL/PostgreSQL:

  • view — эффективное средство для инкапсуляции логики и/или таблиц. С первым не соглашусь, а вот второе использую (переименование таблиц);
  • postgres использует кеш ОС (поэтому предлагают выделять ему или немного, или всю);
  • pgbench — утилита для тестирования Postgres;
  • Common Table Name (CTE) / Recursive CTE;
  • оконные функции;
  • поддержка JSON (c 9.2);
  • поддержка lateral (использование вычисляемых по текущей строке данных в FROM clause, c 9.3);
  • расширение для работы с текстом и триграмами: pg_trgm;
  • расширения для хранения пар ключ-значение: hstore;
  • подключение таблиц из других БД через dblink;
  • расширения для работы с гео-данными: PostGIS и EarthDistance;
  • prepared statements — позволяют избежать накладных расходов на разбор запроса, проверку существования и прав, построение плана (иногда может занимать
    больше времени, чем выполнение);
  • блокировки можно устанавливать явно через конструкцию select… for share;
  • в PgSQL Prepared Statements доступны через execute;
  • блоки кода do $code$… $code$ (c 9.1);
  • upsert лучше реализовывать через insert -> fk constraint -> update;
  • вернуть несколько значений из функции можно вернув курсов (в рамках транзакции) или сгенерировав JSON (row_to_json, array_to_json);

Докладчик, кстати, реально опасен: написал с помощью Recursive CTE и Regexp'a парсер JSON'а в Postgres массив одним запросом! :)

Крадущийся сервер, затаившейся диод

«Здравствуйте, меня зовут Андрей, я Воронежское быдло». На этом описание доклада можно окончить :) В общем Аксёнов во всей своей красе.

Основные тезисы:

  • при бенчмарках — главное помнить о целях: мерить надо нужное, а не load average;
  • средние значения при метриках ничего не значат: средняя температура по больнице — это холодные трупы и лихорадочные коматозники;
  • масштабируемость нелинейна (закон Амдала — даже если не параллелится 5%, то
    64 * CPU = 14,5 X)
        
    C(n) = n /(1 + a*(n-1) + b*n*(n-1)), где
        
    a — степень contention (затраты на нераспараллеленный код),
        
    b — степень coherency (затраты на когерентность, коммуникацию, синхронизацию);
  • можно выделить sweet spot — количество ресурсов, при которых производительность будет оптимальной;
  • после sweet spot производительность наоборот начинает падать — с ростом может быть хуже;
  • не тестировать на дефолтных настройках — у большинства ПО они мягко говоря странные (fsync после каждого Insert'a и innodb_buffer_pool в 32Мб в MySQl);
  • прогнать один и тот же запрос 1000 раз — протестировать кеш;

Мировые константы — важно понимать, какое действие сколько стоит.

  • CPU L1 — 1 000 000 000 op/sec (1e9)
  • CPU L2, misbranch — 100 000 000 op/sec (1e8)
  • RAM Access — 10 000 000 op/sec (1e7)
  • SSD megaraid — 100 000 op/sec (1e5)
  • SSD — 10 000 op/sec (1e4)
  • LAN, 1MB RAM — 1 000 op/sec (1e3)
  • HDD seek, 1MB LAN — 100 op/sec (1e2)
  • WAN roundtrip — 10 op/sec (1e1)
  •  
  • Memcached access — 10 000 op/sec (1e4)
  • RDB simple select — 100 op/sec (1e2)

Go Language

Getting started и Hello, World на языке GO.

День 2

Как устроен поиск

Утренний опохмел от Андрея Аксёнова :)
В работе Любой поисковой системы можно выделить 4 вехи: получение данных (робот-паук), индексация, поиск, масштабирование.

Индексация включает в себя:

  • получение текста (html, pdf->текст),
  • токенизация,
  • морфологическая обработка (стемминг, лемотизация),
  • создание инвертированного индекса (ключевое слово -> номера страниц в книге и позиции слова в них);

Данные в индексе необходимо сжимать. Сжатие может быть битовым, байтовым, блочным. Чем меньше данных — тем меньше нужно обрабатывать при запросе.

Поиск состоит из двух ортогональных этапов: быстро matching (найти) и качественно raking (отранжироать).
Критерии matching'a могут отличаться в зависимости от области его использования (web, data mining). Что касается ранжирования — оно в принципе до конца не может быть релевантным, ибо релевантность персональная для каждого пользователя. Поэтому для ранжирования используются хитрые алгоритмы типа BM25 (TF, IDF, DocLength), результаты которых по возможности персонализируются.

Что касается масштабирования — поиск требует ресурсов. Поэтому у Гугла миллионы серверов поиска, у Яндекса — десятки тысяч.

Поиск в Одноклассниках

Ребята умудрялись использовать fullscan поиск (на MsSQl) при 30М пользователях. На кластере их 16-ти баз один поисковый запрос работал в среднем 15-30 секунд.
Поняв, что так жить нельзя — решили искать решение.

По скольку проект на Java, начали смотреть в сторону Lucene. За 3 года работы с Lucene внесли в него следующие изменения:

  • добавили репликацию;
  • хранение индексов в памяти (пробовали на RAM drive, маппинг файлов в Heap, но в итоге просто затянули файлы в ByteArray — в OldGeneration);
  • переписали поиск по индексам (дефолтный создавал слишком много объектов, что вело к проблемам с GC);

Сейчас в инфраструктуре выделен отдельный сервер-индексер, который создаёт поисковый индекс. Индекс реплицируется на query-сервера, которые хранят индекс как на диске, так и у себя в памяти (для обработки запросов используется индекс из памяти). Индексер получает данные через очередь, что позволяет ему быть недоступным время от времени.

Большой недоработкой было отсутствие логики прямого обновления индекса из БД, минуя очередь. Так как некоторые сообщения из очереди иногда пропадали. Эту же проблему я могу констатировать и у себя в компании. Вывод: sanity check при работе с очередями должен быть всегда.

Постоянно находятся в кеше только 5% запросов (самые часто запрашиваемые). Попадание в кеш 60%.
Для персональных запросов создаются временные кеши (по запросу), которые живут несколько минут.

За каждым пользователем на портале закреплён отдельный app-сервер (вычисляется на основе userId). В случае проблемы с сервером пользователь перекидывается на резерв.

Поиск онлайн-пользователей сначала делали по общему индексу, фильтруя по флагу «онлайн» вне индекса. Потом создали отдельный индекс для этой цели.

Storing data in Evernote

Обзорный доклад про устройство и цифры Evernote.

Software: Java 6, MySQl 5.1, Debian (Stable), DRBD (распределённая система хранения данных), Xen.
HardWare: SuperMicro 1U, 2x L5630 COU, 96 GB RAM, 6x 300GB Intel SSD, LSI RAID 5 (+ spare) ~ $8000.

DataBase: MySQL, 10TB (peak riops 350, peak wiops 50)
Search engine: Lucene (peak riops 800, peak wiops 50)

Сервера расположены в США и Китае (400 Linux серверов).

Доступ к файлам контролируется через Apache/WebDAV. Данные всегда хранятся на одном хосте и не переносятся. По сравнению с NFS WebDav имеет небольшой overhead, но в разы проще и дешевле в развёртывании и поддержке.

Балансировка нагрузки используется железный балансер A10 with SSL (во внешку смотрит HTTPS, во внутрь проксируется HTTP).

С учётом особенностей сервиса (хранение множества мелких файлов) проблемы и их критичность можно описать следующей таблицей:

/ size normal load peak load
bandwith medium medium
latence low low
cpu low medium
file size high low medium
meta data low medium low

Автор не рекомендует использовать облачные платформы, если ваше приложение требовательно к ресурсам (bandwidth, storage, cpu) или использует их в переменном количестве.
Если возникают сомнения — посчитайте, во сколько обойдётся покупка и поддержка своих серверов под нужды приложения, и сколько это будет стоить у Amazon. В случае с Evernote разница в 1-4 порядка.

Механика DDOS

Этот доклад я пропустил почти полностью, так как был на обеде :) Из финальной части мне удалось вычленить только пару тезисов.

Самая простая защита от HTTP атак — limit зоны в Nginx. Так же можно обратить внимание на модуль NGINX «testcookie».

Тем не менее важно понимать, что Nginx — не средство от DDOS атак.

DDOS атаки в России 2012

Всего атак за 2012 год было >2600 атак (в прошлом году было где-то >1700).

Ботнета в 3000 машин достаточно, что бы завалить среднестатистический сайт. Самый крупный ботнет состоял из 150К машин. Больше всего бонетов живёт в Германии, США, Украине, Казахстан (по убыванию).

Активность DDOS совпадает с активностью e-commerce (ну и немного политики).
Политические DDOS'ы используют ботнеты из Пакистана, Индии (там, куда не добраться правоохранительным органам).

Большинство атак генерирует трафик меньше 1GB (обычная пропускная способность сайта не больше).

При атаке первым делом нужно проанализировать логи (access.log) и сделать слепок трафика (tcpdump -s0 -c1000000 -w attack.dump). Далее нейтрализация атаки: вывести из-под мусорного трафика, заблокировать ip-адреса, с которых происходит атака, сменить IP.

Всегда стоит иметь как минимум 2х-кратный запас производительности сайта.

Особенности российского DDOS — Full Browser Stack (использование реальных браузеров, которые могут пройти все анти-ddos тесты).

Ну и да — если ожидаете проблем, обращайтесь в Qrator :)

Scaling in 2012

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

Докладчик упомянул об отсутсnвии какого-либо резервирования и масштабирования на его проекте. Это повлекло сперва довольно неожиданный вопрос: какова бизнес модель вашего сайта? Как оказалось, на проекте докладчика люди платят за доступ к фичам сайта с помощью подписок (на 6, 12, 24 месяца). С учётом того, что факапы, при которых сайт ложится случаются всего пару раз в год, и того, что все пользователи сайта уже оплатили за его использование, — высокая отказоустойчивость есть достаточно дорогая, сложная, а самое главное — не так уж сильно нужная вещь :) Другое дело, если бы монетизация проекта зависела от каждого запроса к проекту! :)

Proactive Web Perfomance Optimization

Предполагалось, что доклад будет читать сотрудник Twitter, поэтому лично я ждал доклад с большим нетерпением, предвкушая что-то интересное. Однако, сразу после начала доклада оказалось, что в Twitter он работает недавно и до этого большую часть времени занимался web-оптимизацией. А что бы окончательно добить толпу, расскажет нам про замечательное средство/утилиту/профайлер/плагин для web-оптимизации — YSlow :(

Система распределённого, масштабируемого и высоконагруженного хранения данных для виртуальных машин

По большому счёту доклад повествует о том, как в Parallels сымплементили свою версию GoogleFS с chunk'ами, серверами метаданных и остальными атрибутами.
Отличительной (для меня) чертой является то, ребята предполагают использование пока ещё дорогих, но быстрых SSD-дисков. В Google, вроде бы, политика сводится к покупке
дешевого железа, которое не жалко выкинуть и заменить.

Основное предназначение виртуализации — разделение ресурсов одного хоста, эффективный бекап и деплоймент.

Лучшее решение для объединение дисков в массив и их последующего разделения между виртуальными машинами SAN storage. Но это дорого, поэтому все используют обычные диски.

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

Из доклада запомнилось упоминание о PAXOS алгоритме. Рассматривается греческий остров PAXOS, на котором есть парламент. В парламенте заседают депутаты, которые по совместительству являются и бизнесменами. В виду последнего они очень часто находятся в отъезде так, что в парламенте всегда находится не более половины парламентариев. Связаться с отсутсnвующими можно только с помощью гонцов. При этом парламент должен всегда работать и принимать законы. Кроме того все парламентарии всегда должны быть в курсе последних законов. На сколько я понял, ребята используют этот алгоритм для синхронизации chunk-серверов.

Отключение электропитания можно эмулировать с помощью посылки SIGSTOP процессу. В этом случае приложение на другом конце TCP соединения будет в той же ситуации, что и при отключении питания. Это быстрее, чем реальное отключение питания.

Сервис рекомендаций на виртуальном Hadoop кластере

Ребята сделали map/reduce задачу «сервис рекомендаций на сайте» с помощью Hadoop. В пример приводятся их частные пhоблемы и решения, так что написать тут, пожалуй, нечего :(

MariaDB: The new MySQL

Обзорный доклад про Maria DB. Форк MySQL полностью переписанный с нуля основателем MySQL, после того, как MySQL перешёл от Sun к Oracle.
В качестве движка по-умолчанию использует XtraDB от Percona. Поддерживает InnoDB.

По сути доклад сводится к Сhangelog'у MariaDB версии 5.5.

Personal story of how MySQL grew and the challenges I've met on the journey

Доклад от Oracle. В основном о том, что будет в новых версиях. Своего рода RoadMap MySQl.

В докладе прозвучал ответ на довольно интересный вопрос: почему Oracle поддерживает MySQL? Компания хочет быть представлена на всех рынках, а MySQL покрывает Web, Mobile и Embedded. Кроме того не хочется терять таких клиентов как Facebook и Twitter. Про огромное comunity можно не упоминать :)

Отдельно хочется отметить, что В MySQL Installer for Windows включена утилита миграции с таких БД как MsSQL, Sybase и т.д. К сожалению, она не умеет за одно переписывать весь сопутствующий код проекта. В виду чего её смысл для меня остаётся потерянным :)

How to choose right index

Доклад о том, что такое индексы и с чем их едят. В докладе было не мало матчасти и примеров, так что всем, кто интересуется вопросом рекомендую посмотреть презентацию.

Наибольшую ценность на мой взгляд представляют следующие тезисы:

  • не надо делать слишком много индексов, так как они уменьшают скорость вставки и вообще влекут накладные расходы — делайте индексы только там, где они действительно
    нужны;
  • выбирайте только те данные, что вам нужны: меньше данных передаётся по сети, меньше данных обрабатывает процессор;
  • сортировка — достаточно дорогая операция;
  • distinct — очень медленная операция, так как работает на результате выборки;
  • index(a,b) better than index(a) only on equals operation;
  • clustering index contains all row data (видимо подразумевалось, что при построении кластерного индекса данные в таблице сортируются согласно индексу);
  • если столбца нет в индексе, но он используется в выборке — придётся делать fetch из таблицы;

Презентации

Все презентации доступны на SlideShare.

P.S.

Пост оставлю без картинок, так как WiFi в кафешке «МуМУ» у Киевского Вокзала не многим лучше, чем местный Оливье :)

Автор: krestjaninoff

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js