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 Технический выбор подбазы данных и подтаблицы
Технический выбор подбазы данных и подтаблицы в основном рассматривается со следующих направлений:
-
Клиентский SDK с открытым исходным кодом
-
Решение с открытым исходным кодом прокси промежуточного программного обеспечения
-
Самостоятельно разработанный фреймворк, предоставленный командой промежуточного программного обеспечения компании.
-
Колеса своими руками
После обращения к предыдущему опыту проекта и общения с командой промежуточного программного обеспечения компании мы приняли решение с открытым исходным кодом.Sharding-JDBCплан. Теперь переименован в Sharding-Sphere.
-
Документация: официальная документация относительно грубая, но онлайн-информация, анализ исходного кода и демонстрация богаче.
-
Сообщество: Активное
-
Особенности: Поставляется в пакете 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