Некоторое время читал и смотрел на временные таблицы в PostgreSQL. Решил сохранить найденную информацию и эксперименты, которые делал.
Я не буду рассказывать что такое временные таблицы в PostgreSQL, но порекомендую прочитать следующие полезные статьи для понимания темы:
- Документация. CREATE TEMP TABLE
- Документация. Параметр temp_tablespaces
- Документация. Отличия от SQL стандарта
- Документация. Зачистка файлов временных таблиц после падения сервера
- Документация. pg_my_temp_schema()
- Иван Фролков. 2016. Временные таблицы в Postgresql — проблемы и методы решения
- Хабр. Типы таблиц в PostgreSQL: logged, unlogged и temporary tables
- Хабр. PostgreSQL и временные таблицы
- dev.to. Postgres Temporary Tables: A Guide to Data Manipulation
- CyberTec. Про memory context, который используется в local buffers
- Исходный код. Реализация Local Buffer SourceCode
Также прочитал про функцию pg_my_temp_schema() в документации по ссылке выше. Решил посмотреть как она реализована, путь моего поиска тоже сохраню:
Это была общая информация, а теперь несколько экспериментов для 16-й версии сервера. В итоге, оформил в репозитарии на github. Даже readme можно почитать 🙂
Перечень экспериментов:
- Временная таблица on commit {reserver|delete|drop}
- Временная таблица и путь поиска
- Временное табличное пространство по умолчанию и нет
- Временная таблица, представление и функция
- Временная схема и суперпользователь
- Временная схема и обычная роль
- Auto {analyze|vacuum} временной и обычной таблицы
- Параллельная обработка временных таблиц
- Параметр temp_buffers
- Временные таблица, индекс, TOAST и их расположение на диске
- Параметр remove_temp_files_after_crash (тут возможно баг?)
К каждому эксперименту прикладываю еще и два скрипта команд, который можно будет выполнить на вашей системе и самостоятельно посмотреть результат. Старался написать так, чтобы все скрипты (кроме последнего) выполнились единой командой без каких-либо подстановок параметров. Нужно скачать код эксперимента, сохранить в файл и выполнить этот файл командами вида:
psql -f TEMP_TABLE_1.sql
Или уже находясь в psql’e:
\i TEMP_TABLE_1.sql
1. Временная таблица on commit {reserver|delete|drop}
1. Временная таблица on commit {reserver|delete|drop}. Код эксперимента
1. Временная таблица on commit {reserver|delete|drop}. ECHO-комментарии
-- 1. Временная таблица on commit {reserver|delete|drop} -- ON COMMIT PRESERVE ROWS — строки в таблице сохраняются до конца сессии -- ON COMMIT DROP — таблица удаляется после завершения транзакции -- ON COMMIT DELETE ROWS — все строки таблицы будут удалены после фиксации транзакции -- Создаем три временные таблицы с разным поведением CREATE TEMP TABLE t_temp1_1 (id integer) ON COMMIT PRESERVE ROWS; CREATE TABLE CREATE TEMP TABLE t_temp1_2 (id integer) ON COMMIT DELETE ROWS; CREATE TABLE CREATE TEMP TABLE t_temp1_3 (id integer) ON COMMIT DROP; CREATE TABLE -- Добавляем в них строки INSERT INTO t_temp1_1 SELECT * FROM generate_series(1,1_000); INSERT 0 1000 INSERT INTO t_temp1_2 SELECT * FROM generate_series(1,1_000); INSERT 0 1000 INSERT INTO t_temp1_3 SELECT * FROM generate_series(1,1_000); psql:1.sql:23: ERROR: relation "t_temp1_3" does not exist LINE 1: INSERT INTO t_temp1_3 SELECT * FROM generate_series(1,1_000)... -- Смотрим что в таблицах SELECT count(*) FROM t_temp1_1; count ------- 1000 (1 row) SELECT count(*) FROM t_temp1_2; count ------- 0 (1 row) SELECT count(*) FROM t_temp1_3; psql:1.sql:32: ERROR: relation "t_temp1_3" does not exist LINE 1: SELECT count(*) FROM t_temp1_3; -- Доступно только две временные таблицы SELECT schemaname, tablename FROM pg_tables WHERE tablename like t_temp1%; schemaname | tablename ------------+----------- pg_temp_3 | t_temp1_1 pg_temp_3 | t_temp1_2 (2 rows) -- Удаляем их DROP TABLE t_temp1_1; DROP TABLE DROP TABLE t_temp1_2; DROP TABLE -- Начинаем новую транзакцию BEGIN; BEGIN -- Создаем три временные таблицы с разным поведением CREATE TEMP TABLE t_temp1_1 (id integer) ON COMMIT PRESERVE ROWS; CREATE TABLE CREATE TEMP TABLE t_temp1_2 (id integer) ON COMMIT DELETE ROWS; CREATE TABLE CREATE TEMP TABLE t_temp1_3 (id integer) ON COMMIT DROP; CREATE TABLE -- Добавляем в них строки INSERT INTO t_temp1_1 SELECT * FROM generate_series(1,1_000); INSERT 0 1000 INSERT INTO t_temp1_2 SELECT * FROM generate_series(1,1_000); INSERT 0 1000 INSERT INTO t_temp1_3 SELECT * FROM generate_series(1,1_000); INSERT 0 1000 -- В транзакции видны все три временные таблицы SELECT schemaname, tablename FROM pg_tables WHERE tablename like t_temp1%; schemaname | tablename ------------+----------- pg_temp_3 | t_temp1_1 pg_temp_3 | t_temp1_2 pg_temp_3 | t_temp1_3 (3 rows) -- Смотрим что в них SELECT count(*) FROM t_temp1_1; count ------- 1000 (1 row) SELECT count(*) FROM t_temp1_2; count ------- 1000 (1 row) SELECT count(*) FROM t_temp1_3; count ------- 1000 (1 row) COMMIT; COMMIT -- После завершения транзакции снова доступно две таблицы SELECT schemaname, tablename FROM pg_tables WHERE tablename like t_temp1%; schemaname | tablename ------------+----------- pg_temp_3 | t_temp1_1 pg_temp_3 | t_temp1_2 (2 rows) -- И в одной из них все строки удалены SELECT count(*) FROM t_temp1_1; count ------- 1000 (1 row) SELECT count(*) FROM t_temp1_2; count ------- 0 (1 row) SELECT count(*) FROM t_temp1_3; psql:1.sql:89: ERROR: relation "t_temp1_3" does not exist LINE 1: SELECT count(*) FROM t_temp1_3;
2. Временная таблица и путь поиска
2. Временная таблица и путь поиска. Код эксперимента
2. Временная таблица и путь поиска. ECHO-комментарии
-- 2. Временная таблица и путь поиска -- Проверим временную схему сеанса, пути поиска SELECT pg_my_temp_schema()::regnamespace; pg_my_temp_schema ------------------- - (1 row) SHOW search_path; search_path ----------------- "$user", public (1 row) SELECT current_schemas(true); current_schemas --------------------- {pg_catalog,public} (1 row) -- Создадим обычную таблицу, добавим туда строки CREATE TABLE temp2 (id integer); CREATE TABLE INSERT INTO temp2 VALUES (1),(2),(3); INSERT 0 3 SELECT * FROM temp2; id ---- 1 2 3 (3 rows) -- Создадим временную таблицу с аналогичным названием CREATE TEMP TABLE temp2 (id integer); CREATE TABLE -- Снова проверим временную схему сеанса и пути поиска SELECT pg_my_temp_schema()::regnamespace; pg_my_temp_schema ------------------- pg_temp_3 (1 row) SHOW search_path; search_path ----------------- "$user", public (1 row) SELECT current_schemas(true); current_schemas ------------------------------- {pg_temp_3,pg_catalog,public} (1 row) -- Выполним эксперимент SELECT * FROM temp2; id ---- (0 rows) SELECT * FROM public.temp2; id ---- 1 2 3 (3 rows) SELECT * FROM pg_temp.temp2; id ---- (0 rows) DROP TABLE temp2; DROP TABLE SELECT * FROM temp2; id ---- 1 2 3 (3 rows) -- Удалим лишние объекты DROP TABLE public.temp2; DROP TABLE
3. Табличное пространство по умолчанию для временных объектов
3. Временное табличное пространство по умолчанию и нет. Код эксперимента
3. Временное табличное пространство по умолчанию и нет. ECHO-комментарии
-- Табличное пространство по умолчанию для временных объектов -- («По умолчанию значение этой переменной — пустая строка. -- С таким значением все временные объекты создаются в табличном -- пространстве по умолчанию, установленном для текущей базы данных») SHOW temp_tablespaces; temp_tablespaces ------------------ (1 row) -- Табличное пространство по умолчанию SHOW default_tablespace; default_tablespace -------------------- (1 row) -- Табличное пространство по умолчанию для текущей БД SELECT db.datname, ts.spcname FROM pg_tablespace ts JOIN pg_database db ON db.dattablespace = ts.oid WHERE db.datname = (SELECT * FROM current_database()); datname | spcname ---------+------------ pavel | pg_default (1 row) -- Изменим табличное пространство по умолчанию SET temp_tablespaces = pg_global; SET -- Снова создадим временную таблицу CREATE TEMP TABLE t_temp3(id integer); psql:3.sql:35: ERROR: only shared relations can be placed in pg_global tablespace -- Создадим обычную таблицу CREATE TABLE t_temp3(id integer); CREATE TABLE -- Удалим лишнее DROP TABLE t_temp_3; DROP TABLE
4. Временная таблица, представление и функция
4. Временная таблица, представление и функция. Код эксперимента
4. Временная таблица, представление и функция. ECHO-комментарии
-- 4. Временная таблица, представление и функция -- Создадим временную таблицу CREATE TEMP TABLE t_temp4 (id integer); CREATE TABLE -- Создадим представление на основе временной таблицы CREATE VIEW v_t_temp4 AS SELECT * FROM t_temp4; psql:4.sql:13: NOTICE: view "v_t_temp4" will be a temporary view CREATE VIEW -- Посмотрим где представление хранится на диске SELECT pg_relation_filepath(v_t_temp4); pg_relation_filepath ---------------------- (1 row) -- Создадим обычную таблицу CREATE TABLE temp4 (id integer); CREATE TABLE -- Создадим представление на основе обычной таблицы CREATE VIEW v_temp4 AS SELECT * FROM temp4; CREATE VIEW -- И посмотрим где это представление хранится на диске SELECT pg_relation_filepath(v_temp4); pg_relation_filepath ---------------------- (1 row) -- Создадим функцию CREATE FUNCTION f_t_func() RETURNS integer AS $$ SELECT count(*) FROM v_t_temp4; $$ LANGUAGE sql; CREATE FUNCTION SELECT f_t_func(); f_t_func ---------- 0 (1 row) CREATE MATERIALIZED VIEW mv AS SELECT * FROM t_temp4 ; psql:4.sql:57: ERROR: materialized views must not use temporary tables or views -- Удалим таблицу DROP TABLE t_temp4; psql:4.sql:63: ERROR: cannot drop table t_temp4 because other objects depend on it DETAIL: view v_t_temp4 depends on table t_temp4 HINT: Use DROP ... CASCADE to drop the dependent objects too. -- Удалим таблицу и каскадно все объекты, связанные с ней DROP TABLE t_temp4 CASCADE; psql:4.sql:69: NOTICE: drop cascades to view v_t_temp4 DROP TABLE -- Удалим лишние объекты DROP TABLE temp4 CASCADE; psql:4.sql:75: NOTICE: drop cascades to view v_temp4 DROP TABLE DROP FUNCTION f_t_func; DROP FUNCTION
5. Временная схема и суперпользователь
5. Временная схема и суперпользователь. Код эксперимента
5. Временная схема и суперпользователь. ECHO-комментарии
-- 5. Временная схема и суперпользователь -- Создаем таблицу, смотрим схему SELECT current_schemas(true); current_schemas --------------------- {pg_catalog,public} (1 row) CREATE TEMP TABLE t_temp5 (id integer); CREATE TABLE SELECT current_schemas(true); current_schemas ------------------------------- {pg_temp_3,pg_catalog,public} (1 row) SELECT n.nspname FROM pg_catalog.pg_namespace n ORDER BY 1; nspname -------------------- information_schema pg_catalog pg_temp_3 pg_temp_4 pg_toast pg_toast_temp_3 pg_toast_temp_4 public (8 rows) -- Добавим строки, проверим временную схему и строки из таблицы INSERT INTO t_temp5 VALUES (1),(2),(3); INSERT 0 3 SELECT pg_my_temp_schema()::regnamespace; pg_my_temp_schema ------------------- pg_temp_3 (1 row) BEGIN; BEGIN -- Анонимный блок нужен чтобы сформировать полное имя: схема.таблица DO $$ DECLARE _query text; _cursor CONSTANT refcursor := _cursor; BEGIN _query := SELECT * FROM || (SELECT n.nspname || . || c.relname relname FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam WHERE c.relname = ('t_temp5')); OPEN _cursor FOR EXECUTE _query; END $$; DO FETCH ALL FROM _cursor; id ---- 1 2 3 (3 rows) COMMIT; COMMIT -- Попробуем удалить временную схему (аналог DROP SCHEMA name) DO $$ BEGIN EXECUTE DROP SCHEMA || pg_my_temp_schema()::regnamespace; END $$; psql:5.sql:55: ERROR: cannot drop schema pg_temp_3 because other objects depend on it DETAIL: table t_temp5 depends on schema pg_temp_3 HINT: Use DROP ... CASCADE to drop the dependent objects too. CONTEXT: SQL statement "DROP SCHEMA IF EXISTS pg_temp_3" PL/pgSQL function inline_code_block line 1 at EXECUTE -- Повторим с CASCADE (схема удалилась успешно) DO $$ BEGIN EXECUTE DROP SCHEMA || pg_my_temp_schema()::regnamespace || CASCADE; END $$; psql:5.sql:57: NOTICE: drop cascades to table t_temp5 DO SELECT current_schemas(true); current_schemas --------------------- {pg_catalog,public} (1 row) -- Создадим еще одну временную таблицу CREATE TEMP TABLE t_temp5_2 (id integer); CREATE TABLE SELECT n.nspname FROM pg_catalog.pg_namespace n ORDER BY 1; nspname -------------------- information_schema pg_catalog pg_temp_4 pg_toast pg_toast_temp_3 pg_toast_temp_4 public (7 rows) SELECT current_schemas(true); current_schemas --------------------- {pg_catalog,public} (1 row) SHOW search_path; search_path ----------------- "$user", public (1 row) INSERT INTO t_temp5_2 VALUES (1), (2), (3); INSERT 0 3 -- OID временной схемы есть, но записи в pg_namespace для него нет SELECT pg_my_temp_schema()::regnamespace; pg_my_temp_schema ------------------- 482607 (1 row) SELECT nspname FROM pg_namespace WHERE oid = pg_my_temp_schema(); nspname --------- (0 rows) SELECT * FROM pg_temp.t_temp5_2; id ---- 1 2 3 (3 rows) SELECT * FROM pg_temp_3.t_temp5_2; psql:5.sql:85: ERROR: relation "pg_temp_3.t_temp5_2" does not exist LINE 1: SELECT * FROM pg_temp_3.t_temp5_2; ^ SELECT current_schemas(true); current_schemas --------------------- {pg_catalog,public} (1 row) SELECT * FROM pg_catalog.pg_namespace; oid | nspname | nspowner | nspacl --------+--------------------+----------+--------------------------------------------------------------- 99 | pg_toast | 10 | 11 | pg_catalog | 10 | {postgres=UC/postgres,=U/postgres} 2200 | public | 6171 | {pg_database_owner=UC/pg_database_owner,=U/pg_database_owner} 13250 | information_schema | 10 | {postgres=UC/postgres,=U/postgres} 432342 | pg_temp_4 | 10 | 432516 | pg_toast_temp_3 | 10 | 432571 | pg_toast_temp_4 | 10 | (7 rows) -- Проверим что пишут в системном каталоге SELECT c.oid, c.relname FROM pg_catalog.pg_class c WHERE c.relname = 't_temp5_2'; oid | relname --------+----------- 482611 | t_temp5_2 (1 row) SELECT oid, relnamespace FROM pg_class WHERE oid = (SELECT c.oid FROM pg_catalog.pg_class c WHERE c.relname = 't_temp5'); oid | relnamespace -----+-------------- (0 rows) SELECT * FROM pg_namespace WHERE oid = (SELECT relnamespace FROM pg_class WHERE oid = (SELECT c.oid FROM pg_catalog.pg_class c WHERE c.relname = 't_temp5')); oid | nspname | nspowner | nspacl -----+---------+----------+-------- (0 rows)
6. Временная схема и обычная роль
6. Временная схема и обычная роль. Код эксперимента
6. Временная схема и обычная роль. ECHO-комментарии
-- 6. Временная схема и обычная роль -- pavel — это роль суперпользователя CREATE ROLE check_tmp LOGIN; CREATE ROLE SHOW hba_file; hba_file ------------------------------------- /etc/postgresql/16/main/pg_hba.conf (1 row) SELECT * FROM pg_hba_file_rules; rule_number | file_name | line_number | type | database | user_name | address | netmask | auth_method | options | error -------------+-------------------------------------+-------------+-------+---------------+-------------+----------+-----------------------------------------+---------------+---------+------- 1 | /etc/postgresql/16/main/pg_hba.conf | 1 | local | {all} | {check_tmp} | | | trust | | 14 | /etc/postgresql/16/main/pg_hba.conf | 123 | local | {all} | {postgres} | | | peer | | 15 | /etc/postgresql/16/main/pg_hba.conf | 128 | local | {all} | {all} | | | peer | | 16 | /etc/postgresql/16/main/pg_hba.conf | 129 | local | {pavel} | {pavel} | | | trust | | 17 | /etc/postgresql/16/main/pg_hba.conf | 131 | host | {all} | {all} | 127.0.0..| 255.255.255.255 | scram-sha-256 | | | | | | | |.1 | | | | 18 | /etc/postgresql/16/main/pg_hba.conf | 133 | host | {all} | {all} | ::1 | ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff | scram-sha-256 | | 19 | /etc/postgresql/16/main/pg_hba.conf | 136 | local | {replication} | {all} | | | peer | | 20 | /etc/postgresql/16/main/pg_hba.conf | 137 | host | {replication} | {all} | 127.0.0..| 255.255.255.255 | scram-sha-256 | | | | | | | |.1 | | | | 21 | /etc/postgresql/16/main/pg_hba.conf | 138 | host | {replication} | {all} | ::1 | ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff | scram-sha-256 | | (21 rows) SELECT pg_reload_conf(); pg_reload_conf ---------------- t (1 row) \c - check_tmp You are now connected to database "pavel" as user "check_tmp". \conninfo You are connected to database "pavel" as user "check_tmp" via socket in "/var/run/postgresql" at port "5432". -- Проверка — все команды из пятого эксперимента -- Обратить внимание на результат команды удаления схемы -- ERROR: must be owner of schema pg_temp_3 -- И на последнюю команду — для схемы pg_temp_3 есть название -- Создаем таблицу, смотрим схему SELECT current_schemas(true); current_schemas --------------------- {pg_catalog,public} (1 row) CREATE TEMP TABLE t_temp6 (id integer); CREATE TABLE SELECT current_schemas(true); current_schemas ------------------------------- {pg_temp_3,pg_catalog,public} (1 row) SELECT n.nspname FROM pg_catalog.pg_namespace n ORDER BY 1; nspname -------------------- information_schema pg_catalog pg_temp_3 pg_temp_4 pg_toast pg_toast_temp_3 pg_toast_temp_4 public (8 rows) -- Добавим строки, проверим временную схему и строки из таблицы INSERT INTO t_temp6 VALUES (1),(2),(3); INSERT 0 3 SELECT pg_my_temp_schema()::regnamespace; pg_my_temp_schema ------------------- pg_temp_3 (1 row) BEGIN; BEGIN -- Анонимный блок нужен чтобы сформировать полное имя: схема.таблица DO $$ DECLARE _query text; _cursor CONSTANT refcursor := _cursor; BEGIN _query := SELECT * FROM || (SELECT n.nspname || . || c.relname relname FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam WHERE c.relname = ('t_temp6')); OPEN _cursor FOR EXECUTE _query; END $$; DO FETCH ALL FROM _cursor; id ---- 1 2 3 (3 rows) COMMIT; COMMIT -- Попробуем удалить временную схему (аналог DROP SCHEMA name) DO $$ BEGIN EXECUTE DROP SCHEMA || pg_my_temp_schema()::regnamespace; END $$; psql:6.sql:84: ERROR: must be owner of schema pg_temp_3 CONTEXT: SQL statement "DROP SCHEMA pg_temp_3" PL/pgSQL function inline_code_block line 1 at EXECUTE -- Повторим с CASCADE DO $$ BEGIN EXECUTE DROP SCHEMA || pg_my_temp_schema()::regnamespace || CASCADE; END $$; psql:6.sql:90: ERROR: must be owner of schema pg_temp_3 CONTEXT: SQL statement "DROP SCHEMA pg_temp_3 CASCADE" PL/pgSQL function inline_code_block line 1 at EXECUTE SELECT current_schemas(true); current_schemas ------------------------------- {pg_temp_3,pg_catalog,public} (1 row) -- Создадим еще одну временную таблицу CREATE TEMP TABLE t_temp6_2 (id integer); CREATE TABLE SELECT n.nspname FROM pg_catalog.pg_namespace n ORDER BY 1; nspname -------------------- information_schema pg_catalog pg_temp_3 pg_temp_4 pg_toast pg_toast_temp_3 pg_toast_temp_4 public (8 rows) SELECT current_schemas(true); current_schemas ------------------------------- {pg_temp_3,pg_catalog,public} (1 row) SHOW search_path; search_path ----------------- "$user", public (1 row) INSERT INTO t_temp6_2 VALUES (1), (2), (3); INSERT 0 3 -- Временная схема не определяется SELECT pg_my_temp_schema()::regnamespace; pg_my_temp_schema ------------------- pg_temp_3 (1 row) SELECT nspname FROM pg_namespace WHERE oid = pg_my_temp_schema(); nspname ----------- pg_temp_3 (1 row) SELECT * FROM pg_temp.t_temp6_2; id ---- 1 2 3 (3 rows) SELECT * FROM pg_temp_3.t_temp6_2; id ---- 1 2 3 (3 rows) SELECT current_schemas(true); current_schemas ------------------------------- {pg_temp_3,pg_catalog,public} (1 row) SELECT * FROM pg_catalog.pg_namespace; oid | nspname | nspowner | nspacl --------+--------------------+----------+--------------------------------------------------------------- 99 | pg_toast | 10 | 11 | pg_catalog | 10 | {postgres=UC/postgres,=U/postgres} 2200 | public | 6171 | {pg_database_owner=UC/pg_database_owner,=U/pg_database_owner} 13250 | information_schema | 10 | {postgres=UC/postgres,=U/postgres} 432342 | pg_temp_4 | 10 | 432516 | pg_toast_temp_3 | 10 | 432571 | pg_toast_temp_4 | 10 | 482748 | pg_temp_3 | 10 | (8 rows) -- Проверим что пишут в системном каталоге SELECT c.oid, c.relname FROM pg_catalog.pg_class c WHERE c.relname = 't_temp6_2'; oid | relname --------+----------- 482794 | t_temp6_2 (1 row) SELECT oid, relnamespace FROM pg_class WHERE oid = (SELECT c.oid FROM pg_catalog.pg_class c WHERE c.relname = 't_temp6'); oid | relnamespace --------+-------------- 482791 | 482748 (1 row) SELECT * FROM pg_namespace WHERE oid = (SELECT relnamespace FROM pg_class WHERE oid = (SELECT c.oid FROM pg_catalog.pg_class c WHERE c.relname = 't_temp6')); oid | nspname | nspowner | nspacl --------+-----------+----------+-------- 482748 | pg_temp_3 | 10 | (1 row) -- Удаляем лишнее DROP TABLE t_temp6; DROP TABLE DROP TABLE t_temp6_2; DROP TABLE \c - pavel You are now connected to database "pavel" as user "pavel". DROP ROLE check_tmp; DROP ROLE
7. Auto {analyze|vacuum} временной и обычной таблицы
7. Auto {analyze|vacuum} временной и обычной таблицы. Код эксперимента
7. Auto {analyze|vacuum} временной и обычной таблицы. ECHO-комментарии
-- 7. Auto {analyze|vacuum} временной и обычной таблицы -- Добавлю настройки ускорения авто -вакуума и -анализа ALTER SYSTEM SET autovacuum_naptime = 1; ALTER SYSTEM ALTER SYSTEM SET autovacuum_vacuum_scale_factor = 0.01; ALTER SYSTEM ALTER SYSTEM SET autovacuum_vacuum_threshold = 0; ALTER SYSTEM ALTER SYSTEM SET autovacuum_analyze_scale_factor = 0.02; ALTER SYSTEM ALTER SYSTEM SET autovacuum_analyze_threshold = 0; ALTER SYSTEM -- Перечитаю конфигурацию SELECT pg_reload_conf(); pg_reload_conf ---------------- t (1 row) -- Создадим две таблицы (обычную и временную) -- И заполним их данными CREATE TEMP TABLE t_temp7 (id integer); CREATE TABLE INSERT INTO t_temp7 SELECT * FROM generate_series(1,1_000_000); INSERT 0 1000000 CREATE TABLE temp7 (id integer); CREATE TABLE INSERT INTO temp7 SELECT * FROM generate_series(1,1_000_000); INSERT 0 1000000 -- Сделаем небольшую паузу SELECT pg_sleep(2); pg_sleep ---------- (1 row) -- Проверим собранную статистику по таблицам SELECT pg_sleep(2); pg_sleep ---------- (1 row) SELECT reltuples, relpages FROM pg_class WHERE relname = 't_temp7'; reltuples | relpages -----------+---------- -1 | 0 (1 row) SELECT reltuples, relpages FROM pg_class WHERE relname = 'temp7'; reltuples | relpages -----------+---------- 1e+06 | 4425 (1 row) SELECT count(*) FROM t_temp7; count --------- 1000000 (1 row) SELECT count(*) FROM temp7; count --------- 1000000 (1 row) SELECT null_frac, avg_width, n_distinct, correlation FROM pg_stats s WHERE s.tablename = 't_temp7' AND s.attname = 'id'; null_frac | avg_width | n_distinct | correlation -----------+-----------+------------+------------- (0 rows) SELECT null_frac, avg_width, n_distinct, correlation FROM pg_stats s WHERE s.tablename = 'temp7' AND s.attname = 'id'; null_frac | avg_width | n_distinct | correlation -----------+-----------+------------+------------- 0 | 4 | -1 | 1 (1 row) -- Показания отличаются -- Для временной таблицы автоанализ не срабатывает -- Соберём статистику по временной таблице руками ANALYZE t_temp7; ANALYZE SELECT reltuples, relpages FROM pg_class WHERE relname = 't_temp7'; reltuples | relpages -----------+---------- 1e+06 | 4425 (1 row) SELECT reltuples, relpages FROM pg_class WHERE relname = 'temp7'; reltuples | relpages -----------+---------- 1e+06 | 4425 (1 row) SELECT null_frac, avg_width, n_distinct, correlation FROM pg_stats s WHERE s.tablename = 't_temp7' AND s.attname = 'id'; null_frac | avg_width | n_distinct | correlation -----------+-----------+------------+------------- 0 | 4 | -1 | 1 (1 row) SELECT null_frac, avg_width, n_distinct, correlation FROM pg_stats s WHERE s.tablename = 'temp7' AND s.attname = 'id'; null_frac | avg_width | n_distinct | correlation -----------+-----------+------------+------------- 0 | 4 | -1 | 1 (1 row) -- Проверим размер полученных таблиц SELECT pg_size_pretty(pg_table_size('t_temp7')); pg_size_pretty ---------------- 35 MB (1 row) SELECT pg_size_pretty(pg_table_size('temp7')); pg_size_pretty ---------------- 35 MB (1 row) -- Обновим все строки в таблицах и снова проверим размер UPDATE t_temp7 SET id = id + 1; UPDATE 1000000 UPDATE temp7 SET id = id + 1; UPDATE 1000000 SELECT pg_size_pretty(pg_table_size('t_temp7')); pg_size_pretty ---------------- 69 MB (1 row) SELECT pg_size_pretty(pg_table_size('temp7')); pg_size_pretty ---------------- 69 MB (1 row) -- Удалим все строки из таблиц DELETE FROM t_temp7; DELETE 1000000 DELETE FROM temp7; DELETE 1000000 -- Сделаем небольшую задержку SELECT pg_sleep(3); pg_sleep ---------- (1 row) -- Автовакуум не очищает временную таблицу SELECT pg_size_pretty(pg_table_size('t_temp7')); pg_size_pretty ---------------- 69 MB (1 row) SELECT pg_size_pretty(pg_table_size('temp7')); pg_size_pretty ---------------- 16 kB (1 row) -- Сделаем очистку руками VACUUM t_temp7; VACUUM SELECT pg_sleep(3); pg_sleep ---------- (1 row) SELECT pg_size_pretty(pg_table_size('t_temp7')); pg_size_pretty ---------------- 16 kB (1 row) SELECT pg_size_pretty(pg_table_size('temp7')); pg_size_pretty ---------------- 16 kB (1 row) -- Удалим лишнее DROP TABLE temp7; DROP TABLE -- И настройки тоже ALTER SYSTEM RESET autovacuum_naptime; ALTER SYSTEM ALTER SYSTEM RESET autovacuum_vacuum_scale_factor; ALTER SYSTEM ALTER SYSTEM RESET autovacuum_vacuum_threshold; ALTER SYSTEM ALTER SYSTEM RESET autovacuum_analyze_scale_factor; ALTER SYSTEM ALTER SYSTEM RESET autovacuum_analyze_threshold; ALTER SYSTEM -- Перечитаю конфигурацию SELECT pg_reload_conf(); pg_reload_conf ---------------- t (1 row)
8. Параллельная обработка временных таблиц
8. Параллельная обработка временных таблиц. Код эксперимента
8. Параллельная обработка временных таблиц. ECHO-комментарии
-- 8. Параллельная обработка временных таблиц -- Создадим две таблицы, одну временную, другую обычную CREATE TEMP TABLE t_temp8 (id integer); CREATE TABLE CREATE TABLE temp8 (id integer); CREATE TABLE -- Добавим туда по миллиону строк INSERT INTO t_temp8 SELECT * FROM generate_series(1,1_000_000); INSERT 0 1000000 INSERT INTO temp8 SELECT * FROM generate_series(1,1_000_000); INSERT 0 1000000 -- Руками сделаем анализ ANALYZE t_temp8; ANALYZE ANALYZE temp8; ANALYZE -- Посмотрим план запросов EXPLAIN ANALYZE SELECT * FROM t_temp8; QUERY PLAN -------------------------------------------------------------------------------------------------------------------- Seq Scan on t_temp8 (cost=0.00..14425.00 rows=1000000 width=4) (actual time=0.029..1249.628 rows=1000000 loops=1) Planning Time: 0.045 ms Execution Time: 2440.071 ms (3 rows) EXPLAIN ANALYZE SELECT * FROM temp8; QUERY PLAN ------------------------------------------------------------------------------------------------------------------ Seq Scan on temp8 (cost=0.00..14425.00 rows=1000000 width=4) (actual time=0.011..1237.203 rows=1000000 loops=1) Planning Time: 0.069 ms Execution Time: 2431.272 ms (3 rows) -- Установим пар-р, с помощью которого можно посмотреть параллельный план SHOW debug_parallel_query; debug_parallel_query ---------------------- off (1 row) SET debug_parallel_query = on; SET -- И снова посмотрим план запросов — обычная таблица распараллеливается EXPLAIN ANALYZE SELECT * FROM t_temp8; QUERY PLAN -------------------------------------------------------------------------------------------------------------------- Seq Scan on t_temp8 (cost=0.00..14425.00 rows=1000000 width=4) (actual time=0.011..1245.712 rows=1000000 loops=1) Planning Time: 0.034 ms Execution Time: 2435.330 ms (3 rows) EXPLAIN ANALYZE SELECT * FROM temp8; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------ Gather (cost=1000.00..115425.00 rows=1000000 width=4) (actual time=4.182..1309.885 rows=1000000 loops=1) Workers Planned: 1 Workers Launched: 1 Single Copy: true -> Seq Scan on temp8 (cost=0.00..14425.00 rows=1000000 width=4) (actual time=0.013..1297.671 rows=1000000 loops=1) Planning Time: 0.032 ms Execution Time: 2594.434 ms (7 rows) -- Удаляем лишнее DROP TABLE temp8; DROP TABLE
9. Параметр temp_buffers
9. Параметр temp_buffers. Код эксперимента
9. Параметр temp_buffers. ECHO-комментарии
-- 9. Параметр temp_buffers -- Подготовка — создадим роль check_tmp, настроим доступ pg_hba -- Проверим значение пар-ра temp_buffers SHOW temp_buffers; temp_buffers -------------- 8MB (1 row) -- Увеличим его SET temp_buffers = 16MB; SET -- Создадим временную таблицу и снова увеличим значение пар-ра CREATE TEMP TABLE t_temp9 (id integer); CREATE TABLE SET temp_buffers = 32MB; SET -- Вроде бы обращаемся к временной таблице - но пар-р снова можно изменить SELECT * FROM t_temp9; id ---- (0 rows) SET temp_buffers = 16MB; SET -- Но после изменения данных во временной таблице уже нельзя менять значение пар-ра INSERT INTO t_temp9 VALUES (1); INSERT 0 1 SET temp_buffers = 32MB; psql:9.sql:36: ERROR: invalid value for parameter "temp_buffers": 4096 DETAIL: "temp_buffers" cannot be changed after any temporary tables have been accessed in the session. -- В плане чтение из локального кеша отображается как LOCAL READ/HIT EXPLAIN (ANALYZE, BUFFERS) SELECT count(*) FROM t_temp9; QUERY PLAN ----------------------------------------------------------------------------------------------------------- Aggregate (cost=41.88..41.88 rows=1 width=8) (actual time=0.016..0.023 rows=1 loops=1) Buffers: local hit=1 -> Seq Scan on t_temp9 (cost=0.00..35.50 rows=2550 width=0) (actual time=0.006..0.009 rows=1 loops=1) Buffers: local hit=1 Planning: Buffers: shared hit=3 Planning Time: 0.046 ms Execution Time: 0.130 ms (8 rows) \c You are now connected to database "pavel" as user "pavel". SET temp_buffers = 1024 kB; SET CREATE TEMP TABLE t_temp9 (id integer); CREATE TABLE CREATE TABLE temp9 (id integer); CREATE TABLE INSERT INTO t_temp9 SELECT * FROM generate_series(1,1_000_000); INSERT 0 1000000 INSERT INTO temp9 SELECT * FROM generate_series(1,1_000_000); INSERT 0 1000000 SELECT count(*) FROM temp9; count --------- 1000000 (1 row) -- local read EXPLAIN (ANALYZE, BUFFERS) SELECT count(*) FROM t_temp9; QUERY PLAN -------------------------------------------------------------------------------------------------------------------------- Aggregate (cost=18529.69..18529.70 rows=1 width=8) (actual time=2452.049..2452.055 rows=1 loops=1) Buffers: local read=4425 dirtied=4425 written=4423 -> Seq Scan on t_temp9 (cost=0.00..15708.75 rows=1128375 width=0) (actual time=0.027..1232.810 rows=1000000 loops=1) Buffers: local read=4425 dirtied=4425 written=4423 Planning: Buffers: shared hit=4 Planning Time: 0.048 ms Execution Time: 2452.079 ms (8 rows) -- Удаляем лишнее DROP TABLE temp9; DROP TABLE
10. Временные таблица, индекс, TOAST и их расположение на диске
10. Временные таблица, индекс, TOAST и их расположение на диске. Код эксперимента
10. Временные таблица, индекс, TOAST и их расположение на диске. ECHO-комментарии
-- 10. Временные таблица, индекс, TOAST и их расположение на диске -- ТАБЛИЦА t_temp10 CREATE TEMP TABLE t_temp10(val text); CREATE TABLE -- Индекс на таблицу t_temp10 CREATE INDEX i_t_temp10 ON t_temp10 (val); CREATE INDEX -- НАЗВАНИЕ временной схемы SELECT pg_my_temp_schema()::regnamespace; pg_my_temp_schema ------------------- pg_temp_3 (1 row) -- НАЗВАНИЕ временной TOAST схемы SELECT n.nspname FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam WHERE c.relname = ( SELECT relname FROM pg_class WHERE OID = ( SELECT reltoastrelid FROM pg_class WHERE relname='t_temp10')); nspname ----------------- pg_toast_temp_3 (1 row) -- Однако, план запроса с опцией VERBOSE показывает без цифр EXPLAIN (ANALYZE, VERBOSE) select * from t_temp10; QUERY PLAN --------------------------------------------------------------------------------------------------------------- Seq Scan on pg_temp.t_temp10 (cost=0.00..23.60 rows=1360 width=32) (actual time=0.002..0.004 rows=0 loops=1) Output: val Planning Time: 0.084 ms Execution Time: 0.012 ms (4 rows) -- OID таблицы t_temp10 SELECT c.oid, c.relname FROM pg_catalog.pg_class c WHERE c.relname = 't_temp10'; oid | relname --------+---------- 490781 | t_temp10 (1 row) -- OID индекса i_t_temp10 SELECT c.oid, c.relname FROM pg_catalog.pg_class c WHERE c.relname = 'i_t_temp10'; oid | relname --------+------------ 490786 | i_t_temp10 (1 row) -- OID TOAST-таблицы на t_temp10 SELECT relfilenode, relname FROM pg_class WHERE OID = ( SELECT reltoastrelid FROM pg_class WHERE relname='t_temp10'); relfilenode | relname -------------+----------------- 490784 | pg_toast_490781 (1 row) -- OID TOAST-таблицы на t_temp10 с именем временной схемы SELECT c.oid, n.nspname || . || c.relname relname FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam WHERE c.relname = (SELECT relname FROM pg_class WHERE OID = ( SELECT reltoastrelid FROM pg_class WHERE relname='t_temp10')); oid | relname --------+--------------------------------- 490784 | pg_toast_temp_3.pg_toast_490781 (1 row) -- Индекс на TOAST-таблицу SELECT relname || '_index' FROM pg_class WHERE OID = (SELECT reltoastrelid FROM pg_class WHERE relname='t_temp10'); ?column? ----------------------- pg_toast_490781_index (1 row) -- Индекс на TOAST-таблицу на t_temp10 с именем временной схемы SELECT c.oid, n.nspname || . || c.relname relname FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam WHERE c.relname = (SELECT relname || '_index' FROM pg_class WHERE OID = (SELECT reltoastrelid FROM pg_class WHERE relname='t_temp10')); oid | relname --------+--------------------------------------- 490785 | pg_toast_temp_3.pg_toast_490781_index (1 row) -- Текущая БД SELECT * FROM current_database(); current_database ------------------ pavel (1 row) -- OID текущей БД SELECT oid, datname FROM pg_catalog.pg_database WHERE datname = (SELECT * FROM current_database()); oid | datname -------+--------- 16391 | pavel (1 row) -- Адрес t_temp10 на диске SELECT pg_relation_filepath('t_temp10') temp10_table; temp10_table ---------------------- base/16391/t3_490781 (1 row) -- Адрес i_t_temp10 на диске SELECT pg_relation_filepath('i_t_temp10') temp10_index; temp10_index ---------------------- base/16391/t3_490786 (1 row) -- Адрес TOAST-таблицы на диске SELECT pg_relation_filepath((SELECT n.nspname || . || c.relname relname FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam WHERE c.relname = (SELECT relname FROM pg_class WHERE OID = (SELECT reltoastrelid FROM pg_class WHERE relname='t_temp10')))) TOAST_table; toast_table ---------------------- base/16391/t3_490784 (1 row) -- Адрес индекса на TOAST-таблицу на диске SELECT pg_relation_filepath((SELECT n.nspname || . || c.relname relname FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam WHERE c.relname = (SELECT relname || '_index' FROM pg_class WHERE OID = (SELECT reltoastrelid FROM pg_class WHERE relname='t_temp10')))) TOAST_temp10_index; toast_temp10_index ---------------------- base/16391/t3_490785 (1 row) SHOW data_directory; data_directory ----------------------------- /var/lib/postgresql/16/main (1 row) -- Проверим на диске sudo ls -al /var/lib/postgresql/16/main/base/16391/ | grep t3 -rw------- 1 postgres postgres 0 ноя 20 16:20 t3_490781 -rw------- 1 postgres postgres 0 ноя 20 16:20 t3_490784 -rw------- 1 postgres postgres 8192 ноя 20 16:20 t3_490785 -rw------- 1 postgres postgres 8192 ноя 20 16:20 t3_490786
11. Параметр remove_temp_files_after_crash (тут возможно баг?)
11. Параметр remove_temp_files_after_crash (тут возможно баг?). Код эксперимента 1
11. Параметр remove_temp_files_after_crash (тут возможно баг?). Код эксперимента 2
11. Параметр remove_temp_files_after_crash (тут возможно баг?). Код эксперимента 3
11. Параметр remove_temp_files_after_crash (тут возможно баг?). ECHO-комментарии 1
11. Параметр remove_temp_files_after_crash (тут возможно баг?). ECHO-комментарии 2
11. Параметр remove_temp_files_after_crash (тут возможно баг?). ECHO-комментарии 3
psql:
-- 11. Параметр remove_temp_files_after_crash -- 11_1 -- А здесь единым скриптом не получилось -- Команды, начинающиеся с доллара — выполняются в ОС SHOW remove_temp_files_after_crash; remove_temp_files_after_crash ------------------------------- off (1 row) -- Включаем параметр remove_temp_files_after_crash в сессии нельзя SET remove_temp_files_after_crash = on; psql:11_1.sql:9: ERROR: parameter "remove_temp_files_after_crash" cannot be changed now ALTER SYSTEM SET remove_temp_files_after_crash = on; ALTER SYSTEM -- Далее перезапускаем экземпляр -- 11.2 -- Проверяем параметр SHOW remove_temp_files_after_crash; remove_temp_files_after_crash ------------------------------- on (1 row) CREATE TEMP TABLE t_temp11 (id integer); CREATE TABLE INSERT INTO t_temp11 VALUES (1),(2),(3); INSERT 0 3 SELECT pg_relation_filepath(t_temp11); pg_relation_filepath ---------------------- base/16391/t3_498992 (1 row) SHOW data_directory; data_directory ----------------------------- /var/lib/postgresql/16/main (1 row) -- ID обслуживающего процесса SELECT pg_backend_pid(); pg_backend_pid ---------------- 77078 (1 row)
bash:
$ sudo ls -al /var/lib/postgresql/16/main/base/16391/ | grep t3 -rw------- 1 postgres postgres 8192 ноя 20 17:37 t3_498992 $ sudo kill -9 77078 $ sudo ls -al /var/lib/postgresql/16/main/base/16391/ | grep t3 # пусто — таких файлов нет
psql:
-- 11_3 -- Сессия оборвана SELECT 1; server closed the connection unexpectedly This probably means the server terminated abnormally before or while processing the request. The connection to the server was lost. Attempting reset: Succeeded. -- Но сразу восстанавливается SELECT 1; ?column? ---------- 1 (1 row) SHOW remove_temp_files_after_crash; remove_temp_files_after_crash ------------------------------- on (1 row) -- Отключаем параметр remove_temp_files_after_crash в сессии нельзя SET remove_temp_files_after_crash = off; psql:11_3.sql:15: ERROR: parameter "remove_temp_files_after_crash" cannot be changed now ALTER SYSTEM SET remove_temp_files_after_crash = off; ALTER SYSTEM -- Далее перезапускаем экземпляр
bash:
$ sudo systemctl restart postgresql@16-main.service
psql:
-- 11.2 -- Проверяем параметр SHOW remove_temp_files_after_crash; remove_temp_files_after_crash ------------------------------- off (1 row) CREATE TEMP TABLE t_temp11 (id integer); CREATE TABLE INSERT INTO t_temp11 VALUES (1),(2),(3); INSERT 0 3 SELECT pg_relation_filepath(t_temp11); pg_relation_filepath ---------------------- base/16391/t3_507187 (1 row) SHOW data_directory; data_directory ----------------------------- /var/lib/postgresql/16/main (1 row) -- ID обслуживающего процесса SELECT pg_backend_pid(); pg_backend_pid ---------------- 78040 (1 row)
bash:
$ sudo ls -al /var/lib/postgresql/16/main/base/16391/ | grep t3 -rw------- 1 postgres postgres 8192 ноя 20 17:37 t3_507187 $ sudo kill -9 78040 $ sudo ls -al /var/lib/postgresql/16/main/base/16391/ | grep t3 -rw------- 1 postgres postgres 8192 ноя 20 17:37 t3_507187 sudo hexdump /var/lib/postgresql/16/main/base/16391/t3_507187 0000000 0000 0000 0000 0000 0000 0000 0000 0000 * 0002000
psql:
-- Однако! Если опять подключиться к серверу SELECT 1; server closed the connection unexpectedly This probably means the server terminated abnormally before or while processing the request. The connection to the server was lost. Attempting reset: Succeeded. SELECT 1; ?column? ---------- 1 (1 row) -- Файлы временных таблиц будут удалены!
bash:
$ sudo ls -al /var/lib/postgresql/16/main/base/16391/ | grep t3 # пусто — файлов больше нет
psql:
-- И еще раз однако! -- 11.2 -- Проверяем параметр SHOW remove_temp_files_after_crash; remove_temp_files_after_crash ------------------------------- off (1 row) CREATE TEMP TABLE t_temp11 (id integer); CREATE TABLE INSERT INTO t_temp11 VALUES (1),(2),(3); INSERT 0 3 SELECT pg_relation_filepath(t_temp11); pg_relation_filepath ---------------------- base/16391/t3_515379 (1 row) SHOW data_directory; data_directory ----------------------------- /var/lib/postgresql/16/main (1 row) -- ID обслуживающего процесса SELECT pg_backend_pid(); pg_backend_pid ---------------- 78839 (1 row)
bash (вот тут?):
$ sudo head -n 1 /var/lib/postgresql/16/main/postmaster.pid 78014 $ sudo ls -al /var/lib/postgresql/16/main/base/16391/ | grep t3 -rw------- 1 postgres postgres 8192 ноя 20 17:37 t3_507187 $ sudo kill -9 78014 $ sudo ls -al /var/lib/postgresql/16/main/base/16391/ | grep t3 # иии пусто — файлов нет! Почему? Я подозреваю что это баг
Leave a Reply