Введение
В этом руководстве я расскажу о практическом опыте переноса базы данных из MySQL 8 в PostgreSQL 16.
Исходные данные:
Приложение на Laravel 11, php 8.3 в Docker-контейнере
MySQL 8 в соседнем контейнере
Задача: перенос данных в AWS RDS PostgreSQL
Выбор инструмента миграции:
После исследования доступных решений выбор пал на pgloader (https://github.com/dimitri/pgloader). Вот почему:
⭐ 6.2k stars на GitHub (самый популярный инструмент для такой задачи)
Последний коммит: 4 июня 2025 (проект немного, но поддерживается)
Открытые issues содержат множество готовых решений для типовых проблем
Альтернативы были рассмотрены, но:
Менее популярны
Хуже документированы
Имеют ограниченную поддержку
Однако при работе с pgloader был встречен ряд проблем, о решении которых напишу ниже.
Пошаговое руководство
1. Установка необходимых пакетов
1.1 Установка зависимостей
Для работы pgloader требуется ряд зависимостей. В Dockerfile это выглядит так:
# Установка базовых зависимостей RUN apt-get update && \ apt-get install -y \ default-mysql-client \ postgresql-client \ make \ sbcl \ php8.3-pgsql \ freetds-dev \ freetds-bin \ libssl-dev \ libsqlite3-dev
Что мы устанавливаем:
default-mysql-client/postgresql-client— клиенты для работы с БДmake,sbcl— сборка pgloader из исходниковphp8.3-pgsql— PHP-расширение для PostgreSQL (Laravel) в моем случаеfreetds-*,ibssl-dev,libsqlite3-dev— дополнительные библиотеки, без них не удалось поставить pgloader из архива
1.2 Установка Pgloader версии 3.6.9
Стандартная установка через apt приводит к неожиданному результату:
apt-get install -y pgloader pgloader --version # Вывод: pgloader version "3.6.7~devel"
Даже при установке конкретной версии, все равно в итоге приходит 3.6.7. К сожалению при использовании версии 3.6.7 я получала ошибку, которая описана тут https://github.com/dimitri/pgloader/issues/1211, как и ее решение - самостоятельно загрузить архив и скомпилировать верную версию.
# Загрузите последнюю версию wget https://github.com/dimitri/pgloader/releases/download/v3.6.9/pgloader-bundle-3.6.9.tgz # Распакуйте архив -tar -xvzf pgloader-bundle-3.6.9.tgz cd pgloader-bundle-3.6.9/ make sudo cp bin/pgloader /usr/local/bin/ hash -r # Проверить версию pgloader --version # должно быть 3.6.9
В моём случае доступ к внешним ресурсам из контейнера был ограничен, поэтому пришлось использовать локальный архив pgloader-bundle-3.6.9.tgz, который я положила в корень проекта и добавила в git:
Dockerfile при этом выглядел так
COPY pgloader-bundle-3.6.9.tgz /tmp/ RUN tar -xzf /tmp/pgloader-bundle-3.6.9.tgz && \ cd pgloader-bundle-3.6.9 && \ make && \ cp bin/pgloader /usr/local/bin/ RUN rm -rf /tmp/pgloader-bundle-3.6.9*
В итоге у вас должна быть установлена версия pgloader 3.6.9
2. Проблема аутентификации MySQL 8 и её решение
Распространенная ошибка при попытке запустить pgloader QMYND:MYSQL-UNSUPPORTED-AUTHENTICATION.
Варианты решений есть в https://github.com/dimitri/pgloader/issues/782
Мой итоговый вариант состоит из того, чтобы поменять аутентификацию для пользователя и изменить конфигурацию MySQL сервера.
3.1 Изменение метода аутентификации пользователя
Зайдите в контейнер MySQL.
mysql> ALTER USER 'sail_mysql'@'%' IDENTIFIED WITH mysql_native_password BY 'pAI'; # проверить mysql> SELECT user, host, plugin, authentication_string FROM mysql.user; # для нашего пользователя должно стоять mysql_native_password
3.1 Изменение конфигов на сервере
# проверка mysql> SHOW VARIABLES LIKE 'default_authentication_plugin'; # будет caching_sha2_password, а нам надо mysql_native_password # проверить откуда берет конфиги bash-4.4# mysql --help | grep -A 1 "Default options" Default options are read from the following files in the given order: /etc/my.cnf /etc/mysql/my.cnf /usr/etc/my.cnf ~/.my.cnf # cоздаем директорию /etc/mysql если ее нет mkdir -p /etc/mysql # cоздаем в /etc/mysql/my.cnf cat > /etc/mysql/my.cnf << 'EOF' [mysqld] default_authentication_plugin = mysql_native_password EOF # cосмотрим содержимое cat /etc/mysql/my.cnf 2>/dev/null || echo "Файл не создан"
Далее рестарт контейнера или сервиса mysql и проверяем еще раз, что переменные установились в native_password.
3. Проверка подключений к базам данных
Перед началом миграции стоит убедиться, что pgloader сможет подключиться к обеим базам данных.
Эти проверки выполняются из того же контейнера, где будет работать pgloader. В моем случае это контейнер с самим приложением.
pgloader --version mysql --version mysql --host=mysql-1 --user=sail_mysql -p -e "USE tableName; SHOW TABLES;" psql --version psql --host=10.100.10.10 --username=user -d tableName -c "\dt"
4. Запуск миграции данных из Mysql в Postgres через pgloader
Моя итоговая команда
pgloader --dynamic-space-size 4096 --verbose \ mysql://sail_mysql:passwordI@mysql-1:3306/tableName \ postgresql://user:password@10.100.10.10:5432/tableName
Структура подключения:
<тип_бд>://<пользователь>:<пароль>@<хост>:<порт>/<имя_базы>--verbose— подробный вывод для отладки
--dynamic-space-size 4096 — выделяем 4GB памяти для сборщика мусора Lisp-машины. Было добавлено из-за ошибки Heap exhausted during garbage collection, решение которой описано в https://github.com/dimitri/pgloader/issues/962
И вуаля, у меня миграция данных прошла успешно.
5. Настройка Laravel для работы с PostgreSQL
Для тех у кого приложение на Laravel, возможно будут полезны следующие шаги.
5.1 Обновление переменных окружения
DB_CONNECTION=pgsql DB_HOST=10.100.10.10 # IP вашего удалённого сервера DB_PORT=5432 DB_DATABASE=tableName DB_USERNAME=user DB_PASSWORD=password
5.2 В config/database.php проверьте выбранную схему, в которой приложение будет искать данные:
'pgsql' => [ ... 'search_path' => 'public', // Важно: указываем правильную схему ], # проверить схему, можно так psql --host=postgres --username=user -d tableName -c "\dt *.migrations"
Проверка, что удачно подключились
# Проверяем подключение php artisan db:show # Проверяем миграции php artisan migrate:status # Все миграции должны быть в статусе "Run"
5.3 Проверка SQL-конструкций
Проверить все конструкции типа ->selectRaw(), ->whereRaw(), ->orderByRaw(), ->groupByRaw() на предмет неупотребляемых выражений в PostgreSQL.
Заключение
Надеюсь эта статья была для вас полезна и вы успе��но решите свою задачу.
