For the complete documentation index, see llms.txt. This page is also available as Markdown.

Миграция с MySQL на PostgreSQL

Эта инструкция описывает перенос действующего проекта iEXExchanger с MySQL на PostgreSQL 18 с помощью встроенного инструмента iEX DB Migrator.

Мигратор создаёт структуру PostgreSQL, переносит данные, проверяет результат и переключает подключение проекта только после успешного завершения основных проверок. При необходимости отдельно переносится база Laravel Pulse.

Для обычной миграции используется пустая PostgreSQL-база, заранее созданная в FASTPANEL. В конфигурации необходимо указать:

"schema_mode": "empty"

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

Перед началом выполните инструкцию

Подключение PostgreSQL

В FASTPANEL должны быть созданы пустые PostgreSQL-базы и отдельные пользователи для основной базы и Laravel Pulse.

Внимание

Как проходит миграция

Что переносит мигратор

iEX DB Migrator переносит:

  • основную базу iEXExchanger;

  • отдельную базу Laravel Pulse;

  • таблицы и колонки;

  • все строки выбранных таблиц;

  • первичные ключи;

  • уникальные ограничения;

  • обычные и составные индексы;

  • внешние ключи;

  • CHECK-ограничения;

  • identity и serial sequences;

  • generated columns;

  • аналоги ON UPDATE CURRENT_TIMESTAMP;

  • значения JSON с преобразованием в jsonb;

  • числовые, строковые, временные и логические значения.

Перед переносом проверяются:

  • версии MySQL и PostgreSQL;

  • кодировки баз;

  • права пользователей;

  • структура таблиц;

  • определения колонок;

  • индексы и ограничения;

  • unsigned-значения;

  • zero-date;

  • значения TIME;

  • JSON;

  • generated columns;

  • числовые диапазоны;

  • количество строк.

После переноса мигратор сравнивает структуру и вычисляет SHA-256 digest данных выбранных таблиц.

Если обнаружена неподдерживаемая структура или значение, миграция останавливается с ошибкой.


Промпт для выполнения миграции

Этот промпт можно скопировать и передать техническому специалисту или агенту, который имеет доступ к серверу.

Промпт для безопасной миграции

Перед началом

Проверьте:

  • PostgreSQL имеет версию 18.x;

  • PostgreSQL работает и доступен на локальном порту;

  • PHP 8.4 видит модули pdo_pgsql и pgsql;

  • PostgreSQL добавлен в FASTPANEL;

  • PostgreSQL-сервер имеет статус «Доступен»;

  • создана пустая основная PostgreSQL-база;

  • создан отдельный пользователь основной базы;

  • при необходимости создана пустая база Laravel Pulse;

  • база и пользователь Pulse называются pulse_pg;

  • PostgreSQL-базы используют кодировку UTF8;

  • исходная MySQL доступна;

  • в Backend-проекте присутствуют .env и artisan;

  • на сервере достаточно свободного места;

  • подготовлено окно обслуживания.

До переноса не запускайте Laravel migrations и Product Updates для пустых PostgreSQL-баз.

Иначе базы перестанут быть пустыми и не пройдут проверку режима empty.

Файлы системы

Файл
Назначение

tools/db-migrator/iex-db-migrator

Основная команда переноса

tools/db-migrator/migration.example.json

Пример конфигурации

tools/db-migrator/db-migrator.json

Приватная конфигурация переноса

storage/logs/db-migrator/

Постоянные логи мигратора

tools/db-backup/iex-db-backup

Инструмент резервного копирования

tools/db-backup/db-backup.json

Приватная конфигурация резервного копирования

storage/logs/db-backup/

Логи резервного копирования

tools/db-auditor/iex-db-auditor

Аудит PostgreSQL после обновления

Какие пользователи используются

Во время переноса задействованы разные учётные записи.

Пользователь
Назначение

root

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

Пользователь MySQL приложения

Чтение исходной базы

Пользователь MySQL lock

Получение глобальной блокировки записи, если недоступен /root/.my.cnf

fastuser

Управление PostgreSQL через FASTPANEL

Пользователь PostgreSQL приложения

Подключение к новой основной базе

pulse_pg

Подключение к новой базе Laravel Pulse

Владелец сайта

Запуск Laravel-команд и процессов проекта

Пример:

Основной режим схемы — empty

Для клиентской миграции используется:

Этот режим предназначен для пустых PostgreSQL-баз, заранее созданных в FASTPANEL.

При запуске мигратор:

  1. подключается через пользователя из target.username;

  2. проверяет версию PostgreSQL;

  3. проверяет кодировку UTF8;

  4. проверяет права на создание объектов;

  5. проверяет отсутствие пользовательских таблиц;

  6. создаёт структуру PostgreSQL;

  7. переносит данные;

  8. создаёт индексы, ключи и ограничения;

  9. настраивает sequences;

  10. выполняет точную проверку.

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


Подготовка конфигурации

1

Подключитесь к серверу

Подключение к серверу по SSH

Мигратор запускается от пользователя root, чтобы точный режим мог получить MySQL read-lock через защищённый файл /root/.my.cnf.

2

Перейдите в Backend-проект

Файлы сайта в FastPanel

Пример:

Проверьте текущую директорию и обязательные файлы:

3

Проверьте файлы мигратора

Должны присутствовать:

Проверьте версию:

Устанавливать Go на клиентский сервер не требуется. Launcher выбирает бинарный файл, соответствующий архитектуре сервера.

4

Создайте рабочую конфигурацию

Если файл ещё не существует:

Установите защищённые права:

Откройте файл:

Пример db-migrator.json

После сохранения повторно установите права:

Откуда брать реквизиты

Поле
Где взять значение

main.source.database

Текущее DB_DATABASE из .env

main.source.username

Текущее DB_USERNAME

main.source.password

Текущее DB_PASSWORD

main.target.database

Основная PostgreSQL-база из FASTPANEL

main.target.username

Пользователь основной PostgreSQL-базы

main.target.password

Пароль пользователя PostgreSQL

pulse.source.database

Текущее PULSE_DB_DATABASE

pulse.source.username

Текущее PULSE_DB_USERNAME

pulse.source.password

Текущее PULSE_DB_PASSWORD

pulse.target.database

pulse_pg

pulse.target.username

pulse_pg

pulse.target.password

Пароль пользователя pulse_pg

Посмотреть основные параметры .env без вывода паролей:


Если Laravel Pulse не используется

Отключите профиль:

После этого команда с параметром --profile all обработает только основную базу.

Если Pulse настроен не у всех проектов, профиль можно оставить необязательным:

При отсутствии доступной базы мигратор должен явно вывести:

Отдельный запуск с параметром --profile pulse при недоступной базе завершится ошибкой.

Политики миграции

Для стандартной клиентской миграции используйте следующие значения:

Параметр
Значение
Назначение

exact

true

Включает точную проверку и MySQL read-lock

maintenance

false

Мигратор не управляет Laravel maintenance mode

switch_env

true

Переключает .env после успешного переноса

workers

3

Количество параллельно обрабатываемых таблиц

schema_mode

empty

Заполняет пустые PostgreSQL-базы

allow_non_empty

false

Запрещает перенос в непустую базу

allow_target_reset

false

Запрещает удаление PostgreSQL target

Значение maintenance=false не означает, что миграцию можно выполнять без остановки записи.

Оно означает только то, что мигратор не выполняет команды php artisan down и php artisan up. Остановить пользовательские и фоновые процессы записи нужно отдельно.

Как работает точная блокировка MySQL

Для получения согласованного снимка мигратор использует:

Пока блокировка активна, MySQL не принимает:

  • INSERT;

  • UPDATE;

  • DELETE;

  • изменение структуры таблиц;

  • другие DDL-операции.

Чтение данных продолжает работать.

Блокировка удерживается до завершения переноса, проверки PostgreSQL, обработки Pulse и переключения .env.

1

Подключение через /root/.my.cnf

При запуске от пользователя root мигратор проверяет:

Файл должен:

  • принадлежать пользователю root;

  • иметь права 0600 или строже;

  • не быть символической ссылкой;

  • содержать секцию [client] или [mysql].

Мигратор читает из файла только имя пользователя и пароль. Пароль не выводится в консоль и постоянный лог.

2

Если /root/.my.cnf недоступен

В секцию source можно добавить отдельное подключение:

Такой учётной записи требуется глобальное право:

или:


Проверка конфигурации

Выполните:

Команда не подключается к базам и не изменяет данные.

Она проверяет:

  • формат JSON;

  • обязательные поля;

  • драйверы;

  • настройки профилей;

  • политики безопасности;

  • неизвестные параметры.

Исправьте все найденные ошибки до продолжения.

Проверка PostgreSQL-баз

Выполните:

Команда не блокирует MySQL и не переносит данные.

Она проверяет:

  • PostgreSQL имеет версию 18.x;

  • кодировка базы равна UTF8;

  • база существует;

  • имя пользователя и пароль подходят;

  • пользователь имеет право подключения;

  • пользователь может создавать объекты;

  • база доступна для записи;

  • база не содержит пользовательских таблиц;

  • база не содержит следов предыдущего переноса.

Проверка плана

Выполните от пользователя root:

Команда не получает глобальную блокировку и не записывает данные.

Она проверяет:

  • соединение с MySQL;

  • учётную запись для read-lock;

  • наличие требуемых прав;

  • версию и кодировку MySQL;

  • таблицы и колонки;

  • индексы и внешние ключи;

  • generated columns;

  • значения JSON;

  • zero-date;

  • числовые диапазоны;

  • количество строк;

  • объём данных;

  • режим схемы;

  • число workers;

  • параметры будущего .env.

Перед переносом должны успешно завершиться:


Создание резервной копии MySQL

Резервная копия создаётся до запуска мигратора, пока .env указывает на MySQL.

Для миграции и резервного копирования используются разные конфигурации:

Если db-backup.json ещё не существует:

Проверьте конфигурацию:

Создайте резервную копию:

По умолчанию файлы сохраняются в каталоге:

Для каждого SQL-файла создаётся файл:

Manifest содержит:

  • профиль;

  • метку запуска;

  • тип базы данных;

  • размер файла;

  • контрольную сумму SHA-256;

  • количество таблиц;

  • индексы и ключи;

  • количество строк.

После создания резервной копии:

  1. Проверьте наличие SQL-файлов и manifest.

  2. Скачайте файлы с сервера.

  3. Сохраните их во внешнем защищённом хранилище.

  4. Не оставляйте единственную копию на сервере с проектом.


Подготовка окна обслуживания

Перед запуском run:

  • запретите создание новых заявок;

  • остановите внешний трафик, который может записывать данные;

  • временно остановите очереди;

  • остановите процессы записи курсов, логов и метрик;

  • не запускайте Product Updates;

  • не запускайте Laravel migrations;

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

  • предупредите сотрудников о технических работах.

Запуск миграции

Находясь в корне Backend-проекта и работая от пользователя root, выполните:

Параметр --schema-mode empty явно подтверждает, что перенос выполняется в пустые базы, созданные в FASTPANEL.

Во время запуска мигратор:

  1. повторно проверяет конфигурацию;

  2. получает MySQL read-lock;

  3. проверяет значения источника;

  4. проверяет пустоту PostgreSQL-баз;

  5. создаёт PostgreSQL DDL;

  6. проверяет DDL внутри транзакции с ROLLBACK;

  7. создаёт таблицы;

  8. переносит строки;

  9. создаёт первичные и уникальные ключи;

  10. создаёт индексы;

  11. создаёт внешние ключи;

  12. создаёт CHECK-ограничения;

  13. настраивает identity и sequences;

  14. создаёт необходимые триггеры;

  15. выполняет ANALYZE;

  16. проверяет структуру;

  17. сравнивает количество строк;

  18. вычисляет SHA-256 digest;

  19. обрабатывает профиль Pulse;

  20. переключает .env;

  21. удаляет Laravel config cache;

  22. снимает MySQL read-lock.

1

Отображение прогресса

Пример:

2

Постоянный лог

Каждый запуск создаёт приватный лог:

Посмотреть последние логи:

Пароли в постоянный лог не записываются.

Как понять, что перенос завершился успешно

Успешный результат должен подтверждать:

  • профиль main завершён успешно;

  • профиль pulse завершён или явно пропущен;

  • все выбранные таблицы обработаны;

  • все строки перенесены;

  • структура PostgreSQL совпала с ожидаемой;

  • количество строк совпало;

  • SHA-256 digest совпал;

  • .env переключён;

  • Laravel config cache удалён;

  • MySQL read-lock снят;

  • команда завершилась кодом 0.

Если в результате присутствует:

или:

миграция не считается завершённой.


Автоматическое переключение .env

При настройке:

мигратор записывает для основной базы:

Для Laravel Pulse:

Перед изменением создаётся резервная копия:

Проверить подключение без вывода паролей:

Независимая точная проверка

До запуска приложения, Product Updates и Laravel migrations выполните от пользователя root:

Команда:

  • повторно получает MySQL read-lock;

  • заново читает исходную MySQL;

  • заново читает PostgreSQL;

  • сравнивает структуру;

  • сравнивает ограничения;

  • сравнивает sequences;

  • сравнивает количество строк;

  • заново вычисляет SHA-256 digest.

Команда verify должна выполняться до Product Updates.

После Product Updates структура PostgreSQL может измениться. Для последующих проверок используется iEX DB Auditor.

Обновление продукта после переноса

После успешного verify переключитесь на владельца Backend-сайта.

Пример:

Сначала проверьте план:

Если проверка завершилась успешно, примените обновления:

Для строгой проверки можно использовать:

Диагностика:

Последние запуски:

Проверка подключения и процессов

Проверьте PostgreSQL-подключение:

Проверьте migrations:

Проверьте Product Updates:

При необходимости очистите runtime cache:

Перезапустите очереди:

Если проект использует Horizon, Reverb, PM2 или systemd-службы, перезапустите их установленным для сервера способом.

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


Функциональная проверка

Перед возвратом клиентского трафика проверьте:

  • открытие административной панели;

  • авторизацию администратора;

  • список пользователей;

  • валюты;

  • резервы;

  • направления обмена;

  • платёжные системы;

  • мерчанты;

  • существующие заявки;

  • создание тестовой заявки;

  • изменение статуса тестовой заявки;

  • поиск и сортировку;

  • задания очередей;

  • CRON;

  • Laravel Pulse;

  • Reverb;

  • отправку уведомлений;

  • логи приложения.

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

  • сортировку строк;

  • полнотекстовый поиск;

  • фильтры;

  • JSON-поля;

  • даты и время;

  • generated values;

  • автоматические временные метки.

Сортировка строк и полнотекстовый поиск в MySQL и PostgreSQL могут работать по-разному даже при точном переносе значений.

Аудит PostgreSQL

После Product Updates строгий verify с MySQL больше не используется, потому что структура PostgreSQL могла измениться.

Выполните проверку конфигурации аудитора:

Запустите аудит:

Аудитор проверяет:

  • таблицы;

  • первичные ключи;

  • внешние ключи;

  • индексы;

  • sequences;

  • triggers;

  • статистику;

  • блокировки;

  • длительные транзакции;

  • невалидные ограничения;

  • неготовые индексы.

Аудитор работает в режиме чтения и не изменяет базу.


Резервная копия PostgreSQL

После Product Updates и функциональной проверки создайте резервную копию PostgreSQL:

После переключения .env профили main и pulse будут использовать PostgreSQL.

Скачайте SQL-файлы и manifest с сервера и сохраните их во внешнем защищённом хранилище.

Возврат клиентского трафика

Возвращайте трафик только после того, как подтверждено:

  • run завершился успешно;

  • отдельный verify завершился успешно;

  • .env указывает на PostgreSQL;

  • Product Updates завершился успешно;

  • PostgreSQL-аудит не обнаружил критических ошибок;

  • очереди и долгоживущие процессы перезапущены;

  • основные функции приложения проверены;

  • резервная копия PostgreSQL создана.

После этого:

  1. Разрешите создание заявок.

  2. Запустите очереди и остальные процессы.

  3. Создайте контрольную заявку.

  4. Проверьте её обработку.

  5. Наблюдайте за логами приложения.

Возврат подключения на MySQL

Перед переключением мигратор создаёт файл:

Посмотрите доступные копии:

Для восстановления используйте точные пути:

Команда:

  • атомарно восстанавливает .env;

  • удаляет Laravel config cache;

  • не удаляет PostgreSQL;

  • не изменяет MySQL;

  • не запускает PHP или Artisan.

После восстановления перезапустите очереди и другие долгоживущие процессы.


Дополнительные режимы для технических специалистов

Стандартная клиентская миграция выполняется только в режиме:

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

Режим
Назначение

empty

Заполнить пустую базу, заранее созданную в FASTPANEL

auto

Автоматически создать роль, промежуточную базу и финальную базу

existing

Использовать полностью подготовленную заранее структуру PostgreSQL

1

Режим auto

Режим может использоваться, когда мигратор должен самостоятельно создать PostgreSQL-роль и базы.

Он требует административных прав PostgreSQL и не является стандартным вариантом для баз, созданных через FASTPANEL.

Не используйте этот режим только ради автоматизации обычного клиентского переноса.

2

Режим existing

Режим предназначен для базы, в которой необходимая PostgreSQL-структура уже полностью создана:

Мигратор проверяет существующую структуру перед переносом данных.

3

Временное изменение режима

Режим можно передать через команду:

Параметр командной строки должен соответствовать реальному состоянию целевой базы.


Тестовая репетиция

Для репетиции создайте отдельные пустые PostgreSQL-базы.

Укажите тестовые имена в target:

Запустите перенос без переключения .env:

Параметр --no-switch:

  • переносит структуру;

  • переносит данные;

  • выполняет проверку;

  • не изменяет .env;

  • не переключает приложение.

Сброс целевой PostgreSQL-базы

Команда reset-target не используется при обычной клиентской миграции.

Для её разрешения необходимо временно установить:

После этого команда:

удалит выбранную PostgreSQL-базу целиком.

Команда не удаляет исходную MySQL и не удаляет PostgreSQL-роль.

После выполнения верните безопасное значение:

Команды мигратора

Команда
Изменяет данные
Назначение

config-check

Нет

Проверить приватную JSON-конфигурацию

target-check

Нет

Проверить PostgreSQL, кодировку, права и пустоту

plan

Нет

Проверить источник и построить план

schema

Только указанный файл

Сформировать PostgreSQL DDL

run

Да

Выполнить перенос и при необходимости переключить .env

verify

Нет

Повторно сравнить MySQL и PostgreSQL

reset-target

Да

Удалить выбранную PostgreSQL-базу

rollback-env

Да

Восстановить .env из резервной копии

Параметры мигратора

Параметр
Назначение

--profile main

Обработать только основную базу

--profile pulse

Обработать только Laravel Pulse

--profile all

Обработать основную базу и Pulse

--config <path>

Указать путь к JSON-конфигурации

--env <path>

Указать путь к Laravel .env

--workers <n>

Временно изменить количество workers

--no-switch

Не переключать .env

--schema-mode empty

Использовать пустую PostgreSQL-базу

--schema-mode auto

Автоматически создать базу

--schema-mode existing

Использовать заранее подготовленную схему

--schema-out <path>

Указать путь для команды schema

--backup <path>

Указать резервную копию .env для возврата

--yes

Подтвердить команду, изменяющую данные

--version

Показать версию

Частые ошибки

Проверьте пользователя, от которого запущен мигратор:

Ожидаемый результат:

Проверьте файл:

Если файл отсутствует, настройте отдельное подключение source.lock.

Не выдавайте глобальные права обычному пользователю приложения и не отключайте exact.

Проверьте в секции target:

  • имя базы;

  • имя пользователя;

  • пароль;

  • порт;

  • sslmode.

Используйте пользователя, созданного вместе с PostgreSQL-базой в FASTPANEL.

Не используйте fastuser как пользователя приложения.

Проверить подключение можно командой:

PostgreSQL-база уже использовалась мигратором и не считается чистой.

Для рабочего переноса создайте новую пустую базу через FASTPANEL. Не удаляйте служебные таблицы вручную, если не уверены в происхождении объектов.

Для пустой базы выбран режим:

Используйте:

И запустите:

До переноса в ней были созданы таблицы.

Не выполняйте для пустой целевой базы:

Не загружайте SQL-схему вручную. Создайте новую пустую PostgreSQL-базу через FASTPANEL.

Удаление базы не всегда удаляет PostgreSQL-роль.

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

Не выполняйте DROP ROLE без проверки зависимостей.

Отключите профиль:

Не оставляйте включённый профиль с вымышленными реквизитами.

Мигратор принимает PostgreSQL 18.x.

Проверьте установленную версию и выполните инструкцию подключения PostgreSQL 18.

Точная команда verify должна выполняться до Product Updates.

Если Product Updates уже изменил структуру PostgreSQL, используйте аудитор:

Не создавайте объект вручную.

Проверьте:

  1. завершился ли run без ошибок;

  2. завершился ли verify;

  3. присутствовал ли объект в исходной MySQL;

  4. не запускалось ли приложение между run и verify;

  5. какую ошибку показывает doctor.

Диагностика:

Посмотрите последний постоянный лог:

Не удаляйте PostgreSQL-базу без проверки состояния переноса.

Если база была опубликована, сначала изучите лог и выполните verify.

Выполните:

Файл:

  • не должен быть символической ссылкой;

  • не должен быть доступен группе;

  • не должен быть доступен другим пользователям;

  • не должен попадать в Git.

Проверьте права:

Проверьте архитектуру:

Launcher должен выбрать соответствующий бинарный файл.


Короткая последовательность команд

Работа выполняется от пользователя root из корня Backend:

После успешного verify переключитесь на владельца сайта:

Выполните:


Контрольный список

1

До переноса

2

После переноса