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 потребовал тщательного планирования. Мне пришлось:
- Проанализировать связи: Определить, как документы MongoDB соотносились друг с другом, и спроектировать соответствующие связи внешних ключей
- Нормализовать данные: Разбить вложенные документы на отдельные таблицы там, где это было уместно
- Сохранить возможности JSON: Использовать столбцы JSONB для действительно неструктурированных данных, которым нужно было оставаться гибкими
- Спроектировать индексы: Создать подходящие индексы для производительности запросов
Пользовательские решения
Миграция потребовала разработки нескольких пользовательских решений:
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
- Шаблоны абстракции баз данных
- Системы мониторинга и оповещения
Личностный и профессиональный рост
Этот проект по миграции стал преобразующим для моего карьерного развития. Он вывел меня на неизведанную территорию, потребовав:
Лидерские навыки:
- Руководства сложным техническим проектом без предварительного опыта
- Принятия критически важных архитектурных решений под давлением
- Координации с несколькими командами и заинтересованными сторонами
Способности к решению проблем:
- Разбиения сложных проблем на управляемые компоненты
- Разработки творческих решений для беспрецедентных вызовов
- Балансирования множества конкурирующих требований и ограничений
Коммуникация и командная работа:
- Объяснения технических концепций нетехническим заинтересованным сторонам
- Документирования процессов и решений для будущего использования
- Наставничества членов команды по новым технологиям и шаблонам
Уроки, извлечённые
Технические уроки
- Выбор базы данных имеет значение: Выбор между NoSQL и SQL должен основываться на конкретных вариантах использования и долгосрочных требованиях
- Тестирование производительности критически важно: Теоретические преимущества не всегда переводятся в реальные улучшения производительности
- Планирование миграции: Комплексное планирование и тестирование необходимы для сложных миграций
- Инвестиции в инструменты: Создание правильных инструментов заранее экономит значительное время и снижает число ошибок
Уроки управления проектами
- Коммуникация с заинтересованными сторонами: Регулярные обновления и ясная коммуникация предотвращают недоразумения
- Управление рисками: Наличие запасных планов и процедур отката является обязательным
- Управление сроками: Закладывайте резерв времени на неожиданные трудности и кривые обучения
- Документация: Тщательная документация обеспечивает передачу знаний и будущую поддержку
Заключение
Миграция STMS с MongoDB на Postgres остаётся самой сложной и одновременно самой вознаграждающей технической задачей, которую я решил в своей карьере. Она требовала не только технической экспертизы, но и лидерства, планирования и адаптивности. Успех проекта показал, что при правильном планировании, тщательном тестировании и стремлении к совершенству даже самые сложные технические вызовы можно преодолеть.
Этот опыт фундаментально изменил мой подход к разработке программного обеспечения, подчеркнув важность:
- Понимания полного контекста и требований перед принятием технических решений
- Инвестирования времени в правильные инструменты и тестирование
- Поддержания ясной коммуникации на протяжении сложных проектов
- Готовности изучать новые технологии и подходы, когда это необходимо
Успех миграции не только улучшил возможности STMS, но и установил шаблоны и процессы, которые продолжают приносить пользу инфраструктурным проектам eBay. Это укрепило мою веру в то, что принятие неизвестных вызовов и достижение успеха через них является ключом как к личностному, так и к профессиональному развитию.
Оглядываясь назад, я понимаю, что этот проект стал поворотным моментом в моей карьере, превратив меня из разработчика, который реализует решения, в инженера, который может проектировать и руководить сложными техническими инициативами. Уверенность и навыки, полученные благодаря этому опыту, продолжают направлять мой подход к новым вызовам и возможностям в разработке программного обеспечения.