Уровень 3.0
Предусловия:
- Изучена лекция 3 «Логическое резервное копирование»
Логическое резервное копирование
Подготовка виртуальной машины Host1
-
В терминале запустите оболочку от имени пользователя
postgresи войдите в сеансpsql:[student@ServerName ~]$ sudo -iu postgres[postgres@ServerName ~]$ psqlpsql (15.5)Type "help" for help. -
Создайте роль
student:postgres=# CREATE ROLE student WITH LOGIN CREATEDB PASSWORD 'student';CREATE ROLE -
Установите пароль для пользователя
postgres:postgres=# ALTER ROLE postgres WITH PASSWORD 'postgres';ALTER ROLE -
Создайте базу данных
pgbench_db1с владельцемstudent:postgres=# CREATE DATABASE pgbench_db1 WITH OWNER student;CREATE DATABASE -
Создайте базу данных
pgbench_db2с владельцемstudent:postgres=# CREATE DATABASE pgbench_db2 WITH OWNER student;CREATE DATABASE -
Выйдите из сеанса
psql:postgres=# \q -
Наполните базу данных
pgbench_db1с использованием тестаpgbench:[postgres@ServerName ~]$ pgbench -i -d pgbench_db1dropping old tables...NOTICE: table "pgbench_accounts" does not exist, skippingNOTICE: table "pgbench_branches" does not exist, skippingNOTICE: table "pgbench_history" does not exist, skippingNOTICE: table "pgbench_tellers" does not exist, skippingcreating tables...generating data (client-side)...100000 of 100000 tuples (100%) done (elapsed 0.02 s, remaining 0.00 s)vacuuming...creating primary keys...done in 0.15 s (drop tables 0.00 s, create tables 0.00 s, client-side generate 0.07 s, vacuum 0.03 s, primary keys 0.04 s).[postgres@ServerName ~]$ pgbench -t 100000 -d pgbench_db1 2> /dev/nullpgbench (15.5)transaction type: <builtin: TPC-B (sort of)>scaling factor: 1query mode: simplenumber of clients: 1number of threads: 1maximum number of tries: 1number of transactions per client: 100000number of transactions actually processed: 100000/100000number of failed transactions: 0 (0.000%)latency average = 0.832 msinitial connection time = 2.326 mstps = 1201.837735 (without initial connection time) -
Таким же образом наполните базу данных
pgbench_db2:[postgres@ServerName ~]$ pgbench -i -d pgbench_db2dropping old tables...NOTICE: table "pgbench_accounts" does not exist, skippingNOTICE: table "pgbench_branches" does not exist, skippingNOTICE: table "pgbench_history" does not exist, skippingNOTICE: table "pgbench_tellers" does not exist, skippingcreating tables...generating data (client-side)...100000 of 100000 tuples (100%) done (elapsed 0.02 s, remaining 0.00 s)vacuuming...creating primary keys...done in 0.18 s (drop tables 0.00 s, create tables 0.02 s, client-side generate 0.07 s, vacuum 0.04 s, primary keys 0.05 s).[postgres@ServerName ~]$ pgbench -t 100000 -d pgbench_db2 2> /dev/nullpgbench (15.5)transaction type: <builtin: TPC-B (sort of)>scaling factor: 1query mode: simplenumber of clients: 1number of threads: 1maximum number of tries: 1number of transactions per client: 100000number of transactions actually processed: 100000/100000number of failed transactions: 0 (0.000%)latency average = 0.829 msinitial connection time = 2.644 mstps = 1205.568153 (without initial connection time) -
Подключитесь в
psqlк базе данныхpgbench_db1, проверьте размер и количество строк в созданных в ней таблицах:
[postgres@ServerName ~]$ psql -d pgbench_db1
psql (15.5)
Type "help" for help.
pgbench_db1=# SELECT relname,
reltuples,
pg_size_pretty(pg_total_relation_size(relname::regclass)) total_table_size,
relhasindex
FROM pg_class
WHERE relname IN ('pgbench_accounts', 'pgbench_branches', 'pgbench_history', 'pgbench_tellers');
relname | reltuples | total_table_size | relhasindex
------------------+-----------+------------------+-------------
pgbench_accounts | 100000 | 15 MB | t
pgbench_branches | 1 | 56 kB | t
pgbench_tellers | 10 | 56 kB | t
pgbench_history | 100000 | 5168 kB | f
(4 rows)
Как видно из результата выполнения запроса, утилитой pgbench создано четыре таблицы, две из которых содержат по 100000 строк. При этом у трех таблиц имеется индекс.
Подготовка виртуальной машины Host2
-
Подключитесь к виртуальной машине посредством
webssh, указав ip-адрес Host2. -
В домашнем каталоге пользователя
studentсоздайте файл паролей и установите необходимые права доступа к нему. При этом используйте ip-адрес, соответствующий вашей виртуальной машине Host1:[student@ServerName ~]$ echo "172.29.53.222:5432:*:postgres:postgres" > .pgpass[student@ServerName ~]$ chmod 0600 .pgpassФайл паролей потребуется при использовании утилиты
pg_dumpall, чтобы многократно не вводить пароль при каждом подключении к базе данных.В качестве цели применения пароля указан экземпляр Pangolin на виртуальной машине Host1.
-
В домашнем каталоге пользователя
studentсоздайте каталогdumpи перейдите в него:[student@ServerName ~]$ mkdir dump && cd dumpДалее все действия выполняются на виртуальной машине Host2.
Копия кластера в однопоточном режиме
-
На виртуальной машине Host2 создайте логическую копию кластера, развернутого на виртуальной машине Host1. При этом используйте ip-адрес, соответствующий вашей виртуальной машине Host1:
[student@ServerName dump]$ pg_dumpall -h 172.29.53.222 -U postgres -f host1_cluster.sqlВспомним, что утилита
pg_dumpallсоздает копию всего кластера баз данных в виде SQL-скрипта. -
Посмотрите на начало созданного SQL-скрипта:
[student@ServerName dump]$ head -n 20 host1_cluster.sql---- PostgreSQL database cluster dump--SET default_transaction_read_only = off;SET client_encoding = 'UTF8';SET standard_conforming_strings = on;---- Roles--CREATE ROLE postgres;ALTER ROLE postgres WITH SUPERUSER INHERIT CREATEROLE CREATEDB LOGIN REPLICATION BYPASSRLS;UPDATE pg_authid SET rolpassword='SCRAM-SHA-256$4096:rQ0Zkpy3YPEjYNaOcRIthQ==$KoDZ3xX9Oba+v3fDOEoDh4f1ILv40m+a8V7boOWZMTA=:xyGhh8b8WQZ5n04sgJVaa3i+Bk6x7LNi/RASGJvlRBY=' WHERE rolname='postgres';CREATE ROLE student;ALTER ROLE student WITH NOSUPERUSER INHERIT NOCREATEROLE CREATEDB LOGIN NOREPLICATION NOBYPASSRLS;UPDATE pg_authid SET rolpassword='SCRAM-SHA-256$4096:DN0ALFFZLCYv3tAbSaVDvQ==$qTeQMd3C2J/GS9ExbJYZmpqFQ6rAxR//kczxyxGXGTo=:NMLRKM0/eXO7F+SW9SlIS2V916UVIIfoWzI8MqDeQe8=' WHERE rolname='student';В начале скрипта в том числе имеются команды для создания глобальных объектов, В этом случае ролей.
-
Посмотрите в SQL-скрипте команды создания баз данных:
[student@ServerName dump]$ grep 'CREATE DATABASE' host1_cluster.sqlCREATE DATABASE pgbench_db1 WITH TEMPLATE = template0 ENCODING = 'UTF8' LOCALE_PROVIDER = libc LOCALE = 'en_US.UTF-8';CREATE DATABASE pgbench_db2 WITH TEMPLATE = template0 ENCODING = 'UTF8' LOCALE_PROVIDER = libc LOCALE = 'en_US.UTF-8';Также имеются команды для создания баз данных. Обратите внимание, что базы данных создаются на основе чистого шаблона
template0.Вспомним, что использование шаблона
template0необходимо для исключения дублирования объектов при восстановлении копии базы данных, которая, как правило, создана на базе шаблонаtemplate1, допускающего изменения. -
Посмотрите в SQL-скрипте наличие команд
COPY:[student@ServerName dump]$ grep 'COPY' host1_cluster.sqlCOPY public.pgbench_accounts (aid, bid, abalance, filler) FROM stdin;COPY public.pgbench_branches (bid, bbalance, filler) FROM stdin;COPY public.pgbench_history (tid, bid, aid, delta, mtime, filler) FROM stdin;COPY public.pgbench_tellers (tid, bid, tbalance, filler) FROM stdin;COPY public.pgbench_accounts (aid, bid, abalance, filler) FROM stdin;COPY public.pgbench_branches (bid, bbalance, filler) FROM stdin;COPY public.pgbench_history (tid, bid, aid, delta, mtime, filler) FROM stdin;COPY public.pgbench_tellers (tid, bid, tbalance, filler) FROM stdin;Загрузка данных в таблицы по умолчанию выполняется командой
COPY. Для наполнения таблиц в двух базах данных потребовалось всего 8 вызовов данной команды. -
Восстановите копию кластера путем выполнения SQL-скрипта в
psqlс замером времени восстановления:[student@ServerName dump]$ time psql -U postgres -f host1_cluster.sql > /dev/nullpsql:host1_cluster.sql:14: ERROR: role "postgres" already existsreal 0m0.855suser 0m0.017ssys 0m0.014sВосстановление выполняется путем простого выполнения скрипта утилитой
psql.Для исключения вывода большого количества сообщений стандартный вывод
psqlнаправлен в/dev/null.Для замера времени выполнения восстановления перед командой
psqlиспользована командаtime.В результате восстановление заняло 0.855 сек.
-
Подключитесь в
psqlк базе данныхpgbench_db1и проверьте список имеющихся в кластере ролей и баз данных:[student@ServerName dump]$ psql -U postgres -d pgbench_db1psql (15.5)Type "help" for help.pgbench_db1=# \duList of rolesRole name | Attributes | Member of-----------+------------------------------------------------------------+-----------postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {}student | Create DB | {}pgbench_db1=# \lList of databasesName | Owner | Encoding | Collate | Ctype | ICU Locale | Locale Provider | Access privileges-------------+----------+----------+-------------+-------------+------------+-----------------+-----------------------pgbench_db1 | student | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc |pgbench_db2 | student | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc |postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc |template0 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc | =c/postgres +| | | | | | | postgres=CTc/postgrestemplate1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc | =c/postgres +| | | | | | | postgres=CTc/postgres(5 rows)Все созданные на виртуальной машине Host1 роли и базы данных восстановились.
-
Проверьте размер и количество строк в таблицах базы данных
pgbench_db1:pgbench_db1=# SELECT relname,reltuples,pg_size_pretty(pg_total_relation_size(relname::regclass)) total_table_size,relhasindexFROM pg_classWHERE relname IN ('pgbench_accounts', 'pgbench_branches', 'pgbench_history', 'pgbench_tellers');relname | reltuples | total_table_size | relhasindex------------------+-----------+------------------+-------------pgbench_accounts | 100000 | 15 MB | tpgbench_branches | 1 | 24 kB | tpgbench_history | 100000 | 5168 kB | fpgbench_tellers | 10 | 24 kB | t(4 rows)Также восстановились таблицы с данными и индексы.
Однако размер некоторых таблиц немного отличается в меньшую сторону с сохранением количества строк. Это можно объяснить тем, что
pgbenchв ходе теста порождает какое-то количество мертвых строк, в результате чего таблицы могут немного распухнуть. А поскольку логическая копия — это набор команд SQL, при ее восстановлении произошло полное перестроение таблиц и индексов с уплотнением.Аналогичную проверку при желании можно выполнить и для базы данных
pgbench_db2. -
Выйдите из psql:
pgbench_db1=# \q -
Создайте логическую копию кластера с использованием операторов
INSERT. При этом используйте ip-адрес, соответствующий вашей виртуальной машине Host1:[student@ServerName dump]$ pg_dumpall --clean --column-inserts -h 172.29.53.222 -U postgres -f host1_cluster_inserts.sqlДля использования операторов
INSERTвместоCOPYбыл применен ключ--column-inserts.Помимо этого, поскольку базы данных уже восстанавливались в кластере Host2, был указан ключ
--cleanдля генерации команд по их удалению. -
Посмотрите, сколько команд
INSERTбыло добавлено в SQL-скрипт:
[student@ServerName dump]$ grep -c 'INSERT' host1_cluster_inserts.sql
400032
В скрипте оказалось чуть более 400000 команд INSERT, то есть для каждой строки таблицы по отдельной команде INSERT.
Вспомним, что при использовании COPY потребовалось всего 8 вызовов команд.
Естественно, выполнение такого большого количества команд негативно скажется на скорости восстановления, поскольку каждую команду планировщику необходимо проанализировать.
- Восстановите копию кластера из скрипта с командами
INSERT:
[student@ServerName dump]$ time psql -U postgres -f host1_cluster_inserts.sql > /dev/null
psql:host1_cluster_inserts.sql:24: ERROR: current user cannot be dropped
psql:host1_cluster_inserts.sql:32: ERROR: role "postgres" already exists
real 3m23.174s
user 0m3.309s
sys 0m1.786s
Восстановление длилось более трех минут при том, что в случае использования команды COPY оно заняло около одной секунды.
Многие ошибки, выдаваемые psql при выполнении скрипта восстановления, несущественны.
В этом случае ошибки были связаны с тем, что сначала была предпринята попытка удаления роли postgres, а потом попытка ее создания, в то время как подключение было выполнено как раз с ролью postgres.
Это произошло из-за указания ключа --clean при создании логической копии.
- Подключитесь к базе данных
postgresи удалите базы данныхpgbench_db1иpgbench_db2:
[student@ServerName dump]$ psql -U postgres -d postgres
psql (15.5)
Type "help" for help.
postgres=# DROP DATABASE pgbench_db1;
DROP DATABASE
postgres=# DROP DATABASE pgbench_db2;
DROP DATABASE
- Выйдите из
psql:
postgres=# \q
Копия кластера в многопоточном режиме
-
Создайте логическую копию в многопоточном режиме с использованием утилит
pg_dumpallиpg_dump. При этом используйте ip-адрес, соответствующий вашей виртуальной машине Host1:[student@ServerName dump]$ (pg_dumpall --clean --globals-only -h 172.29.53.222 -U postgres -f host1_cluster_globals.sql &pg_dump -F d -j 2 --column-inserts -h 172.29.53.222 -U postgres -d pgbench_db1 -f pgbench_db1.directory &pg_dump -F d -j 2 --column-inserts -h 172.29.53.222 -U postgres -d pgbench_db2 -f pgbench_db2.directory &wait)Вспомним, что
pg_dumpallимеет возможность выгрузки данных только в однопоточном режиме. В связи с этим кластеры большого объема могут выгружаться долго.Однако имеется возможность выгрузки только глобальных объектов. Для этого был использован ключ
--globals-only.А базы данных были выгружены отдельно с использованием утилиты
pg_dumpв параллельном режиме (В этом случае в два потока — ключ-j 2) в архивном форматеdirectory(ключ-F d).Выполнение всех указанных команд происходило параллельно. Для этого в оболочке в конце команды указан символ
&и добавлена командаwaitдля ожидания завершения всех параллельно выполняемых команд. -
Восстановите кластер из созданных копий:
[student@ServerName dump]$ time (psql -U postgres -f host1_cluster_globals.sql > /dev/null && (pg_restore --create -F d -j 2 -d postgres -U postgres pgbench_db1.directory &pg_restore --create -F d -j 2 -d postgres -U postgres pgbench_db2.directory &wait))psql:host1_cluster_globals.sql:16: ERROR: current user cannot be droppedpsql:host1_cluster_globals.sql:24: ERROR: role "postgres" already existsreal 0m12.402suser 0m1.338ssys 0m1.070sСначала с использованием
psqlбыли созданы глобальные объекты, далее с использованием утилитыpg_restoreпараллельно были восстановлены имеющиеся копии баз данных.При этом в
pg_restoreвосстановление выполнялось с использованием двух потоков-j 2.Также в
pg_restoreуказан ключ--createдля создания базы данных и подключения к ней перед восстановлением.В результате восстановление в многопоточном режиме выполнялось чуть более 12 секунд, против трех минут в однопоточном режиме.
Вспомним, что длительное время восстановления в нашем эксперименте связано с использованием в копии большого количества операторов
INSERT.При использовании операторов
COPYпри таком небольшом объеме баз данных разница не была бы заметна. -
Подключитесь в
psqlи убедитесь, что база данныхpgbench_db1успешно восстановилась:[student@ServerName dump]$ psql -U postgres -d pgbench_db1psql (15.5)Type "help" for help.pgbench_db1=# SELECT relname,reltuples,pg_size_pretty(pg_total_relation_size(relname::regclass)) total_table_size,relhasindexFROM pg_classWHERE relname IN ('pgbench_accounts', 'pgbench_branches', 'pgbench_history', 'pgbench_tellers');relname | reltuples | total_table_size | relhasindex------------------+-----------+------------------+-------------pgbench_history | 100000 | 5168 kB | fpgbench_accounts | 100000 | 15 MB | tpgbench_tellers | 10 | 24 kB | tpgbench_branches | 1 | 24 kB | t(4 rows)В многопоточном режиме база данных
pgbench_db1успешно восстановилась.Аналогичную проверку можно выполнить и для базы данных
pgbench_db2. -
Подключитесь к базе данных
postgresи удалите базу данныхpgbench_db1:pgbench_db1=# \c postgresYou are now connected to database "postgres" as user "postgres".postgres=# DROP DATABASE pgbench_db1;DROP DATABASE -
Выйдите из
psql:postgres=# \q -
Посмотрите содержимое каталога копии какой-нибудь базы данных, например
pgbench_db1:[student@ServerName dump]$ ls -1 pgbench_db1.directory4430.dat.gz4431.dat.gz4432.dat.gz4433.dat.gztoc.datВ формате
directoryв каталоге для каждой таблицы создается отдельный файл, который по умолчанию сжат.Также имеется файл с оглавлением
toc.dat. Посмотрим содержащееся в нем его оглавление. -
С помощью утилиты
pg_restoreпосмотрите список объектов в файле с оглавлением:[student@ServerName dump]$ pg_restore --list pgbench_db1.directory;; Archive created at 2025-10-01 09:57:22 UTC; dbname: pgbench_db1; TOC Entries: 15; Compression: -1; Dump Version: 1.14-0; Format: DIRECTORY; Integer: 4 bytes; Offset: 8 bytes; Dumped from database version: 15.5; Dumped by pg_dump version: 15.5;;; Selected TOC Entries:;242; 1259 16538 TABLE public pgbench_accounts postgres243; 1259 16541 TABLE public pgbench_branches postgres240; 1259 16532 TABLE public pgbench_history postgres241; 1259 16535 TABLE public pgbench_tellers postgres4432; 0 16538 TABLE DATA public pgbench_accounts postgres4433; 0 16541 TABLE DATA public pgbench_branches postgres4430; 0 16532 TABLE DATA public pgbench_history postgres4431; 0 16535 TABLE DATA public pgbench_tellers postgres4274; 2606 16553 CONSTRAINT public pgbench_accounts pgbench_accounts_pkey postgres4276; 2606 16549 CONSTRAINT public pgbench_branches pgbench_branches_pkey postgres4272; 2606 16551 CONSTRAINT public pgbench_tellers pgbench_tellers_pkey postgresВ оглавлении имеется список таблиц, данные для таблиц в виде отдельных записей и ограничения первичного ключа.
Указанный список можно отредактировать (например, удалить какую-нибудь таблицу) и сохранить в виде отдельного файла. Далее им можно воспользоваться для выборочного восстановления с использованием ключа
--use-list файл_объектовв утилитеpg_restore.Однако мы применим другой способ восстановления — укажем таблицы в параметрах команды
pg_restore. -
Выполните частичное восстановление базы данных
pgbench_db1:[student@ServerName dump]$ pg_restore --create -F d -j 2 -d postgres -U postgres -t pgbench_accounts -t pgbench_history pgbench_db1.directoryКаждая таблица для восстановления была указана с использованием отдельного ключа
-t. -
Подключитесь в
psqlи проверьте содержимое базы данныхpgbench_db1:[student@ServerName dump]$ psql -U postgres -d pgbench_db1psql (15.5)Type "help" for help.pgbench_db1=# SELECT relname,reltuples,pg_size_pretty(pg_total_relation_size(relname::regclass)) total_table_size,relhasindexFROM pg_classWHERE relname IN ('pgbench_accounts', 'pgbench_branches', 'pgbench_history', 'pgbench_tellers');relname | reltuples | total_table_size | relhasindex------------------+-----------+------------------+-------------pgbench_history | 100000 | 5168 kB | fpgbench_accounts | 100000 | 13 MB | f(2 rows)В базе данных восстановились только указанные таблицы.
Обратите внимание, что индекс для первичного ключа таблицы
pgbench_accountsвосстановлен не был, так как для восстановления были указаны только таблицы.Для восстановления индекса можно было воспользоваться ключом
-I.
Завершение
Завершение на виртуальной машине Host1
-
Подключитесь к базе данных
postgres:pgbench_db1=# \c postgresYou are now connected to database "postgres" as user "postgres". -
Удалите базы данных
pgbench_db1иpgbench_db2:postgres=# DROP DATABASE pgbench_db1;DROP DATABASEpostgres=# DROP DATABASE pgbench_db2;DROP DATABASE -
Удалите роль
student, выйдите изpsqlи режима выполнения команд от имени пользователяpostgres:postgres=# DROP ROLE student;DROP ROLEpostgres=# \q[postgres@ServerName ~]$ exitlogout
Завершение на виртуальной машине Host2
-
Подключитесь к базе данных
postgres:pgbench_db1=# \c postgresYou are now connected to database "postgres" as user "postgres". -
Удалите базы данных
pgbench_db1иpgbench_db2:postgres=# DROP DATABASE pgbench_db1;DROP DATABASEpostgres=# DROP DATABASE pgbench_db2;DROP DATABASE -
Удалите роль
studentи выйдите изpsql:postgres=# DROP ROLE student;DROP ROLEpostgres=# \q -
Удалите каталог
dump:[student@ServerName dump]$ cd ~ && rm -r dump
Самопроверка
Вопрос 1
Восстановление данных из логической резервной копии выполняется следующей командой:
[student@pangolin-prac-l964qa ~]$ pg_restore -F d -d some_db -U student -t some_table /dump/db_dump.directory
Какие объекты будут восстановлены в результате успешного выполнения данной команды?
Вопрос 2
В оболочке bash выполнена следующая последовательность команд:
[student@pangolin-prac-l964qa ~]$ psql -d some_db -U postgres -c '\d'
List of relations
Schema | Name | Type | Owner
--------+------+-------+----------
public | t1 | table | postgres
public | t2 | table | postgres
public | t3 | table | postgres
(3 rows)
[student@pangolin-prac-l964qa ~]$ pg_dump -F d -j 2 -U postgres -d some_db -f some_db.directory
Какое количество файлов было создано в каталоге some_db.directory в результате успешного выполнения последней команды?
Вопрос 3
Создание логической копии выполняется следующей командой:
[student@pangolin-prac-l964qa ~]$ pg_dumpall --globals-only -U postgres -f dump.sql
Какие SQL-команды могут оказаться в файле dump.sql в результате успешного выполнения указанной команды? Выберите все верные варианты ответа
Вопрос 4
Какие утилиты требуются для многопоточного восстановления логической копии кластера баз данных? Выберите все верные варианты ответа