Уровень 3.0
Предусловие: пройдены предыдущие практические задания: «Клиентское подключение», «Работа в среде psql», «Конфигурация сервера». Предусловие: пройдены предыдущие практические задания: «Клиентское подключение», «Работа в среде psql», «Конфигурация сервера».
Практика. Архитектура
-
Подключитесь к базе данных под пользователем
postgres. -
Подключитесь к базе данных под пользователем
postgres.[student@ServerName ~]$ sudo su - postgres[student@ServerName ~]$ sudo su - postgres[postgres@ServerName ~]$ psql[postgres@ServerName ~]$ psqlpsql (15.5)Type "help" for help.В представлении
pg_stat_activityможно получить информацию о текущей активности процессов в экземпляре:В представлении
pg_stat_activityможно получить информацию о текущей активности процессов в экземпляре:postgres@postgres=# SELECT pid, backend_start, backend_type FROM pg_stat_activity;postgres@postgres=# SELECT pid, backend_start, backend_type FROM pg_stat_activity;pid | backend_start | backend_type-------+-------------------------------+------------------------------32292 | 2025-05-19 20:23:50.133245+03 | autounite launcher32295 | 2025-05-19 20:23:50.134435+03 | logical replication launcher32293 | 2025-05-19 20:23:50.134921+03 | integrity check launcher32291 | 2025-05-19 20:23:50.137111+03 | autovacuum launcher33049 | 2025-05-19 21:20:42.79377+03 | client backend32287 | 2025-05-19 20:23:50.128779+03 | background writer32286 | 2025-05-19 20:23:50.126141+03 | checkpointer32290 | 2025-05-19 20:23:50.1327+03 | walwriter(8 rows)Обратите внимание на время старта процесса в поле
backend_start, которое показывает время запуска каждого процесса. Фоновые процессы (checkpointer, walwriter и другие) запускаются при старте экземпляра, а клиентский процесс (client backend) появляется только при подключении клиента. -
Получите только
PIDпроцессов из того же представления и передайте их команде ОСps -fpдля отображения подробной информации: Обратите внимание на время старта процесса в полеbackend_start, которое показывает время запуска каждого процесса. Фоновые процессы (checkpointer, walwriter и другие) запускаются при старте экземпляра, а клиентский процесс (client backend) появляется только при подключении клиента. -
Получите только
PIDпроцессов из того же представления и передайте их команде ОСps -fpдля отображения подробной информации:postgres@postgres=# SELECT pid FROM pg_stat_activity \g (tuples_only=on) | xargs ps -fppostgres@postgres=# SELECT pid FROM pg_stat_activity \g (tuples_only=on) | xargs ps -fpUID PID PPID C STIME TTY STAT TIME CMDpostgres 32286 32282 0 20:23 ? Ss 0:00 postgres: checkpointerpostgres 32287 32282 0 20:23 ? Ss 0:00 postgres: background writerpostgres 32290 32282 0 20:23 ? Ss 0:00 postgres: walwriterpostgres 32291 32282 0 20:23 ? Ss 0:00 postgres: autovacuum launcherpostgres 32292 32282 0 20:23 ? Ss 0:00 postgres: autounite launcherpostgres 32293 32282 0 20:23 ? Ss 0:00 postgres: integrity check launcherpostgres 32295 32282 0 20:23 ? Ss 0:00 postgres: logical replication launcherpostgres 33049 32282 0 21:20 ? Ss 0:00 postgres: postgres postgres [local] idleUID PID PPID C STIME TTY STAT TIME CMDpostgres 32286 32282 0 20:23 ? Ss 0:00 postgres: checkpointerpostgres 32287 32282 0 20:23 ? Ss 0:00 postgres: background writerpostgres 32290 32282 0 20:23 ? Ss 0:00 postgres: walwriterpostgres 32291 32282 0 20:23 ? Ss 0:00 postgres: autovacuum launcherpostgres 32292 32282 0 20:23 ? Ss 0:00 postgres: autounite launcherpostgres 32293 32282 0 20:23 ? Ss 0:00 postgres: integrity check launcherpostgres 32295 32282 0 20:23 ? Ss 0:00 postgres: logical replication launcherpostgres 33049 32282 0 21:20 ? Ss 0:00 postgres: postgres postgres [local] idleРазбор команды:
\g– метакоманда, которая позволяет записать строки результата выполненного запроса в файл или передать через конвейер на обработку команде ОС;(tuples_only=on)– отключает вывод заголовков и служебных строк;xargsпринимает из потока вводаPIDпроцессов. Получив строку сPID, командаxargsподставляет этотPIDкомандеps -fp;ps -fp– выводит информацию о процессе в подробном формате.
Обратите внимание на поле
STIMEв выводеps -f— это время запуска процесса, которое совпадает с данными в полеbackend_startизpg_stat_activity.Разбор команды:
\g– метакоманда, которая позволяет записать строки результата выполненного запроса в файл или передать через конвейер на обработку команде ОС;(tuples_only=on)– отключает вывод заголовков и служебных строк;xargsпринимает из потока вводаPIDпроцессов. Получив строку сPID, командаxargsподставляет этотPIDкомандеps -fp;ps -fp– выводит информацию о процессе в подробном формате.
Обратите внимание на поле
STIMEв выводеps -f— это время запуска процесса, которое совпадает с данными в полеbackend_startизpg_stat_activity.
Процессы и структуры в памяти
-
Посмотрим, какой тип разделяемой памяти использует экземпляр. Для этого выведем значение параметра
dynamic_shared_memory_type:postgres@postgres=# \dconfig+ dynamic_shared_memory_typeList of configuration parametersParameter | Value | Type | Context | Access privileges----------------------------+-------+------+------------+-------------------dynamic_shared_memory_type | posix | enum | postmaster |(1 row)Этот параметр определяет механизм работы с разделяемой памятью.
-
Чтобы узнать все возможные значения этого параметра, обратитесь к представлению
pg_settings, где в полеenumvalsперечислены допустимые варианты:postgres@postgres=# SELECT name, setting, enumvals FROM pg_settings WHERE name = 'dynamic_shared_memory_type' \gx-[ RECORD 1 ]------------------------name | dynamic_shared_memory_typesetting | posixenumvals | {posix,sysv,mmap}Поддерживаются следующие типы:
posix;sysv;windows(только в соответствующей ОС);mmap.
-
shared_buffers– это область в разделяемой памяти, куда считываются страницы данных с диска. Определите текущий объем выделенной памяти для буферов: Поддерживаются следующие типы:posix;sysv;windows(только в соответствующей ОС);mmap.
-
shared_buffers– это область в разделяемой памяти, куда считываются страницы данных с диска. Определите текущий объем выделенной памяти для буферов:postgres@postgres=# SHOW shared_buffers;shared_buffers----------------128MB(1 row) -
С помощью представления
pg_settingsузнайте подробнее об этом параметре, например, можно ли изменить значение этого параметра в экземпляре без его рестарта:postgres@postgres=# SELECT name, setting, unit, context FROM pg_settings WHERE name ~ '^sh.*rs$';name | setting | unit | context----------------+---------+------+------------shared_buffers | 16384 | 8kB | postmaster(1 row)Контекст
postmasterозначает, что изменение этого параметра требует рестарта экземпляра. Столбецunitпоказывает, что размер буфера измеряется количеством8 Кбстраниц, так как каждый буфер предназначен для размещения одной страницы, считанной с диска. По умолчанию таких буферов16384, что дает в результате128 Мб. Это очень малое значение.
Влияние размера кеша буферов
-
Установите размер кеша буферов, равный
512 Мб. После перезагрузки экземпляра проверьте результат:postgres@postgres=# ALTER SYSTEM SET shared_buffers = '512MB';ALTER SYSTEMpostgres@postgres=# \q[postgres@ServerName ~] exit[student@ServerName ~]$ sudo systemctl restart postgresqlpostgres@postgres=# \q[postgres@ServerName ~] exit[student@ServerName ~]$ sudo systemctl restart postgresql[student@ServerName ~] sudo su - postgres[postgres@ServerName ~]$ psql -U postgres -h localhostpsql (15.5)Type "help" for help.postgres@postgres=# SHOW shared_buffers;shared_buffers----------------512MB(1 row)В данном случае перезапуск выполнен командой
systemctl restart, поскольку для экземпляра СУБД Pangolin ранее был создан и запущен сервисsystemd.ПримечаниеЕсли экземпляр СУБД Pangolin был запущен командой
pg_ctl start, перезапуск выполнялся бы от имени пользователяpostgresкомандойpg_ctl restart. -
Проверьте, существует ли зарегистрированная роль
student:postgres@postgres=# \du studentList of rolesRole name | Attributes | Member of-----------+------------+-----------student | | {}Если такой роли нет, создайте ее командой
CREATE USER student PASSWORD 'student'; -
Проверьте наличие БД
student, принадлежащей ролиstudent:postgres@postgres=# \x \l+ student \xExpanded display is on.List of databases-[ RECORD 1 ]-----+------------Name | studentOwner | studentEncoding | UTF8Collate | en_US.UTF-8Ctype | en_US.UTF-8ICU Locale |Locale Provider | libcAccess privileges |Size | 9462 kBTablespace | pg_defaultDescription |Expanded display is off.Если БД нет, создайте ее командой
CREATE DATABASE student OWNER student; -
Переключитесь в сеанс
studentи создайте в БДstudentтаблицу для проверки кеширования:postgres@postgres=# \c student student localhostYou are now connected to database "student" as user "student".student@student=> CREATE TABLE rndtab AS SELECT g.i AS id, random() AS nm FROM generate_series(1,100000) AS g(i);SELECT 100000 -
Получите план выполненного запроса:
student@student=> EXPLAIN (analyze,buffers,costs off,timing off,summary off) SELECT * FROM rndtab ;QUERY PLAN-------------------------------------------------Seq Scan on rndtab (actual rows=100000 loops=1)Buffers: shared read=541Planning:Buffers: shared hit=21(4 rows)Обратите внимание на строку
Buffers: shared read=541– это означает, что 541 считана с диска.Выполним тот же запрос еще раз:
student@student=> EXPLAIN (analyze,buffers,costs off,timing off,summary off) SELECT * FROM rndtab;QUERY PLAN-------------------------------------------------Seq Scan on rndtab (actual rows=100000 loops=1)Buffers: shared hit=541(2 rows)Теперь видно
shared hit=541– все страницы были найдены в кеше буферов, чтения с диска не потребовалось. -
Проверьте, останутся ли страницы в кеше после переподключения (в другой сессии). Метакоманда
\cразрывает текущую сессию и открывает новую:student@student=> \cYou are now connected to database "student" as user "student".student@student=> EXPLAIN (analyze,buffers,costs off,timing off,summary off) SELECT * FROM rndtab;QUERY PLAN-------------------------------------------------Seq Scan on rndtab (actual rows=100000 loops=1)Buffers: shared hit=541Planning:Buffers: shared hit=23(4 rows)Видно, что страницы остались в кеше буферов (разделяемая память), так как он не очищается при завершении сессии.
Локальная память процессов
-
Перезагрузите экземпляр для очистки разделяемой памяти:
student@student=> \q[postgres@ServerName ~] exit[student@ServerName ~]$ sudo systemctl restart postgresql -
Откройте новую сессию от имени
student:[student@ServerName ~] sudo su - postgres[student@ServerName ~] sudo su - postgres[postgres@ServerName ~]$ psql student studentpsql (15.5)Type "help" for help. -
Подготовьте оператор для такого же запроса, как и в предыдущем случае (
SELECT * FROM rndtab):student@student=> PREPARE preprndtab AS SELECT * FROM rndtab;PREPARE -
Для подготовленного оператора оптимизатором производится разбор запроса, дерево разобранного запроса запоминается в локальной памяти обслуживающего процесса. Далее строится план выполнения запроса, который запоминается в локальной памяти обслуживающего процесса. Но происходит это не сразу. Проверьте наличие подготовленного оператора:
student@student=> SELECT * FROM pg_prepared_statements \gx-[ RECORD 1 ]---+--------------------------------------------name | preprndtabstatement | prepare preprndtab as select * from rndtab;prepare_time | 2025-05-19 21:52:11.776894+03parameter_types | {}from_sql | tgeneric_plans | 0custom_plans | 0Поле
generic_plans = 0означает, что план еще не построен. -
Выполните подготовленный оператор с получением плана и информации о буферах:
student@student=> EXPLAIN (analyze,buffers) EXECUTE preprndtab;QUERY PLAN---------------------------------------------------------------------------------------------------------------Seq Scan on rndtab (cost=0.00..1541.00 rows=100000 width=12) (actual time=0.030..13.662 rows=100000 loops=1)Buffers: shared read=541Planning:Buffers: shared hit=15 read=8Planning Time: 0.693 msExecution Time: 17.909 ms(6 rows)Так как после рестарта сервера разделяемая память пуста и в кеше буферов еще нет страниц, то при выполнении запроса с диска были считаны 541 страница (
shared read=541), как и без предварительной подготовки. Но это и ожидалось, так как в локальной памяти запоминается лишь план подготовленных операторов. Время, затраченное на планирование, составило0.693 ms. -
Теперь план построен и сохранен в локальной памяти процесса. Проверьте
pg_prepared_statements:student@student=> SELECT * FROM pg_prepared_statements \gx-[ RECORD 1 ]---+--------------------------------------------name | preprndtabstatement | prepare preprndtab as select * from rndtab;prepare_time | 2025-05-19 21:52:11.776894+03parameter_types | {}from_sql | tgeneric_plans | 1custom_plans | 0generic_plans = 1– план построен и запомнен в локальной памяти. -
Выполните подготовленный оператор еще раз, получив план реального выполнения:
student@student=> EXPLAIN (analyze,buffers) EXECUTE preprndtab;QUERY PLAN--------------------------------------------------------------------------------------------------------------Seq Scan on rndtab (cost=0.00..1541.00 rows=100000 width=12) (actual time=0.051..8.323 rows=100000 loops=1)Buffers: shared hit=541Planning Time: 0.013 msExecution Time: 12.450 ms(4 rows)Обратите внимание на то, как снизилось время на планирование —
0.013 ms. Дело в том, что повторно планировать уже не надо. Планы подготовленных запросов кешируются в локальной памяти обслуживающего процесса.Также обратите внимание на то, что 541 страница была извлечена из кеша буферов (
shared hit=541), а он находится в разделяемой памяти. -
Что изменилось в
pg_prepared_statementsтеперь?student@student=> SELECT * FROM pg_prepared_statements \gx-[ RECORD 1 ]---------------+--------------------------------------------name | preprndtabstatement | prepare preprndtab as select * from rndtab;prepare_time | 2024-11-08 19:24:58.201862+03parameter_types | {}from_sql | tgeneric_plans | 2custom_plans | 0План, запомненный в локальной памяти был использован повторно, о чем свидетельствует увеличение счетчика
generic_plans. -
Проверьте, останется ли в локальном кеше подготовленный оператор после рестарта сессии: План, запомненный в локальной памяти был использован повторно, о чем свидетельствует увеличение счетчика
generic_plans.Поле
generic_plansпоказывает сколько раз запрос был выполнен в соответствии с общим планом. Для подготовленных запросов без параметров есть только общие планы. -
Проверьте, останется ли в локальном кеше подготовленный оператор после рестарта сессии:
student@student=> \cYou are now connected to database "student" as user "student".student@student=> SELECT * FROM pg_prepared_statements \gx(0 rows)Нет, не остался, так как при рестарте сессии старый обслуживающий процесс завершается и память его освобождается. Вместо него стартует новый обслуживающий процесс, но его локальная память исходно пуста.
-
Отключитесь от psql:
student@student=> \q
Самопроверка
Вопрос 1
Имеется следующий план выполненного запроса:
Seq Scan on test_table (actual rows=100000 loops=1)
Buffers: shared hit=229 read=441
Planning:
Buffers: shared hit=60 read=9
Planning Time: 5.220 ms
Execution Time: 11.434 ms
Сколько страниц данных понадобилось обслуживающему процессу для выполнения запроса в соответствии с указанным планом?
Вопрос 2
Каким образом может быть увеличен размер буферного кеша до 1 GB? Рассмотрите следующие варианты:
- Вариант 1. Путем последовательного выполнения следующих команд:
- в psql:
ALTER SYSTEM SET shared_buffers='1GB'; - в psql:
SELECT pg_reload_conf();
- в psql:
- Вариант 2. Путем последовательного выполнения следующих команд:
- в psql:
ALTER SYSTEM SET shared_buffers='1024MB'; - в shell:
sudo systemctl restart postgresql
- в psql:
- Вариант 3. Путем последовательного выполнения следующих команд:
- в psql:
ALTER SYSTEM SET shared_buffers='512MB'; - в psql:
ALTER SYSTEM SET temp_buffers='512MB'; - в shell:
sudo systemctl restart postgresql
- в psql:
- Вариант 4. Путем последовательного выполнения следующих команд:
- в psql:
ALTER SYSTEM SET shared_buffers=131072; - в shell:
sudo systemctl restart postgresql
- в psql:
Выберите верные варианты.
Вопрос 3
Открыто два сеанса psql. Первый сеанс обслуживается процессом с pid=13646, второй — процессом с pid=13917.
У каждого из сеансов в начале приглашения указывается pid обслуживающего процесса.
В указанных сеансах последовательно выполняются следующие команды:
13646 test_db=# PREPARE pre_statement AS SELECT * FROM test_table;
13646 test_db=# EXECUTE pre_statement;
13917 test_db=# PREPARE pre_statement AS SELECT * FROM test_table;
Какие этапы обработки запроса будут выполнены последней командой?
Вопрос 4
Какие из перечисленных процессов могут записывать «грязные» страницы в файловую систему?