
Начинаем новый цикл статей с обзором изменений 19 версии. И первая статья о событиях летнего июльского коммитфеста прошлого года.
Файл служб соединения в параметре libpq и переменной psql
regdatabase — тип для идентификаторов баз данных
pg_stat_statements: счетчики частных и общих планов
pg_stat_statements: нормализация команды FETCH
pg_stat_statements: нормализация команд со списками параметров в IN
EXPLAIN: оценки для узла Memoize
btree_gin: операторы сравнения целочисленных типов
pg_upgrade: оптимизация переноса больших объектов
Оптимизация усечения временных таблиц
Планировщик: инкрементальная сортировка в узлах Append и MergeAppend
Проверка ограничения домена не блокирует команды DML
Параметры команды CHECKPOINT
COPY FROM: пропуск нескольких начальных строк
pg_dsm_registry_allocations: использование динамической общей памяти (DSM)
Файл служб соединения в параметре libpq и переменной psql
commit: 092f3c63efc6, 6b1c4d326b06
Если для подключения к серверу используется файл служб соединения, то его имя теперь можно указать в строке соединения в новом параметре servicefile.
Например в текущем каталоге есть файл demo.conf:
$ cat ./demo.conf
[demo]
host=localhost
port=5401
user=postgres
dbname=demo
options=-c search_path=bookings
Его можно использовать для подключения:
$ psql 'servicefile=./demo.conf service=demo'
psql (19devel)
Type "help" for help.
postgres@demo(19.0)=#
А в psql появилась переменная SERVICEFILE с именем файла службы:
postgres@demo(19.0)=# \echo :SERVICEFILE
./demo.conf
regdatabase — тип для идентификаторов баз данных
commit: bd09f024a
Прибавление в семействе типов-псевдонимов для идентификаторов объектов. Тип regdatabase поможет конвертировать имя базы данных в идентификатор и обратно.
SELECT current_database()::regdatabase,
current_database()::regdatabase::oid
\gx
-[ RECORD 1 ]----+------
current_database | demo
current_database | 16561
pg_stat_statements: счетчики частных и общих планов
commit: 3357471cf
Два новых столбца в представлении pg_stat_statements показывают сколько раз выбирались общие и частные планы для выполнения запросов:
SELECT pg_stat_statements_reset();
SELECT * FROM bookings WHERE book_ref = $1 \bind 'NWQI2S' \g /dev/null
SELECT * FROM bookings WHERE book_ref = $1 \bind 'WF2DGZ' \g /dev/null
SELECT query, calls, generic_plan_calls, custom_plan_calls
FROM pg_stat_statements
WHERE query ~ 'bookings'
\gx
-[ RECORD 1 ]------+-------------------------------------------
query | SELECT * FROM bookings WHERE book_ref = $1
calls | 2
generic_plan_calls | 0
custom_plan_calls | 2
pg_stat_statements: нормализация команды FETCH
commit: bee23ea4d
При нормализации команды FETCH размер выборки заменяется константой. Это значит, что команды FETCH с разным количеством выбираемых строк, получат общий идентификатор запроса и будут представлены в pg_stat_statements одной строкой:
SELECT pg_stat_statements_reset();
BEGIN;
DECLARE cur CURSOR FOR SELECT * FROM flights;
FETCH 1 cur\g /dev/null
FETCH 2 cur\g /dev/null
FETCH -1 cur\g /dev/null
COMMIT;
SELECT queryid, query, calls
FROM pg_stat_statements
WHERE query ~ '^FETCH'
\gx
-[ RECORD 1 ]----------------
queryid | 4164749676997500190
query | FETCH $1 cur
calls | 3
pg_stat_statements: нормализация команд со списками параметров в IN
commit: c2da1a5d6
Команды со списком констант в IN были нормализованы в 18-й версии. В 19-й версии эта же нормализация выполняется не только для констант, но и для списка параметров:
SELECT pg_stat_statements_reset();
SELECT * FROM flights WHERE flight_id IN (1,2) \g /dev/null
SELECT * FROM flights WHERE flight_id IN (1,2,3) \g /dev/null
SELECT * FROM flights WHERE flight_id IN ($1,$2) \bind 11 12 \g /dev/null
SELECT * FROM flights WHERE flight_id IN ($1,$2,$3) \bind 21 22 23 \g /dev/null
SELECT * FROM flights WHERE flight_id IN ($1,$2,$3,$4) \bind 31 32 33 34 \g /dev/null
SELECT queryid, query, calls
FROM pg_stat_statements
WHERE query ~ 'flights'
-[ RECORD 1 ]-----------------------------------------------------
queryid | -5928905334469394952
query | SELECT * FROM flights WHERE flight_id IN ($1 /*, ... */)
calls | 5
Это особенно полезно для приложений, использующих расширенный протокол запросов.
EXPLAIN: оценки для узла Memoize
commit: 4bc62b86849
Для того чтобы понять, почему планировщик решил использовать узел Memoize, в вывод команды EXPLAIN добавлены оценки планировщика для этого узла (строка Estimates):
EXPLAIN
SELECT * FROM routes r
JOIN airports_data a ON r.departure_airport = a.airport_code
WHERE r.days_of_week = '{1,2,3,4,5,6,7}';
QUERY PLAN
--------------------------------------------------------------------------------------------------------
Nested Loop (cost=0.29..212.71 rows=898 width=293)
-> Seq Scan on routes r (cost=0.00..111.12 rows=898 width=103)
Filter: (days_of_week = '{1,2,3,4,5,6,7}'::integer[])
-> Memoize (cost=0.29..1.08 rows=1 width=190)
Cache Key: r.departure_airport
Cache Mode: logical
Estimates: capacity=73 distinct keys=73 lookups=898 hit percent=91.87%
-> Index Scan using airports_data_pkey on airports_data a (cost=0.28..1.07 rows=1 width=190)
Index Cond: (airport_code = r.departure_airport)
(9 rows)
В этой строке представлены ожидания планировщика для следующих показателей:
размер хеш-таблицы;
количество уникальных элементов в хеш-таблице;
сколько раз будет выполняться поиск в хеш-таблице;
процент попадания.
Выполнив EXPLAIN с параметром analyze можно сверить оценки с фактическими результатами. Вот две интересующие строки плана этого же запроса:
…
Estimates: capacity=73 distinct keys=73 lookups=898 hit percent=91.87%
Hits: 830 Misses: 68 Evictions: 0 Overflows: 0 Memory Usage: 19kB
…
btree_gin: операторы сравнения целочисленных типов
commit: e2b64fcef35, fc896821c44
Небольшое удобство для пользователей расширения btree_gin.
CREATE EXTENSION btree_gin;
CREATE TABLE t (a bigint, b text);
INSERT INTO t SELECT random(1,100), random()::text FROM generate_series(1, 100000);
CREATE INDEX idx ON t USING GIN (a, b gin_trgm_ops);
VACUUM ANALYZE t;
SELECT * FROM t WHERE a = 42 AND b LIKE '*42*';
Для такого запроса планировщик раньше использовал индекс только по условию для столбца b, но не включал в условие поиска по индексу a = 42, т.к. столбец a типа bigint, а 42 по умолчанию преобразуется в int. Требовалось явное указание в запросе a = 42::bigint.
Теперь btree_gin подправили и int2, int4, int8 будут автоматически преобразовываться к нужным для использования индекса типам.
EXPLAIN SELECT * FROM t WHERE a = 42 AND b LIKE '*42*';
QUERY PLAN
--------------------------------------------------------------------
Bitmap Heap Scan on t (cost=229.49..233.51 rows=1 width=27)
Recheck Cond: ((a = 42) AND (b ~~ '*42*'::text))
-> Bitmap Index Scan on idx (cost=0.00..229.49 rows=1 width=0)
Index Cond: ((a = 42) AND (b ~~ '*42*'::text))
(4 rows)
pg_upgrade: оптимизация переноса больших объектов
commit: 161a3e8b682, 3bcfcd815e1
При обновлении с версий начиная с 12-й, pg_dump с параметром --binary-upgrade для больших объектов формирует команды COPY для вставки данных напрямую в таблицы pg_largeobject_metadata и pg_shdepend.
Восстановление из такой копии значительно быстрее, чем выполнение команд ALTER LARGE OBJECT для назначения владельца и GRANT для установки привилегий. Поэтому pg_upgrade будет тратить меньше времени на обновление сервера с большим количеством больших объектов.
А обновление с более свежих версий 16+ (второй коммит) будет еще быстрее. Файлы таблицы pg_largeobject_metadata будут переноситься как файлы обычной пользовательской таблицы.
pg_dump без параметра --binary-upgrade, как и раньше, выгружает метаинформацию о больших объектах через команды SQL.
Оптимизация усечения временных таблиц
commit: 78ebda66bf2
TRUNCATE временной таблицы требовал тройного сканирования локального буферного кеша (temp_buffers) для каждого слоя таблицы: основного, карты видимости и карты свободного пространства. Теперь достаточно одного сканирования. Очень полезная оптимизация для приложений активно использующих временные таблицы.
Для обычных таблиц похожая оптимизация была сделана еще в 13-й версии.
Планировщик: инкрементальная сортировка в узлах Append и MergeAppend
commit: 55a780e9476
Планировщик сможет использовать инкрементальную сортировку в узлах, вложенных в Append или MergeAppend. Если для вложенных узлов есть возможность получить частично отсортированные данные, то планировщик будет считать, что предпочтительнее досортировать строки при помощи инкрементальной сортировки, чем сортировать весь набор строк полностью.
Проверка ограничения домена не блокирует команды DML
commit: 16a0039dc0d
Команда ALTER DOMAIN … VALIDATE CONSTRAINT … теперь использует более легкую блокировку SHARE UPDATE EXCLUSIVE, вместо прежней SHARE. Это позволяет не блокировать параллельное изменение таблиц, использующих домен.
Параметры команды CHECKPOINT
commit: cd8324cc89a, bb938e2c3c7, a4f126516e6, 2f698d7f4b7, 8d33fbacbac
У команды CHECKPOINT появились параметры:
=# \h checkpoint
Command: CHECKPOINT
Description: force a write-ahead log checkpoint
Syntax:
CHECKPOINT [ ( option [, ...] ) ]
where option can be one of:
FLUSH_UNLOGGED [ boolean ]
MODE { FAST | SPREAD }
URL: https://www.postgresql.org/docs/devel/sql-checkpoint.html
С включенным параметром FLUSH_UNLOGGED на диск будут сбрасываться измененные буферы нежурналируемых объектов (таблиц, индексов и др.). По умолчанию параметр выключен.
Параметр поможет сократить время на перезагрузку сервера, где активно используются нежурналируемые таблицы. Перед остановкой сервера можно выполнить вручную CHECKPOINT, чтобы последующая контрольная точка во время останова выполнялась быстрее. Однако грязные буферы нежурналируемых объектов раньше не сбрасывались на диск процессом контрольной точки, это происходило во время останова. Теперь, выполняя вручную контрольную точку с FLUSH_UNLOGGED ON, можно предварительно сделать еще больше задач перед остановом сервера.
А чтобы не сильно нагружать систему записью на диск, предварительную контрольную точку можно выполнить с другим новым параметром MODE SPREAD. Контрольная точка выполнится протяженно, учитывая значение checkpoint_completion_target. Это похоже на параметр --checkpoint={fast|spread} утилиты pg_basebackup.
COPY FROM: пропуск нескольких начальных строк
commit: bc2f348e87c
Параметр header команды COPY помимо значений true/false и match теперь может принимать положительное числовое значение. Это число указывает, сколько начальных строк команда должна пропустить, прежде чем начнет загружать данные в таблицу.
CREATE TABLE test(id int);
=# COPY test FROM stdin (header 3);
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself, or an EOF signal.
>> первая строка заголовка
>> вторая строка
>> третья, дальше данные
>> 1
>> 2
>> \.
COPY 2
SELECT * FROM test;
id
----
1
2
(2 rows)
Заметим, что в этом случае не получится использовать вариант header match для сопоставления столбцов в файле и таблице.
pg_dsm_registry_allocations: использование динамической общей памяти (DSM)
commit: 167ed8082f4
Новое системное представление pg_dsm_registry_allocations показывает информацию о содержимом динамической общей памяти.
=# \d pg_dsm_registry_allocations
View "pg_catalog.pg_dsm_registry_allocations"
Column | Type | Collation | Nullable | Default
--------+--------+-----------+----------+---------
name | text | | |
type | text | | |
size | bigint | | |
На этом пока всё. Впереди новости сентябрьского коммитфеста.