Практика подстолового проекта системы заказов Хуцзян

MySQL

Текст: Хэ Цзянхуа

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

задний план

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

Анализ ситуации

Статус системы заказов в основном из следующего описания нескольких измерений:

Таксономия

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

  • Периферийные системы: интерфейсная расчетная страница, клиринговая и расчетная система, система BI-анализа, CRM-система, система управления бизнесом. За исключением того, что система анализа регулярно экспортирует данные заказов в центр обработки данных, другие системы обрабатывают данные заказов через службу заказов.

Классификация данных

По частоте генерации и модификации заказов мы разделяем данные заказов на две категории:

  • Горячие данные, частота изменений в течение 2 месяцев после успешного размещения заказа высока, например: статус оплаты, статус доставки, адрес доставки и т. д.

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

Система системы и данных

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

  • Горячий доступ к данным, включая систему размещения заказов, систему изменения заказов и доступ к системе распределения.

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

параллелизм

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

Выбор схемы

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

Технология разделения таблиц MySQL

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

  • Выбор правил раздела, MySQL предоставляет диапазон, список, HASH и другие методы. Различные правила будут влиять на эффективность запроса данных.

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

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

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

горизонтальная подтаблица

Запись и запрос данных распределены по нескольким таблицам, и преимущества очевидны:

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

  • Сильная управляемость.

В то же время он оказывает большое влияние на существующую систему:

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

  • Все подсистемы обслуживания заказов должны быть обновлены.

  • Требуется время простоя для публикации.

Разделение горячих и холодных данных

Холодные данные и термические данные могут храниться отдельно в зависимости от характеристик заказа горячих и горячих данных заказа и связанной системы заказа. Это решение имеет следующие преимущества:

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

  • Количество холодных баз данных можно контролировать.

  • Не требуется время простоя.

Есть и некоторые недостатки:

  • Емкость таблицы базы данных истории слишком велика.

  • В случае высокой параллелизма существует единственное узкое место.

  • Активность библиотеки данных неконтролируемы.

Разделение холодных и горячих данных + горизонтальная подтаблица холодных данных

С помощью схемы 2 + схемы 3 он может не только решить проблему чрезмерной емкости одной таблицы, но и соответствовать текущему развитию бизнеса. Прежде всего, разделение данных уменьшает объем данных одной таблицы в активной базе данных.Согласно текущему системному параллелизму, емкость одной таблицы активной базы данных может поддерживаться в разумных пределах. В то же время база данных, в которой находятся холодные данные, выполняет операции горизонтального разделения таблиц, что может гарантировать, что емкость одной таблицы находится в контролируемом диапазоне.

Запрос

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

  • Добавлена ​​глобальная таблица, но возникает проблема слишком большой отдельной таблицы.

  • Поисковая система с высокой эффективностью запросов и горизонтальной масштабируемостью. Команда инфраструктуры компании предоставила стабильную и эффективную службу поиска (Elasticsearch), которая может напрямую подключаться для решения проблем с запросами.

Архитектура хранения данных

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

  • Библиотека действий хранит данные, сгенерированные в режиме реального времени.

  • Исторический репозиторий хранит данные двухмесячной давности.

  • Полное хранилище хранит все данные.

  • ES хранит данные об индексе ордеров в базе данных истории.

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

После этого преобразования становится структура, показанная ниже:

выполнение программы

В процессе реализации основное внимание уделяется следующим аспектам:

Холодная миграция данных и горизонтальная сегментация

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

  • Мигрируйте последовательно в соответствии с определением холодных данных. Например, заказ 2 месяца назад относится к холодным данным, заказ сортируется по времени создания, заказ мигрирует первым.

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

  • Процесс миграции не отвечает за создание данных индекса для холодных данных.

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

  • Данные одной таблицы являются управляемыми.

  • Холодные данные не имеют одновременного давления записи.

  • Его легко поддерживать, а сегментированные данные не меняются при добавлении подтаблицы.

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

  • Диапазон номеров заказов разделен, например, 1~10000 находится в таблице tb_order_0001, а 10001~20000 – в таблице tb_order_0002.

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

Создать индекс холодных данных

Создание индекса библиотеки истории создается двумя способами:

  • Поисковая система полностью загружена и используется на этапе инициализации и при перестроении индекса.

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

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

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

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

  • Заказы, превышающие этот номер заказа, не были перенесены или проиндексированы.

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

Синхронизация полной библиотеки заказов

В качестве инструмента синхронизации в квазиреальном времени упоминается открытый проект Alibaba Otter, реализованный на основе MySql binlog, обладающий высокой стабильностью и поддерживающий развертывание с высокой доступностью. Чтобы избежать синхронизации операторов DELETE с целевой библиотекой, измените часть исходного кода в Otter, чтобы реализовать фильтрацию операторов DELETE. Конкретный принцип см. во введении на официальном сайте Otter Мы принимаем режим развертывания в одной комнате. В то же время развертываются запланированные задачи проверки для проверки результатов синхронизации в реальном времени.

Согласованность с несколькими источниками

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

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

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

  • Сравните данные в историческом репозитории перед удалением успешно перенесенных данных в активном репозитории.

Для гарантии согласованности полного хранилища, в дополнение к собственному набору гарантийных механизмов Otter, основанных на binlog, мы добавили регулярную задачу проверки, чтобы проверить, согласуются ли данные полного хранилища и активного хранилища в течение 1 часа.

Отслеживание заказа

Запрос заказа использует 2 репозитория: активный репозиторий и исторический репозиторий. Конкретный процесс выполнения выглядит следующим образом:

  1. Запросить номер заказа в соответствии с правилами шардинга

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

  • Библиотека действий, чтобы найти только: x> n заказов.

  • Историческая библиотека запрашивает только: x

Служба запроса заказа включает два метода запроса пейджинга:

  • Предыдущая страница, запрос следующей страницы.

  • Запрос нумерации страниц.

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

В процессе реализации возникла проблема, с которой мы столкнулись, было постранично ограничить глубину поиска (возвращается до 1000 записей), например: 20 записей на страницу, можно перейти только на 50 страницу. В то время как за кулисами в реальной ситуации некоторых запросов бизнес-операций за этот предел. Поэтому мы берем модификацию стратегии запроса для достижения глубины разбиения по страницам, упрощения описания глубины разбиения на страницы для достижения следующего: Предположим, что имеется 10 записей службы поиска, интерфейс запроса для реализации разбиения по страницам через смещение и предел, в частности, как

Если ограничения на разбиение на страницы нет и на страницу приходится 2 фрагмента данных, синтаксис данных для запроса страниц 1, 2 и 3 выглядит следующим образом: Страница 1, смещение=0, ограничение=2, возврат 100, 102 Страница 2 , offset=2, limit=2, возврат 110 200 Страница 3, offset=4, limit=2, возврат 201 300 Теперь offset и limit добавляют следующие ограничения: offset

  • Сначала решите проблему смещения. Если offset>maxOffset, измените условие запроса, чтобы сделать offset=0, например, запросите данные на странице 2 (offset=2, limit=2), добавьте orderId>102 в условие запроса и затем измените смещение на 0.

  • После того, как смещение удовлетворяет условиям, обрабатывается предельная задача. Если limit>maxLimit, зациклите запрос, изменив условия запроса, чтобы сделать limit

  1. public static void paging(int offset, int limit) {

  2.        List<Integer> result = null;

  3.        if (offset <= MAX_OFFSET) {

  4.            result = pagingForLimit(offset, limit, null);

  5.        } else if (offset > MAX_OFFSET) {

  6.            int skipSize = offset; //需跳跃个数

  7.            Integer currentItem = skip(skipSize);

  8.            result = pagingForLimit(0, limit, currentItem);

  9.        }

  10.        if (result != null) {

  11.            System.out.println(result.toString());

  12.        } else {

  13.            System.out.println("无值");

  14.        }

  15.    }

скопировать код

Если смещение больше предела (смещение > MAX_OFFSET), вам необходимо найти номер предыдущего заказа, который удовлетворяет условию, чтобы изменить условие запроса, чтобы сделать смещение = 0. код показывает, как показано ниже:

  1. else if (offset > MAX_OFFSET) {

  2.            int skipSize = offset; //需跳跃个数

  3.            Integer currentItem = skip(skipSize);

  4.            result = pagingForLimit(0, limit, currentItem);

  5. }

скопировать код

Основной метод поиска предыдущего номера заказа — пропуск.После нахождения номера заказа вы можете изменить смещение=0, чтобы решить ограничение смещения. Часть кода для метода пропуска указана ниже, как показано ниже.

  1. /**

  2. * @param size 表示前一个订单号的位置

  3. * @return 返回前一个订单号

  4. */

  5.    private static Integer skip(int size) {

  6.        int maxSkipStep = MAX_OFFSET + MAX_LIMIT;//最大跳跃步长

  7.        if (size <= maxSkipStep) {

  8.            return get(size, null);

  9.        } else {

  10.            Integer preInteger = null;

  11.            while (size > maxSkipStep) {

  12.                List<Integer> tmp = query(MAX_OFFSET, MAX_LIMIT, preInteger);

  13.                preInteger = tmp.get(tmp.size() - 1);

  14.                size -= maxSkipStep;

  15.            }

  16.            return get(size, preInteger);

  17.        }

  18.    }

скопировать код

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

  1. /**

  2.     * 带限制的分页

  3.     * @param offset 请求偏移量

  4.     * @param limit 请求个数

  5.     * @param currentItem 前一个订单号

  6.     * @return

  7.     */

  8.    private static List<Integer> pagingForLimit(int offset, int limit, Integer currentItem) {

  9.        if (limit <= MAX_LIMIT) {

  10.            return query(offset, limit, currentItem);

  11.        } else {

  12.            List<Integer> result = query(offset, MAX_LIMIT, currentItem);

  13.            limit -= MAX_LIMIT;

  14.            while (limit > MAX_LIMIT) {

  15.                Integer pre = null;

  16.                if (result != null) {

  17.                    pre = result.get(result.size() - 1);

  18.                }

  19.                result.addAll(query(0, MAX_LIMIT, pre));

  20.                limit -= MAX_LIMIT;

  21.            }

  22.            if (limit > 0) {

  23.                Integer preInteger = result.get(result.size() - 1);

  24.                result.addAll(query(0, limit, preInteger));

  25.            }

  26.            return result;

  27.        }

  28.    }

скопировать код

Онлайн-релиз

Чтобы обеспечить плавный запуск, весь проект разделен на этапы 6 и выпуски 4. Конкретный план реализации выглядит следующим образом:

Выпуск 1-й партии:

  • Задачи миграции заказов отделены от горячих и холодных данных.

  • Развертывание Otter, полная синхронизация библиотек.

Выпущена 2 партия:

  • Запланированная задача добавочной холодной миграции данных.

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

Выпустить 3-ю партию:

  • Справочно-информационная служба по заказу на реконструкцию.

Выпуск 4:

  • Справочная служба приема заказов на ремонт.

  • Запустите запланированное задание, чтобы удалить холодные данные из горячего хранилища.

Суммировать

В рамках этого проекта емкость формы заказа эффективно контролировалась, количество запросов в секунду для запроса заказа на стороне пользователя было увеличено в 2 раза, а исторический запрос заказа на стороне оператора увеличился в 4 раза. Текущее решение также имеет некоторые проблемы: проблема полной емкости хранилища Основная цель этого полного хранилища — уменьшить изменения в периферийных системах, которые напрямую зависят от библиотеки транзакций. Далее необходимо сформулировать более разумный способ получения полного объема данных заказа с периферийной системы, чтобы убрать зависимость от полного объема склада.

Рекомендуемое чтение

Практика тестовой платформы Hujiang ABTest

Деконструкция: технология человеко-машинного общения, стоящая за звонками Google

Hello World, AndroidX

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

Вы действительно понимаете A/B-тестирование? (начальство)