null

Краткий обзор pgloader: инструмент для миграции баз данных

Во время крупных обновлений инфраструктуры системы, при необходимости значительных архитектурных изменений и в других неприятных ситуациях зачастую возникает потребность в серьезных правках структуры используемой базы данных. В таком случае полезной может оказаться команда pgloader. Утилита pgloader представляет собой мощный специализированный инструмент с открытым исходным кодом, предназначенный для автоматизированной миграции и загрузки данных в PostgreSQL из различных источников. Главная ценность этого инструмента заключается в полной автоматизации процесса. Pgloader умеет самостоятельно считывать схему данных источника, транслировать типы данных в совместимые с PostgreSQL, создавать таблицы, индексы и переносить саму информацию за одну команду.

Что же умеет pgloader и в чем состоит его польза? Перечислим несколько основных достоинств:

  • Мультиплатформенный импорт: утилита поддерживает миграцию «на лету» из MySQL, MS SQL, SQLite, а также загрузку из файлов формата CSV (и многих других)
  • Автоматическое преобразование типов: утилита сама сопоставляет типы данных (например, datetime из MySQL преобразует в timestamp для Postgres)
  • Умная обработка ошибок: вместо падения при первой же битой строке, pgloader отправляет некорректные данные в отдельный reject-лог, продолжая загрузку остальных записей
  • Высокая скорость: использует встроенный протокол PostgreSQL COPY для потоковой передачи больших объемов данных без накладных расходов на парсинг каждого SQL-запроса
  • Возможность настраивать правила трансформации (например, замену пустых строк на NULL или изменение кодировки).

Кроме всего перечисленного, как и у любого инструмента, у pgloader есть не только плюсы, но и свои недостатки. Так, при обработке огромных баз данных или сложных связей он может быть крайне требователен к оперативной памяти, а стэк-трейс на Common Lisp, на котором написана утилита, бывает трудно прочитать разработчику без специфического опыта. Кроме того, при всех возможностях кастомизации работа с DDL ограничена, поэтому если нужна тонкая настройка внешних ключей или специфических индексов, схему лучше подготовить в Postgres заранее, а pgloader использовать только для данных (data only).

Сама же команда pgloader запускается с помощью файла конфигурации (обычно с расширением .load). Перед примером классического файла для миграции перечислим некоторые важные флаги (добавляются при вызове команды) и параметры конфигурации в самом файле:

  • --version: показать установленную версию
  • -v, --verbose: выводить подробный лог работы (полезно для отслеживания прогресса)
  • -d, --debug: включить режим отладки
  • workers = X: количество одновременных потоков для чтения данных из источника
  • concurrency = X:  количество потоков для одновременной записи в PostgreSQL
  • include drop:  удалять существующие таблицы в Postgres перед началом импорта
  • data only:  импорта только содержимого (схема должна быть уже создана)
  • schema only: перенос только структуры таблиц без самих данных

Запуск скрипта осуществляется через простой вызов pgloader migrate.load​​​​​​​. Приведем пример файла migrate.load с комментариями:

/* 
  Указываем тип миграции. В данном случае мы переносим всю базу данных (DATABASE),
  включая схемы, таблицы, индексы и данные.
*/
LOAD DATABASE
     -- Источник: строка подключения к исходной базе данных MySQL
     FROM mysql://root:password@localhost/my_old_db
     
     -- Цель: строка подключения к целевой базе данных PostgreSQL
     INTO postgresql://postgres:password@localhost/my_new_db

 /* 
   Секция WITH задает глобальные параметры поведения pgloader 
 */
 WITH include drop,      -- Удалять таблицы в Postgres, если они там уже существуют
      create tables,     -- Автоматически создавать структуры таблиц в Postgres
      create indexes,    -- Автоматически переносить и создавать индексы
      reset sequences,   -- Сбросить и выставить автоинкременты (последовательности) в актуальные значения
      workers = 4,       -- Использовать 4 параллельных потока для чтения данных из MySQL
      concurrency = 2    -- Использовать 2 параллельных потока для записи данных в PostgreSQL

  /* 
    Секция SET позволяет задать специфические настройки для сессии СУБД.
    В данном случае увеличиваем таймауты MySQL, чтобы соединение не оборвалось на больших таблицах.
  */
  SET MySQL parameters
      net_read_timeout  = '120',
      net_write_timeout = '120'

 /* 
   Секция CAST - это правила трансформации типов данных (из MySQL в Postgres)
 */
 CAST type datetime to timestamp drop default drop not null,
      -- Превращаем datetime в timestamp, сбрасывая дефолтные значения и ограничение NOT NULL
      
      type tinyint to boolean using tinyint-to-boolean
      -- Конвертируем тип tinyint в логический тип (boolean) с помощью встроенной функции

 /* 
   Секция BEFORE LOAD DO позволяет выполнить любые SQL-команды 
   в целевой базе PostgreSQL строго ДО начала миграции.
 */
 BEFORE LOAD DO
   $$ drop schema if exists mysql cascade; $$; 
   -- Удаляем старую схему mysql (если она осталась от прошлых тестов), чтобы избежать конфликтов

В результате этих манипуляций после запуска pgloader командой выше мы получим вывод в стиле:

                                 table name       read   imported     errors      total time
-------------------------------------------  ---------  ---------  ---------  --------------
                            before load do...        1          1          0          0.015s
                             fetch meta data        42         42          0          0.218s
-------------------------------------------  ---------  ---------  ---------  --------------
                             mysql.users          5000       5000          0          0.452s
                             mysql.orders        12500      12498          2          1.120s
                        index build & phrs          --         --          0          0.340s
-------------------------------------------  ---------  ---------  ---------  --------------
                    reset sequences (auto)           2          2          0          0.042s
-------------------------------------------  ---------  ---------  ---------  --------------
                          Total import time      17542      17540          2          2.187s

По сводной таблице сразу видно, как прошла миграция. Колонка table name показывает этапы работы и имена перенесенных таблиц (с указанием схемы), read - сколько строк pgloader смог прочитать из MySQL, imported - сколько строк успешно записалось в PostgreSQL, errors - количество «битых» строк, которые Postgres отказался принимать (например, из-за нарушения уникальности или невалидных данных), total time - время, затраченное на каждую таблицу и операцию. Если количество ошибок больше нуля, то стоит изучить файлы *.reject и *.log, которые содержат сами строки, которые не удалось вставить, и описание конкретной ошибки соответственно. Найденные ошибки можно исправить самостоятельно и догрузить только пропущенные данные.

После того, как утилита отработает, в MySQL (источник) ничего не изменится, т.к. pgloader использует режим Read-Only для источника. Никакие данные не удалятся и не модифицируются. В PostgreSQL (Цель) сначала выполнится блок BEFORE LOAD DO: если в Postgres уже была схема с именем mysql, она полностью удалится со всеми таблицами (CASCADE). Затем последовательно создадутся - новые таблицы со структурой, аналогичной MySQL (но с адаптированными типами данных: например, tinyint превратится в boolean), все строки из MySQL, индексы и внешние ключи. Значения автоинкремента (sequences) также обновятся до актуального максимума, чтобы новые записи в Postgres начинались с правильного ID. Подобным образом, мы получим полностью готовую смигрированную новую базу с преобразованными старыми данными.

Next

Коротко о себе:

 

Работаю Java\Kotlin Backend Developer в компании Tune-it. На работе занимаюсь проектами, связанными с Liferay, NiFi, Spring Framework, а вне работы - философской антропологией