Привет! Меня зовут Максим Панфилов, я ИТ-предприниматель. Мой основной бизнес – студия разработки, которой больше 10 лет. Мы специализируемся на создании ИТ-проектов для бизнеса из ниши стройматериалов, товаров для дома и инженерных систем.
В начале мы делали сайты на 1С-Битрикс. Сейчас в команде по-прежнему сохранилась эта компетенция, но в основном мы разрабатываем веб-приложения с подходом Sinlge Page Application: на беке Symfony или Node.js, на фронте – Vue / Nuxt.
К сожалению, у клиентов на рынке по-прежнему большой спрос на разработку на 1С-Битрикс как CMS. От крупных компаний к нам приходят запросы с обязательным требованием по использованию Битрикса. Условия сотрудничества хорошие, поэтому мы соглашаемся.
Однако на стороне клиентов всё чаще понимают ограничения Битрикса и соглашаются на то, чтобы расширять его другими технологиями. Один из таких проектов – сайт крупнейшего производителя кабеленесущих систем с каталогом продукции.
В основе проекта заложен Битрикс, но для выборки блока с аналогами и аксессуарами в карточке товара стандартных возможностей CMS было недостаточно.
Поэтому для реализации этой подборки мы выделили агрегацию данных в отдельный сервис на Symfony + MongoDB.
В этой статье расскажу, как это было и с какими сложностями мы столкнулись.
Фильтрация и агрегация большой базы данных
На проекте в базе товаров больше 80 тыс. товаров, и дальше планируется увеличение до 250 тыс.
У товаров есть набор различных характеристик, причём они разного вида:
есть базовый справочник общих характеристик,
могут дополняться свои характеристики для группы товаров.
Всего сейчас в таблице БД больше 1 миллиона записей связок «товар — характеристика».
Наш опыт разработки на похожих проектах показал, что сложные операции фильтрации и агрегации данных на Битриксе могут оказаться неэффективными. Нужно задействовать дополнительные решения.
Мы уже успешно разрабатывали похожие функции на другом проекте, который был написан на Symfony с базой MongoDB. Там мы реализовали сложную фильтрацию, подборку «Аналогов и Аксессуаров», сборку комплектов и не только.
Чтобы использовать ценный опыт, для нового проекта мы решили сделать отдельный сервис, который возьмет на себя все заботы по фильтрации и агрегации данных и снимет нагрузку с Битрикса.
Для реализации сервиса выбрали Symfony с базой данных MongoDB и придумали схему взаимодействия с Битриксом.
Данные из Битрикса передаются через очередь RabbitMQ, тогда как запросы на фильтрацию и агрегацию отправляются по HTTP внутри локальной сети.
Средства взаимодействия Битрикса и сервиса успешно прошли проверку. Проблем с передачей больших объемов данных и скоростью обработки запросов не возникло.
Но работа с данными из основной системы Битрикса оказалась сложнее, чем мы предполагали.
Мы воспринимали наш сервис как независимый элемент, что привело к недостаточной проработке вопросов хранения данных на стороне Битрикса и вызвало ряд сложностей.
Обмен данными
Мы знали, что подборки «Аналогов и Аксессуаров» станут лишь первым шагом, и в дальнейшем планируется расширение сервиса для фильтрации товаров. Поэтому мы изначально спроектировали обмен данными таким образом, чтобы учесть возможности фильтрации в будущем.
Система хранения данных на стороне Битрикса была разработана до внедрения сервиса и не предусматривала необходимости обмена данными.
Первоначально мы полагали, что оптимальным решением будет оставить схему хранения и обработки данных «Аналогов и Аксессуаров» на стороне Битрикса без изменений.
Первое пробное предложение для Битрикса по получению данных было слишком оптимистичным.
Мы хотели упростить обработку на стороне сервиса, поэтому попросили присылать нам денормализованные данные. Например, в параметрах категории мы хотели сразу получить свойства, которые доступны для фильтрации в ней.
Но все наши попытки как-то упростить или изменить схему приводили к тому, что на стороне Битрикса требовалось вносить серьёзные изменения или строить фасетный фильтр, а мы хотели этого избежать.
В итоге отказались от денормализации в данных и пришли к универсальному виду сообщения для отправки в очередь.
Параметры сообщения в очередь:
timestamp - string, метка времени создания сообщения
entity - string, сущность с которой связанно событие
action - string, тип события
message - string, данные события
Вот так это выглядит в JSON:
{
"timestamp": "1720771159",
"entity": "b_iblock_section",
"action": "update",
"message": {}
}
Обработка очереди на стороне сервиса
Из нашего опыта и общих правил работы с RabbitMQ мы вынесли важный момент: RabbitMQ не любит, когда в очереди накапливается много сообщений.
При накоплении большого количества сообщений, RabbitMQ потребляет много памяти, упирается в лимиты и умирает.
Здесь простое решение — быстро обрабатывать то, что пришло в очередь. Спасибо, Кэп :)
Но мы помним, что не меняли обработку данных и хранение на стороне Битрикса, поэтому не можем быть уверены в содержимом сообщений. Нам нужно не просто быстро достать данные из очереди, но и проверить их перед сохранением в БД.
Мы организовали промежуточное хранилище и отдельный документ с отдельной таблицей в реляционной БД.
Так мы:
быстро очищали очередь. При сохранении в промежуточное хранилище мы опускаем сложные обработки и преобразования, которые нужны для сохранения в БД.
упростили обработку данных. Само хранилище организовано так, что при сохранении происходит отсеивание ошибочных данных и возможных дублей.
настроили приоритеты. Данные могут приходить разрозненно, например на момент обработки свойств, у нас может не быть товара, для которого эти свойства пришли. Поэтому мы сначала обрабатываем базовые данные, которые нужны для связей в других.
Такая схема усложнила разработку, но полностью себя окупила — в разы упростились этапы тестирования и отладки сервиса.
Опасения, что обработка будет медленной, не оправдались. Если целостность данных не нарушена, все обрабатывалось очень быстро. Но если всё- таки происходило нарушение целостности данных в Битрикс, это замедляло процесс, но не останавливало его.
Хранение данных на стороне сервиса
Это был самый трудный и важный этап проектирования. Весь успех зависел от схемы хранения данные в MongoDB. Ошибки на этом этапе отражались бы на всём функционале сервиса.
Одна из сильных сторон MongoDB — денормализация данных. Например, в данных товара можно сложить все его свойства в виде вложенной конструкции. Одним запросом фильтровать товары, получать список их свойств со значениями для выбранной локали.
Перед нами стояла задача — преобразовать данные из Битрикса в удобную схему для фильтрации и выборок.
Например, вот какие правила выборки были для «Аналогов» или «Аксессуаров»:
Активность разделов. Мы не должны отдавать товар, если он лежит в неактивном разделе, или неактивный один из родительских разделов.
Локаль (языковая версия сайта) раздела. Мы не должны отдавать товар, если он лежит в разделе, который не доступен для выбранной локали.
Активность товаров. Мы должны отдавать только активные товары-аналоги.
Локаль товара. Мы должны отдавать товары-аналоги, которые доступны для выбранной локали.
Пагинация.
«Аналогом» или «Аксессуаром» может быть не только товар, но и целый раздел товаров.
Эти правила работают и для формирования списка разделов «Аналогов» или «Аксессуаров», и для списка самих «Аналогов» или «Аксессуаров».
Разработчики могут представить себе все сложности, которые придётся пережить на реляционной базе. Но для MongoDB с её подходами к выборке данных это простая задача.
Схема отлично показала себя при дальнейшей реализации фильтрации и агрегации данных:
Сложности в разработке
Для разработки сервиса мы использовали последние версии PHP и Symfony со всеми их преимуществами. Например, в Symfony расширили возможности встроенного профайлера, он очень помог при отладке запросов к БД.
Сильно упрощал работу наш большой опыт с MongoDB.
На стороне Битрикса основной проблемой было хранение и обновление данных по «Аналогам» и «Аксессуарам». Поначалу решили работать с тем, что есть.
В итоге потеряли целостность: у данных в Битриксе менялись id при обработке новой выгрузки. Это усложнило процесс обработки данных в сервисе, нужно было исключать логические дубли данных. Страдала хронология отправки данных. Сначала могло прийти удаление, а потом создание этих же данных.
На стороне Битрикса появилась проблема при обработке файлов выгрузки с большим количеством данных с «Аналогами» или «Аксессуарами». И во многом причина в том, что функционал не рассчитывался на обмен данных с очередью.
Корень проблемы — мы проектировали системы отдельно, делали упор только на общую схему обмена данных, но это был не совсем верный подход. Как это разрешили?
Мы помним, что сервис является неотъемлемой частью всей системы и подчиняется Битриксу. Битрикс, в этой системе:
источник данных,
инициатор передачи данных,
инициатор запроса агрегации,
выполняющий компенсирующие действия, если что-то пошло не так.
Разработка на стороне Битрикса должна была учитывать наличие сервиса, который берет на себя часть функций. При этом Битрикс оставался управляющей системой, которая отвечала за надежность и целостность данных.
Мы пересмотрели процесс обработки данных на стороне Битрикса. Главная система стала следить за целостностью данных и наличием дублей. Мы решили вопросы с загрузкой больших файлов, разделили обработку выгрузки и отправку данных в очередь.
Сервису стало проще обрабатывать данные. Это положительно сказалось на скорости обработки.
Итоги
Мы получили ценный опыт в проектировании сервиса, который расширяет функции основной системы.
Связка Битрикс и Symfony + MongoDB может приносить хороший результат. Запросы в локальной сети ходят быстро. MongoDB легко справляется с большими объемами данных.
Есть и альтернативные варианты решения задачи: можно было использовать связку Битрикс + Sphinx или Elasticsearch, и мы работали с таким системами.
Но в нашем случае Symfony + MongoDB — дешевле в разработке за счёт использования большого опыта и знаний.
Реализация прозрачна, легко расширяема и имеет большой потенциал для развития.
И это только начало для проекта. В сервисе уже готовится фасетный фильтр товаров, для которого мы планируем использовать надстройку из Symfony и MongoDB над CMS 1С-Битрикс.
Если у вас были похожие задачи совмещения Битрикса с другими технологиями, поделитесь в комментариях, как вы их решили :)
Комментарии (9)
Zivaka
03.12.2024 11:48По интернет-магазинам только с DIY работаете сферой? Есть похожая задача по Битриксу и новым технологиям, как раз в поисках исполнителя)
mihacoder
03.12.2024 11:48Миллион записей - это как бы немного. У нас 100 млн, обходимся просто postgresql.
mpanfilov Автор
03.12.2024 11:48Точно, объемы данных сами по себе – не проблема для реляционок. Подскажите, был ли у вас опыт разработки аналогичного сервиса фильтрации и подборок на PostgreSQL? Если да, то как делали, с помощью Elasticsearch или по-другому?
Mausglov
И дальше показана схема, для реализации которой в РСУБД я не вижу никаких сложностей.
Вы сами не делитесь ( в статье только общие слова), но ожидаете, что делиться будут с Вами?
mpanfilov Автор
Привет! Спасибо за внимание к нашей публикации!
Мы не говорим, что это не возможно. Мы только рассказали про то, как использовали свой опыт не в РСУБД для упрощения этой конкретной задачи.
В статье постарались рассказать о том, как решили сложности совмещения Битрикса с другими технологиями для решения локальной задачи – постарались рассказать верхнеуровнево об архитектуре решения.
Подскажите, чего вам именно не хватило в статье, чтобы вы посчитали, что мы поделились своим опытом? Нам важно, чтобы учесть это в будущих публикациях.