|
Персональные инструменты |
|||
|
Highload-2012: Отчёт Виталия Филиппова — различия между версиямиМатериал из CustisWiki
Текущая версия на 15:51, 1 ноября 2012Highload — «конференция разработчиков высоконагруженных систем» — пожалуй, самая распиаренная из Бунинских конференция. Проходила 22-23 октября 2012 (понедельник и вторник) в конференц-центре гостиницы Radisson Славянская (м. Киевская). Общие замечания:
Содержание
День первыйBuilding and Packaging Highly Scalable Services for Maximum Market Penetration / Michael Toutonghi (Parallels)Мишель Тутонги — раньше работал сначала 10 лет в стартапах, потом 10 лет в мелкософте — писал ядро Windows 95. Ух ты! У Windows 95 было ядро! Он его писал и до сих пор жив! — прим.вред. А теперь работает в Parallels и осуществляет там глубокий penetration рынка (судя по названию доклада) :) Однако, полезного содержимого в докладе было мало — что-то рассказывал про OEM/партнёров/прямые продажи, что-то про Parallels’овский стандарт APS… В общем, в моей голове как-то ничего не осело. Что делать со своим первым миллиардом? / Евгений Поляков, Антон Кортунов (Яндекс)Так хитро на самом деле был назван доклад Яндекса про Кокаиновое облако — платформу для облачных приложений (типа App Engine), которую они там у себя запилили и используют, и выложили в OpenSource на гитхаб под лицензией LGPL 3. Что очень кошерно =) А под «миллиардом» на самом деле понимался миллиард не денег, а объектов данных. «Сиськи и кокаин» (они были в докладе с пометкой «размер не имеет значения»). Итак, составляющие кокаина:
MySQL в Google / Ievgen Varavva (Google)Евгений Варавва рассказывал про то, как в Гугле готовят MySQL, чтобы он был быстр и отказоустойчив. Почему MySQL? Ответ: он быстрый и надежный, особенно с гугловскими патчами. Плюс ОЧЕНЬ распространённый, так что проблемы всегда можно погуглить :) Евгений так и сказал, зал очень радовался тому, что сотрудники Гугла тоже гуглят :) у них >100 пользователей MySQL, начиная с 2003 года и проектов Ads и Youtube. Всё-таки естественно у MySQL есть и минусы:
Что они делают с MySQL’ем? Как ни странно, довольно очевидное — нечто похожее мы пытались делать во vsem.ru, только там это делали во-первых на 3-ей версии, а во-вторых криво. А гугл сделал прямо:
Кое-что из этого находится в OpenSource:
Но вот самого вкусного — готового кластера в том виде, в котором его используют они — в этих репозиториях не обнаружено :-(задал вопрос автору презентации по почте — он ответил, что есть надежда ожидать их версию в открытом доступе к концу зимы, а аналогичный скрипт есть в оригинальной 5.6: http://dev.mysql.com/doc/workbench/en/mysqlfailover.html - интересно, насколько он аналогичный, разве там есть group ids? Цикл разработки, визуальный деплой, автоматизация и интернационализация / Михаил Буйлов (Mamba)С записи хорошо поставленным голосом сначала сказали, что сейчас нам расскажут про деплой, а потом произнесли «Михаил, задеплойтесь на сцену» :) Сначала они разрабатывали всю мамбу в trunk’е SVN’а. Проблемы очевидные:
Дальше они очевидным образом перешли на Git. Вообще в DVCS’ах чувак явно не особо сильно разбирается, потому что произнёс фразу «нормально с ветками можно только в гите работать». Для тех, кто будет это читать, добавлю пометку — вообще-то гит — не единственная хорошая DVCS, а гитовые ветки — это вообще не ветки! Это грёбаные листья — не может лист быть веткой :) Так вот, они перешли на Git и на разработку в ветках (Feature Branches). Очевидным образом напоролись на вероятность возникновения проблем слияния веток. Для их решения сделали отдельную простенькую веб-систему, в которой разработчики создают «заявки», указывают доработку и имя ветки, а система сама пытается мержить и проверять результат, и откатывать с возвратом разработчику на доработку, если что-то ломается. В частности, проверяются шаблоны сайта на отсутствие в них русского языка — у них там есть система локализации с «меташаблонами» и визуальным интерфейсом для переводчиков, а исходными ID сообщений служат их тексты на русском языке. Если одновременно проносятся две сильно связанные ветки — обычно руками создаётся третья, являющаяся их объединением, и проносится цельно. Перед деплоем производится заморозка — запрет на добавление новых заявок. Всё тестируется и вываливается на бой. Ещё рассказал про то, как надо переключать версии кода при деплое на боевых серверах, «если у вас > 1 одновременного запроса»:
А как делать хорошо? Ответ — переключать Document Root на уровне nginx и делать reload. Тогда все старые запросы обработаются старой версией, а новые — новой. Ещё была пара слайдов про демон сбора статистики для мониторинга ВСЕГО — BTP. Его сделали открыто-свободным, под GPLv2, есть на гитхабе:
SPDY: быстрее на 146 % / Валентин Бартенев (NGINX)Доклад про новый гугловский протокол Спиди («SPDY» — ускорятор HTTP), и про то, реально ли он такой спиди. Рассказывал разработчик Nginx’а. К слову, то ли 90 %, то ли даже больший процент всех серверов, поддерживающих спиди на текущий момент — это nginx. Соответственно, доклад — «из первых рук». Сначала не открывалась презентация, докладчик такой — да… вот со спиди тоже всё сложно… :) Зачем оно было сделано? А затем, что страницы сейчас, в отличие от 90-х, жирные, с кучей графики, css’ов и яваскриптов. Грузится всё это зачастую с довольно печальной скоростью, в первую очередь из-за многократных round-trip’ов (обменов пакетами туда-сюда) до сервера. А без следующих мер, которые сейчас широко применяются, было бы всё совсем печально:
Так вот, говорит докладчик, а гугл предложил заместо всех этих костылей свой кос… протокол. :) Основные идеи спиди — мультиплексирование в обе стороны, сжатие и server-push, который, правда, работает пока только в Chrome и только криво. Ещё там есть Flow Control, но он тоже пока не работает. Однако:
Полнотекстовый поиск в PostgreSQL за миллисекунды / Олег Бартунов, Александр КоротковДоклад о том, что Постгресовцы наконец-то допёрли, что хранить кроме индекса ещё и отдельную tsvector колонку в таблице для ранжирования результатов запроса — это тормозной костыль, и позиции слов в текстах нужно хранить прямо в индексе. Если так делать, то скорость постгрешного полнотекстового поиска резко подскакивает и становится сильно похожей на Sphinx. То есть очень быстрой. Индекс, правда, вырастает. Однако они там придумали сжатие — «дельта-компрессию» позиций слов — благодаря чему индекс на некоторых тестах получался даже меньше, чем в Sphinx. Кроме того, и с удалением данных из индекса вопросов не возникает, хотя удаление из обратного индекса — не такая простая операция. Оно как делалось VACUUM’ом, так и делается. А почему тормозит то, что есть сейчас? А потому, что index scan — операция быстрая, а вот heap scan с проверкой видимости строки — операция медленная. В PostgreSQLьном MVCC в индекс попадают все версии строк, а из результата старые версии отфильтровываются уже после выборки по индексу (что и называется проверкой видимости строки). Кроме того, хранение позиций в индексе позволит сделать поиск по точной фразе, что было ранее невозможно. Данную доработку они сразу обобщают на уровень GIN’а (который Generalized Inverted iNdex). Некоторые вопросы там ещё не до конца решены, но к PostgreSQL 9.3 планируют дорешать и включить всё это в релиз. Круто. Tricky SQL. Особые возможности SQL-диалекта PostgreSQL / Иван ФролковЧувак рассказывал про альтернативно-одаренные возможности постгрешного SQL, постоянно добавляя «можно? можно. нужно ли? а хрен его знает, зачем, но можно». « CREATE VIEW AS SELECT * FROM VALUES… То есть, VIEW из значений, указанных прямо в запросе. Благодаря тому, что DDL в постгресе транзакционный, можно на это VIEW навесить триггеры, его пересоздающие, и в результате выглядеть оно будет вполне себе как обычная таблица. Если всегда нужно использовать все или почти все данные из него — удивительно, но это ещё и быстрее будет процентов на 10. Почти что самое безобидное — Top N for M (стандартное «N самых активных пользователей по каждой теме») через использование массивов. Нормальный вариант. Рекурсивные Common Table Expressions (WITH RECURSIVE) как конечный автомат для, скажем, разбора JSON’а. O_O Код этой жести
WITH parsed AS( SELECT n, token AS token, array[token] AS stack, array['$']::text[] AS path, '' AS jsp, array[0]::INTEGER[] AS poses FROM tokens t WHERE n=1 UNION ALL SELECT t.n, t.token AS token, CASE WHEN t.token IN (']','}') THEN p.stack[1:array_upper(p.stack,1)-1] WHEN t.token IN ('[','{') THEN p.stack || t.token ELSE p.stack END, CASE WHEN t.token IN ('[','{') THEN p.path || CASE WHEN p.stack[array_upper(p.stack,1)]='{' THEN regexp_replace(p.token,'^"|"$','','g') ELSE '[' || (p.poses[array_upper(p.poses,1)]+1)::text || ']' END WHEN t.token IN (']','}') THEN p.path[1:array_upper(p.path,1)-1] ELSE p.path END, CASE WHEN p.stack[array_upper(p.stack,1)]='{' THEN p.token WHEN p.stack[array_upper(p.stack,1)]='[' THEN '[' || (p.poses[array_upper(p.poses,1)]+1)::text || ']' ELSE '' END, CASE WHEN t.token IN ('[','{') THEN p.poses[1:array_upper(p.poses,1)-1]||(p.poses[array_upper(p.poses,1)]+1)||0 WHEN t.token IN (']','}') THEN p.poses[1:array_upper(p.poses,1)-1] ELSE p.poses[1:array_upper(p.poses,1)-1]||(p.poses[array_upper(p.poses,1)]+1) END FROM parsed p, tokens t WHERE t.n=p.n+1 ), Возврат нескольких resultset’ов из функции через глобальные курсоры. Плюс можно написать функции, которые имя курсора делают хешем от запроса, и таким образом возвращать произвольное количество resultset’ов. Код этого безобразия
CREATE OR REPLACE FUNCTION generic_cursor(IN SQL text, VARIADIC param text[] DEFAULT ARRAY[]::text[]) RETURNS refcursor AS $BODY$ DECLARE rv refcursor; sthhash text := 'GENCURS'||md5(SQL); EXEC text := 'execute ' || sthhash || CASE WHEN array_length(param,1) IS NULL THEN '' ELSE '('||(SELECT string_agg(COALESCE(quote_literal(pv),'NULL'),',') FROM unnest(param) AS u(pv))||')' END; BEGIN BEGIN OPEN rv FOR EXECUTE EXEC; exception WHEN sqlstate '26000' -- prepared statement does not exist OR sqlstate '0A000' -- table definition had changed OR sqlstate '42703' -- -/- OR sqlstate '42P11' THEN IF sqlstate='0A000' OR sqlstate='42703' THEN EXECUTE 'deallocate '||sthhash; END IF; EXECUTE 'prepare '||sthhash|| COALESCE((SELECT '('||string_agg('text',',') || ')' FROM unnest(param) AS u(pv)),'') ||' as '||SQL; OPEN rv FOR EXECUTE EXEC; END; RETURN rv; END; $BODY$ LANGUAGE plpgsql CREATE OR REPLACE FUNCTION generic_exec(IN SQL text, VARIADIC param text[] DEFAULT ARRAY[]::text[]) RETURNS SETOF record AS $BODY$ DECLARE cr refcursor := generic_cursor(SQL, variadic param); BEGIN RETURN query EXECUTE 'fetch all from '||quote_ident(cr::text); END; $BODY$ LANGUAGE plpgsql Юзать следующим образом: SELECT * FROM generic_exec(SELECT n FROM generate_series(1,$1::INTEGER) AS gs(n), 20::text) AS ge(n INTEGER) Крадущийся сервер, затаившийся диод / Андрей Аксенов (Sphinx)Очередной доклад Аксёнова, который, как известно, жжот — теперь про то, как правильно делать бенчмарки. Андрей неплохо прикололся над Badoo:
Публика рукоплескала — один из прошлых докладов был как раз про то, как Badoo «модерирует миллион фотографий в год» 120+ Мысль доклада номер раз — зачастую бенчмарки проводят криво. Например
Мысль доклада номер два — перед тем, как делаете бенчмарк, нужно определить, ЗАЧЕМ вы его делаете, и от этого уже плясать — выбирать показатели и так далее. Варианты целей (копипаст слайда):
Ну, ещё закон Амдала про масштабируемость, и ещё — вместо типичного подсчёта среднего, которое ничего не значит, лучше считать распределение, или хотя бы какие-нибудь перцентили (50 %, 80 %, 95 % и т.п). Использование очередей асинхронных сообщений с PostgreSQL / Илья Космодемьянский (PostgreSQL-Consulting.com)Рассказ про mbus — движок для очередей сообщений на основе чистого PostgreSQL. Единственная зависимость — hstore. Реализация весьма банальная — просто таблица с событиями. Возможности получить событие, блокируясь в случае пустой очереди, там нет, просто селекты, завёрнутые в функции. Из паттернов использования — queue, publish/subscribe, request/response, селекторы по параметрам сообщений, задержка доставки, экспайр доставки. Дополнительные мысли:
В Roadmap’е mbus’а 2PC и JMS-клиент. Короче говоря, в данный момент mbus к распределёнщине никакого отношения, насколько я понял, не имеет, и вообще хз, зачем нужен. Суперкомпьютеры сегодня и завтра: архитектура, проблемы, перспективы / Андрей Слепухин (Т-Платформы)Доклад — история, цифры и фантазии из мира суперкомпьютеров. История стандартная:
Любопытные факты:
Перспективы развития:
День второйOne Billion Notes as 'Small Data': Scaling Evernote through Horizontal Partitioning / Dave Engberg (Evernote)Англоязычный доклад рассказывал товарисч из Evernote… про этот самый Evernote. В начале рассказал, что это такое и почему оно хайлоад. Я это не юзаю, поэтому, например, не знал, что оно пытается распознавать текст с загружаемых туда фотографий. Любопытная фича. Ну, в общем у них там полный хайлоад, 14 миллиардов HTTP-запросов за последние 30 дней. Они для метаданных используют MySQL, для поиска — Lucene. Любопытный факт: у них Lucene генерирует больше обращений к дискам, чем MySQL. Для хранения файлов — DRBD. Шардят всё это добро по юзерам, благо их дофига и поэтому распределение получается равномерным. Мысль номер раз — оптимизировать надо 1-2-… основных бутылочных горлышка. Мысль номер два про облака. Они, конечно, могут оказаться хороши, если вам нужно держать ВНЕЗАПНЫЕ нагрузки, а потом отдавать неиспользуемые ресурсы обратно. И/или если у вас мало обращений к диску… И то не факт. Но по их подсчётам, для них тот же Amazon Web Services был бы сильно дороже:
Percona XtraBackup: экспертные возможности / Алексей Копытов (Percona)Рассказ про Percona XtraBackup:
MySQL Plugins — why should I bother? / Sergei Golubchik (MariaDB)Обзор плагинов к MySQL’ю. В целом они бывают:
В MariaDB — плагины SphinxSE, OQGRAPH, HandlerSocket уже есть искаропки, MyISAM заменён на совместимый ARIA, InnoDB — на совместимый XtraDB. В целом MariaDB кошерен скоростью работы и очень кошерен свободой от оракла. Proactive Web Performance Optimization / Marcel Duran (Twitter)Основная (единственная?) мысль доклада товарища из Twitter’а — прикручивание YSlow к серверу непрерывной интеграции в виде теста на скорость загрузки страниц. Ещё было про webpagetest. Прикручивать можно, используя headless webkit браузер PhantomJS. phantomjs yslow.js умеет выдавать результаты в виде junit XML, или TAP (Test Anything Protocol). Следовательно, прикрутить можно почти ко всему. Adventures in Bug Hunting / Joseph Damato (Boundary)Очередной иностранец с красивой презентацией о том, как он ловил баг в bprobe, а баг оказался в Linux ядре, в драйвере e1000e. Он сначала думал, что в ядро пакеты попадают. А оказалось, что не попадают, причём только на Debian Lenny 64bit)) ну что ж, бывает)) bprobe — это некая его утилита для ловли пакетов на bonding сетевых интерфейсах. Bonding — линуксовый термин, означающий создание одного «общего» сетевого интерфейса из нескольких для распределения нагрузки и обеспечения отказоустойчивости. Моё мнение о докладе — занимательно, но не более того. Многие линуксоиды таким багхантингом занимаются время от времени)) А картинки были прикольные: Cервис рекомендаций на виртуальном Hadoop кластере / Роман Зыков (Wikimart)Чувак рассказывал про рекомендации в викимарте. Раньше он был поклонником вендорских систем, внедрял адобовский SiteCatalyst. Потом одумался и они стали вычислять рекомендации сами. Сначала они пробовали делать это тупо на MSSQL’е, но это было печально. Двуксеон с 48 гигами оперативы и 8 SAS-дисками в RAID10 с задачей справлялся в лучшем случае часов за 5, с 30 % вероятностью — то есть, каждый третий раз — падал по разным причинам (то логи кончались, то ещё что-нибудь), и на всё время обновления блокировал загрузку данных в хранилище. А потом они решили попробовать Hadoop — Apache’вский свободный аналог распределённого гугловского хранилища BigTable. Собрали маленький кластер всего из 6 более простых компов, по 2 Гб оперативки, 30 гб HDD и 16 ядер (O_o не очень-то попроще компы были))). Поставили на него Cloudera Manager, который бесплатен до 50 нод. Заюзали sqoop для передачи данных из hadoop’а в СУБД. Hive и Pig — для обработки данных. Hive — почти SQL поверх HDFS’а. И тот, и этот поддерживают User-Defined функции. В частности, в Pig 0.10 добавлены оные от LinkedIn (DataFu UDF), типа удобные. Для сжатия выбрали кодек snappy (by гугл), который может жмёт и немного похуже LZO, но зато распаковывает быстро, что приводит к общему ускорению ввода/вывода. Для этого ускорения он, собственно, и создавался. Плюс у LZO дебильная лицензия, а у snappy нормальная и он есть в hadoop из коробки. И типа всё стало хорошо, и можно двигаться в сторону аналитики в реальном времени. Спасение 6 млн файлов в условиях полного Хецнера / Даниил Подольский, Дмитрий Симонов (Setup.ru)Говорит, есть такой известный немецкий хостер на букву Х, и оборудование у него тоже на букву Х — хорошее. Но не очень надёжное. Мысль: вполне нормально хранить файлы в Postgreшных блобах. Ибо нет дублирования метаданных на уровне базы и ФС, поиск по параметрами (дате и т. п.) очень быстрый, плюс очень важно, что в PostgreSQL есть API для потокового доступа к блобам, через которое их можно раздавать. Конечно не sendfile и не zero-copy, но утверждают, что не хуже, ибо в сеть упёрлись сильно раньше, чем в процессор — 1000 соединений, 30 % нагрузка на проц, 10 % на диск, всю память под кэш не выжирает, а гигабитный канал забит полностью! То есть «а нам быстрее и не нужно, всё равно в сеть не пролезает». Ещё пробовали HyperTable, но он пока что нов и падуч. Бэкапов они не делают :-) типа, потому что всегда есть под рукой живая реплика. Хотя вообще-то бэкапы всё равно нужны, потому что от TRUNCATE TABLE реплика не спасёт. А я посмотрю, как они свои блобы будут rsync’ом бэкапить :-D у них, правда, почему-то 6 миллионов файлов разложено по 6 миллионам директорий, и обход этого дикого дерева занимает 6 часов O_o мда. «Вывод: технологии каменного века ещё актуальны!» Выжимаем из сервера максимум! Приёмы кеширования и передачи данных на Java / Андрей Паньгин (Одноклассники)В одноглазниках 4000 серверов и всё написано на Java, а архитектура сервис-ориентированная. То есть, всё поделено на сервисы, относительно слабо связанные друг с другом. Следствием SOA является то, что, между серверами происходит много взаимодействия. То бишь, данные сериализуются и гоняются по сети. Вот о том, какие с этим проблемы и что же делать, и рассказывал докладчик. Плюс про кэш. Проблемы Java-сокетов: криво работают неблокирующие сокеты. Где-то они не thread safe, где-то утекают управляемая память из-за финализаторов (не очень понимаю, что имели ввиду, так как не копал тему, но он сказал «мы обнаружили, что в сокетах есть финализаторы»), где-то утекает нативная память, где-то не срабатывают таймауты. А нормального libev’а / libevent’а там нет. Отсюда либо многотредовость и блокирующий ввод/вывод, либо фреймворки типа APR, MINA, Netty. Сериализация: стандартная медленная и даёт большой объём данных, JBoss’овская отваливается при изменениях в определении класса, ручная — не вариант, ибо классов тысячи. Были варианты использовать Avro / Thrift / Protobuf, но они в итоге написали свою. Чтобы вертеть private свойствами других классов — юзают Unsafe, чтобы обойти верификатор доступа — наследуют sun.reflect.MagicAccessorImpl. При переключении на эту сериализацию производительность резко улучшилась на 20 %, а объём передаваемых по сети данных уменьшился на 50 %. Однако с Protobuf и прочими они её не сравнивали. А и правда, зачем, можно ж своё написать :)) Сериализация, вроде, открыта и находится на гитхабе: https://github.com/odnoklassniki/rmi-samples/ (а может, там только RMI, не проверял) Кэширование: в Java стандартная проблема, ибо память-то управляемая, и GC должен по ней время от времени проходиться. Если её много, получаются тормоза, самые худшие — когда кучи остаётся совсем мало и начинается Full GC («stop-the-world»). Посему надо как-то использовать off-heap (неуправляемую, нативную) память. Как сказал докладчик — а это опять мой любимый class Unsafe. Либо ByteBuffer.allocateDirect(), либо mmap (через MappedByteBuffer), либо писать свою JNI-обёртку. Проблема прогрева кэша (медленный старт, пока кэш пустой) решается созданием снимков. Которые должны быть целостными. Но вместо очередного stop-the-world лучше делать fork() и создавать снимок в дочернем процессе — засчёт Copy-On-Write память не задублируется, но при этом состояние снимка меняться не будет. Либо юзать разделяемую память. Они используют именно разделяемую память, сами бьют её на сегменты и сами лочат. Их реализация получилась быстрее Ehcache и JCS, на каких-то операциях раза в 2, а на каких-то раз в 20. Сие тоже открыто: https://github.com/odnoklassniki/shared-memory-cache Внимание — Авторы, когда выкладываете что-то куда-то, определяйте лицензию! А то вот это добро выложено, а файла COPYING нету :) AddressSanitizer, или как сделать программы на C/С++ надежнее и безопаснее / Константин Серебряный (Google)AddressSanitizer — это такой Valgrind, только гугловский, на основе LLVM и сильно быстрее. Замедление всего в 1.5-2, максимум в 3 раза (редко), по сравнению с Valgrind’овскими минимум 20-ю это очень круто. Но нужно, чтобы ваш проект собирался LLVM’ом. Тогда использовать очень просто — собрать с ключиком -faddress_sanitizer. Штуковина, собственно, отлавливает ошибки доступа к памяти. Не совсем корректно сравнение с Valgrind — он утечки памяти умеет искать, а эта штука не умеет. Работает относительно просто — на каждые 8 байт памяти сохраняется 1 байт состояния. Вариантов которого всего 9 — от «все 8 байт хорошие» до «все 8 байт плохие», только планка посередине сдвигается. Кроме того, есть «карантин» — только что освобождённая память какое-то время не выделяется снова. Служит для определения доступа к освобождённой памяти. Плюс в комплект входит библиотека, перехватывающая стандартные функции работы с памятью типа memset, memcpy. Широко используется в гугле и за его пределами (например, Firefox). Постоянно что-нибудь находят. «Всё написано на C/C++, даже если вы об этом не знаете — компиляторы, виртуальные машины, веб-серверы, СУБД…» «А ещё у них есть ThreadSanitizer и MemorySanitizer». Вот.
Любые правки этой статьи будут перезаписаны при следующем сеансе репликации. Если у вас есть серьезное замечание по тексту статьи, запишите его в раздел «discussion». Репликация: База Знаний «Заказных Информ Систем» → «Highload-2012: Отчёт Виталия Филиппова» |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||