MongoDB в Postgres

ПРИМЕЧАНИЕ: Эта запись в блоге представляет собой объединение моих MongoDB to Postgres (2024-03-06) и Sequelize vs. Prisma (2023-05-25). Оригинальные блоги были удалены, и этот блог занял их место, поскольку оба содержали по сути один и тот же контент/информацию. Миграция началась в начале марта 2023 года, переключение произошло середине ноября 2023 года, а все экземпляры старой системы MongoDB были полностью отключены в начале января 2024 года.

Введение

Во время моей работы в eBay я столкнулся с тем, что стало самой технически сложной задачей в моей карьере: миграцией Storage Management System (STMS) с MongoDB на Postgres. Это была не просто замена базы данных; это была полная архитектурная трансформация критически важной системы, которая принимает более 1,5 миллиона метрик в минуту в центрах обработки данных eBay, при требовании нулевого простоя и сохранения почти всей существующей функциональности.

Что такое STMS?

Storage Management System (STMS) служит критически важным внутренним инструментом для команды Service & Storage Infrastructure (SSI) в eBay. Он отслеживает и управляет устройствами в центрах обработки данных eBay, позволяя инженерам:

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

На STMS приходится более 70 массивов, 60 коммутаторов, 1100 хостов, 900 групп дисков и 200 кластеров в 3 центрах обработки данных eBay. Учитывая его жизненно важную роль в инфраструктуре eBay, любой простой или потеря функциональности напрямую повлияли бы на основные сервисы и бизнес-операции компании.

Задача

Почему миграция была необходима

Решение о миграции с MongoDB на Postgres было принято не сразу. Хотя MongoDB изначально хорошо служила STMS, растущая сложность наших связей между данными и необходимость в более сложных возможностях запросов сделали Postgres лучшим долгосрочным решением для нашего случая использования.

Что делало эту задачу сложной

Сложность этой миграции проистекала из нескольких фундаментальных проблем:

1. Фундаментальные различия баз данных MongoDB и Postgres — принципиально разные базы данных. MongoDB — это документо-ориентированная база данных (NoSQL), что означает, что данные хранятся как JSON в коллекциях, как документы в картотеке. Postgres — реляционная база данных (SQL), что означает, что данные хранятся как строки в таблицах, как в электронной таблице.

2. Архитектура кодовой базы Весь бэкенд STMS был построен для обработки и управления данными как JSON, используя пакеты, совместимые исключительно с MongoDB для операций с базой данных. Это означало не просто смену базы данных, а перестройку того, как всё наше приложение обрабатывало данные.

3. Требование нулевого простоя Из-за того, насколько важен STMS как внутренний инструмент, во время миграции не могло быть простоя. Система должна была продолжать обслуживать более 1,5 миллиона метрик в минуту на протяжении всего процесса.

4. Сжатые сроки и ограниченный опыт Миграцию нужно было завершить в течение нескольких месяцев, при этом изначально не было чёткого плана выполнения. Ни у меня, ни у моих коллег не было опыта миграции большой устаревшей кодовой базы с NoSQL на SQL-базы данных, и у меня был ограниченный предыдущий опыт работы с Postgres.

5. Масштаб и сложность Миграция включала преобразование 36 коллекций MongoDB в 74 таблицы Postgres, что требовало тщательного учёта связей, индексации и оптимизации запросов.

Выбор правильного ORM: Sequelize против Prisma

Одним из первых крупных решений был выбор инструмента ORM (Object-Relational Mapping, объектно-реляционное отображение). Поскольку наша кодовая база уже была спроектирована для использования Mongoose для MongoDB, использование ORM обеспечило бы наиболее плавный путь перехода.

Анализ требований

После тщательного анализа потребностей проекта я определил основные критерии для любого решения ORM:

  • Должен быть пакетом JavaScript (большая часть нашего кода была написана на JavaScript)
  • Должен поддерживать Postgres и большинство его возможностей
  • Производительность должна быть как минимум не хуже, а лучше, чем у Mongoose
  • Должен быть с открытым исходным кодом и поддерживаться

Кандидаты

После обширного исследования я сузил выбор до двух основных претендентов: Sequelize и Prisma. Я создал комплексные тестовые среды с использованием Docker для Postgres и преобразовал наш самый большой и сложный набор данных из структуры документов в структуру таблиц.

Методология тестирования

Для каждого ORM я измерял производительность по ключевым операциям:

  • Время создания записи
  • Время обновления записи
  • Время обновления вложенных записей (связей и пар ключ-значение JSON)
  • Время удаления записи
  • Время запроса/получения записи

Решение: Sequelize

Примерно 15 мая 2023 года я решил, что Sequelize — лучший ORM для нашего случая использования. Вот почему:

Преимущества Sequelize:

  • По-настоящему с открытым исходным кодом и не поддерживается финансируемым стартапом
  • Поддерживает большинство возможностей Postgres
  • Лучшая производительность, особенно по сравнению с Prisma
  • Зрелая экосистема с более чем 10 годами разработки
  • Гибкое представление модели/схемы с использованием классов JavaScript
  • Поддержка сложных join-операций и вариантов фильтрации, включая Regex

Результаты производительности:

В моих тестах Sequelize значительно превзошёл Prisma. Для записей в нашем большом наборе данных:

  • Sequelize: ~2,26 секунды на запись
  • Prisma: ~11,21 секунды на запись

Prisma был примерно в 5 раз медленнее, чем Sequelize, для нашего случая использования. Кроме того, удаление одной записи из нашего крупнейшего набора данных занимало у Prisma почти 4 минуты, что было неприемлемо для наших требований.

Проблемы Sequelize:

  • Более сложные и раздутые представления модели (564 строки против 262 строк у Mongoose)
  • Сбивающий с толку синтаксис в некоторых случаях
  • Сложность миграции базы данных
  • Менее полная документация по сравнению с Prisma

Сравнение плюсов и минусов Sequelize и Prisma

Чтобы дать более полную картину того, почему я выбрал Sequelize, я хочу поделиться подробными плюсами и минусами, которые я составил для обоих ORM во время оценки. Я также посмотрел, как они сравнивались с точки зрения представления схемы и поддержки сообщества по состоянию на 15 мая 2023 года. Это более глубокое погружение помогло укрепить мой выбор, и я надеюсь, что оно может быть полезно и другим, кто столкнётся с похожим решением.

Плюсы Sequelize:

  • Имеет функцию sync(), которая автоматически создаёт и обрабатывает таблицы за вас, экономя много ручных усилий.
  • Может обрабатывать сложные join-операции для вложенных данных, что было критично для структуры STMS.
  • Поддерживает широкий спектр вариантов фильтрации, включая Regex, обеспечивая гибкость в запросах.
  • Представление модели/схемы выполняется на чистом JavaScript с использованием классов, которые очень настраиваемы под конкретные нужды.
  • Плавно обрабатывает подключения к базе данных, включая поддержку нескольких read-подключений.
  • Поддерживает сырые SQL-запросы, когда нужно заглянуть «под капот».
  • Статистика сообщества на 15 мая 2023 года: на NPM в последний раз обновлялся 14 дней назад с 1 505 835 еженедельными загрузками; на GitHub в последний раз обновлялся вчера с 4,2k Forks и 27,9k Stars. Он был с открытым исходным кодом под лицензией MIT более 10 лет, так что я уверен, что так и останется.

Минусы Sequelize:

  • Представление модели/схемы может стать очень сложным и раздутым. Например, в то время как представление Mongoose для нашего большого набора данных занимало около 262 строк (включая пробелы), тот же набор данных в Sequelize разросся до 564 строк.
  • Синтаксис в некоторых сценариях может быть запутанным и сложным, что временами замедляло меня.
  • Миграция или редактирование базы данных — это морока. Даже с тем, что sequelize-cli генерирует скрипты миграции, это всё равно громоздко, хотя я заметил, что это распространённая болевая точка у большинства ORM.
  • Документация не отличная, хотя она улучшается. К счастью, такие инструменты, как ChatGPT, хорошо понимают Sequelize благодаря его долгой истории, что помогало закрывать пробелы.
  • Не столь чувствителен к типам, как Prisma, что может привести к проблемам в некоторых проектах.
  • Ограниченная поддержка TypeScript, хотя это не было проблемой для STMS, для других это может стать решающим минусом.

Плюсы Prisma:

  • Использует собственный язык схем, делая создание моделей более чистым и лаконичным. Для сравнения, Mongoose занял 262 строки для нашего большого набора данных, а Prisma справился с этим всего в 221 строке.
  • Поставляется с CLI-инструментом, который упрощает создание и миграцию базы данных, и это лучшее, что я видел у ORM на сегодняшний день, даже если он не идеален.
  • Поддерживает сырые SQL-запросы, предоставляя гибкость при необходимости.
  • Синтаксис кода чистый и проще для понимания по сравнению с Sequelize, что облегчает изучение.
  • Автоматически генерирует построители запросов для Node.js и TypeScript через свой клиент, что является приятным дополнением.
  • Отличная, чистая документация. ChatGPT не так актуален по Prisma, но официальная документация часто компенсировала это.
  • Статистика сообщества на 15 мая 2023 года: на NPM в последний раз обновлялся 6 дней назад с 1 344 705 еженедельными загрузками; на GitHub в последний раз обновлялся 3 часа назад с 1,1k Forks и 31,3k Stars.

Минусы Prisma:

  • Не поддерживает фильтрацию Regex для Postgres, хотя предлагает альтернативы, такие как “contains”, “includes” и “startsWith.”
  • Производительность была серьёзной проблемой в моих тестах. Создание записей для нашего большого набора данных занимало у Prisma около 11,21 секунды на запись по сравнению с 2,26 секунды у Sequelize, то есть примерно в 5 раз медленнее.
  • Удаление одной записи из большого набора данных занимало почти 4 минуты, что было решающим недостатком для наших нужд.
  • Даже при честном сравнении на сложном наборе данных со связями в три уровня глубины Sequelize был значительно быстрее при удалениях.
  • Prisma поддерживается стартапом с финансированием в размере 56,5 миллиона долларов. Хотя основной код ORM является открытым исходным кодом под Apache-2.0, я с осторожностью отношусь к возможным изменениям лицензирования в будущем, подобно тому, что произошло с MongoDB.

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

Процесс миграции

Преобразование структуры данных

Переход от документной структуры MongoDB к реляционной структуре Postgres потребовал тщательного планирования. Мне пришлось:

  1. Проанализировать связи: Определить, как документы MongoDB соотносились друг с другом, и спроектировать соответствующие связи внешних ключей
  2. Нормализовать данные: Разбить вложенные документы на отдельные таблицы там, где это было уместно
  3. Сохранить возможности JSON: Использовать столбцы JSONB для действительно неструктурированных данных, которым нужно было оставаться гибкими
  4. Спроектировать индексы: Создать подходящие индексы для производительности запросов

Пользовательские решения

Миграция потребовала разработки нескольких пользовательских решений:

1. Скрипты миграции данных Я создал комплексные скрипты, чтобы:

  • Извлекать данные из коллекций MongoDB
  • Преобразовывать структуры документов в реляционный формат
  • Импортировать данные в таблицы Postgres с правильными связями

2. Слой совместимости API Чтобы поддерживать нулевое время простоя, я построил слой совместимости, который мог:

  • Направлять запросы либо в MongoDB, либо в Postgres в зависимости от статуса миграции
  • Обеспечивать согласованность данных в течение переходного периода
  • Предоставлять механизмы резервного перехода

3. Пользовательский промежуточный слой Разработал промежуточный слой для обработки различий в том, как MongoDB и Postgres выполняют определённые операции, обеспечивая, чтобы существующие конечные точки API продолжали работать без изменений.

Преодоление технических трудностей

Обработка сложных связей

Одной из самых больших трудностей было преобразование встроенных документов MongoDB в связи Postgres. Например, один документ MongoDB мог содержать:

  • Базовые свойства
  • Вложенные объекты, представляющие связанные сущности
  • Массивы встроенных документов

Это нужно было тщательно разложить на:

  • Основные таблицы для главных сущностей
  • Таблицы-связки для связей многие-ко-многим
  • Связи внешних ключей для ассоциаций один-ко-многим

Оптимизация запросов

Шаблоны запросов MongoDB не переводятся напрямую в SQL. Мне пришлось:

  • Переписать сложные конвейеры агрегации в SQL-объединения
  • Оптимизировать индексы под новые шаблоны запросов
  • Убедиться, что производительность запросов соответствовала или превосходила производительность MongoDB

Целостность данных

Обеспечение целостности данных во время миграции потребовало:

  • Комплексных скриптов валидации
  • Процедур отката
  • Синхронизации данных в реальном времени в переходные периоды

Результаты и влияние

Миграция STMS с MongoDB на Postgres была успешно завершена с нулевым временем простоя при сохранении почти всех функций и возможностей. Результаты превзошли ожидания:

Улучшения производительности:

  • Производительность запросов улучшилась для сложных реляционных запросов
  • Лучшая согласованность и целостность данных
  • Более эффективное использование хранилища

Операционные преимущества:

  • Расширенные возможности мониторинга и отладки
  • Лучшая интеграция с существующими SQL-инструментами eBay
  • Улучшенные процедуры резервного копирования и восстановления

Влияние на команду:

  • Расширенные знания команды о реляционных базах данных
  • Сформированы шаблоны для будущих миграций баз данных
  • Созданы повторно используемые инструменты и процессы

Полученные технические навыки

Этот проект значительно расширил мою техническую экспертизу:

Технологии баз данных:

  • Глубокое понимание возможностей Postgres и оптимизации
  • Оптимизация SQL-запросов и настройка производительности
  • Шаблоны проектирования баз данных и нормализация
  • Конфигурации баз данных primary-standby

Инструменты разработки:

  • ORM Sequelize и построение запросов
  • Стратегии миграции баз данных
  • Методологии тестирования производительности
  • Проверка данных и целостности

Архитектурные шаблоны:

  • Стратегии миграции с нулевым временем простоя
  • Слои совместимости API
  • Шаблоны абстракции баз данных
  • Системы мониторинга и оповещения

Личностный и профессиональный рост

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

Лидерские навыки:

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

Способности к решению проблем:

  • Разбиения сложных проблем на управляемые компоненты
  • Разработки творческих решений для беспрецедентных вызовов
  • Балансирования множества конкурирующих требований и ограничений

Коммуникация и командная работа:

  • Объяснения технических концепций нетехническим заинтересованным сторонам
  • Документирования процессов и решений для будущего использования
  • Наставничества членов команды по новым технологиям и шаблонам

Уроки, извлечённые

Технические уроки

  1. Выбор базы данных имеет значение: Выбор между NoSQL и SQL должен основываться на конкретных вариантах использования и долгосрочных требованиях
  2. Тестирование производительности критически важно: Теоретические преимущества не всегда переводятся в реальные улучшения производительности
  3. Планирование миграции: Комплексное планирование и тестирование необходимы для сложных миграций
  4. Инвестиции в инструменты: Создание правильных инструментов заранее экономит значительное время и снижает число ошибок

Уроки управления проектами

  1. Коммуникация с заинтересованными сторонами: Регулярные обновления и ясная коммуникация предотвращают недоразумения
  2. Управление рисками: Наличие запасных планов и процедур отката является обязательным
  3. Управление сроками: Закладывайте резерв времени на неожиданные трудности и кривые обучения
  4. Документация: Тщательная документация обеспечивает передачу знаний и будущую поддержку

Заключение

Миграция STMS с MongoDB на Postgres остаётся самой сложной и одновременно самой вознаграждающей технической задачей, которую я решил в своей карьере. Она требовала не только технической экспертизы, но и лидерства, планирования и адаптивности. Успех проекта показал, что при правильном планировании, тщательном тестировании и стремлении к совершенству даже самые сложные технические вызовы можно преодолеть.

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

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

Успех миграции не только улучшил возможности STMS, но и установил шаблоны и процессы, которые продолжают приносить пользу инфраструктурным проектам eBay. Это укрепило мою веру в то, что принятие неизвестных вызовов и достижение успеха через них является ключом как к личностному, так и к профессиональному развитию.

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