Как заставить интернет-магазин выдерживать нагрузку 280 000 посетителей в час?


    Привет, Хабр!

    К сожалению, на данном этапе развития веб-программирования в нашей стране рефакторинг проекта чаще воспринимается как работа программиста в режиме «все плохо» и проводится только в тот момент, когда сайт уже находится в критическом состоянии. С подобной ситуацией нам пришлось столкнуться в 2012 году, когда к нам на обслуживание пришел один крупный российский интернет-магазин со следующей проблемой: начиная с 10 утра сайт каждые полчаса падал на 5-10 минут и поднимался или с большим трудом, или после жесткого ребута. После ребутов сайт немного работал, а затем падал опять. Особую остроту проблеме придавал тот факт, что приближался Новый год, высокий сезон для всех продающих сайтов, и в данном случае фраза «за 10 минут компания теряет десятки тысяч долларов» была не шуткой.


    Давайте немного отвлечемся от нашей истории и поговорим о том, что разработчики теперь исключительно счастливые люди. Наконец-то серверные мощности стали настолько дешевыми, что любую систему при нехватке ресурсов можно легко масштабировать до нужного размера. Что-то не так напрограммировали? Повысилась нагрузка на проц? Отлично, давайте добавим процов серверу. Не хватает оперативы? Давайте добавим оперативы. Проблема нехватки ресурсов — не проблема.

    Многие из вас прекрасно помнят то время, когда люди сидели по диалапу, вдумчиво слушали звук модема, с первых нот определяя, удалось ли законнектиться или нужно переподключиться. В то время медленный сайт просто закрывался через минуту, потому что ждать его было очень дорого (в прямом смысле этого слова). Сегодня же медленный сайт — это сайт, который открывается дольше 3 секунд без учета скорости канала. Мир наращивает скорость, время стоит дорого. А что делать, если сайту не хватает скорости? Есть замечательные акселераторы и более мощные и очень дешевые при этом сервера, есть веб-кластер от того же 1С-Битрикса, например, а в крайнем случае и море всякого ПО для тех же целей. Кажется, что можно больше не следить за качеством кода, резко понизить уровень разработчиков и существенно сэкономить бюджет компании.

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

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

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

    Что мы имели на входе?


    Мы знали, что графики нагрузки на разные части системы должны быть достаточно плавными. Например, такими:



    Это связано с тем, что очень редко в одну секунду бывает ноль пользователей на сайте, а в следующую — все 110 000 пользователей пиковой нагрузки.

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

    Apache:


    Возможно, стоило бы порадоваться возросшей по графикам нагрузке — больше посетителей, больше денег — но Google Analytics говорил, что количество посетителей не только не выросло, а даже упало.

    Как правило, в стабильном и корректном состоянии системы увеличение нагрузки на Apache коррелируется с увеличением нагрузки на MySQL. Но в нашем случае выглядело все иначе.

    MySQL:


    А теперь давайте сопоставим графики MySQL и Apache:


    Что-то явно пошло не так. И мы начали искать проблему.

    Что же мы обнаружили?


    Во-первых, мы обнаружили измененное ядро CMS «1С-Битрикс», причем изменено оно было в тех частях, которые работают с кэшем, что означало потенциально неправильную работу с ним. В принципе, в этом была некоторая логика — если кэш сбоит и сбрасывается, то в эти моменты будут всплески на Апаче. Но тогда должны были быть еще и всплески на MySQL, а их не было.
    Во-вторых, из-за измененного ядра CMS проект не обновлялся уже несколько лет, так как любое обновление ломало сайт.
    В-третьих, сервер был настроен некорректно. Поскольку наша история о рефакторинге, на данном пункте подробно останавливаться не будем.
    В-четвертых, проект создавался примерно в 2005-2006 году и рассчитывался совсем на другую нагрузку — раз в 10 меньше текущей. Архитектура проекта и код совершенно не были рассчитаны на возросшую нагрузку. Запросы, которые при старой нагрузке выполнялись в пределах терпимых 0.5 — 1 секунды, при новой нагрузке выполнялись уже 4-15 секунд и попадали в slow log.
    И в-пятых: в процессе сотрудничества заказчика с разными подрядчиками за годы существования проекта в нем появилось много дублированного кода, лишние циклы и неэффективный кэш.

    Собственно, немного дебаггера, пару гигабайт логов, живой аналитик весом не менее 60 кг, посолить, поперчить, перемешать — там мы получили отличный рецепт быстрой стабилизации проекта. Он смог пережить пик нового года и приобрел маленький, но все-таки запас прочности.


    Как исправляли все остальное?


    Напомним, что проект был на «1С-Битриксе». Сначала мы обновили версию CMS до последней, что привело, как мы предположили в начале, к тому, что значительная часть функционала перестала работать, её нужно было восстанавливать. После базовой аналитики выяснилось, что в ядре и в стандартных компонентах было изменено более 1000 файлов. После обновления первым этапом нам пришлось восстанавливать сайт до полнофункциональной версии. Зато это позволило нам подключить модуль Веб-кластера со всеми плюшками.

    Вторым этапом мы проанализировали архитектуру проекта, запросы к БД и код и обнаружили, что из-за специфического построения архитектуры с одной каталожной страницы генерируется от 3000 до 4000 запросов к БД без кэша, с кэшем—около двухсот запросов. При этом он сбрасывался неоптимально и при любом обновлении контента.

    Для оптимизации запросов к БД нам пришлось модифицировать структуру инфоблоков: денормализировать часть данных и вынести часть данных в отдельные таблицы. Это позволило избавиться от наиболее тяжелых джоинов со скоростью выполнения 30-40с и заменить их на несколько быстрых селектов. Также мы проставили индексы на наиболее используемые данные и убрали лишние, оставшиеся от старой структуры. Все это позволило значительно увеличить общую скорость выполнения запросов.

    Также мы добавили бонус для последующих разработчиков проекта: для того, чтобы в дальнейшем код легко читался, мы разбросали многотысячные простыни кода на отдельные классы и файлы, прокомментировали их и почистили лишние устаревшие файлы.

    Что в итоге получилось?


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

    На графиках теста извне видно, что до рефакторинга и реинжиниринга системы при нагрузке в 25 одновременных пользователей страница грузится около 8с, а после нее – только 4,2с.
    До:

    После:


    На графиках тестов изнутри кластера видно, что, например, главная страница до рефакторинга при 200 000 посетителях грузилась 40с, а стала грузиться 1,9с.
    До:

    После:


    Чем мы порадовали своих разработчиков?


    Мы сократили количество запросов к БД как с кэшем, так и без него. Увеличили читабельность кода. Упростили внутреннюю структуру проекта. Удалили устаревшие данные. Изменили больше 14 000 файлов. Упростили масштабирование проекта. И в финале — дали значительный запас проекту для наращивания нагрузки.

    Чтобы мы хотели в завершении статьи посоветовать всем веб-разработчикам:


    • Пользуйтесь мониторингами состояния серверов. Обычно там все видно.
    • Пользуйтесь дебаггерами, следите за самыми ресурсоемкими операциями и циклами.
    • Кэшируйте все, что используется постоянно. Скидывайте кэш частями, а не полностью.
    • Следите за количеством запросов к БД как с кэшом, так и без кэша
    • Профилируйте запросы, следите за количеством данных, передаваемых от БД к апачам. Следите за логикой и оптимальностью выполнения запроса.
    • Расставляйте индексы на таблицы в БД.
    • Обновляйте CMS.

    Наталья Чиликина, руководитель отдела bitrix-разработки ADV/web-engineering

    ADV/web-engineering co.

    46,00

    Компания

    Поделиться публикацией

    Похожие публикации

    Комментарии 35
      +25
      Изменили больше 14 000 файлов.

      модуль Веб-кластера


      вот за это вас и не любят
        0
        Не могли бы Вы развернуть свой комментарий:
        — чем плох модуль веб-кластера
        — по какой причине Вы считаете, что изменение такого количества файлов в рамках рефакторинга — плохо?
        • НЛО прилетело и опубликовало эту надпись здесь
            0
            В ядре битрикса версии 8.0, которую мы получили от клиента — больше 10 000 файлов. Любая достаточно сложная многоуровневая система с несколькими типами прав и несколькими админками будет содержать очень много файлов. В этом нет ничего плохого, если все структурировано и откомментировано.
            • НЛО прилетело и опубликовало эту надпись здесь
          +10
          14 тыщ файлов? 4 тыщи запросов? Да как такое возможно-то вообще?
            0
            Такого добиться легко, если выполнять запросы в цикле и без джоинов :)
              0
              А что там выбирать? Один запрос на выборку товаров из категорий, один запрос с парочкой жоинов на выборку инфы товара. Плюс еще ну 10-20 запросов на построение страницы, половина которых в кеше.

              А уж раз с циклами, то на странице, ну максимум, может быть 50 товаров + несколько запросов на выбор цены, параметров, картинки = 400-600 запросов. Но это тоже неправильно.
                +1
                еще: один запрос на регион, один запрос на меню, примерно 20 на формирование динамических фильтров, которые подстраиваются под характеристики товаров в наличии, один запрос на цены, один запрос на наличие, штук 5-10 на корзину, несколько запросов на различные акции, несколько запросов на дополнительную информацию о товаре, которая не хранится в информации о товаре (типа отзывов), почти все с джоинами, потому что все связано или с регионами, или с товарами.
              +1
              Да, мы тоже сначала не поверили — но статистика репозитория и статистика отладчика говорили, что именно столько. Это очень сомнительные цифры, мы понимаем, но у нас есть экземпляр старого сайта и перед написанием статьи мы все проверили.
              0
              Также мы добавили бонус для последующих разработчиков проекта: для того, чтобы в дальнейшем код легко читался, мы разбросали многотысячные простыни кода на отдельные классы и файлы

              Бонус так бонус.
              +10
              Думаю, дело «в общем», а не в рефакторинге.
                +3
                С одной каталожной страницы генерируется от 3000 до 4000 запросов к БД без кэша, с кэшем—около двухсот запросов.
                до рефакторинга и реинжиниринга системы при нагрузке в 25 одновременных пользователей страница грузится около 8с, а после нее – только 4,2с.

                О_о

                Как же там все ужасно, похоже. 4,2с это не нормально, тем более для всего 25 юзеров.

                Имхо — готовые CMS для проектов сколько нибудь серьезного уровня использовать… ну не стоит, мне кажется. Да и дешевле может выйти, если нормально разрабатывать заточенное решение. Чем потом заказывать такие рефакторинги на полгода.
                  0
                  Когда клиент к нам пришел, решение уже было сделано на 1С-Битриксе, мы лишь помогли спасти проект. Но как следует из описания, проблема была не в CMS, и мы это успешно доказали финальным нагрузочным тестированием. Готовые CMS хороши с точки зрения преемственности кода, в таком случае смена программистов на проекте не будет критичной. Если вы разрабатываете полностью свое кастомное решение, уход носителя знаний с проекта будет очень критичен, потому что каждый новый человек будет начинать с фразы «давайте перепишем все с нуля».
                    +1
                    Все аргументы применимы для любого более менее популярного фреймворка.
                  +10
                  Странно, но для меня пол секунды на страницу — уже очень много. Кеш — это не способ исправить плохую архитектуру.
                    0
                    Все зависит от проекта. У нас часть данных подтягивалась в режиме реального времени из CRM клиента. Как Вы понимаете, это дает определенный прирост времени.
                      +1
                      Ахаха, перестаньте смешить. За такое «архитектору» надо по башке дать.
                        +1
                        Если не секрет, то ради каких данных в реальном времени опрашивать CRM?
                      +1
                      какой то перебор мне кажется в цифрах, что за 3000-4000 запросов, мне кажется столько mysql не сможет пережить если они выполняются на каждой странице.
                        0
                        Хорошо, что Вы не сталкивались в своей практике с подобными проектами, нам, к сожалению, не так повезло. 3000-4000 запросов к БД как раз и были критичными для данного проекта, когда мы замерили статистику после первичной стабилизации — было 600 со страницы. Проект начал работать, хоть и медленно.
                          0
                          Вот скажем возьмем маленькую нагрузку, зашло 100 юзеров это 400 000 запросов к бд, да мускул никогда такое держать не будет.
                            0
                            100 юзеров в секунду — это 360 000 в час или чуть больше 8,5 миллионов в сутки. Это очень много. У клиента было 20-25 в пик примерно.
                              0
                              Экстраполировать 100 юзеров в секунду до 8 млн. в сутки — некорректно. Распределение нагрузки всегда неравномерно, даже если проектом пользуются по всему миру из разных часовых поясов.
                          0
                          Зависит от самих запросов. Вот пример:

                          habrastorage.org/storage2/6d6/cef/3e3/6d6cef3e3445eedf2969d1e7ab9683b9.png

                          Это без кеша на каждой странице. С кешом — 1600-2000 запросов за пользователя, посещаемость — 200-300 человек в день. На сервере еще один нагруженный проект, но он не жалуется. Понятно, что 4к запросов с джойнами — совсем не то, что 4к без, но тем не менее — не надо все под одну гребенку, mysql способен на многое ;)
                          +2
                          Я правильно понимаю, что Заказчик получил расхода на битрикс, на интеграцию с 1С, на разработку сайта + работы по ускорению системы?
                          Эти расходы больше или меньше чем решения на фреймворках?
                            0
                            Это сравнимо. Решение в пользу рефакторинга было принято из-за клиентских данных, из-за интеграционных частей не на стороне сайта и из-за легкости перехода с одного ядра на другое. Если вы помните, то Утконос, чтобы перевести сайт на другую платформу, закрывался на неделю. Даунтайм нашего клиента при переходе составил всего 36 часов.
                              0
                              Перевод сайта с одной платформы на другую при сохранении связей с бэком (УТ или что там еще стоит) можно сделать за 5 секунд (без учета времени разработки системы на новой платформе.)
                              Интересно через какое время Заказчик упрется в очередной «потолок».
                                –1
                                К сожалению, у нас не было права выбора в данном случае. Мы рассчитывали, что на данных серверных мощностях ему хватит примерно на полтора-два года. После этого нужно будет масштабироваться по серверам.
                                  +2
                                  Все просто: Упрется в потолок — заплатит еще раз

                                  6 месяцев человекочасов — это же такая большая приятная куча денег
                                    0
                                    Cайт не был масштабируемым по серверам. Мы сделали его масштабируемым, вряд ли он сможет упереться в потолок.
                              +1
                              77 запросов в секунду. И что в этом такого?
                                +1
                                Не понимаю как после таких статей хоть кто-то еще пользуется битриксом
                                  –1
                                  На последовательное проведение всех перечисленных работ у нас ушло около 6 месяцев.
                                  Вот это прикол :)
                                  Внимание вопрос:
                                  Сколько надо времени чтобы сделать интернет магазин, если выкинуть битрикс и учесть что уже готово следующие: дизайн, верстка, всякие js и БД??

                                  это чё ж за магазин такой?
                                    0
                                    Интернет магазин с 8ю серверами и 300 уникальных посетителей в день?
                                    Что это за магазин такой, наркотиками что ли торгуют?
                                    Может ссылку хотя бы опубликовать на него?

                                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                    Самое читаемое