Один из наших длительных проектов - это крупное многопользовательское SaaS-решение (CRM-система) основанное на микросервисной архитектуре и развернутое в облаке Azure. Изначально это был MVP, где все части (сервисы, базы данных и т. д.) располагались на одной виртуальной машине. Со временем проект вырос в облачное распределенное решение с множеством веб- и мобильных клиентов. 

В этой статье мы расскажем, как решили одну из проблем, с которой столкнулись в процессе разработки.

Содержание

Проблема

На тот момент проект включал около 12-14 сервисов, каждый из которых имел собственную базу данных в Azure SQL. Однако большинство сложных и критичных поисков осуществлялось по основной базе данных, в которой хранилось огромное количество информации о кандидатах (принятых, отклонённых, с которыми ведётся работа) по всем арендаторам.

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

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

  • Поддержка импорта большого количества кандидатов из внешних систем.  

  • Интеграция с несколькими внешними источниками кандидатов (данные из которых автоматически подтягивались в основную БД) 

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

  • Необходимость расширения поиска для включения фильтрации на основе данных в другой БД (история взаимодействия).  

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

Анализ и Решение

Сервис Search Engine

Решением стала разработка Search Engine - отдельного сервиса для эффективной реализации поиска.   

Первым шагом было решено вынести функционал поиска в новый сервис, развернутый в собственном Azure App Service Plan. Это позволило выделить больше памяти и процессорных ресурсов и масштабировать их независимо. Важно, что App Service Plan также поддерживает масштабирование и переменное количество экземпляров сервиса, что положительно сказалось на надёжности и производительности критически важных функций.

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

Источник данных

Другой сложной задачей было избежать снижения производительности поиска при массовых обновлениях в основной БД. Мы рассмотрели несколько подходов:  

  1. Elasticsearch  

  2. Azure SQL с SQL Data Sync

  3. High Availability Read-only Replica (Высокодоступная реплика только для чтения)

  4. Geo-replication Azure SQL 

Elasticsearch

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

Основная проблема заключалась в том, что наша база данных допускала и поддерживала единичные и массовые обновления данных и кастомизацию. Это означало, что для внесения изменений потребовалось бы переписывать большие объемы данных в Elasticsearch и часто перестраивать индексы.  

Кроме того, это означало бы, что нам придется разрабатывать и поддерживать мост данных между двумя базами данных.

SQL Data Sync в Azure

Идея синхронизации баз данных выглядела перспективно, но инструмент имеет ограничения и не поддерживает некоторые типы данных. Поэтому мы были вынуждены отказаться от него.

High Availability Read-only Replica

Рекомендуемый подход от Microsoft - использовать архитектуру High Availability с первичной БД для чтения и записи, и одной или несколькими вторичными репликами только для чтения.   

Но эта функция поддерживается только на уровнях Premium и Business Critical в Azure SQL, что увеличило бы расходы. Поэтому для нашего клиента это решение не подошло. 

Geo-replication Azure DB

Geo-replication - оказалось единственным подходящим для нас решением. Функция Geo-replication позволила реализовать подход read-scale out, зарезервировав дополнительный Azure SQL Server на котором была развернута Geo-реплика основной базы данных.

Внешний источник данных

Еще одной задачей была необходимость поддержки кросс-запросов к базе данных. Поисковые запросы могли содержать условия, направленные к данным в основной БД, но также и дополнительные условия, которые должны были быть адресованы другой базе (в нашем случае БД с историей взаимодействия).

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

В качестве решения задачи мы использовали функции внешних источников данных и Cross-DB запросов. Но поскольку мы стали обращаться с поисковыми запросами к новому сервису, который работает с репликой, то было решено реализовать следующий подход:  

  1. Мы настроили новый Azure SQL Server с репликами обеих баз данных.  

  2. Добавили ссылку на реплику БД с историей взаимодействия в основную БД (не в реплику, а в мастер копию доступную для записи и чтения).

  3. В результате в реплике основной БД появилась ссылка на реплику БД с историей взаимодействия.

Настройка нового Azure SQL Server с репликами обеих баз данных позволило адресовать все условия поиска в одну Базу Данных, которая использовала перекрестные запросы ко второй реплике, не привлекая к поиску ни одну из исходных баз данных.

Результаты

Реализованный подход позволил:

  • Разделить запросы на чтение и запись между двумя базами данных. Read-only реплики можно настраивать и масштабировать отдельно и, соответственно, не обязательно было выделять им много ресурсов. 

  • Избежать затрат времени на создание и интеграцию отдельного кластера (Elastic).  

  • Удержать расходы клиента на облачную инфраструктуру на приемлемом уровне.  

  • Значительно повысить эффективность, стабильность и надёжность критически важной функции поиска.  

API и реализация поиска при этом остались прозрачной для всей команды разработчиков, что также немаловажно для поддержания высокого уровня качества и скорости процесса разработки.

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

Комментарии (0)