Переход с Oracle и MS SQL на PostgreSQL: как честно оценить трудозатраты
Заявка на продление лицензии Oracle или MS SQL зависла, зарубежный вендор не отвечает или прямо отказывает — и руководство ставит задачу «переезжаем на PostgreSQL, посчитайте сроки». Первая оценка на глаз почти всегда оказывается заниженной в полтора-два раза, потому что считают перенос данных и забывают про хранимые процедуры, тестирование под нагрузкой и обучение людей, которые с PostgreSQL раньше не работали. Ниже — из чего реально складываются трудозатраты перехода и как оценить их так, чтобы не краснеть перед заказчиком через три месяца.
Содержание
- Почему оценка «на глаз» почти всегда занижена
- Перенос схемы и данных: где действительно просто, а где не очень
- Хранимые процедуры и специфичный SQL: самая недооценённая часть
- Тестирование производительности: где ловятся сюрпризы уже после переезда
- Обучение команды: недооценённая статья расходов по времени
- Как оценивать сроки, чтобы не промахнуться в разы
Почему оценка «на глаз» почти всегда занижена
Типичная ошибка — оценивать миграцию по объёму данных: «у нас база 500 ГБ, перельём за выходные». Перенос данных — это действительно самая предсказуемая и часто самая быстрая часть проекта. Утилиты вроде pgloader или встроенных экспортёров считают строки в секунду, и по этой части оценка почти всегда сходится с реальностью.
Основной объём работы прячется не в данных, а в коде вокруг данных: хранимых процедурах, триггерах, представлениях, специфичных функциях в SQL-запросах приложения, заданиях планировщика (Oracle Scheduler, SQL Server Agent), интеграциях через linked server или DB link. Этот код никто не считал построчно на этапе первичной оценки — его просто не видно, пока не откроешь исходники или не выгрузишь DDL всей схемы.
Практический приём: прежде чем называть заказчику или руководству сроки, выгрузите полный DDL базы и посчитайте объекты по типам:
-- Oracle: количество объектов по типам в схеме
SELECT object_type, COUNT(*)
FROM user_objects
GROUP BY object_type
ORDER BY COUNT(*) DESC;
-- MS SQL: количество объектов по типам
SELECT type_desc, COUNT(*)
FROM sys.objects
WHERE is_ms_shipped = 0
GROUP BY type_desc
ORDER BY COUNT(*) DESC;
Число строк в PROCEDURE, FUNCTION, TRIGGER, PACKAGE (для Oracle) — это и есть тот объём, который определяет реальный срок проекта, а не размер данных в гигабайтах. Если хранимых процедур и функций больше пары десятков, а среди них есть пакеты (Oracle PACKAGE BODY) на тысячи строк PL/SQL — закладывайте на миграцию логики отдельный, самый крупный блок оценки.
Перенос схемы и данных: где действительно просто, а где не очень
Сама схема — таблицы, индексы, ограничения — переносится инструментами достаточно надёжно, если типы данных выбраны заранее осознанно, а не автоматическим маппингом «как получится». Основные точки, которые стоит проверить руками, а не доверять конвертеру:
- Числовые типы. Oracle
NUMBERбез указания точности превращается либо вnumericбез ограничений (больше места, медленнее вычисления), либо конвертер молча подставляетnumeric(38). MS SQLmoney/smallmoneyлучше явно переводить вnumeric(19,4)— прямого аналогаmoneyсо всеми его округлениями в PostgreSQL нет. - Даты и время. Oracle
DATEхранит время наравне с датой (в отличие от ANSI SQL, гдеDATE— только дата), при бездумном переносе в PostgreSQLdateчасть с временем суток теряется — нуженtimestamp. - Идентификаторы автоинкремента. Oracle до 12c генерировал ID через
SEQUENCE+ триггерBEFORE INSERT, MS SQL — черезIDENTITY. В PostgreSQL этоGENERATED ALWAYS AS IDENTITY— переносится концептуально просто, но триггеры под старую логику Oracle нужно удалить, а не продублировать. - Пустые строки против NULL. В Oracle пустая строка
''иNULL— одно и то же на уровне движка. В PostgreSQL (как и в стандарте SQL) — разные значения. Сравнения видаWHERE column = ''после переноса перестанут находить строки, что раньше содержали NULL, и наоборот. - Регистр идентификаторов. Oracle хранит имена в кавычках как есть, PostgreSQL без кавычек всё приводит к нижнему регистру — если это не проговорить один раз на всю схему, часть запросов начнёт падать с «relation does not exist» при физически существующей таблице. Разбор этой и похожих проблем есть в статье про подводные камни переноса на PostgreSQL — логика та же, даже если у вас не MySQL, а Oracle или MS SQL.
Для самого переноса данных удобно опираться на существующие инструменты (ora2pg для Oracle, pgloader или экспорт через bcp/SSIS для MS SQL), но закладывайте отдельное время на прогон и сверку контрольных сумм после переноса — не «данные перелились», а «данные перелились и сошлись построчно с источником».
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверХранимые процедуры и специфичный SQL: самая недооценённая часть
Это тот блок, где расхождение между первой оценкой и реальностью обычно самое большое. PL/SQL (Oracle) и T-SQL (MS SQL) — это не диалекты одного языка, а разные процедурные надстройки над SQL, и PostgreSQL не понимает ни ту, ни другую напрямую. Придётся переписывать на PL/pgSQL или выносить логику из базы в код приложения — и второе часто оказывается более здравым решением, чем механический перевод один в один.
Что усложняет переписывание конкретно:
| Конструкция | Oracle PL/SQL / MS SQL T-SQL | Что в PostgreSQL |
|---|---|---|
| Пакеты (Oracle) | PACKAGE / PACKAGE BODY группируют процедуры, функции и переменные пакета | Прямого аналога нет — переносится в набор отдельных функций + схему для группировки, состояние пакета придётся эмулировать через сессионные переменные или таблицы |
| Курсоры с автоматическим циклом | FOR rec IN (SELECT ...) LOOP | Похожий синтаксис есть в PL/pgSQL, но семантика блокировок и обработки ошибок отличается |
| Обработка исключений | EXCEPTION WHEN OTHERS THEN с кодами ошибок Oracle/TRY...CATCH в T-SQL | EXCEPTION WHEN в PL/pgSQL — другой набор именованных условий, коды ошибок SQLSTATE не всегда совпадают по смыслу |
| Иерархические запросы | CONNECT BY PRIOR (Oracle) | WITH RECURSIVE — переписывается не построчно, а логически заново |
| Табличные функции, курсоры как результат | REF CURSOR, табличные функции T-SQL | RETURNS TABLE, RETURNS SETOF — маппинг есть, но сигнатуры вызова у клиентского кода меняются |
| Автономные транзакции | PRAGMA AUTONOMOUS_TRANSACTION | Нет прямого аналога, нужен dblink с отдельным соединением либо вынос логики в приложение |
Скорость переписывания хранимого кода зависит не от числа строк, а от того, сколько в нём специфичных для вендора конструкций из таблицы выше — простая процедура с парой SELECT/UPDATE переписывается быстро, а пакет с автономными транзакциями и вложенными курсорами может занять в разы больше времени, чем кажется по объёму. Это ориентировочная закономерность, а не измеренный норматив: точную скорость на вашей кодовой базе даст только пилотный перенос десятка реальных объектов на старте проекта, а не оценка по строкам.
Отдельно — специфичные функции в обычных SQL-запросах приложения: NVL, DECODE, ROWNUM, конкатенация через || с неявными преобразованиями типов в Oracle; ISNULL, TOP, GETDATE(), квадратные скобки [table] в T-SQL. Их нужно найти по всей кодовой базе, а не только в хранимых процедурах — грепом по исходникам приложения, ORM-мапперам и отчётным системам (BI, генераторы отчётов), которые часто содержат собственный SQL в отрыве от основного репозитория.
Тестирование производительности: где ловятся сюрпризы уже после переезда
Функционально мигрировавшее приложение может работать корректно и при этом резко просесть по скорости — это отдельная категория проблем, которая либо всплывает в проде через неделю после переезда, либо ловится заранее нагрузочным тестированием. Основные источники просадки:
- Планировщик запросов ведёт себя иначе. Oracle CBO (Cost-Based Optimizer) и планировщик PostgreSQL по-разному оценивают стоимость планов на одних и тех же данных. Запрос, который годами летал на Oracle с одним набором индексов, на PostgreSQL с тем же набором индексов может пойти по последовательному сканированию. Без
EXPLAIN (ANALYZE, BUFFERS)до и после миграции на репрезентативном объёме данных это не поймать. - Статистика планировщика после переноса пустая или устаревшая. Сразу после массовой загрузки данных обязательно нужен
ANALYZE(а на больших таблицах — с настройкойdefault_statistics_targetпод конкретные колонки), иначе PostgreSQL строит планы вслепую. - Особенности блокировок. Oracle по умолчанию использует многоверсионность (MVCC) похоже на PostgreSQL, но MS SQL в конфигурации по умолчанию блокирует читающие запросы пишущими куда агрессивнее. Приложения, написанные под MS SQL, иногда неявно полагались на это поведение для последовательности данных — на PostgreSQL с его MVCC такая логика может начать давать гонки, которых не было раньше.
- Особенности индексов. Oracle Bitmap-индексы и функциональные индексы, MS SQL — колоночные (columnstore) индексы для аналитики. Прямых аналогов не всегда достаточно: PostgreSQL предлагает
GIN/GiSTдля отдельных сценариев, но подбор под конкретный запрос — это не автоматическая замена, а отдельная работа руками. - Параметры соединений и пулинг. Приложения, рассчитанные на пул соединений Oracle или MS SQL, часто переносятся с настройками по умолчанию, а PostgreSQL при большом числе одновременных соединений без внешнего пулера (
PgBouncer) деградирует иначе, чем исходная СУБД. Про порог деградации при росте соединений и что с этим делать — в статье сколько соединений к PostgreSQL до деградации.
Практический план: не ждите переезда в прод, чтобы узнать, что не так. Разверните PostgreSQL на отдельном сервере ещё на этапе миграции схемы, прогоните на нём копию продовых данных (или репрезентативную выборку, если полная копия юридически или по объёму неудобна) и реальный или синтетический профиль нагрузки — до того, как объявлять переход завершённым. Отдельный сервер под нагрузочные тесты, не разделяемый с продом старой СУБД, экономит нервы: тесты не будут искажены посторонней нагрузкой.
Обучение команды: недооценённая статья расходов по времени
Даже опытные администраторы Oracle или MS SQL не знают PostgreSQL «по умолчанию» — это не то же самое, что выучить новый диалект SQL за выходные. Разные модели работы с памятью (Oracle SGA/PGA против shared_buffers/work_mem в PostgreSQL), разная архитектура репликации, разный подход к резервному копированию (RMAN у Oracle против pg_basebackup/pg_dump/WAL-архивирования в PostgreSQL), разные утилиты мониторинга — это отдельный пласт знаний, который нельзя пропустить, просто «поставив PostgreSQL и перелив данные».
Что закладывать в план по обучению:
- Администраторам БД — освоение
VACUUM/автовакуума (прямого аналога в Oracle и MS SQL нет, а без понимания механизма раздувание таблиц и WAL — частая проблема на старте), настройку репликации (pg_basebackup), восстановление на конкретную точку времени (PITR). - Разработчикам — переход с T-SQL/PL-SQL на PL/pgSQL, работу с
EXPLAIN, понимание MVCC на практике (в том числе почемуUPDATEне меняет строку «на месте», а создаёт новую версию). - DevOps — новый стек мониторинга (
pg_stat_statements,pg_stat_activityвместо привычных представлений Oracle/MS SQL), другие процедуры апгрейда версий.
Не пытайтесь совместить обучение с боевой миграцией «по ходу дела» — это стабильно вызывает задержки и ошибки в проде от людей, которые прямо сейчас учатся на живых данных. Отдельная неделя-две на пилотный проект (одна некритичная система целиком, от схемы до нагрузочного теста) перед основной миграцией окупается: команда набивает шишки не на боевой базе, а оценка сроков для остальных систем становится точнее, потому что построена на собственном опыте, а не на чужих кейсах.
Как оценивать сроки, чтобы не промахнуться в разы
Рабочий подход — не одна общая цифра «переезд займёт N месяцев», а оценка по компонентам с явным запасом на неизвестность:
- Инвентаризация (обычно 1-2 недели независимо от размера базы): выгрузка DDL, подсчёт объектов по типам, поиск специфичного SQL в коде приложения и отчётах, список интеграций (linked server, DB link, ETL-задания).
- Пилотный перенос одной некритичной, но показательной части системы — от схемы до нагрузочного теста. Это даёт не абстрактную, а измеренную на вашем коде скорость переписывания хранимой логики, которую дальше можно экстраполировать на оставшийся объём.
- Перенос схемы и данных по всей базе — обычно самая предсказуемая по срокам часть, если типы данных проверены заранее (см. раздел выше).
- Переписывание хранимого кода и специфичного SQL — блок с наибольшей неопределённостью, экстраполируйте от пилота, а не от общей оценки «на глаз».
- Нагрузочное тестирование и тюнинг — отдельный этап, а не «проверим между делом»; закладывайте минимум несколько дней на итерации «нашли просадку → поправили план/индекс → перепроверили».
- Обучение и период параллельной работы систем — время, когда старая и новая СУБД работают бок о бок (репликация изменений, сверка расхождений), прежде чем переключить продакшн окончательно.
К сумме оценок по этим пунктам стоит закладывать резерв — на практике проекты миграции между разными СУБД почти всегда находят что-то незапланированное: забытое legacy-приложение с прямым подключением к базе в обход основного кода, отчётный сервер BI со своим SQL, задание в планировщике, о котором никто не вспомнил. Точный процент резерва зависит от зрелости документации в компании — там, где инвентаризация систем ведётся формально и полно, неожиданностей меньше. Отдельно для тестового стенда и нагрузочных прогонов удобно взять выделенный сервер под большую базу данных — не смешивать пилот с продовой инфраструктурой старой СУБД, чтобы не зависеть от чужих лицензий и не создавать конкуренцию за ресурсы в разгар тестов.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Можно ли автоматически перевести все хранимые процедуры Oracle или MS SQL в PostgreSQL без участия разработчика?
Полностью автоматически — нет. Конвертеры (например, ora2pg для Oracle) справляются с типовыми конструкциями, но код с автономными транзакциями, пакетами и нетривиальной обработкой ошибок требует ручного разбора и тестирования.
Сколько времени в среднем занимает переход с Oracle на PostgreSQL?
Зависит от объёма хранимой логики, а не от размера данных — систему с парой десятков простых таблиц можно перенести за недели, систему с сотнями процедур и интеграциями — за месяцы. Ориентируйтесь на собственную инвентаризацию и пилотный перенос, а не на чужие сроки из статей.
Нужно ли сразу переписывать всю логику на PL/pgSQL, или можно вынести часть в приложение?
Часто разумнее вынести часть бизнес-логики из процедур в код приложения — особенно там, где логика в базе появилась исторически, а не по архитектурному решению. Это увеличивает объём работы на старте, но упрощает поддержку в перспективе.
Что делать, если лицензия старой СУБД истекает раньше, чем закончится миграция?
Оцените, можно ли на переходный период сократить использование до бесплатного/ограниченного уровня лицензии для некритичных систем, пока идёт перенос основной нагрузки — решение не универсальное, ограничения версий нужно сверять отдельно под вашу задачу.
Стоит ли переносить всё сразу или частями?
Частями почти всегда безопаснее — постепенный перенос по системам с периодом параллельной работы даёт возможность откатиться на конкретном участке, если что-то пошло не так.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →