Url
https://nsign.ru/blog/optimizatsiya-logiki-bitriks-python-flask
Name
Оптимизация логики Битрикс на Python/Flask: опыт миграции
Blog

Как мы оптимизировали логику Битрикс на Python/Flask и уложили ее в 1 МБ

 

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

Название заказчика раскрыть не можем — подписан NDA, так что просим воспринимать это как историю, из которой можно вынести полезный урок.

Что было не так с Битриксом

Исходная система поиска работала на Битриксе. Какое-то время это было терпимо, но когда количество товаров перевалило за несколько тысяч, начались серьёзные проблемы. Ситуацию усугубляла география: пользователю из Кемерово нужно было показывать товары с учётом его региона, а это требовало сложных выборок с множеством условий.

Главная архитектурная проблема Битрикса — единая таблица инфоблоков, в которой хранятся все сущности. Когда данных становится много, масштабирование упирается в конфликты ресурсов. Тяжёлые JOIN-ы к огромным таблицам при высоком трафике превращались в неконтролируемые тормоза. Система буквально разваливалась под нагрузкой, время отклика росло, серверы не справлялись.

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

Выбор нового стека

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

Остановились на Flask. Это легковесный микрофреймворк, который позволяет подключать только то, что действительно необходимо. Нужна авторизация? Берём Flask-Login. Понадобилась валидация форм? Добавляем WTForms. В результате получаем ровно тот функционал, который требуется, без лишнего балласта. Ключевое преимущество Flask — контроль над потреблением ресурсов, что критично для высоконагруженных систем. Плюс заказчик использовал сертифицированный софт с Python 3.6, и Flask отлично вписался в эти ограничения.

С базой данных решили не мудрить. У нас были жёстко структурированные связи: категории привязаны к регионам, регионы — к отраслям. Здесь идеально подходит реляционная СУБД. Выбрали MySQL — она уже была развёрнута на сервере под систему веб-аналитики Matomo, что упростило инфраструктуру.

Для работы с базой взяли ORM Peewee. Эта библиотека минималистична, хорошо дружит с Flask и Python 3.6, не создаёт проблем с многоуровневыми запросами, в отличие от громоздкой SQLAlchemy. С Peewee может работать даже начинающий разработчик — код получается простым и понятным.

Архитектурные решения для снижения нагрузки

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

Для заливки данных на продуктив применили жёсткий подход: перед обновлением старые таблицы полностью удалялись и создавались заново. Это гарантировало актуальность данных и исключало дублирование. Поскольку переключение пользователей на новую версию происходило уже после завершения миграции, проблема простоев нас не волновала — сервис оставался доступен.

Чтобы не перегружать базу тяжёлыми запросами, мы внедрили двухэтапную обработку. Для обычных запросов со списками продуктов использовали стандартную пагинацию. А для массовых выгрузок справочников применили другой подход: настроили крон-задачу, которая раз в час генерирует статический JSON-файл со всеми актуальными данными. Когда приходит запрос на полный набор, система просто отдаёт этот файл, не обращаясь к базе. Генерация файла занимает пару минут в фоновом режиме и никак не влияет на производительность основного сервиса. Нагрузка на базу данных снизилась в разы.

Отдельно разобрались с медиафайлами. Изображения часто дублировались в разных разделах. Мы внедрили механизм дедупликации на основе хеширования: каждый файл проверялся по SHA-256, и при обнаружении дубликата система не загружала его повторно, а сохраняла ссылку на существующий объект. В результате объём каталога с изображениями сократился с потенциальных 500 МБ до фактических 20 МБ.

Работа с legacy-данными: как переехать без потерь

Самый сложный этап — перенос унаследованных данных. Тысячи записей с нестандартными идентификаторами, битыми ссылками и структурами, которые не влезали в типовые поля.

Начали с ремаппинга регионов. В Битриксе идентификаторы генерировались произвольно: например, Москва могла иметь id 7145, а по федеральному стандарту нужен код 77. Для интеграции с внешними API требовалось привести всё к единому виду. Мы составили карту соответствий и скриптами заменили все ссылки во всех связанных таблицах.

Следом — справочники. Выгрузили битриксовские справочники, провели тотальное сравнение с внешними и выполнили перенумерацию. Тут нас ждали сюрпризы: уровень детализации не совпадал кардинально. Например, в битриксовском справочнике «Торговля» содержалось 60 подотраслей, а во внешней информационной системе — всего 30. Что с чем связывать, чем жертвовать? Каждый такой случай приходилось обсуждать с заказчиком и принимать решение вручную.

Дальше — JSON-структуры продуктов. Изначально мы использовали стандартное поле с лимитом 65 килобайт. По ходу работы выяснилось, что у части продуктов описание с историей и документами достигает 1 мегабайта. При сохранении данные обрезались, структура JSON ломалась, и на фронтенде возникали ошибки. Решение нашлось быстро: сменили тип поля на MEDIUMTEXT, который вмещает до 16 мегабайт. После доработки все 4000 продуктов загружались без потерь.

Отдельно продумали историю изменений. Мы не стали повторять битриксовский подход с десятками связанных таблиц — это дорого и тяжело для JOIN-ов. Вместо этого создали одну таблицу history, куда каждое опубликованное изменение продукта падает полным JSON-слепком его состояния. Черновые правки при этом вносятся напрямую в основные таблицы. По внутренним регламентам заказчика каждые три месяца требовалось подтверждать актуальность продукта. Если реальных изменений не было, в историю новая запись не добавлялась — просто корректировались даты в существующей. Выборка истории теперь занимает менее 50 миллисекунд даже для продуктов с длинной биографией. Важный момент: мы сохранили ID продуктов неизменными, чтобы ссылки из поисковых систем и на внутренних ресурсах продолжали работать.

Интеграция с внешними системами

Аутентификацию не стали изобретать с нуля. Вместо полноценной реализации OAuth2 мы подключились к существующей инфраструктуре заказчика через LDAP/Active Directory. Неавторизованный пользователь перенаправляется на корпоративную страницу входа, а после успешной аутентификации в куках браузера появляется JWT-токен со структурированными данными. Для верификации система использует открытый ключ. Поскольку все сервисы работают в едином домене, куки авторизации доступны всем компонентам без дополнительных настроек.

Для обмена данными с внешними сервисами использовали RabbitMQ. Мы не стали разворачивать собственную инфраструктуру очередей — заказчик предоставил доступ к своим. Наша задача сводилась к тому, чтобы отправлять сообщения по спецификации: указывать получателя и текст. Дальнейшая обработка — рассылка SMS, email или внутренние оповещения — происходила уже на стороне заказчика. Логика на бэкенде получилась предельно простой: получили запрос, сформировали ответ, отправили в RabbitMQ. Каждую ночь мы аналогично отправляли уведомления о продуктах: администраторам — напоминания об актуализации, подписчикам — об изменениях. Такой подход избавил нас от необходимости поддерживать сложную инфраструктуру и позволил сосредоточиться на бизнес-логике.

Безопасность отдали на откуп корпоративному WAF — межсетевому экрану уровня веб-приложений, который выступал как внешний шлюз. Для нас он был «чёрным ящиком», но через него мы получили базовый уровень защиты от SQL-инъекций и XSS-атак без дополнительной головной боли. Время отклика осталось в пределах комфортных 2-3 секунд с учётом прохождения через WAF. Мониторинг доступности также встроили в подсистему заказчика, дополнив её скриптом, который проверяет состояние наших компонентов и отдаёт статус в формате «ПОДСИСТЕМА: OK/Error».

Результаты миграции в цифрах

Производительность: на тестировании система стабильно обрабатывает 100 запросов в секунду при типичной нагрузке. Половина запросов — просмотр продуктов, четверть — поиск по тексту, ещё четверть — фильтрация по связям базы данных. И это всего на 4 процессорных ядрах и 8 гигабайтах оперативной памяти. Для сравнения: аналогичная нагрузка в прежней инфраструктуре на Битриксе приводила к падению сервиса. Во время приёмочных испытаний мы провели стресс-тест: 100 потоков непрерывно открывали случайные страницы в течение нескольких часов. Система выдержала и сохранила отзывчивость.

Компактность кода порадовала отдельно. Весь бэкенд на Flask, миграторы и интеграции заняли около 1 мегабайта, а после упаковки в дистрибутив — 5 мегабайт. Когда добавились дизайн и шрифты, распакованный проект потянул на 10 мегабайт. Детализация: модели для работы с базой — 50 килобайт, контроллеры — 150 килобайт, мигратор данных — 19 килобайт, шаблоны и фронтенд — 0,5 мегабайта шаблонов плюс 9 мегабайт css стилей, js скриптов фронтенда и шрифтов. Такой проект можно передать другой команде без инструкции — разберутся за час.

Финансовый эффект: мы отказались от лицензий Битрикса и сократили затраты на хостинг за счёт более эффективного использования ресурсов.

Итоги и выводы

Минимализм в коде снижает риски ошибок. Наши модели данных укладывались в 3-20 строк на Python — этого оказалось достаточно для всех бизнес-задач.Flask позволяет собирать систему как конструктор, подключая только нужные компоненты и не тащить балласт неиспользуемых модулей. Витрина REST для массовых запросов — простое и эффективное решение. Отказ от генерации JSON в пользу статических файлов снизил нагрузку на базу данных в 10 раз.

Для полнотекстового поиска мы взяли Sphinx — он работает как внешний сервис с асинхронным обновлением индексов. Flask только отправляет запросы и получает ID нужных записей. Скорость поиска — 20 миллисекунд даже на больших объёмах текстов.

Заключение

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

В ЭНСАЙН мы умеем работать с legacy-системами любого уровня сложности — поддерживать, развивать и, если нужно, полностью переводить на современный стек. С нами вы получаете управляемый и предсказуемый результат. Узнайте больше о нашей экспертизе в области поддержки и миграции цифровых решений.