shocker: (Default)
[personal profile] shocker
Боюсь потерять записи сделанные на последней конференция разработчиков высоконагруженных систем (Highload 2008) поэтому напишу сюда. Лучше поздно чем никогда, да и основные концепции останутся неизменными еще очень долго.
Это будут основные тезисы и местами мои комменты.

Среди присутствующих на конференции хочу отметить следующие личности: Игорь Сысоев (ведущий, nginx, поклонник мультиплексинга ввода/вывода для решения проблемы C10K), Алексей Тутубалин (русский Апач, сидел в зале и частенько делал едкие замечания), Анатолий Орлов (Яндекс, отличный спец по специфике железа), Филипп Дельгядо (Яндекс, отличный project-менеджер и системный архитектор), Антон Самохвалов (Яндекс, программер, базовый поиск, ярый фанат 10000 потоков на фрюхе как ответ на проблему C10K, много спорил с Сысоевым).

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

Итак погнали, идеи не мои, а уважаемых людей, материал в основном для программистов и архитекторов (в основном Си/С++ и в основном не Windows):

- Обращаем внимание на load average в top`е, это количество конкурирующих потоков за процессорное время. Если цифры большие - все плохо даже если top показывает низкую загрузку процессора (бывает и так).

- Пользуемся iostat из sysstat и похожими инструментами для выявления узких мест про процессе эксплуатации софта.

- Процессор по своей сути последовательное устройство и механизм многозадачности на уровне операционной системы может не оптимально подходить к решению ваших задач, поэтому лучше выстраивать таски в очередь (4 ядра - 4 очереди) и выполнять последовательно. Пример - есть 5 задач, каждая выполняется 1 секунду. Если запустить их на выполнение одновременно велика вероятность того, что каждая из них в результате отработает примерно за 5 секунд с учетом конкуренции. Если выстроить их в очередь, то получим следующее: первая гарантированно отработает за секунду, вторая за 2, третья за 3 и т.д. В итоге в условиях конкуренции хоть кто-то будет удовлетворен быстрее.

- Не рекомендуется мешать разнородные задачи в рамках одной машины (база данных должна быть базой данных, а Web-сервер Web-сервером - очень разная специфика у задач и разная конфигурация железа в идеале).

- Данные и код в ОЗУ могут кэшироваться процессором, если научиться подстраиваться под этот процесс можно добиться сильного увеличения производительности. Например, первым после 'if' лучше ставить блок вероятность исполнения которого выше.

- При работе с диском поменьше seek`ов! Линейная скорость чтения/записи на порядки выше скорости позиционирования головок (readahead).

- При возникновении ошибок в процессе эксплуатации библиотеки OpenSSL необходимо выбирать их из стека ошибок в цикле (пока не кончатся), если надеяться на то, что она там одна можно получить неприятные сюрпризы.

- идеальная схема сервера приложений: мультиплексинг ввода/вывода (epoll, kqueue, как вариант libevent) в одном или нескольких потоках/процессах для общения с медленными клиентами, далее очередь, далее несколько обработчиков в отдельных потоках/процессах (для использования возможностей SMP). Медленный клиент не будет впустую занимать обработчик долгой отправкой запроса/чтением ответа. За время ввода/вывода обработчик успеет выполнить кучу запросов. Количество обработчиков можно прикинуть исходя из формулы: кол-во ядер + кол-во дисков + 5 :)

- Правильные пацаны смотрят в сторону FreeBSD 7

- Многоядерный/многопроцессорный сервер не панацея - ядра будут упираться в шину памяти. Лучше собрать кластер из нескольких дешевых машин чем покупать дорогой 16-и ядерник (от себя добавлю, возможны самые последние варианты: 16-ти ядерник с VMWare и кучей гостевых ОС на борту :) ).

- При тестировании софта используем профайлеры.

- Не забываем о страничной адресации памяти, если угадали и наши данные попали в одну страницу получаем прирост производительности.

- Двухзвенные системы для серьезных проектов не годятся (это когда как можно больше всего стараются впихнуть в базу данных) - не масштабируются.

- Надо иметь как минимум 3 независимых датацентра :).

- Для выживания бизнеса после серьезной аварии необходимы регулярные бэкапы и подробные логи.

- Как можно больше всего кэшировать вне базы снимая с нее нагрузку. Масштабировать базу гораздо сложнее чем сервер приложений. База должна использоваться только для длительного хранения (persist).

- Необходимо добиться от системы возможности горизонтального масштабирования - это когда можно тупо воткнуто в стойку еще один юнит и получить еще производительности. Об этом стоит задумываться уже на этапе проектирования системы!

- Лучше использовать stateless протоколы. Это снимет нагрузку с базы/кэша при хранении промежуточных данных (типа сессий PHP). Хорошо подойдут средства криптографии. Можно заставить клиента возвращать обратно что-то (промежуточные данные), что вы вернули на предыдущем шаге и проверять валидность этого, например, при помощи MD5 или RSA.

- memcached сила, последние версии используют libevent. Основное достоинство - отсутствие блокировок при конкуренции!

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

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

- Если в базе данных появились тригеры - значит у вас в архитектуре уже что-то не так :)

- Для масштабирования баз данных необходимо придумать механизм распределения контента между базами и алгоритм поиска нужной базы по входным данным (Database sharding).

- Java - идеальный вариант для стартапа, из-за своей ущербности она на даст вам накосячить :)

- DB2 - предпочтительнее Oracle т.к. сейчас развилось множество быдлоспециалистов по последнему. DB2шников найти сложнее, но уровень у них будет выше.

- Автоматические тесты софта - наше все.

- INSERT лучше UPDATE!

- База данных должна быть простой.

- Возможность сериализации внутренних структур программы для хранения в БД или кэше.

- Асинхронная обработка данных лучше синхронной, если есть выбор - выбираем первое.

- В лог пишем как можно больше всего, логи должны быть удобными для машинного разбора (поможет выжить).

- Необходима возможность постоянного мониторинга состояния узлов системы.

- Не забываем о системах контроля версий (SVN,CVS).

- Длинные транзакции в БД - зло!

- Люди - дорого, железо - дешево!

- Все базы INSERT делают одинаково, так что и MySQL по большому счету может сделать тот, же Oracle.

- fork очень дорог.

- Не забываем про TCP_NODELAY и TCP_CORK.

- Нельзя доверять операционной системе, если что-то от нее получили - не отдавайте (например, память или поток... используем механизм пулов).

- Как можно меньше копирований, это сожрет всю производительность, используйте ссылки (zerocopy)!

- Кэшируем-кэшируем и еще раз кэшируем.

- Говорят на FreeBSD потоки реализованы лучше чем в Linux.

- Местами Windows показывает себя очень хорошо в плане производительности, выбирайте ту ОС, где оно работает лучше.

- Как можно меньше блокировок, FSM - наш путь :).


От себя добавлю, STL это сила но надо думать головой когда выбираешь контейнер или что-то делаешь. Стоит обратить внимание на такие методы контейнеров как reserve (избегаем realloc`ов) и swap (избегаем копирований).
Следующий код может сильно ускорить процесс в сравнении с более лаконичным list.pash_back(s)
list.push_back(std::string());
list.back().swap(s)

В критических местах вообще не рекомендуют использовать STL из-за ее непредсказуемости в плане работы с памятью.
Поэтому собственные аллокаторы, контейнеры и вперед к звездам.

Вот интересная статья про оптимизацию программ с STL.

К вопросу о многопроцессном программировании - оно подкупает надежностью программы из-за полной изоляции потоков исполнения, но может сильно усложнить жизнь и сделать невозможной реализацию концепции zerocopy из-за необходимости межроцессного взаимодействия (все что может нас спасти - разделяемая память). Поэтому тут верный путь - потоки.

Еще надо стремиться к тому, что бы производительность сервера не зависила от количества клиентов, так называемый O(1) (O от единицы). Например, epoll в линуксе соответствует этому требованию, а старые select и pool - нет.
Возьмете за основу select и привет, O(1) не видать :) Если все отдельные алгоритмы удовлетворяют данному требованию можно утверждать что эффективность всей программы стремится к O(1). Среди узких мест могу отметить память и таймеры, надо искать подходящие алгоритмы.

Последний параметр (backlog) системного вызова listen очень важен, в серьезных приложениях стандартных 5 не хватит, а вот 50 самое то :)

Не забываем об оптимизации компилятора, например для gcc подойдут ключи '-O2' и '-O3', только внимательнее... могут быть сюрпризы если код не совсем корректен. Если у нас есть сигналы, разделяемая память или потоки  обязательно использовать ключевое слово volatile.

Хотел сказать - Core 2 Duo очень крут, сам лично наблюдал как старый дорогой Xeon 3.6 Ггц слил Core 2 Duo E8400 3.0 Ггц (15000 запросов в секунду на одном ядре против 10000).

Перед Web-сервером общего назначения (например, Apache) обязательно выставлять реверсный прокси, например, nginx. Это снимит с "дорогих" воркеров бремя чтения/записи при общении с клиентом. Может получиться так, что это будет отнимать времени больше чем полезная работа + это отличная дырка для DOS атак.

И на последок...  CGI это зло и пережиток прошлого - fork`и, пайпы и множественные копирования делают свое дело. На крайний случай FastCGI, а лучше модули расширения Web-сервера... если используем динамические языки для Web-программирования, то PHP, mod_perl или даже luasp - наш путь.

This account has disabled anonymous posting.
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting

Profile

shocker: (Default)
shocker

December 2019

S M T W T F S
1234567
891011121314
15161718 192021
22232425262728
293031    

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated 7 Jan 2026 21:03
Powered by Dreamwidth Studios