Search
Write a publication
Pull to refresh

Работа с временными таблицами в PostgreSQL

Reading time18 min
Views1.1K

При создании временных таблиц в PostgreSQL изменяются до 13 таблиц системного каталога, при этом особенно сильно разрастаются pg_attributepg_class, pg_depend и pg_type. Массовое создание и усечение временных таблиц активно применяется в том числе в 1C:ERP. В статье рассматриваются особенности работы с временными таблицами и описано решение проблемы раздувания таблиц системного каталога, реализованное в СУБД Tantor Postgres. Начиная с версии 17.5, при создании и удалении временных таблиц, а также при работе с временными объектами таблицы системного каталога не изменяются. Новый функционал управляется параметром конфигурации enable_temp_memory_catalog, который можно включать на нескольких уровнях, в том числе на уровне сессии, без необходимости перезапуска экземпляра.

Раздувание таблиц системного каталога в PostgreSQL

Создание, удаление и изменение отношений в PostgreSQL приводит к внесению изменений в таблицы системного каталога. При создании таблиц, включая временные, внешние и нежурналируемые, могут автоматически создаваться ограничения целостности и индексы. В общей сложности изменения затрагивают до 13 таблиц системного каталога. Число актуальных версий строк в этих таблицах можно получить с помощью запроса:

Запрос

select 'pg_class' table, pg_total_relation_size('pg_class') size, count(*)

 from pg_class union all

select 'pg_attribute', pg_total_relation_size('pg_attribute'), count(*)

 from pg_attribute union all

select 'pg_type', pg_total_relation_size('pg_type'), count(*)

 from pg_type union all

select 'pg_depend', pg_total_relation_size('pg_depend'), count(*)

 from pg_depend union all

select 'pg_inherits', pg_total_relation_size('pg_inherits'), count(*)

 from pg_inherits union all

select 'pg_statistic', pg_total_relation_size('pg_statistic'), count(*)

 from pg_statistic union all

select 'pg_attrdef', pg_total_relation_size('pg_attrdef'), count(*)

 from pg_attrdef union all

select 'pg_collation', pg_total_relation_size('pg_collation'), count(*)

 from pg_collation union all

select 'pg_constraint', pg_total_relation_size('pg_constraint'), count(*)

 from pg_constraint union all

select 'pg_description', pg_total_relation_size('pg_description'), count(*)

 from pg_description union all

select 'pg_index', pg_total_relation_size('pg_index'), count(*) from

 pg_index union all

select 'pg_namespace', pg_total_relation_size('pg_namespace'), count(*)

 from pg_namespace union all

select 'pg_sequence', pg_total_relation_size('pg_sequence'), count(*)

 from pg_sequence;

----------------------

     table      |    size    | count
----------------+------------+------- 
pg_sequence    |       8192 |     0 
pg_attrdef     |      24576 |     0 
pg_inherits    |      16384 |     0 
pg_namespace   |      57344 |   102 
pg_constraint  |     114688 |   114 
pg_index       |      65536 |   168 
pg_collation   |     229376 |   822 
pg_description |     565248 |  5233 
pg_statistic   |    2187264 |   416 
pg_class       |  542957568 |   428 
pg_attribute   | 3064348672 |  3195 
pg_depend      | 1002225664 |  1808 
pg_type        | 1000480768 |   637
(13 rows)

Проведем тест по созданию и удалению временных таблиц и посмотрим как это повлияет на число строк в таблицах системного каталога. В результате мы увидим, какие из таблиц системного каталога раздуваются больше других. Для наблюдения за состоянием буферного кэша установим расширение:

postgres=# create extension pg_buffercache;
CREATE EXTENSION

При небольшом количестве строк размер этих таблиц может быть значительным. Создадим файл, который сгенерирует скрипт для создания и удаления временных таблиц.

postgres@tantor:~$ cat gen2.pydef main():    
for count in [100]:        
with open(f'workl1c_{count}.sql', 'w') as f:            
for i in range(1, count):                
f.write(f"""create temporary table ttt{i} (c int);
drop table ttt{i};
""")
if __name__ == "__main__":
main()

Выполним скрипт для создания файла теста с названием workl1c_100.sql:

postgres@tantor:~$ python3 gen2.py

Чтобы таблицы системного каталога не очищались, можно удерживать горизонт базы. Для этого в одной из сессий можно открыть транзакцию и выполнить в ней любое изменение:

postgres=# begin;
create temporary table xt (n int);
BEGIN;
CREATE TABLE

Автовакуум можно не отключать, при удержании горизонта автовакуум не сможет очистить старые версии строк. Для наблюдения за автовакуумом установим параметр:

postgres=# alter system set log_autovacuum_min_duration = '1s';
select pg_reload_conf();
 pg_reload_conf
----------------
 t
(1 row)
ALTER SYSTEM

Если вакуумирование какой-либо таблицы превысит одну секунду, будет создана запись в логе.

Пример параметров конфигурации:

postgres@tantor:~$ cat $PGDATA/postgresql.auto.conf
# Do not edit this file manually!
# It will be overwritten by the ALTER SYSTEM command.
max_connections = '1000'
shared_preload_libraries = 'pg_stat_statements'
logging_collector = 'on'
log_filename = 'postgresql-%F.log'
log_autovacuum_min_duration = '1s'

Для запуска файла создания временных таблиц используем утилиту pgbench:

postgres@tantor:~$ pgbench -n -T 10000 -c 16 -P 60 -f workl1c_100.sql\
progress: 60.0 s, 19.8 tps, lat 798.487 ms stddev 97.788, 0 failed
progress: 120.0 s, 16.0 tps, lat 1000.391 ms stddev 100.195, 0 failed
...
progress: 2340.0 s, 2.2 tps, lat 7131.160 ms stddev 1289.401, 0 failed
progress: 2400.0 s, 2.2 tps, lat 7343.940 ms stddev 1204.763, 0 failed

Постепенное уменьшение tps связано с заполнением буферного кэша. По мере увеличения размеров таблиц системного каталога процесс, создающий временные таблицы, вынужден вытеснять из кэша блоки, то есть буферный кэш оказывается полностью заполненным:

postgres=# select * from pg_buffercache_usage_counts();
 usage_count | buffers | dirty | pinned
-------------+---------+-------+--------
           0 |     776 |    71 |      0
           1 |     833 |    85 |      1
           2 |    1134 |   102 |      0
           3 |     801 |    26 |      0
           4 |    2758 |    28 |      0
           5 |   10082 |   139 |      1
(6 rows)

Размеры таблиц системного каталога увеличились:

     table      |   size    | count
----------------+-----------+-------
 pg_class       | 161521664 |   431
 pg_type        | 293175296 |   637
 pg_depend      | 298180608 |  1836
 pg_attribute   | 899604480 |  3251
(13 rows)

Если остановить тест, немного подождать и запустить его заново, значения tps вернутся к первоначальным показателям — это происходит потому, что после остановки теста bgwriter успевает освободить буферы (можно поразмышлять, какие именно, почему и всегда ли это так):

postgres=# select * from pg_buffercache_usage_counts();
 usage_count | buffers | dirty | pinned
-------------+---------+-------+--------
           0 |   15946 |     0 |      0
           1 |     242 |    81 |      0
           2 |       9 |     0 |      0
           3 |       9 |     0 |      0
           4 |     174 |    25 |      0
           5 |       4 |     2 |      0
(6 rows)

При этом размеры таблиц системного каталога не уменьшаются, а время выполнения запросов, которые выдают размер, остается прежним. После некоторого времени показатели постепенно уменьшаются:

progress: 60.0 s, 18.9 tps, lat 841.028 ms stddev 95.998, 0 failed
progress: 120.0 s, 15.4 tps, lat 1038.162 ms stddev 107.813, 0 failed
progress: 180.0 s, 13.1 tps, lat 1218.092 ms stddev 119.285, 0 failed
progress: 240.0 s, 11.7 tps, lat 1370.945 ms stddev 97.170, 0 failed
...
progress: 7380.0 s, 1.0 tps, lat 15925.498 ms stddev 2271.770, 0 failed
progress: 7440.0 s, 1.0 tps, lat 16131.646 ms stddev 2646.911, 0 failed
progress: 7500.0 s, 0.9 tps, lat 16450.132 ms stddev 3068.155, 0 failed

Через некоторое время в журнале появятся сообщения автовакуума и автоанализа. Ниже приведен пример таких сообщений спустя 6000 секунд:

postgres@tantor:~$ cat $PGDATA/log/postgresql*
 10:06:58.014 MSK [24548] LOG:  automatic analyze of table "postgres.pg_catalog.pg_class"
 elapsed: 2.59 s
 10:07:27.693 MSK [24551] LOG:  automatic vacuum of table "postgres.pg_catalog.pg_attribute": index scans: 0
 tuples: 0 removed, 19759720 remain, 19759694 are dead but not yet removable, oldest xmin: 13261300
elapsed: 35.63 s
 10:07:29.844 MSK [24551] LOG:  automatic analyze of table "postgres.pg_catalog.pg_attribute"
elapsed: 2.15 s
 10:07:40.733 MSK [24551] LOG:  automatic vacuum of table "postgres.pg_catalog.pg_depend": index scans: 0
 removable cutoff: 13261300, which was 5661621 XIDs old when operation ended
elapsed: 10.87 s
 10:07:43.996 MSK [24551] LOG:  automatic analyze of table "postgres.pg_catalog.pg_depend"
elapsed: 3.26 s
 10:07:49.832 MSK [24554] LOG:  automatic vacuum of table "postgres.pg_catalog.pg_type": index scans: 0
 tuples: 0 removed, 5659547 remain, 5658922 are dead but not yet removable, oldest xmin: 13261300
elapsed: 17.41 s
 10:07:52.590 MSK [24551] LOG:  automatic vacuum of table "postgres.pg_catalog.pg_class": index scans: 0
elapsed: 8.57 s
 10:07:52.891 MSK [24554] LOG:  automatic analyze of table "postgres.pg_catalog.pg_type"
elapsed: 3.05 s
 10:07:55.511 MSK [24551] LOG:  automatic analyze of table "postgres.pg_catalog.pg_class"
elapsed: 2.92 s

Мы приводим здесь характерный фрагмент диагностического лога. По значению elapsed видно, что автовакуум больше всего времени тратит на обработку таблиц системного каталога — pg_attribute, pg_type, pg_depend. Если в каждом цикле автовакуум тратит примерно одинаковое время на одни и те же таблицы, это означает, что он не может очистить эти таблицы и повторяет попытки впустую, расходуя ресурсы.

Цикл автовакуума и автоанализа системных таблиц в примере длился более 70 секунд, при этом результата не было — автовакуум не мог очистить таблицы из-за удержания горизонта. Циклы автовакуума и автоанализа выполняются последовательно. Задержка между циклами по умолчанию невелика и определяется параметром autovacuum_naptime = 1min.

Набор следующих параметров:

autovacuum_vacuum_cost_delay          | 2ms
autovacuum_vacuum_cost_limit          | -1
vacuum_cost_delay                     | 0
vacuum_cost_limit                     | 200

заставляет автовакуум работать с задержками. В нашем примере горизонт удерживается открытой транзакцией, и время работы автовакуума и автоанализа не влияет на сдвиг горизонта базы. Однако при реальной работе автовакуум и автоанализ могут сами удерживать горизонт, что нежелательно.

Настройка автовакуума. Устранение задержки

Если отключить задержку, например, выполнив команды:

postgres=# alter system set autovacuum_vacuum_cost_delay=0;
select pg_reload_conf();

то время вакуумирования таблиц уменьшится:

10:15:00.457 MSK [24588] LOG:  automatic vacuum of table "postgres.pg_catalog.pg_depend": index scans: 0
elapsed: 6.69 s
10:15:01.999 MSK [24588] LOG:  automatic analyze of table "postgres.pg_catalog.pg_depend"
elapsed: 1.54 s
10:15:04.983 MSK [24588] LOG:  automatic vacuum of table "postgres.pg_catalog.pg_class": index scans: 0
elapsed: 2.96 s
10:15:31.568 MSK [24595] LOG:  automatic vacuum of table "postgres.pg_catalog.pg_type": index scans: 0
elapsed: 6.74 s
10:15:32.864 MSK [24595] LOG:  automatic analyze of table "postgres.pg_catalog.pg_type"
elapsed: 1.28 s
10:15:50.476 MSK [24595] LOG:  automatic vacuum of table "postgres.pg_catalog.pg_attribute": index scans: 0
elapsed: 17.59 s
10:15:51.878 MSK [24595] LOG:  automatic analyze of table "postgres.pg_catalog.pg_attribute"
elapsed: 1.40 s

В приведённом примере время вакуумирования таблицы pg_attribute уменьшилось с 35 до 17 секунд. Однако в обоих случаях, пока удерживается горизонт базы, автовакуум работает впустую — он не может очищать версии строк. Перед запуском автовакуум не способен предсказать, удастся ли ему очистить хоть какую-то часть версий строк. Автовакуум мог бы проверять, сдвинулся ли горизонт базы по сравнению с предыдущим циклом, но на практике циклы автовакуума довольно продолжительны, и горизонт сдвигается лишь незначительно. При этом количество версий строк, вышедших за горизонт, практически не зависит от величины сдвига, поскольку в одной транзакции может быть изменено большое число строк.

Влияние раздувания таблиц системного каталога на скорость создания таблиц

Когда влияние невелико

Раздувание таблиц и индексов системного каталога обычно не влияет на скорость создания и удаления relations:

postgres=# \timing on \\
Timing is on.
postgres=# create temp table aaa (x int);
CREATE TABLE
Time: 15.370 ms
postgres=# create table aaa1 (x int);
CREATE TABLE
Time: 1.750 ms

Таблицы создаются быстро.

Когда влияние значительно

В ходе теста время выполнения запросов к таблицам системного каталога постепенно увеличивается:

explain (analyze, buffers) SELECT n.nspname, c.relname
ROM 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 n.nspname <> 'pg_catalog';
   ->  Bitmap Heap Scan on pg_class c  (cost=165.17..491.18 rows=86 width=72) (actual time=179.958..179.967 rows=1 loops=135)
               Buffers: shared hit=2707 read=547283 written=5695
 Planning Time: 13.527 ms
 Execution Time: 24299.326 ms
(17 rows)
Time: 24319.014 ms (00:24.319)

После остановки теста:

  ->  Bitmap Heap Scan on pg_class c  (cost=166.25..747.37 rows=161 width=72) (actual time=24.597..24.599 rows=1 loops=135)
         Buffers: shared hit=556152 read=106322 dirtied=53 written=187
 Planning Time: 3.363 ms
 Execution Time: 3324.657 ms
Time: 3333.285 ms (00:03.333)

Влияние страничного кэша Linux на скорость доступа к раздутым таблицам системного каталога

Через некоторое время выполнение запросов замедлится:

  ->  Bitmap Heap Scan on pg_class c  (cost=165.78..681.47 rows=141 width=72) (actual time=49.566..49.568 rows=1 loops=135)
         Buffers: shared hit=556164 read=106310 written=10
 Planning Time: 7.597 ms
 Execution Time: 6695.839 ms
Time: 6715.433 ms (00:06.715)

Увеличение времени связано с вытеснением страниц файлов индексов и таблиц системного каталога из страничного кэша Linux, содержимое которого невозможно отследить средствами PostgreSQL.

Последствия раздувания таблиц системного каталога

Нежелательность раздувания таблиц системного каталога заключается в том, что физические размеры индексов не уменьшаются даже после снятия удержания горизонта и вакуумирования. Вероятность уменьшения размеров файлов таблиц также низка, поскольку при создании временных таблиц могут создаваться объекты постоянного хранения, строки которых располагаются ближе к концу файлов основного слоя системных таблиц каталога. Однако это сказывается только на занимаемом пространстве и никак не влияет на производительность, поскольку доступ к таблицам системного каталога обычно осуществляется посредством индексом.

До снятия удержания горизонта время выполнения запросов чрезвычайно велико:

postgres=# \timing on \\
Timing is on.
postgres=# explain (analyze, buffers) select 'pg_attribute', pg_total_relation_size('pg_attribute'), count(*) from pg_attribute;
         Buffers: shared hit=6 read=598191
 Planning Time: 0.084 ms
 Execution Time: 39773.733 ms
Time: 39774.720 ms (00:39.775)

Обращения к счётчику времени TSC приводят к накладным расходам и увеличивают время выполнения explain(analyse) примерно в три раза. Без таких обращений реальное время выполнения запроса составляет:

postgres=# explain (analyze, buffers, timing off) select 'pg_attribute', pg_total_relation_size('pg_attribute'), count(*) from pg_attribute;
         Buffers: shared hit=1 read=598196
 Planning Time: 0.065 ms
 Execution Time: 12976.249 ms
Time: 12976.864 ms (00:12.977)

Размеры таблиц после 10 000 секунд работы теста:

     table      |    size    | count
----------------+------------+-------
 pg_class       | 1041825792 |   425
 pg_attribute   | 5905104896 |  3171
 pg_depend      | 1936924672 |  1796
 pg_type        | 1891008512 |   627
(13 rows)
Time: 30579.073 ms (00:30.579)

Пропорция размеров таблиц pg_attribute и pg_class зависит от числа столбцов в создаваемых таблицах. В тесте создавались временные таблицы с одним столбцом фиксированного размера. 

До снятия удержания горизонта автовакуум и автоанализ работают без перерывов, пытаясь обработать таблицы системного каталога. После снятия удержания горизонта им удаётся успешно выполнить эту обработку. За работой автовакуума можно наблюдать через представление:

select * from pg_stat_progress_vacuum \gx
-[ RECORD 1 ]--------+------------------
pid                  | 27131
datid                | 5
datname              | postgres
relid                | 1249
phase                | vacuuming indexes
heap_blks_total      | 516654
heap_blks_scanned    | 516654
heap_blks_vacuumed   | 0
index_vacuum_count   | 0
max_dead_tuple_bytes | 67108864
dead_tuple_bytes     | 12780544
num_dead_item_ids    | 29945377
indexes_total        | 2
indexes_processed    | 0
-[ RECORD 2 ]--------+------------------
pid                  | 27114
datid                | 5
datname              | postgres
relid                | 1259
phase                | vacuuming heap
heap_blks_total      | 99573
heap_blks_scanned    | 99573
heap_blks_vacuumed   | 82095
index_vacuum_count   | 1
max_dead_tuple_bytes | 67108864
dead_tuple_bytes     | 3081216
num_dead_item_ids    | 4280219
indexes_total        | 0
indexes_processed    | 0

При повторных обращениях значение в столбце будет меняться:

heap_blks_vacuumed   | 181104

Сканирование индексов при вакуумировании:

select pid, relid::regclass, phase, heap_blks_vacuumed from pg_stat_progress_vacuum;
  pid  |    relid     |        phase        | heap_blks_vacuumed
-------+--------------+---------------------+--------------------
 27185 | pg_attribute | cleaning up indexes |             510699

После снятия удержания горизонта

В журнале отражается, что индексы просканированы за один проход:

12:16:35.631 MSK [27131] LOG:  automatic vacuum of table "postgres.pg_catalog.pg_statistic": index scans: 1
 system usage: CPU: user: 0.10 s, system: 0.02 s, elapsed: 1.23 s
12:20:38.721 MSK [27114] LOG:  automatic vacuum of table "postgres.pg_catalog.pg_depend": index scans: 1
 tuples: 12840612 removed, 1796 remain, 0 are dead but not yet removable, oldest xmin: 21822422
 index "pg_depend_depender_index": pages: 88958 in total, 88941 newly deleted, 88941 currently deleted, 0 reusable
 index "pg_depend_reference_index": pages: 52304 in total, 52284 newly deleted, 52284 currently deleted, 26614 reusable
 buffer usage: 1249768 hits, 331656 misses, 331637 dirtied
 WAL usage: 707844 records, 330198 full page images, 784190586 bytes
 system usage: CPU: user: 17.68 s, system: 8.18 s, elapsed: 315.05 s
12:22:30.224 MSK [27126] LOG:  automatic vacuum of table "postgres.pg_catalog.pg_type": index scans: 1
 tuples: 8560408 removed, 629 remain, 4 are dead but not yet removable, oldest xmin: 21822422
 index "pg_type_oid_index": pages: 23475 in total, 23467 newly deleted, 23467 currently deleted, 5544 reusable
 index "pg_type_typname_nsp_index": pages: 8188 in total, 8173 newly deleted, 8173 currently deleted, 0 reusable
 buffer usage: 457668 hits, 430004 misses, 429933 dirtied
 WAL usage: 692059 records, 429925 full page images, 319674174 bytes
 system usage: CPU: user: 24.85 s, system: 12.94 s, elapsed: 414.71 s
12:22:50.901 MSK [27114] LOG:  automatic vacuum of table "postgres.pg_catalog.pg_class": index scans: 1
 pages: 0 removed, 99573 remain, 99573 scanned (100.00% of total)
 tuples: 843 removed, 422 remain, 0 are dead but not yet removable, oldest xmin: 21822427
 index scan needed: 99566 pages from table (99.99% of total) had 4280219 dead item identifiers removed
 index "pg_class_oid_index": pages: 11739 in total, 11731 newly deleted, 11731 currently deleted, 0 reusable
 index "pg_class_relname_nsp_index": pages: 4095 in total, 4086 newly deleted, 4086 currently deleted, 0 reusable
 index "pg_class_tblspc_relfilenode_index": pages: 11738 in total, 11731 newly deleted, 11731 currently deleted, 0 reusable
 buffer usage: 319841 hits, 226761 misses, 127214 dirtied
 WAL usage: 281670 records, 127216 full page images, 230305466 bytes
 system usage: CPU: user: 9.48 s, system: 3.44 s, elapsed: 132.17 s
12:27:09.884 MSK [27131] LOG:  automatic vacuum of table "postgres.pg_catalog.pg_attribute": index scans: 1
 pages: 5955 removed, 510699 remain, 516600 scanned (99.99% of total)
 tuples missed: 16066 dead from 277 pages not removed due to cleanup lock contention
 index scan needed: 516323 pages from table (99.94% of total) had 29945377 dead item identifiers removed
 index "pg_attribute_relid_attnam_index": pages: 122212 in total, 121547 newly deleted, 121547 currently deleted, 0 reusable
 index "pg_attribute_relid_attnum_index": pages: 81827 in total, 81328 newly deleted, 81328 currently deleted, 0 reusable
 buffer usage: 2461858 hits, 1040521 misses, 935880 dirtied
 WAL usage: 1856568 records, 935889 full page images, 1763589809 bytes
 system usage: CPU: user: 51.65 s, system: 22.52 s, elapsed: 634.24 s
12:27:44.400 MSK [27185] LOG:  automatic vacuum of table "postgres.pg_catalog.pg_attribute": index scans: 0
 tuples: 6240 removed, 26 remain, 0 are dead but not yet removable, oldest xmin: 21822428
 index scan bypassed: 280 pages from table (0.05% of total) have 16100 dead item identifiers
 index "pg_attribute_relid_attnam_index": pages: 122212 in total, 0 newly deleted, 121547 currently deleted, 121547 reusable
 index "pg_attribute_relid_attnum_index": pages: 81827 in total, 0 newly deleted, 81328 currently deleted, 81328 reusable
 buffer usage: 219551 hits, 189542 misses, 166 dirtied
 WAL usage: 170 records, 163 full page images, 498360 bytes
 system usage: CPU: user: 1.75 s, system: 1.11 s, elapsed: 13.07 s
12:27:46.339 MSK [27185] LOG:  automatic analyze of table "postgres.pg_catalog.pg_attribute"
 avg read rate: 121.051 MB/s, avg write rate: 0.040 MB/s
 buffer usage: 949 hits, 30044 misses, 10 dirtied
 system usage: CPU: user: 0.28 s, system: 0.16 s, elapsed: 1.93 s

После вакуумирования время выполнения запросов к таблицам системного каталога значительно сокращается:

postgres=# explain (analyze, buffers, timing off) select 'pg_attribute', pg_total_relation_size('pg_attribute'), count(*) from pg_attribute;
                 QUERY PLAN
----------------------------------------------------------------
 Aggregate  (cost=327460.66..327460.67 rows=1 width=48) (actual rows=1 loops=1)
   Buffers: shared hit=470
   ->  Index Only Scan using pg_attribute_relid_attnum_index on pg_attribute  (cost=0.40..327455.72 rows=1975 width=0) (actual rows=3153 loops=1)
         Heap Fetches: 0
         Buffers: shared hit=470
 Planning Time: 0.068 ms
 Execution Time: 1.277 ms
(7 rows)
Time: 1.655 ms

Сканирование индексов таблиц системного каталога будет выполняться не в каждом цикле автовакуума. Если периодически возникает раздувание файлов индексов, смысла в их перестроении нет. Блоки в файлах индексов, не включённые в логическую структуру индекса («дерево»), могут добавляться в эту структуру.

Параметры конфигурации enable_temp_memory_catalog и enable_delayed_temp_file в СУБД Tantor Postgres

Параметр конфигурации enable_temp_memory_catalog, реализованный в СУБД Tantor Postgres, исключает изменение любых таблиц системного каталога при работе с временными таблицами. Параметр присутствует начиная с 17 версии в редакциях Special Edition, Special Edition 1C, а после прохождения сертификации ФСТЭК России параметр присутствует в редакции СУБД Tantor Certified 16.8 в исполнении 1. Параметр можно включить на разных уровнях, в том числе на уровне сессии. Включим на уровне кластера, чтобы упростить тестирование:

postgres=# alter system set enable_temp_memory_catalog = on;
ALTER SYSTEM
postgres=# select pg_reload_conf();
 pg_reload_conf
----------------
 t
(1 row)

Будем удерживать горизонт базы данных:

postgres=# begin;
create table xt (n int);
BEGIN
CREATE TABLE

Запустим тест:

postgres@tantor:~$ pgbench -n -T 10000 -c 16 -P 60 -f workl1c_100.sql
pgbench (17.2)
progress: 60.0 s, 41.9 tps, lat 379.749 ms stddev 35.921, 0 failed
progress: 120.0 s, 41.9 tps, lat 381.668 ms stddev 41.316, 0 failed
progress: 180.0 s, 42.1 tps, lat 380.539 ms stddev 37.818, 0 failed
progress: 240.0 s, 41.8 tps, lat 383.191 ms stddev 39.267, 0 failed

Показатели ТPS при включенном параметре со временем не меняются даже при удержании горизонта базы данных. Скорость создания и удаления временных таблиц примерно в два раза выше (без параметра TPS=23) по сравнению с обычным способом создания временных таблиц, затрагивающим таблицы системного каталога. Размеры таблиц системного каталога не изменяются, и изменения в них не вносятся.

При использовании этого параметра метаданные хранятся в локальной памяти серверного процесса, а не в блоках таблиц системного каталога. При этом не требуется настройка параметров распределения памяти, таких как work_mem и maintenance_work_mem. Скорость доступа к метаданным уже созданной временной таблицы выше, поскольку данные находятся в локальной памяти серверного процесса, и при этом не используется блокировка таблиц и индексов системного каталога, что снижает конкуренцию за ресурсы. Параметр изменяет способ хранения метаданных, однако способ хранения данных во временных объектах и способ доступа к самим данным остаются без изменений, за исключением случаев, когда транзакция затрагивает только временные таблицы. Если же в транзакции не участвуют объекты постоянного хранения, фиксация транзакции происходит быстрее.

Параметр enable_delayed_temp_file

Способ работы с файлами временных таблиц изменяется параметром конфигурации enable_delayed_temp_file, который вместе с параметром enable_temp_memory_catalog был добавлен в СУБД Tantor Postgres версии 17.5 :

postgres@tantor:~$ psql -c "alter system set enable_delayed_temp_file = on;"
ALTER SYSTEM
postgres@tantor:~$ psql -c "select pg_reload_conf();"
 pg_reload_conf
----------------
 t
(1 row)

postgres@tantor:~$ pgbench -n -T 10000 -c 16 -P 5 -f workl1c_100.sql
pgbench (17.2)
progress: 5.0 s, 47.2 tps, lat 324.453 ms stddev 41.019, 0 failed
progress: 10.0 s, 49.0 tps, lat 324.568 ms stddev 34.198, 0 failed
progress: 15.0 s, 49.2 tps, lat 324.529 ms stddev 41.999, 0 failed
progress: 20.0 s, 50.0 tps, lat 321.942 ms stddev 35.568, 0 failed
progress: 25.0 s, 48.4 tps, lat 328.826 ms stddev 42.060, 0 failed

При включении параметра enable_delayed_temp_file TPS увеличивается с ~42 до ~49.

Параметр конфигурации enable_temp_memory_catalog

В сборке PostgreSQL 16 от 1С ИТС реализован параметр  enable_temp_memory_catalog, решающий аналогичную задачу — кэширование метаданных временных таблиц. Название параметра для новой опции в СУБД Tantor Postgres 17 было выбрано таким же, как и в сборке 1С ИТС, однако несмотря на одинаковое название, реализации различаются. Параметр enable_delayed_temp_file в сборке от 1С отсутствует.

Размер патча от «Тантор Лабс», реализующего функционал enable_temp_memory_catalog составляет 217Кб (7377 строк). Изменения внесены в 30 функций и в более чем 30 файлов исходного кода. Для быстрого поиска метаданных в локальной памяти процесса используется индексная структура типа Red-Black tree, что позволяет серверным процессам работать с большим числом временных таблиц. Однако благодаря использованию такой индексной структуры при создании большого (десятки и сотни тысяч) числа временных таблиц в одной сессии скорость их создания замедляется нелинейно.

Для поиска метаданных используется структура типа Red-Black tree
Для поиска метаданных используется структура типа Red-Black tree
----------------------
 Core Operations and Hooks:
 inmem_init(): Initializes the in-memory storage engine, creating shared memory data structures and registering callbacks.
 inmem_insert(), inmem_delete(), inmem_update(), inmem_inplace_update(): Hooks that intercept heap operations on temporary tables and redirect them to the in-memory storage engine.

 inmem_index_insert(), inmem_index_getnext_tid_merge(), inmem_index_getbitmap(): Hooks that intercept index operations and handle them using the in-memory indexes.

 inmem_beginscan(), inmem_getnext(), inmem_hot_search_buffer(): Functions that support heap scans, retrieving data from the in-memory storage.

 inmem_index_beginscan(), inmem_index_endscan(), inmem_index_rescan(): Functions that manage index scans on in-memory indexes.

 inmem_subxact_cb(), inmem_xcact_cb(): Callbacks that handle transaction and subtransaction events, managing the visibility of changes.

 find_relation_entry(): get entry for in-memory relation, if it handled.

 GetRelationItemIdxByOidInternal(), GetSnapshotRelationIdxByOid(): get idx in arrays of relation.

 tuples_handler_is_empty(): check, that relation have no tuples.

 IsInmemHandledRelationId(), IsInmemHandledIndexId(): determine whether to apply in-memory logic.

 GetLocalMemoryContext(), GetLocalTransactionMemoryContext(): local contexts for data storing.

 generic_is_inmem_tuple(), pg_class_is_inmem_tuple(): check if tuple is in-memory.

 inmem_clean_catalog_on_relation_delete(): delete all data about deleting relation from indexes.

 delete_pg_depend_entries(), delete_pg_class_entries(), delete_pg_type_entries(): deleting entries from pg_depend, pg_class, pg_type catalogs.

---------------------

Буферный кэш не засоряется блоками таблиц системного каталога:

postgres=# select * from pg_buffercache_usage_counts();
 usage_count | buffers | dirty | pinned
-------------+---------+-------+--------
           0 |   15644 |     0 |      0
           1 |     175 |     6 |      0
           2 |     135 |     0 |      0
           3 |      73 |     0 |      0
           4 |      56 |     1 |      0
           5 |     301 |    25 |      0
(6 rows)

Запрос к таблицам системного каталога выполняется быстро:

postgres=# explain (analyze, buffers, timing off) select 'pg_attribute', pg_total_relation_size('pg_attribute'), count(*) from pg_attribute;


   ->  Seq Scan on pg_attribute  (cost=0.00..89.37 rows=3237 width=0) (actual rows=3237 loops=1)
         Buffers: shared hit=57
 Planning Time: 0.059 ms
 Execution Time: 0.672 ms

Time: 1.446 ms

Результат

При создании и удалении временных таблиц в PostgreSQL в таблицы системного каталога вносятся изменения — несмотря на то, что временные таблицы доступны только процессу, в сессии которого они были созданы. Это может привести к раздуванию таблиц системного каталога и дополнительной нагрузке на экземпляр со стороны процессов автовакуума. 

Параметр конфигурации enable_temp_memory_catalog, реализованный в СУБД Tantor Postgres 17.5, позволяет сохранять метаданные временных объектов в локальной памяти процесса, который работает с ними, предотвращая изменения в таблицах системного каталога. Параметр конфигурации enable_delayed_temp_file ускоряет работу с временными таблицами, позволяя не создавать файлы временных таблиц, пока хватает памяти локального буфера серверного процесса.

Tags:
Hubs:
+7
Comments3

Articles

Information

Website
tantorlabs.ru
Registered
Employees
101–200 employees
Location
Россия