HighLoad.org – блог о высоких нагрузках
HighLoad.org > Анатомия Кита

Анатомия Кита
2010-02-17 20:51 my_fess 
Иногда реально очень сложно выяснить, что вызывает проблемы на таком web-сайте как Twitter. Но мы постоянно изучаем новые методы, помогающие нам решать множество проблем, которые возникают в сложном web-сайте. Несколько недель назад мы заметили нечто необычное: более 100 посетителей Twitter в секунду видели «падение кита». Обычно эти киты редки; 100 раз в секунду было достаточной причиной, чтобы забить тревогу. Хотя 100 в секунду - это очень маленькая часть от всего нашего трафика, это все равно значило, что много пользователей получили плохое впечатление от посещения нашего сайта. Поэтому мы организовали команду выяснить причину проблемы.
Что вызывает китов? Что это вообще такое «падение кита»? Это визуальное представление ошибки HTTP «503: Service Unavailable». Это означает что Twitter не хватает производительности для обслуживания всех его пользователей. Точнее сказать, мы показываем это сообщение об ошибке, когда для начала выполнения запроса необходимо ждать больше чем несколько секунд пока освободятся ресурсы. Поэтому, чтобы не заставлять пользователей ждать целую вечность, мы отклоняем их запросы и выводим сообщение об ошибке. Иногда это может произойти, когда одновременно много пользователей пытаются использовать Twitter и у нас не хватает компьютеров для обработки всех их запросов. Но более вероятно, что некоторый компонент Twitter внезапно сломался и начал замедлять процесс.
Обнаружение корня проблем может быть очень сложным, потому что Киты- косвенный симптом коренной проблемы, которая может быть в одно из множестве компонент. Другими словами, мы знаем только что в определенный момент времени где-то была проблема. Мы намеривались выяснить где именно в жизненном цикле обработки запроса к Twitter была поломка. Очень сложно дебажить проблемы производительности. Но сложно не потому что не хватает данных, а потому что данных слишком много. Мы оценивали множество метрик на каждый индивидуальный запрос к сайту, который при умножение на общий трафик сайта, является большим объемом информации о производительности Twitter в любой момент времени. Расследование проблем производительности в этом мире больше соответствует искусству чем науке. Очень легко спутать причины с симптомами и даже программное обеспечение записывающее данные само по себе ненадежно. В приведенном ниже анализе мы использовали стратегию, которая включает в себя движение от наиболее общих измерений системы в целом к изучению все более мелких, гранулированных частей. Как строится web-страница? Составление web страницы для запроса к Twitter часто включает две фазы. Первая: сбор данных с удаленных источников так называемых «сетевых служб». Например, на домашней странице Twitter изображаются ваши твиты и сколько у вас фоловеров. Эти данные получается соответственно из нашего кэша твитов и нашей базы данных социального графа, который содержит информацию о том кто является чьим фоловером в Twitter. Второй фазой является приведение всех этих данных к виду приятному для пользователя. Мы называем первую фазу фазой IO, а вторую фазой CPU. Для того чтобы выяснить какая фаза вызывает проблемы, мы проверили записи данных о том сколько времени тратится на каждую фазу при сборке web страниц Twitter.
Зеленый график показывает время затраченное на фазу IO, а синяя линия представляет CPU фазу. Этот график показывает примерно данные за 1 день. Вы можете видеть как меняется взаимоотношение в течение дня. Пока нет никаких пик в трафике время CPU является доминирующим в наших запросах и наши сетевые службы отвечают относительно быстро. Однако, когда в трафике появляются пики, задержки IO почти удваиваются и становятся главной частью общей задержки выполнения запроса. Понимание падения производительности. Существуют два варианта объяснения изменения этого отношения в течение дня. Первое: возможно пользователи используют Twitter одним образом в одно время суток и по другому в другое. Второе: произошло снижение производительности некоторой сетевой службы. В идеальном мире, каждая сетевая служба имела бы одинаковую производительность для одинаковых запросов, но в реальности, одинаковые запросы выполняются медленнее чем больше их одновременно запущено. Проверка различных показателей подтвердила, что пользователи используют Twitter одинаково в течение всего дня. Поэтому мы предположили, что проблема должна быть в портящейся сетевой службе. Мы были все еще не уверены, в любом хорошем расследование кто-нибудь должен оставаться скептически настроенным. Но мы решили что у нас достаточно информации, чтобы перейти от этого общего анализа системы к более специфичным вещам, поэтому мы посмотрели на данные о задержках IO.
Этот график представляет общее количество времени ожидания для наших сетевых служб для доставки данных. Так как объем получаемого нами трафика меняется в течение дня, мы ожидали в общем пропорциональные изменения. Но этот график является независимым от трафика, так как мы делим измеряемую задержку на количество трафика в каждый момент времени. Так как независимо от трафика задержки меняются в течение дня, мы поняли что соответствующая сетевая служба портится вместе с трафиком. Фиолетовая линия на графике (которая показывает Memcached) сильно ухудшается когда трафик возрастает в часы большой нагрузки. Так как фиолетовая линия выше всех остальных то она представляет наибольшую часть ожиданий сетевых служб. Все это коррелирует с предыдущим графиком и теперь у нас была сильная гипотеза: производительность Memcached сильно падает в течение дня, что ведет к долгому времени ответа, что приводит к появлению Китов. Такой тип поведения согласуется с недостатком мощности ресурса. Когда сервис с ограниченным ресурсом, такой как Memcached, приближается к его лимиту запросы начинают бороться друг с другом чтобы Memcached выполнил именно их. Например, если Memcached может обработать только 10 запросов а поступает 11 запросов, то 11-ый запрос вынужден ждать пока он обслужится. Фокусируемся на главном участнике проблемы. Если мы сможем добавить достаточно мощности Memcached чтобы уменьшить это соревнование за ресурс, то мы существенно увеличим пропускную способность Twitter.com. Если вы посмотрите на вышеприведенный график, то можете сделать вывод, что оптимизация может увеличить производительность Twitter на 50%. Существует два способа прибавить мощности. Мы можем сделать это добавив больше компьютеров (серверов с Memcached). И еще мы можем сменить программное обеспечение, которое общается с Memcached, чтобы оно работало с запросами настолько эффективно насколько это возможно. В идеале надо сделать и то и другое. Мы решили сначала рассмотреть как мы запрашиваем Memcached, чтобы посмотреть есть ли простой оптимизировать работу, уменьшением общего число запросов. Но существует много типов запросов к Memcached и возможно некоторые из них выполняются дольше чем другие. Мы хотели потратить наше время рационально и сфокусировались на оптимизации наиболее дорогих в совокупности запросов. Мы испытали живой процесс для записи некоторой статистики о запросах, которые выполняются дольше всего. Далее приведена информация о каждом типе запроса к Memcached и о том сколько он выполняется в среднем:
get         0.003s
get_multi   0.008s
add         0.003s
delete      0.003s
set         0.003s
incr        0.003s
prepend     0.002s
Видно что get_multi немного дороже чем другие, а все остальные примерно одинаковы. Но это еще не означает, что именно он источник проблемы. Нам также нужно узнать сколько запросов в секунду происходит для каждого типа запросов.
get         71.44%
get_multi    8.98%
set          8.69%
delete       5.26%
incr         3.71%
add          1.62%
prepend      0.30%
Если умножить среднее время задержки на процент запроса - вы получите оценку общего вклада в задержки. Таким образом, мы узнали что get был самым крупным источником медлительности. Поэтому мы решили узнать можем ли мы снизить количество запросов get. Поток трассировки программы После того как мы выделили запросы Memcached из всего программного обеспечения Twitter, было не совсем понятно где начать искать возможности оптимизации. Нашим первым шагом было начать собирать трассировки стэка, тоесть вести логи представляющие информацию какая программа работает в какой момент времени. Мы выделили один из компьютеров для того, чтобы он брал небольшую часть запросов get к Memcached и записывал кто именно их вызывает. К сожалению, мы собрали так много данных что было очень трудно в них разобраться. Следуя нашему опыту использования графической визуализации для получения информации о больших объемах данных, мы немного вдохновились проектом Google perf-tools и написали небольшую программу, которая генерировала облачный граф различных путей нашего кода, которые вызывали get в Memcached. Вот упрощенная картинка:
Каждый овал представляет один компонент или функцию. Размер овала показывает насколько часто get к Memcached вызывается из этого компонента или функции. Линии между овалами показывают какая функция вызывает какую. Самый большой овал — check_api_rate_limit но он в основном вызывается из authenticate_user и attempt_basic_auth. В самом деле attempt_basic_auth является главной возможностью для улучшения. Она помогает нам вычислить кто запрашивает данную страницу, чтобы мы выдали персонализированную (и частную) информацию в правильные руки. Любая оптимизация Memcached которую мы можем сделать будет иметь сильный эффект на всю производительность Twitter. Подсчитав реальное количество запросов get делающихся за один запрос,мы обнаружили что в среднем один вызов attempt_basic_auth делает 17 вызовов get. Встает вопрос: может ли хотя бы один из них быть убран? Чтобы это выяснить нам необходимо посмотреть очень внимательно на все эти запросы. Нужна история наиболее популярных web страниц которые вызывают attempt_basic_auth. Это API запрос к http://twitter.com/statuses/friends_timeline.format — самая популярная страница на сайте Twitter.
# перевести имя пользователя в его идентификатор get(["User:auth:missionhipster", # получение объекта пользователя по его идентификатору (используется для проверки пароля) get(["User:15460619", # предотвращение атаки по словарю get(["limit:count:login_attempts:...", # не необходимо в большинстве случаев, баг set(["limit:count:login_attempts:...", set(["limit:timestamp:login_attempts:...", get(["limit:timestamp:login_attempts:...", # может быть запомнено get(["limit:count:login_attempts:...", # тоже может быть запомнено get(["limit:count:login_attempts:...", # оптимизация для избежания вызова bcrypt get(["user:basicauth:...", # глобальный лимит частоты использования API get(["limit:count:api:...", # не необходимо в большинстве случаев, баг set(["limit:count:api:...", set(["limit:timestamp:api:...", get(["limit:timestamp:api:...", # может быть запомнено еще с предыдущего вызова get(["limit:count:api:...", # определение какие твиты нужно показать get(["home_timeline:15460619", # определение любимых твитов get(["favorites_timeline:15460619", # параллельная загрузка всех твитов которые нужно показать get_multi([["Status:fragment:json:7964736693",
Заметьте что все запросы ”limit:” исходят из attempt_basic_auth. Еще мы обнаружили несколько (относительно незначительных) ненужных запросов. Из этих данных получается, что мы можем исключить 7 из 17 вызовов Memchached — 42% улучшение для самой популярной странице Twitter. Нам нужно написать немного кода, чтобы иметь возможность убрать эти плохие запросы. Некоторые из них нужно закешировать (то есть не выполнять один и тот же запрос дважды), некоторые из них просто баги и их легко пофиксить. Некоторые из них мы можем попробовать запустить одновременно. Но эта 42% оптимизация (особенно в сочетание с новым железом) потенциально может устранить падение производительности нашего Memcached кластера и наши страницы будут загружаться намного быстрее. Все это возможно, в реальности мы можем получить более чем 50% возрастание мощности Twitter благодаря этим оптимизациям. Эта истории рассказывает о парочке фундаментальных принципов, которые мы используем для отладки проблем производительности которые ведут к появлению Китов. Первое правило: начать движение от общего к более конкретному. Мы двигались от изучения времени I/O и CPU и в конце-концов пришли к запросам к Memcached которые и являлись источником проблем. Второе правило: используйте данные, но не доверяйте им. Несмотря на перспективы 50% роста производительности, которые нам предвещают данные, вряд ли мы получим рост производительности близкий к этому. Но надежды на это будет тоже достаточно.

Авторы статьи: Ed Ceaser и Nick Kallen Дата: 9 февраля 2010


комментарии [0]  | комментировать

  © 2010-2018 HIGHLOAD