Введение

В этом руководстве я расскажу о практическом опыте переноса базы данных из 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 — клиенты для работы с БД

  • makesbcl — сборка pgloader из исходников

  • php8.3-pgsql— PHP-расширение для PostgreSQL (Laravel) в моем случае

  • freetds-* , ibssl-devlibsqlite3-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.

Заключение

Надеюсь эта статья была для вас полезна и вы успешно решите свою задачу.