Введение
В этом руководстве я расскажу о практическом опыте переноса базы данных из 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_password3.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=password5.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.
Заключение
Надеюсь эта статья была для вас полезна и вы успешно решите свою задачу.
