--- # Инструкция по переносу схем public из 15 БД в одну БД ## Этап 1. Бэкап исходных БД **Ваш скрипт (без изменений):** ```bash #!/bin/bash # Список баз данных для дампа databases=("edemo_ehd1" "edemo_ehd2" "edemo_ehd3" "edemo_ehd4" "edemo_ehd5" "edemo_ehd6" "edemo_ehd7" "edemo_ehd8" "edemo_ehd9" "edemo_ehd10" "edemo_ehd11" "edemo_ehd12" "edemo_ehd13" "edemo_ehd14" "edemo_ehd15") for db in "${databases[@]}"; do echo "Dumping database: $db" # Создаем директорию для дампа (если не существует) mkdir -p "/opt/pg_dumps/${db}.dir" pg_dump -p 5432 -U postgres -d "$db" \ -F d \ -j 4 \ --no-tablespaces \ --no-owner \ -f "/opt/pg_dumps/${db}.dir" if [ $? -eq 0 ]; then echo "$db dumped successfully" else echo "$db failed" fi done ``` **Выполнить:** `chmod +x script.sh && ./script.sh` --- ## Этап 2. Рестор бэкапов во временные БД **Ваш скрипт (без изменений):** ```bash #!/bin/bash DB_HOST="localhost" DB_PORT="5432" DB_USER="postgres" DB_OWNER="eds" DUMP_BASE_PATH="/pgbkp/20260730" # Список баз databases=("db01" "db02" "db03") # Создаем базы for db in "${databases[@]}"; do echo "Creating: $db" psql -p "$DB_PORT" -U "$DB_USER" -c "DROP DATABASE IF EXISTS $db WITH (FORCE)" -q psql -p "$DB_PORT" -U "$DB_USER" -v ON_ERROR_STOP=1 << EOF CREATE DATABASE $db WITH OWNER = $DB_OWNER ENCODING = 'UTF8' LC_COLLATE = 'en_US.UTF-8' LC_CTYPE = 'en_US.UTF-8' ICU_LOCALE = 'en-US' LOCALE_PROVIDER = 'icu' TABLESPACE = pg_default CONNECTION LIMIT = -1 IS_TEMPLATE = False; ALTER DATABASE $db SET log_statement TO 'ddl'; EOF if [ $? -eq 0 ]; then echo "Database $db created with owner $DB_OWNER" else echo "Failed to create database $db" exit 1 fi done export PGPASSWORD="" # Восстанавливаем for db in "${databases[@]}"; do echo "Restoring: $db" if pg_restore -h "$DB_HOST" -p "$DB_PORT" -U "$DB_OWNER" -d "$db" -F d -j 4 --no-owner --no-privileges --no-tablespaces "$DUMP_BASE_PATH/${db}_dir"; then echo "Restore $db Done" else echo "Restore $db Failed" fi done echo "All done" unset PGPASSWORD ``` **Выполнить:** `chmod +x script.sh && ./script.sh` **ВНИМАНИЕ:** В этом скрипте нужно заменить: - `databases=("db01" "db02" "db03")` → `databases=("edemo_ehd1" ... "edemo_ehd15")` - `DUMP_BASE_PATH="/pgbkp/20260730"` → `DUMP_BASE_PATH="/opt/pg_dumps"` - `${db}_dir` → `${db}.dir` --- ## Этап 3. Переименование схем public во временных БД **Выполнять вручную через psql!** ```sql \c edemo_ehd1 ALTER SCHEMA public RENAME TO ehd1; \c edemo_ehd2 ALTER SCHEMA public RENAME TO ehd2; \c edemo_ehd3 ALTER SCHEMA public RENAME TO ehd3; \c edemo_ehd4 ALTER SCHEMA public RENAME TO ehd4; \c edemo_ehd5 ALTER SCHEMA public RENAME TO ehd5; \c edemo_ehd6 ALTER SCHEMA public RENAME TO ehd6; \c edemo_ehd7 ALTER SCHEMA public RENAME TO ehd7; \c edemo_ehd8 ALTER SCHEMA public RENAME TO ehd8; \c edemo_ehd9 ALTER SCHEMA public RENAME TO ehd9; \c edemo_ehd10 ALTER SCHEMA public RENAME TO ehd10; \c edemo_ehd11 ALTER SCHEMA public RENAME TO ehd11; \c edemo_ehd12 ALTER SCHEMA public RENAME TO ehd12; \c edemo_ehd13 ALTER SCHEMA public RENAME TO ehd13; \c edemo_ehd14 ALTER SCHEMA public RENAME TO ehd14; \c edemo_ehd15 ALTER SCHEMA public RENAME TO ehd15; ``` --- ## Этап 4. Бэкап временных БД **Использовать скрипт из Этапа 1, заменив список баз:** ```bash databases=("edemo_ehd1" "edemo_ehd2" ... "edemo_ehd15") ``` Дампы сохранятся в `/opt/pg_dumps/` (перезапишут исходные дампы). --- Добавляю создание схем в этап 5: --- ## Этап 5. Подготовка целевой БД ### 5.1. Создание целевой БД (если не создана) ```sql CREATE DATABASE mmedo_main WITH OWNER = eds ENCODING = 'UTF8' LC_COLLATE = 'en_US.UTF-8' LC_CTYPE = 'en_US.UTF-8' ICU_LOCALE = 'en-US' LOCALE_PROVIDER = 'icu' TABLESPACE = pg_default CONNECTION LIMIT = -1 IS_TEMPLATE = False; ``` ### 5.2. Подключение к целевой БД и создание схем ```sql \c mmedo_main -- Создание схемы public (для расширений) CREATE SCHEMA IF NOT EXISTS public; -- Создание схем для данных CREATE SCHEMA IF NOT EXISTS ehd1; CREATE SCHEMA IF NOT EXISTS ehd2; CREATE SCHEMA IF NOT EXISTS ehd3; CREATE SCHEMA IF NOT EXISTS ehd4; CREATE SCHEMA IF NOT EXISTS ehd5; CREATE SCHEMA IF NOT EXISTS ehd6; CREATE SCHEMA IF NOT EXISTS ehd7; CREATE SCHEMA IF NOT EXISTS ehd8; CREATE SCHEMA IF NOT EXISTS ehd9; CREATE SCHEMA IF NOT EXISTS ehd10; CREATE SCHEMA IF NOT EXISTS ehd11; CREATE SCHEMA IF NOT EXISTS ehd12; CREATE SCHEMA IF NOT EXISTS ehd13; CREATE SCHEMA IF NOT EXISTS ehd14; CREATE SCHEMA IF NOT EXISTS ehd15; -- Назначение владельца для всех схем ALTER SCHEMA public OWNER TO eds; ALTER SCHEMA ehd1 OWNER TO eds; ALTER SCHEMA ehd2 OWNER TO eds; ALTER SCHEMA ehd3 OWNER TO eds; ALTER SCHEMA ehd4 OWNER TO eds; ALTER SCHEMA ehd5 OWNER TO eds; ALTER SCHEMA ehd6 OWNER TO eds; ALTER SCHEMA ehd7 OWNER TO eds; ALTER SCHEMA ehd8 OWNER TO eds; ALTER SCHEMA ehd9 OWNER TO eds; ALTER SCHEMA ehd10 OWNER TO eds; ALTER SCHEMA ehd11 OWNER TO eds; ALTER SCHEMA ehd12 OWNER TO eds; ALTER SCHEMA ehd13 OWNER TO eds; ALTER SCHEMA ehd14 OWNER TO eds; ALTER SCHEMA ehd15 OWNER TO eds; ``` ### 5.3. Перенос расширений в схему public **Посмотреть все расширения:** ```sql \c mmedo_main \dx ``` **Вывод будет примерно таким:** ``` List of installed extensions Name | Version | Schema | Description ----------+---------+------------+------------------------------------------------------------------- pg_trgm | 1.6 | ehd1 | text similarity measurement and index searching based on trigrams plpgsql | 1.0 | pg_catalog | PL/pgSQL procedural language uuid-ossp| 1.1 | ehd2 | generate universally unique identifiers (UUIDs) ``` **Для каждого расширения, у которого Schema не public, выполнить:** ```sql ALTER EXTENSION pg_trgm SET SCHEMA public; ALTER EXTENSION uuid-ossp SET SCHEMA public; -- и так далее для всех расширений, которые находятся не в public ``` **Проверить результат:** ```sql \dx ``` Теперь все расширения должны быть в схеме `public`. --- ## Этап 6. Восстановление в целевую БД **Выполнять вручную для каждой схемы!** ### Для схемы ehd1: ```bash # pre-data pg_restore -p 5432 -U eds -d mmedo_main \ -F d \ -n ehd1 \ --no-tablespaces \ --no-owner \ --no-privileges \ --section=pre-data \ /opt/pg_dumps/edemo_ehd1.dir/ # data pg_restore -p 5432 -U eds -d mmedo_main \ -F d \ -j 4 \ -n ehd1 \ --no-tablespaces \ --no-owner \ --no-privileges \ --section=data \ --disable-triggers \ /opt/pg_dumps/edemo_ehd1.dir/ # post-data pg_restore -p 5432 -U eds -d mmedo_main \ -F d \ -j 4 \ -n ehd1 \ --no-tablespaces \ --no-owner \ --no-privileges \ --section=post-data \ /opt/pg_dumps/edemo_ehd1.dir/ ``` ### Для схемы ehd2: ```bash # pre-data pg_restore -p 5432 -U eds -d mmedo_main \ -F d \ -n ehd2 \ --no-tablespaces \ --no-owner \ --no-privileges \ --section=pre-data \ /opt/pg_dumps/edemo_ehd2.dir/ # data pg_restore -p 5432 -U eds -d mmedo_main \ -F d \ -j 4 \ -n ehd2 \ --no-tablespaces \ --no-owner \ --no-privileges \ --section=data \ --disable-triggers \ /opt/pg_dumps/edemo_ehd2.dir/ # post-data pg_restore -p 5432 -U eds -d mmedo_main \ -F d \ -j 4 \ -n ehd2 \ --no-tablespaces \ --no-owner \ --no-privileges \ --section=post-data \ /opt/pg_dumps/edemo_ehd2.dir/ ``` **И так далее до ehd15, подставляя соответствующий номер.** --- ## Этап 7. Пост-миграционная проверка ### Поиск объектов со ссылками на public: ```sql WITH defs AS ( SELECT 'function'::text AS kind, n.nspname AS schema_name, p.oid::regprocedure::text AS object, pg_get_functiondef(p.oid) AS definition FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace WHERE p.prokind IN ('f', 'p') UNION ALL SELECT 'view/matview', n.nspname, c.oid::regclass::text, pg_get_viewdef(c.oid) FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind IN ('v', 'm') UNION ALL SELECT 'trigger', n.nspname, format('%s.%s', t.tgrelid::regclass, t.tgname), pg_get_triggerdef(t.oid) FROM pg_trigger t JOIN pg_class c ON c.oid = t.tgrelid JOIN pg_namespace n ON n.oid = c.relnamespace WHERE NOT t.tgisinternal UNION ALL SELECT 'default', n.nspname, format('%s.%s', a.attrelid::regclass, a.attname), pg_get_expr(d.adbin, d.adrelid) FROM pg_attrdef d JOIN pg_attribute a ON a.attrelid = d.adrelid AND a.attnum = d.adnum JOIN pg_class c ON c.oid = a.attrelid JOIN pg_namespace n ON n.oid = c.relnamespace UNION ALL SELECT 'constraint', n.nspname, format('%s.%s', c.conrelid::regclass, c.conname), pg_get_constraintdef(c.oid) FROM pg_constraint c JOIN pg_class cl ON cl.oid = c.conrelid JOIN pg_namespace n ON n.oid = cl.relnamespace WHERE c.conrelid <> 0 UNION ALL SELECT 'index', n.nspname, i.indexrelid::regclass::text, pg_get_indexdef(i.indexrelid) FROM pg_index i JOIN pg_class c ON c.oid = i.indexrelid JOIN pg_namespace n ON n.oid = c.relnamespace UNION ALL SELECT 'rule', n.nspname, format('%s.%s', r.ev_class::regclass, r.rulename), pg_get_ruledef(r.oid) FROM pg_rewrite r JOIN pg_class c ON c.oid = r.ev_class JOIN pg_namespace n ON n.oid = c.relnamespace ) SELECT kind, schema_name, object, definition FROM defs WHERE definition LIKE '%public.%' OR definition LIKE '%"public".%' ORDER BY schema_name, kind, object; ``` --- ### Этап 8. Финальная сборка статистики После завершения накатывания пост-данных обновляем статистику, чтобы оптимизатор Postgres знал о размерах таблиц: ```bash psql -h localhost -p 5432 -U postgres -d eds-main -c "VACUUM ANALYZE;" ```