Уровень 3.0
Предусловия:
- Изучен модуль «Очистка» данного курса
Мониторинг и настройка автоочистки
-
Откройте терминал и подключитесь в psql к базе данных
postgresc рольюpostgres:[student@pkles-gt0040964 ~]$ sudo -iu postgres-bash-4.4$ psqlpsql (15.5)Введите "help", чтобы получить справку. -
Создайте базу данных
pgbench_dbи подключитесь к ней:postgres=# CREATE DATABASE pgbench_db;CREATE DATABASEpostgres=# \c pgbench_dbВы подключены к базе данных "pgbench_db" как пользователь "postgres". -
Откройте второй терминал и перейдите в режим выполнения команд от имени пользователя
postgres:[student@pkles-gt0041054 ~]$ sudo -iu postgres
Скорость автоочистки
-
В первом сеансе посмотрите значения конфигурационных параметров, определяющих частоту срабатывания автоочистки:
pgbench_db=# SELECT name, setting FROM pg_settings WHERE name IN ( 'autovacuum_naptime', 'autovacuum_vacuum_threshold', 'autovacuum_vacuum_scale_factor', 'autovacuum_vacuum_insert_threshold', 'autovacuum_vacuum_insert_scale_factor');name | setting---------------------------------------+---------autovacuum_naptime | 60autovacuum_vacuum_insert_scale_factor | 0.2autovacuum_vacuum_insert_threshold | 1000autovacuum_vacuum_scale_factor | 0.2autovacuum_vacuum_threshold | 50(5 строк) -
В первом сеансе уменьшите значение параметра
autovacuum_naptimeдо 10 секунд:pgbench_db=# ALTER SYSTEM SET autovacuum_naptime = 10;ALTER SYSTEMpgbench_db=# SELECT pg_reload_conf();pg_reload_conf----------------t(1 строка)Уменьшение значения параметра
autovacuum_naptimeтребуется для снижения времени ожидания запуска рабочих процессов автоочистки в ходе эксперимента. -
В первом сеансе создайте представление, получающее информацию о текущем состоянии очистки пользовательских таблиц.
pgbench_db=# CREATE OR REPLACE VIEW user_tables_vacuum AS(SELECT pg_class.relname, reltuples, n_ins_since_vacuum, n_dead_tup,round(dead_tuples_threshold.value) AS dead_tup_threshold,((n_dead_tup > dead_tuples_threshold.value) OR (n_ins_since_vacuum > insert_tuples_threshold.value)) AS is_vacuum_need,autovacuum_count,now() - pg_stat_get_last_autovacuum_time(pg_class.oid) AS last_vacuum_timeFROM pg_class JOIN pg_stat_user_tables ON (pg_class.oid = pg_stat_user_tables.relid),LATERAL (SELECT current_setting('autovacuum_vacuum_threshold')::integer + current_setting('autovacuum_vacuum_scale_factor')::numeric * pg_class.reltuples) AS dead_tuples_threshold(value),LATERAL (SELECT current_setting('autovacuum_vacuum_insert_threshold')::integer + current_setting('autovacuum_vacuum_insert_scale_factor')::numeric * pg_class.reltuples) AS insert_tuples_threshold(value));CREATE VIEWСозданное представление для каждой пользовательской таблицы в базе данных позволяет получить следующую информацию:
relname— имя таблицыn_ins_since_vacuum— число строк, вставленных с момента последней очисткиn_dead_tup— число «мертвых» строк в таблицеdead_tup_threshold— число «мертвых» строк, при котором таблица попадает в список для очистки (порог срабатывания очистки)is_vacuum_need— необходимость очистки таблицыautovacuum_count— число вызовов очистки таблицы автоочисткойlast_vacuum_time— время выполнения текущей очистки таблицы
-
Во втором сеансе инициализируйте
pgbench, в первом сеансе сбросьте статистику, после чего запустите во втором сеансе тестpgbench:-bash-4.4$ pgbench -i pgbench_dbdropping old tables...creating tables...generating data (client-side)...100000 of 100000 tuples (100%) done (elapsed 0.01 s, remaining 0.00 s)vacuuming...creating primary keys...done in 0.27 s (drop tables 0.01 s, create tables 0.03 s, client-side generate 0.12 s, vacuum 0.07 s, primary keys 0.05 s).pgbench_db=# SELECT pg_stat_reset();pg_stat_reset---------------(1 строка)-bash-4.4$ pgbench -T 75 pgbench_dbpgbench (15.5)starting vacuum...end.transaction type: <builtin: TPC-B (sort of)>scaling factor: 1query mode: simplenumber of clients: 1number of threads: 1maximum number of tries: 1duration: 75 snumber of transactions actually processed: 58210number of failed transactions: 0 (0.000%)latency average = 1.288 msinitial connection time = 4.580 mstps = 776.174046 (without initial connection time)Тест запущен на 75 секунд (
-T 75). Скорость выполнения транзакций оказалась 776 tps (tps = 776.174046). -
Через минуту после начала теста, запущенного в предыдущем пункте, в первом сеансе несколько раз выполните запрос:
pgbench_db=# SELECT * FROM user_tables_vacuum;relname | reltuples | n_ins_since_vacuum | n_dead_tup | dead_tup_threshold | is_vacuum_need | autovacuum_count | last_vacuum_time------------------+-----------+--------------------+------------+--------------------+----------------+------------------+------------------pgbench_accounts | 100000 | 0 | 4383 | 20050 | f | 0 |pgbench_branches | 1 | 0 | 0 | 50 | t | 6 | 00:00:01.518687pgbench_tellers | 10 | 0 | 0 | 52 | t | 6 | 00:00:01.448515pgbench_history | 54394 | 4275 | 0 | 10929 | f | 6 | 00:00:08.636163(4 строки)Выше представлен результат одного из запросов по прошествии 1 минуты.
Для анализа полученных результатов вспомним критерии необходимости очистки таблицы:
pg_stat_all_tables.n_dead_tup > autovacuum_vacuum_threshold + autovacuum_vacuum_scale_factor x pg_class.reltuplespg_stat_all_tables.n_ins_since_vacuum > autovacuum_vacuum_insert_threshold + autovacuum_vacuum_insert_scale_factor x pg_class.reltuplesИз результатов выполнения видно, что для трех таблиц количество вызовов автоочистки равняется 6. Это означает, что при каждом вызове рабочих процессов для данных таблиц срабатывал порог очистки и таблица успевала очиститься за время
autovacuum_naptime = 10(время тестирования — 60 секунд, вызов рабочих процессов — каждые 10 секунд).Также это подтверждается временем последней очистки для этих таблиц —
last_vacuum_time<autovacuum_naptime.При этом для таблиц
pgbench_branchesиpgbench_tellersочистка срабатывала по причине малого количества строк и активных изменений в них. Вклад параметраautovacuum_vacuum_threshold = 50в критерий был определяющим.В то же время для таблицы
pgbench_history, в которой строки только вставляются, очистка срабатывала на основе второго критерия при превышении порога в 20 % вставленных строк (autovacuum_vacuum_insert_scale_factor = 0.2).Таблица же
pgbench_accountsни разу не очищалась, несмотря на интенсивные изменения в ней (n_dead_tup = 4383). Дело в том, что указанная таблица относительно большая (reltuples = 100000) и в критерии необходимости очистки определяющим является параметрautovacuum_vacuum_scale_factor = 0.2, в соответствии с которым требуется не менее 20 % «мертвых» строк.Также из результатов видно, что в настоящий момент ни одна из таблиц в очистке не нуждается (
is_vacuum_need=f).В целом, можно сделать вывод, что автоочистка справляется с такой скоростью изменений одной небольшой базы данных.
Далее в целях демонстрации попробуем «сломать» автоочистку таким образом, чтобы она не успевала очищать таблицы, подпадающие под критерий необходимости очистки.
-
В первом сеансе посмотрите значения параметров, отвечающих за скорость выполнения очистки:
postgres=# SELECT name, setting FROM pg_settings WHERE name IN ( 'autovacuum_vacuum_cost_limit', 'autovacuum_vacuum_cost_delay', 'vacuum_cost_limit', 'autovacuum_max_workers');name | setting------------------------------+---------autovacuum_max_workers | 3autovacuum_vacuum_cost_delay | 2autovacuum_vacuum_cost_limit | -1vacuum_cost_limit | 200(4 строки)Исходя из заданных параметров, очистка выполняет работу объемом 200 условных единиц с паузами в 2 мс.
При этом значение параметра
autovacuum_vacuum_cost_limitопределяется значениемvacuum_cost_limit.Над очисткой может работать не более трех рабочих процессов. При этом второй рабочий процесс подключается к очистке базы данных, если первый не успевает ее выполнить за время
autovacuum_naptime. -
В первом сеансе увеличьте до предела значение
autovacuum_vacuum_cost_delay(до 100 мс), уменьшите в 10 раз значениеautovacuum_vacuum_cost_limit, уменьшите количество рабочих процессов до 1:pgbench_db=# ALTER SYSTEM SET autovacuum_vacuum_cost_delay = 100;ALTER SYSTEMpgbench_db=# ALTER SYSTEM SET autovacuum_vacuum_cost_limit = 20;ALTER SYSTEMpgbench_db=# ALTER SYSTEM SET autovacuum_max_workers = 1;ALTER SYSTEM -
В первом сеансе уменьшите в 20 раз значение параметра
autovacuum_vacuum_scale_factor:pgbench_db=# ALTER SYSTEM SET autovacuum_vacuum_scale_factor = 0.01;ALTER SYSTEMpgbench_db=# SELECT pg_reload_conf();pg_reload_conf----------------t(1 строка)Теперь таблица
pgbench_accountsдолжна подпадать под критерий необходимости очистки. -
Выйдите из первого сеанса
psql, перезапустите экземпляр Pangolin и снова подключитесь к базе данныхpgbench_dbвpsql:pgbench_db=# \q-bash-4.4$ pg_ctl restart -l logfileожидание завершения работы сервера.... готовосервер остановленожидание запуска сервера.... готовосервер запущен-bash-4.4$ psql -d pgbench_dbpsql (15.5)Введите "help", чтобы получить справку.Перезапуск экземпляра потребовался для применения значений измененных параметров (
autovacuum_max_workersтребуют перезапуска). -
Во втором сеансе повторно инициализируйте
pgbench:-bash-4.4$ pgbench -i pgbench_dbdropping old tables...creating tables...generating data (client-side)...100000 of 100000 tuples (100%) done (elapsed 0.01 s, remaining 0.00 s)vacuuming...creating primary keys...done in 0.27 s (drop tables 0.01 s, create tables 0.03 s, client-side generate 0.12 s, vacuum 0.07 s, primary keys 0.05 s). -
В первом сеансе сбросьте статистику:
pgbench_db=# SELECT pg_stat_reset();pg_stat_reset---------------(1 строка) -
Во втором сеансе запустите тест
pgbench:-bash-4.4$ pgbench -T 75 pgbench_dbpgbench (15.5)starting vacuum...end.transaction type: <builtin: TPC-B (sort of)>scaling factor: 1query mode: simplenumber of clients: 1number of threads: 1maximum number of tries: 1duration: 75 snumber of transactions actually processed: 54967number of failed transactions: 0 (0.000%)latency average = 1.364 msinitial connection time = 4.110 mstps = 732.928895 (without initial connection time) -
Через минуту после начала теста, запущенного в предыдущем пункте, в первом сеансе несколько раз выполните запрос:
pgbench_db=# SELECT * FROM user_tables_vacuum;relname | reltuples | n_ins_since_vacuum | n_dead_tup | dead_tup_threshold | is_vacuum_need | autovacuum_count | last_vacuum_time------------------+-----------+--------------------+------------+--------------------+----------------+------------------+------------------pgbench_accounts | 100005 | 0 | 4383 | 1050 | f | 3 | 00:00:05.30329pgbench_branches | 1 | 0 | 0 | 50 | t | 2 | 00:00:30.871773pgbench_tellers | 10 | 0 | 0 | 52 | t | 2 | 00:00:29.295784pgbench_history | 33852 | 2539 | 0 | 389 | f | 2 | 00:00:27.046092(4 строки)Как и в предыдущем эксперименте, выше представлен результат одного из запросов по прошествии 1 минуты.
Теперь таблица
pgbench_accountsпри каждом запуске рабочего процесса очищается.Скорость очистки значительно снизилась при примерно том же количестве транзакций.
Очистка не успевает выполняться между запусками рабочих процессов — текущее время очистки трех таблиц
last_vacuum_timeбольшеautovacuum_naptimeпримерно в 3 раза.Помимо этого, количество совершенных очисток таблиц за минуту не более трех, хотя ожидалось шесть.
Количество накопленных «мертвых» строк
n_dead_tupу таблиц значительно превышает значения из предыдущего эксперимента.Эксперимент построен искусственно с заданием граничных значений конфигурационных параметров.
В реальных системах может потребоваться проведение более глубокого исследования на длительном участке времени.
Таким образом, автоочистка может не успевать очищать «мертвые» версии строк, что, в свою очередь, может привести к разрастанию базы данных и другим проблемам.
С другой стороны, слишком высокая скорость очистки может приводить к проблемам с производительностью обслуживающих процессов из-за высоких накладных расходов.
Разрастание таблиц
-
В первом сеансе сбросьте значения всех конфигурационных параметров:
pgbench_db=# ALTER SYSTEM RESET ALL;ALTER SYSTEMpgbench_db=# \q-bash-4.4$ pg_ctl restart -l logfileожидание завершения работы сервера.... готовосервер остановленожидание запуска сервера.... готовосервер запущен-bash-4.4$ psql -d pgbench_dbpsql (15.5)Введите "help", чтобы получить справку. -
В первом сеансе создайте расширение
pgstattuple:pgbench_db=# CREATE EXTENSION pgstattuple;CREATE EXTENSIONРасширение
pgstattupleпозволяет отслеживать разрастание таблиц. Сбор информации осуществляется путем сканирования страниц, поэтому не стоит злоупотреблять данным расширением. -
Во втором сеансе проинициализируйте
pgbench:-bash-4.4$ pgbench -i -s 10 pgbench_dbdropping old tables...creating tables...generating data (client-side)...1000000 of 1000000 tuples (100%) done (elapsed 0.62 s, remaining 0.00 s)vacuuming...creating primary keys...done in 1.69 s (drop tables 0.03 s, create tables 0.01 s, client-side generate 1.06 s, vacuum 0.18 s, primary keys 0.40 s).В этот раз при инициализации был указан коэффициент масштаба
-s 10.Это сделано с целью увеличения количества порождаемых «мертвых» строк в таблице
pgbench_accounts, которая будет использоваться для оценки разрастания. -
В первом сеансе посмотрите характеристики таблицы
pgbench_accountsпосле инициализации:pgbench_db=# SELECT tuple_count, pg_size_pretty(table_len) table_size, pg_size_pretty(free_space) free_space, free_percent FROM pgstattuple('pgbench_accounts');tuple_count | table_size | free_space | free_percent-------------+------------+------------+--------------1000000 | 128 MB | 1413 kB | 1.08(1 строка)Выполненный запрос с использованием расширения
pgstattupleвыводит следующую информацию:tuple_count— количество строк в таблице (не версий строк)table_size— объем таблицы, приведенный к читаемому виду с использованием функцииpg_size_prettyfree_space— объем свободного пространства в таблице, приведенный к читаемому видуfree_percent— доля свободного пространства в процентах
-
В первом сеансе запомните в переменной объем таблицы
pgbench_accounts:pgbench_db=# SELECT table_len as base_table_size FROM pgstattuple('pgbench_accounts') \gsetТеперь в переменной
base_table_sizeхранится объем таблицы в байтах, посчитанный сразу после инициализацииpgbench.В конце инициализации
pgbenchвыполняется очистка таблиц, поэтому «мертвых» строк в таблице быть не должно.Проверим это.
-
В первом сеансе посмотрите статистику по таблице
pgbench_accounts:pgbench_db=# SELECT relname, n_live_tup, n_dead_tup, n_tup_upd, autovacuum_count FROM pg_stat_all_tables WHERE relname = 'pgbench_accounts';relname | n_live_tup | n_dead_tup | n_tup_upd | autovacuum_count------------------+------------+------------+-----------+------------------pgbench_accounts | 1000000 | 0 | 0 | 0(1 строка)Системное представление
pg_stat_all_tablesпоказывает, что в таблице 1 000 000 «живых» строк (n_live_tup).«Мертвых» строк при этом нет (
n_dead_tup= 0). -
Во втором сеансе выполните тест
pgbench:-bash-4.4$ pgbench -T 30 -c 10 pgbench_dbpgbench (15.5)starting vacuum...end.transaction type: <builtin: TPC-B (sort of)>scaling factor: 10query mode: simplenumber of clients: 10number of threads: 1maximum number of tries: 1duration: 30 snumber of transactions actually processed: 44682number of failed transactions: 0 (0.000%)latency average = 6.710 msinitial connection time = 37.952 mstps = 1490.225187 (without initial connection time)Тест выполняется 30 секунд (
-T 30) с 10 параллельными сеансами (-c 10). -
В первом сеансе проверьте статистику по таблице
pgbench_accountsпосле выполнения тестаpgbench:pgbench_db=# SELECT relname, n_live_tup, n_dead_tup, n_tup_upd, autovacuum_count FROM pg_stat_all_tables WHERE relname = 'pgbench_accounts';relname | n_live_tup | n_dead_tup | n_tup_upd | autovacuum_count------------------+------------+------------+-----------+------------------pgbench_accounts | 1000000 | 27754 | 44682 | 0(1 строка)Тест
pgbenchобновил 44682 строк (n_tup_upd).При этом с настройками по умолчанию автоочистка ни разу не сработала (
autovacuum_count= 0).В то же время «мертвых» строк (
n_dead_tup) оказалось меньше количества обновленных строк.Это можно объяснить работой внутристраничной очистки, срабатывающей при обращении к табличной странице в случае ее заполненности.
Более подробно внутристраничная очистка будет рассмотрена в следующей теме.
-
В первом сеансе выполните ручную очистку таблицы
pgbench_accountsи посмотрите ее объем:pgbench_db=# VACUUM pgbench_accounts;VACUUMpgbench_db=# SELECT tuple_count, pg_size_pretty(table_len) table_size, pg_size_pretty(free_space) free_space, free_percent, pg_size_pretty(table_len - :base_table_size) difference_size from pgstattuple('pgbench_accounts');tuple_count | table_size | free_space | free_percent | difference_size-------------+------------+------------+--------------+-----------------1000000 | 130 MB | 3283 kB | 2.46 | 2056 kB(1 строка)Ручная очистка понадобилась для оценки объема свободного пространства, поскольку автоочистка не вызывалась.
В результате объем таблицы вырос примерно на 2056 kB (
difference_size) со 128 MB до 130 MB. Параметрdifference_sizeбыл посчитан с использованием ранее запомненного значения в переменнойbase_table_size.Свободное пространство в таблице увеличилось до 3283 kB, что составляет 2.46 % от общего объема.
При этом количество строк в таблице осталось неизменным — 1 000 000.
-
В первом сеансе измените настройки автоочистки:
pgbench_db=# ALTER SYSTEM SET autovacuum_naptime = 1;ALTER SYSTEMpgbench_db=# ALTER SYSTEM SET autovacuum_vacuum_scale_factor = 0.0001;ALTER SYSTEMpgbench_db=# SELECT pg_reload_conf();pg_reload_conf----------------t(1 строка)Теперь рабочие процессы автоочистки будут порождаться каждую секунду (
autovacuum_naptime= 1). При этом порог срабатывания автоочистки установлен небольшим (autovacuum_vacuum_scale_factor= 0.0001) с учетом значительного количества строк в таблицеpgbench_accountsи скорости порождения «мертвых» строк в тестеpgbench. -
Во втором сеансе проинициализируйте и выполните тест
pgbench:-bash-4.4$ pgbench -i -s 10 pgbench_dbdropping old tables...creating tables...generating data (client-side)...1000000 of 1000000 tuples (100%) done (elapsed 0.77 s, remaining 0.00 s)vacuuming...creating primary keys...done in 1.65 s (drop tables 0.03 s, create tables 0.01 s, client-side generate 1.05 s, vacuum 0.20 s, primary keys 0.36 s).-bash-4.4$ pgbench -T 30 -c 10 pgbench_dbpgbench (15.5)starting vacuum...end.transaction type: <builtin: TPC-B (sort of)>scaling factor: 10query mode: simplenumber of clients: 10number of threads: 1maximum number of tries: 1duration: 30 snumber of transactions actually processed: 43599number of failed transactions: 0 (0.000%)latency average = 6.877 msinitial connection time = 39.964 mstps = 1454.034966 (without initial connection time) -
В первом сеансе проверьте статистику по таблице
pgbench_accountsпосле выполнения тестаpgbench:pgbench_db=# SELECT relname, n_live_tup, n_dead_tup, n_tup_upd, autovacuum_count FROM pg_stat_all_tables WHERE relname = 'pgbench_accounts';relname | n_live_tup | n_dead_tup | n_tup_upd | autovacuum_count------------------+------------+------------+-----------+------------------pgbench_accounts | 1000229 | 0 | 43599 | 27(1 строка)Теперь автоочистка сработала 27 раз. «Мертвых» строк в таблице нет.
Значение
n_live_tup= 1 000 229 немного не соответствует действительности (1 000 000).Дело в том, что подсистема сбора статистики с целью экономии ресурсов для некоторых параметров формирует лишь приблизительную оценку их значений.
-
В первом сеансе посмотрите объем таблицы
pgbench_accounts:pgbench_db=# SELECT tuple_count, pg_size_pretty(table_len) table_size, pg_size_pretty(free_space) free_space, free_percent, pg_size_pretty(table_len - :base_table_size) difference_size from pgstattuple('pgbench_accounts');tuple_count | table_size | free_space | free_percent | difference_size-------------+------------+------------+--------------+-----------------1000000 | 129 MB | 1916 kB | 1.45 | 552 kB(1 строка)При настроенной автоочистке объем таблицы вырос значительно меньше — на 552 kB вместо 2056 kB.
При этом доля свободного пространства уменьшилась с 2.46 % до 1.45 %.
Количество строк в таблице осталось неизменным.
Завершение
-
В первом сеансе сбросьте значения всех конфигурационных параметров:
pgbench_db=# ALTER SYSTEM RESET ALL;ALTER SYSTEMpgbench_db=# SELECT pg_reload_conf();pg_reload_conf----------------t(1 строка) -
В первом сеансе подключитесь к базе данных
postgres, удалите базу данныхpgbench_dbи выйдите из сеанса:pgbench_db=# \c postgresВы подключены к базе данных "postgres" как пользователь "postgres".postgres=# DROP DATABASE pgbench_db;DROP DATABASEpostgres=# \q
Самопроверка
Вопрос 1
В сеансе psql выполнен следующий запрос:
postgres=# SELECT name, setting
FROM pg_settings
WHERE name IN ('autovacuum_vacuum_cost_limit', 'autovacuum_naptime', 'autovacuum_vacuum_threshold', 'autovacuum_vacuum_cost_delay', 'vacuum_cost_limit');
name | setting
------------------------------+---------
autovacuum_naptime | 20
autovacuum_vacuum_cost_delay | 10
autovacuum_vacuum_cost_limit | -1
autovacuum_vacuum_threshold | 30
vacuum_cost_limit | 100
(5 строк)
С какой периодичностью (в секундах) происходит запуск рабочих процессов автоочистки?
Вопрос 2
Какие из приведенных конфигурационных параметров определяют скорость выполнения автоочистки? Выберите все верные варианты ответа:
Вопрос 3
В сеансе psql выполнена следующая последовательность команд:
mvcc_db=# SELECT name, setting
FROM pg_settings
WHERE name IN ('autovacuum_vacuum_threshold', 'autovacuum_vacuum_scale_factor', 'autovacuum_vacuum_insert_threshold', 'autovacuum_vacuum_insert_scale_factor');
name | setting
---------------------------------------+---------
autovacuum_vacuum_insert_scale_factor | 0.2
autovacuum_vacuum_insert_threshold | 500
autovacuum_vacuum_scale_factor | 0.2
autovacuum_vacuum_threshold | 50
(4 строки)
mvcc_db=# SELECT cl.relname, cl.reltuples, st.n_dead_tup, st.n_ins_since_vacuum
FROM pg_class cl, pg_stat_user_tables st
WHERE cl.oid = st.relid;
relname | reltuples | n_dead_tup | n_ins_since_vacuum
------------+-----------+------------+--------------------
table1 | 1000 | 300 | 0
table2 | 1000 | 100 | 500
table3 | 1000 | 50 | 800
table4 | 1000 | 200 | 500
(1 строка)
Параметры хранения для таблиц table1, table2, table3, table4 не определены.
Какие таблицы нуждаются в очистке? Выберите все верные варианты ответа:
Вопрос 4
Какие последствия могут возникнуть при снижении частоты выполнения очистки таблицы? Выберите все верные варианты ответа: