Files
psql_tasks/move_psql04.md
T
2026-08-07 18:21:31 +08:00

465 lines
11 KiB
Markdown

---
# Инструкция по переносу схем 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.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;"
```