vivo Global Mall: дизайн и практика архитектуры центра заказов

Архитектура

1. Предпосылки

С быстрым ростом пользователей монолитная архитектура vivo Official Mall v1.0 постепенно обнажила свои недостатки: модули становятся все более и более раздутыми, эффективность разработки низкая, производительность узкие места, обслуживание системы затруднено.

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

Модуль заказа является транзакционным ядром системы электронной коммерции.Накапливаемые данные скоро достигнут узкого места однотабличного хранилища.Система не может поддерживать трафик во время выпуска новых продуктов и во время рекламных акций.Сервисно-ориентированная трансформация. является обязательным.

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

2. Архитектура системы

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

Архитектура системы показана на следующем рисунке:

3. Технические проблемы

3.1 Объем данных и проблемы с высоким параллелизмом

Первая проблема связана с системой хранения:

  • проблема с объемом данных

    С накоплением исторических заказов объем данных в таблице заказов в MySQL достиг десятков миллионов.

    Мы знаем, что структура хранения подсистемы хранения InnoDB представляет собой дерево B+, а сложность времени поиска — O(log n), поэтому при увеличении общего объема данных n скорость поиска неизбежно замедляется. добавить индексы или оптимизировать, это не решить.Только найти способ уменьшить количество данных в одной таблице.

    Решения с большими объемами данных включают в себя:Архивация данных, подтаблица

  • проблема высокого параллелизма

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

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

    Решения с высокой степенью параллелизма включают:Использовать кеш, разделение чтения-записи, подбиблиотеку

Эти схемы кратко описаны ниже:

  • архивирование данных

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

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

  • использовать кеш

    Использование Redis в качестве внешнего кеша MySQL может заблокировать большинство запросов запросов и уменьшить задержку ответа.

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

  • разделение чтения-записи

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

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

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

  • Филиальная библиотека

    Подбиблиотека также включает в себя вертикальную подбиблиотеку и горизонтальную подбиблиотеку.

    **① Горизонтальная подбаза данных: ** Данные одной и той же таблицы распределяются по разным базам данных в соответствии с определенными правилами, и каждая база данных может быть размещена на другом сервере.

    **② Вертикальная подбаза данных: **Классифицируйте таблицы в соответствии с бизнесом и распределяйте их по разным базам данных. Каждая база данных может быть размещена на другом сервере. Его основная концепция заключается в том, что специальная база данных является выделенной.

  • подтаблица

    Подтаблицы включают вертикальные подтаблицы и горизонтальные подтаблицы.

    **Горизонтальные подтаблицы: ** В одной базе данных данные одной таблицы разбиваются на несколько таблиц по определенным правилам.

    **Вертикальное разделение таблицы: ** Разделите таблицу на несколько таблиц в соответствии с полями, и каждая таблица хранит часть полей.

Мы всесторонне рассмотрели стоимость, эффект и влияние на существующий бизнес и решили использовать последнее средство напрямую:Подбиблиотека и подтаблица

3.2 Технический выбор подбазы данных и подтаблицы

Технический выбор подбазы данных и подтаблицы в основном рассматривается со следующих направлений:

  1. Клиентский SDK с открытым исходным кодом

  2. Решение с открытым исходным кодом прокси промежуточного программного обеспечения

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

  4. Колеса своими руками

После обращения к предыдущему опыту проекта и общения с командой промежуточного программного обеспечения компании мы приняли решение с открытым исходным кодом.Sharding-JDBCплан. Теперь переименован в Sharding-Sphere.

  • Гитхаб:GitHub.com/Killing-Ticketing…

  • Документация: официальная документация относительно грубая, но онлайн-информация, анализ исходного кода и демонстрация богаче.

  • Сообщество: Активное

  • Особенности: Поставляется в пакете jar, относится к сегментированию на стороне клиента, поддерживает транзакции xa.

3.2.1 Стратегия подбазы данных и подтаблицы

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

Тогда метод расчета номера таблицы библиотеки:

- Серийный номер библиотеки: Hash(userId) / m % n

- Номер таблицы: Hash(userId) % m

Процесс маршрутизации показан на следующем рисунке:

3.2.2 Ограничения и контрмеры подбазы данных и подтаблицы

Подбаза данных и подтаблица решают проблему объема данных и параллелизма, но это значительно ограничивает возможности запросов к базе данных.До этого были некоторые простые корреляционные запросы, которые могут быть не реализованы после подбазы данных и подтаблицы, поэтому его нужно проверять отдельно.Они переписаны для SQL, который Sharding-JDBC не поддерживает.

В дополнение к этому столкнулись с такими проблемами:

(1) Глобальный уникальный дизайн идентификатора

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

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

(2) Исторический порядковый номер не содержит неявной информации о библиотечной таблице.

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

(3) Фон управления должен разбивать на страницы и запрашивать все заказы, которые соответствуют условиям в соответствии с различными условиями фильтрации.

Резервно хранить данные о заказах в поисковой системе Elasticsearch только для фоновых запросов.

3.3 Как синхронизировать данные из MySQL в ES

Как было сказано выше, чтобы управлять запросом в фоновом режиме, мы избыточно храним данные заказа в Elasticsearch.Тогда как синхронизировать данные заказа в ES после изменения данных заказа MySQL?

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

  • Схема MQ

    Служба обновления ES действует как потребитель и обновляет ES после получения MQ-сообщения об изменении заказа.

  • Бинлог решение

    С помощью проектов с открытым исходным кодом, таких как canal, служба обновления ES маскируется под подчиненный узел MySQL, получает Binlog и анализирует его для получения информации об изменении данных в реальном времени, а затем обновляет ES в соответствии с этой информацией об изменении.

Среди них решение BinLog является более общим, но и более сложным в реализации.В итоге мы выбрали решение MQ.

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

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

3.4 Как безопасно заменить базу данных

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

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

Мы рассмотрели два сценария простой миграции и миграции без простоя:

(1) План непрерывной миграции:

  • Скопируйте данные старой библиотеки в новую библиотеку, запустите программу синхронизации и используйте Binlog и другие решения для синхронизации данных старой библиотеки с новой библиотекой в ​​режиме реального времени.

  • Онлайн-заказ новой и старой библиотеки с двойной записью, только чтение и запись старой библиотеки.

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

  • Постепенно переключайте запросы на чтение в новую библиотеку.

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

  • Автономная старая библиотека, автономная функция двойной записи заказа, автономная программа синхронизации и программа компенсации сравнения.

(2) План миграции во время простоя:

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

  • Остановите приложение торгового центра V1, чтобы убедиться, что данные старой базы данных не изменились.

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

  • Запустите приложение торгового центра V2, начните тестирование и проверку и вернитесь к приложению торгового центра V1 в случае сбоя (в новой системе заказов есть переключатель для двойной записи старой библиотеки).

Принимая во внимание, что стоимость преобразования безостановочного решения высока, а бизнес-потери от решения с ночным отключением невелики, в конечном итоге было выбрано решение миграции с отключением.

3.5 Проблемы с распределенными транзакциями

В процессе транзакций электронной коммерции распределенные транзакции являются классической проблемой, например:

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

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

Как обеспечить согласованность данных в микросервисной архитектуре?

Различные бизнес-сценарии имеют разные требования к согласованности данных.Среди основных решений в отрасли есть двухэтапная фиксация (2PC) и трехэтапная фиксация (3PC) для обеспечения строгой согласованности, а также TCC, локальное сообщение, транзакционные сообщения и лучшие решения. -уведомления об усилиях и т. д.

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

На следующем рисунке показан пример уведомления системы начисления баллов о раздаче баллов после выполнения заказа.

3.6 Безопасность и стабильность системы

  • сетевая изоляция

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

  • одновременная блокировка

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

  • идемпотентность

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

  • предохранитель

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

  • Мониторинг и оповещение

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

3.7 Ступенчатые ямы

Связанные с заказом данные базы данных синхронизируются с ES посредством потребления MQ, а обнаруженные записанные данные не являются последними данными заказа.

Левая часть изображения ниже представляет собой первоначальный план:

При использовании MQ с синхронизацией данных заказа, если поток A выполняется первым и узнает данные, то данные заказа обновляются, поток B начинает выполнять операцию синхронизации, и после обнаружения данных заказа они записываются в ES на один шаг перед потоком A. Когда поток A выполняет запись, он перезапишет данные, записанные потоком B, в результате чего данные заказа в ES не будут актуальными.

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

sharding-jdbc сортировка и поиск по страницам запрашивают все проблемы с данными после группировки

Пример: выберите a из временной группы с помощью порядка a, b по пределу описания 1,10.

Выполнение заключается в том, что группировка и порядок полей в Sharding-jdbc несовместимы с порядком, а 10 установлено в Integer.MAX_VALUE, что приводит к сбою запроса на подкачку.

io.shardingsphere.core.routing.router.sharding.ParsingSQLRouter#processLimit

private void processLimit(final List<Object> parameters, final SelectStatement selectStatement, final boolean isSingleRouting) {
     boolean isNeedFetchAll = (!selectStatement.getGroupByItems().isEmpty() || !selectStatement.getAggregationSelectItems().isEmpty()) && !selectStatement.isSameGroupByAndOrderByItems();
    selectStatement.getLimit().processParameters(parameters, isNeedFetchAll, databaseType, isSingleRouting);
}

io.shardingsphere.core.parsing.parser.context.limit.Limit#processParameters

/**
* Fill parameters for rewrite limit.
*
* @param parameters parameters
* @param isFetchAll is fetch all data or not
* @param databaseType database type
* @param isSingleRouting is single routing or not
*/
public void processParameters(final List<Object> parameters, final boolean isFetchAll, final DatabaseType databaseType, final boolean isSingleRouting) {
    fill(parameters);
    rewrite(parameters, isFetchAll, databaseType, isSingleRouting);
}


private void rewrite(final List<Object> parameters, final boolean isFetchAll, final DatabaseType databaseType, final boolean isSingleRouting) {
    int rewriteOffset = 0;
    int rewriteRowCount;
    if (isFetchAll) {
        rewriteRowCount = Integer.MAX_VALUE;
    } else if (isNeedRewriteRowCount(databaseType) && !isSingleRouting) {
         rewriteRowCount = null == rowCount ? -1 : getOffsetValue() + rowCount.getValue();
    } else {
       rewriteRowCount = rowCount.getValue();
    }
    if (null != offset && offset.getIndex() > -1 && !isSingleRouting) {
       parameters.set(offset.getIndex(), rewriteOffset);
     }
     if (null != rowCount && rowCount.getIndex() > -1) {
        parameters.set(rowCount.getIndex(), rewriteRowCount);
      }
}

Правильный способ написания должен быть select a from temp group by a desc , b limit 1,10; используется версия 3.1.1 sharing-jdbc.

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

4. Результаты

  • Разовый успешный запуск, стабильная работа более года

  • Производительность основных сервисов повышена более чем в десять раз

  • Разделение системы значительно повышает эффективность итераций

  • Может поддерживать быстрое развитие торгового центра в течение как минимум пяти лет

V. Заключение

Мы не следовали слепо передовым технологиям и идеям в проектировании систем и не внедряли напрямую основные решения для электронной коммерции, когда сталкивались с проблемами, а выбирали наиболее подходящий метод в соответствии с реальной деловой ситуацией.

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

Автор: команда разработчиков торгового центра официального сайта vivo