PostgreSQL 18 (2025)
Začátkem dubna byly zmrazen vývoj PostgreSQL 18. Od té doby do vydání (na přelomu září října) se do této verze dostanou pouze opravy, takže už nyní je jasné, co v nové verzi Postgresu bude. V osmnáctce se finalizují práce na podpoře tzv index skip skanu nebo větší schopnosti optimalizátoru zredukovat prováděcí plán o některé zbytečné operace. Nová verze také přináší částečnou podporu asynchronních IO operací.
PostgreSQL 18 (30 let vývoje)
Třicetiny ještě asi letos komunita slavit nebude - u software je někdy problém s identitou, nicméně verze PostgreSQL 1.0 byla uvolněna 5. září 1995. První záznam v historii je ale z roku 1996:
Author: Marc G. Fournier <scrappy@hub.org> Date: Tue Jul 9 06:22:35 1996 +0000 Postgres95 1.01 Distribution - Virgin Sources diff --git a/src/Makefile b/src/Makefile new file mode 100644
Takže je možné, že třicetiny se budou oslavovat až příští rok. V každém případě je to úspěch, kam se tento projekt dostal, kde je dnes, a že vůbec přežil. Databáze PostgreSQL navazovala na starší POSTGRES z roku 1986, která ideově vycházela z databáze INGRES, na které se začalo pracovat v roce 1973.
Práce na verzi 18 pokračovaly víceméně v poklidném a zažitém tempu. Neotevírají se nová větší témata, pokračuje se v rozdělané práci a sem tam se docela nahodile přidá pár drobnosti. Podpora AIO, která před třemi roky vypadala jako vzdálená fantazie, je v osmnáctce skutečností. Zrovna tak i index skip scan a další optimalizace.
Z pohledu komunity asi největší změnou je nástup Microsoftu coby významného zaměstnavatele vývojářů Postgresu (a tudíž i sponzora vývoje Postgresu). Padla i další lokální meta - poprvé velká banka (Komerční banka) anoncovala nasazení PostgreSQL ve svém core systému. V bankovnictví se PostgreSQL používá delší dobu - ale žádná větší banka působící v ČR se k tomu extra nehlásila a stejně tak se nejednalo o core systém (naopak fintech používá Postgres běžně).
Prostředí uložených procedur a extenzí
Při změně obsahu proměnné (v PL/pgSQL) dochází k náhradě původního obsahu novým obsahem. U atomických datových typů není co řešit. U neatomických datových typů (např. pole, kompozity, …) takový přístup není moc efektivní, protože náhrada jednoho prvku pole, jednoho atributu kompozitu vede k nutnosti vytvořit kompletní novou hodnotu. U polí je to spojené často s alokací a překopírováním velkého bloku paměti (vyřešeno v 9.5). Kompozitní typy vetšinou nejsou tak velké, ale zase mají mnohem komplikovanější formát (interně proto PL/pgSQL rozlišoval mezi ROW a RECORD typy, pro které měl různé implementace). Dnes PL/pgSQL podporuje tzv in-place update hodnot pro vybrané třídy typů a některé operace. Tato podpora musela být natvrdo zadrátována do kódu PL/pgSQL. Nově lze podporu in-place update napsat i pro vlastní datové typy (extenze).
Pro uživatele neviditelnou (ale vítanou) změnou bude použití implicitní plan cache i pro funkce napsané v jazyku SQL (tělo funkce obsahuje posloupnost (většinou pouze jeden) SQL příkazů). Jazyk SQL je pro psaní funkcí zajímavý tím, že umožňuje v některých případech inlining (místo volání funkce se nahradí tělem funkce). Pokud dojde k inliningu, tak volání funkce má nulovou režii (a funkce je jakoby rychlejší než napsaná v PL/pgSQL). Pokud ale k inliningu nedošlo, tak se SQL příkazy uvnitř funkce překládaly (do prováděcích plánů) vždy pro každé volání funkce, a tehdy byly SQL funkce výrazně pomalejší než funkce v PL/pgSQL. Od verze 18 už i SQL funkce budou používat implicitní cache plánů a funkcí (stejně jako v PL/pgSQL). Touto změnou se trochu zmenší magie (proč je něco rychlé, a proč je něco pomalé) pro uživatele bez hlubších znalostí Postgresu (kterých není málo).
V posledních dvou letech finišovala podpora buildu pomocí nového
nástroje meson
.
Jelikož mám už odladěné skripty pro moje extenze orafce
,
plpgsql_check
a pager pspg
postavené nad
configure
, tak jsem meson
neřešil a nechtěl
jsem se jím zabývat. Bohužel párkrát do roka musím ve Visual Studiu
připravit dllka orafce
a plpgsql_check
, ruční
konfigurace (bez hlubších znalostí VS) je nepříjemná opičí práce, takže
chca nechca jsem napsal build skripty pro zmíněné extenze pro
meson
. Je to docela šikovný nástroj, a nyní je build
extenzí ve windows čajíček, učící křivka mesonu je ale hodně strmá.
Zvlášť pro vývojáře zvyklé na configure
a
make
. Nicméně je to použitelné, a je to dobře použitelné.
Nový build Postgresu pro AIX poběží čistě přes meson
.
Instalace extenzí doposud vyžadovala zápis do systémového adresáře
Postgresu. V některých prostředích to byl problém (ať už bezpečnostní
nebo kvůli požadavku rebuildu image). S novou konfigurační proměnnou
extension_control_path
by to už být problém neměl, a
extenze se nyní mohou kompletně instalovat mimo systémové adresáře
PostgreSQL (nutností může být nastavení proměnné
dynamic_library_path
). Kromě jiného to může znamenat o něco
více komfortu pro autory extenzí (testy bude možné startovat příkazem
make check
a nikoliv speciálním příkazem
make installcheck
, který vyžaduje běžící instanci Postgresu
spolu s přístupy).
Funkce pro práci s datovými typy
Agregační funkce min
a max
lze nově použít
i pro kompozitní typy.
U regulárních funkcí lze použít pojmenované parametry (což dává smysl
hlavně u funkce regexp_replace
). Pojmenované parametry
dostaly i funkce substring
a substr
.
Celočíselné hodnoty lze nyní přetypovat do typu
bytea
:
postgres=# SELECT '10'::smallint::bytea; ┌────────┐ │ bytea │ ╞════════╡ │ \x000a │ └────────┘ (1 row) postgres=# SELECT '\x000a'::bytea::smallint; ┌──────┐ │ int2 │ ╞══════╡ │ 10 │ └──────┘ (1 row)
Funkci reverse
můžeme použít i pro typ
bytea
:
postgres=# SELECT reverse('\x000a'::bytea); ┌─────────┐ │ reverse │ ╞═════════╡ │ \x0a00 │ └─────────┘ (1 row)
Používání uuid
považuji za zlozvyk, který může mít
nepříjemný dopad na výkon databáze (větší objem zápisů do transakčního
logu). Objektivní problémy s uuid
by měl řešit nový
generátor uuidéček verze sedm. V této verzi generované hodnoty vytváří
monotónní vzestupnou řadu. Navíc lze z vygenerované hodnoty lze získat
čas, kdy byla hodnota vygenerována:
(2025-03-18 08:54:00) postgres=# SELECT uuid_extract_timestamp(uuidv7()); ┌────────────────────────────┐ │ uuid_extract_timestamp │ ╞════════════════════════════╡ │ 2025-03-18 08:54:43.346+01 │ └────────────────────────────┘ (1 row)
Případně lze uuid
vygenerovat zpětně:
(2025-03-18 08:57:06) postgres=# SELECT uuid_extract_timestamp(uuidv7('-1year')); ┌────────────────────────────┐ │ uuid_extract_timestamp │ ╞════════════════════════════╡ │ 2024-03-18 08:57:18.809+01 │ └────────────────────────────┘ (1 row)
Nová funkce array_reverse
prohodí pořadí prvků v
poli:
postgres=# SELECT array_reverse(ARRAY[1,2,3,4]); ┌───────────────┐ │ array_reverse │ ╞═══════════════╡ │ {4,3,2,1} │ └───────────────┘ (1 row) postgres=# SELECT array_reverse(ARRAY[[1,8],[3,4]]); ┌───────────────┐ │ array_reverse │ ╞═══════════════╡ │ {{3,4},{1,8}} │ └───────────────┘ (1 row)
Nová funkce array_sort
řadí pole (pole mohou být více
dimenzionální podobně jako u array_reverse
):
postgres=# SELECT array_sort(ARRAY[2,1,4,3]); ┌────────────┐ │ array_sort │ ╞════════════╡ │ {1,2,3,4} │ └────────────┘ (1 row) postgres=# SELECT array_sort(ARRAY[1,2,3,4], descending=>true); ┌────────────┐ │ array_sort │ ╞════════════╡ │ {4,3,2,1} │ └────────────┘ (1 row)
K dispozici jsou dvě nové funkce pro výpočet CRC -
crc32
a crc32c
(argumentem je hodnota typu
bytea
).
Spíš pro úplnost (konzistenci s to_char
) a kompatibilitu
s Oraclem funkce to_number
nově podporuje konverzi z
římských číslic:
postgres=# SELECT to_number('MMXX ', 'RN'); ┌───────────┐ │ to_number │ ╞═══════════╡ │ 2020 │ └───────────┘ (1 row)
Funkce json_strip_nulls
má nový volitelně druhý parametr
s výchozí hodnotou false
. S hodnotou true
se
odstraní null hodnoty i z JSON polí:
postgres=# SELECT json_strip_nulls('[10,20,null,10]', true); ┌──────────────────┐ │ json_strip_nulls │ ╞══════════════════╡ │ [10,20,10] │ └──────────────────┘ (1 row)
Collations
Postgres sám (bez použití externí knihovny) nyní dokáže interně
pracovat se speciálními víceznakovými symboly a dokáže správně provést
jejich konverzi mezi malými a velkými znaky (např. “ß” je zkonvertováno
do “SS”, “dž” na “DŽ” nebo “Dž” (podle kontextu) atd). To je důležité
pro možnost implementace interního collation
pg_unicode_fast
. Výhodou tohoto collation je rychlé řazení,
nezávislost na externích knihovnách a plně korektní převody mezi malými
a velkými písmeny (funkce lower
, upper
a
initcap
) (oproti dřívě přidanému pg_c_utf8
,
které pro výše uvedené funkce používá zjednodušené mapování simple
case mapping). Pozor, stejné řazení je u těchto collations
garantované pouze v rámci major verze, a tudíž je možné, že po upgrade
bude nutný rebuild indexů (z důvodu aktualizace Unicode).
Nově se zrušila některá omezení pro nedeterministická collations (s dopadem na výkon protože není možné použít určité optimalizace).
postgres=# CREATE COLLATION ignore_accent_case (provider = icu, deterministic = false, locale = 'und-u-ks-level1'); CREATE COLLATION postgres=# CREATE TABLE foo(a varchar); CREATE TABLE postgres=# INSERT INTO foo VALUES('Stěhule'); INSERT 0 1 postgres=# SELECT * FROM foo WHERE a = 'stehule' collate "ignore_accent_case"; ┌─────────┐ │ a │ ╞═════════╡ │ Stěhule │ └─────────┘ (1 row) postgres=# SELECT * FROM foo WHERE a like 'ste%' collate "ignore_accent_case" ┌─────────┐ │ a │ ╞═════════╡ │ Stěhule │ └─────────┘ (1 row)
Výchozí collation databáze stále musí být deterministické.
Pro case insensitive porovnání se dost často porovnávané řetězce
převedou na velká nebo malá písmena a pak teprve porovnají. Ve
speciálních případech tento postup nebude fungovat. Nová funkce
casefold
je navržena speciálně pro transformaci řetězců
tak, aby následné case insensitive porovnání bylo korektní (a až na
zmíněné speciální případy se tato funkce chová podobně jako funkce
lower
):
postgres=# SELECT casefold('Ahojẞ' COLLATE "tr-x-icu"), lower('Ahojẞ' COLLATE "tr-x-icu"); ┌──────────┬───────┐ │ casefold │ lower │ ╞══════════╪═══════╡ │ ahojss │ ahojß │ └──────────┴───────┘ (1 row)
SQL
DML
Nově můžeme v klauzuli RETURNING
v příkazech
INSERT
, UPDATE
, DELETE
a
MERGE
použít aliasy NEW
a
OLD
:
postgres=# UPDATE foo SET a = 100, b = 1000 RETURNING NEW.*, OLD.a AS old_a, OLD.b AS old_b; ┌─────┬──────┬───────┬───────┐ │ a │ b │ old_a │ old_b │ ╞═════╪══════╪═══════╪═══════╡ │ 100 │ 1000 │ 100 │ 1000 │ └─────┴──────┴───────┴───────┘ (1 row)
DDL
V nové verzi máme možnost používat generované virtuální sloupce (už dříve bylo možné používat materializované virtuální sloupce - PostgreSQL 12):
postgres=# CREATE TABLE foo(a int, b int, c int GENERATED ALWAYS AS (a+b) VIRTUAL); CREATE TABLE postgres=# INSERT INTO foo VALUES(10,20); INSERT 0 1 postgres=# SELECT * FROM foo; ┌────┬────┬────┐ │ a │ b │ c │ ╞════╪════╪════╡ │ 10 │ 20 │ 30 │ └────┴────┴────┘ (1 row)
Generované virtuální sloupce mají zatím řadu omezení - například nemohou být oindexované nebo nemohou být doménového typu, ale většina omezení by měla být postupně odstraněna - nemožnost indexace je dost nepříjemná. Generované sloupce si patrně najdou svoje uživatele, možná i z důvodu přenositelnosti. Jestli si správně pamatuji, tak v MySQL se pomocí generovaných sloupců implementují funkční indexy.
Pomalu se blíží podpora temporálních databází podle standardu. V temporálních databázích mohou být v databázi uložená data limitovaná časovým úsekem. Ve verzi 18 můžeme definovat temporální primární klíč (je unikátní v daném časovém rozsahu (v Postgresu v libovolném rozsahu (range)) a temporální cizí klíč v ANSI/SQL syntaxi:
CREATE TABLE temporal_fk_rng2rng ( id integer, valid_at daterange, parent_id integer, CONSTRAINT temporal_fk_rng2rng_pk PRIMARY KEY (id, valid_at WITHOUT OVERLAPS), CONSTRAINT temporal_fk_rng2rng_fk FOREIGN KEY (parent_id, PERIOD valid_at) REFERENCES temporal_rng (id, PERIOD valid_at) );
Na plnou implementaci této části standardu SQL:2011 si ještě rok - dva budeme muset počkat. Kdo by si chtěl hrát s touto funkcionalitou, může vyzkoušet implementaci temporálních tabulek v MariaDB 10.4.3 a vyšší.
Administrace
Novinkou, které si určitě hned všimnete, je zobrazení
BUFFERS
ve výchozím nastavení příkazu EXPLAIN
.
S touto volbou se zobrazí počty načtených datových stránek (z disku, z
cache):
postgres=# EXPLAIN (COSTS OFF, ANALYZE) SELECT * FROM pg_class ; ┌─────────────────────────────────────────────────────────────────────┐ │ QUERY PLAN │ ╞═════════════════════════════════════════════════════════════════════╡ │ Seq Scan on pg_class (actual time=0.022..0.112 rows=433.00 loops=1) │ │ Buffers: shared hit=14 │ │ Planning Time: 0.118 ms │ │ Execution Time: 0.186 ms │ └─────────────────────────────────────────────────────────────────────┘ (4 rows)
EXPLAIN
nyní umí zobrazit zápisy do WAL (transakčního
logu):
postgres=# CREATE TABLE foo(a int); CREATE TABLE postgres=# EXPLAIN (ANALYZE, WAL, COSTS OFF) INSERT INTO foo VALUES(1); ┌────────────────────────────────────────────────────────────┐ │ QUERY PLAN │ ╞════════════════════════════════════════════════════════════╡ │ Insert on foo (actual time=0.027..0.027 rows=0.00 loops=1) │ │ Buffers: shared hit=1 │ │ WAL: records=1 bytes=59 │ │ -> Result (actual time=0.002..0.002 rows=1.00 loops=1) │ │ Planning Time: 0.024 ms │ │ Execution Time: 0.045 ms │ └────────────────────────────────────────────────────────────┘ (6 rows)
Zápisy do WAL zobrazí i příkaz VACUUM VERBOSE
. Sledovat
lze i časovou náročnost zápisů do transakčního logu nastavením
track_wal_io_timing
(zobrazí se v
pg_stat_io
).
Všimněte si, že se počty řádků nyní zobrazují jako desetinné číslo. Může to být trochu neintuitivní. Důvodem pro změnu zobrazení je nested loop. V případě, že část dotazu byla prováděna opakovaně, tak se počty vrácených řádků (i časy) dělily počtem iterací. Tím, že se počty řádků zobrazovaly jako celé číslo, tak samozřejmě docházelo k velkému zkreslení (a zobrazované hodnoty někdy nedávaly smysl).
Nově se v explainu zobrazuje i počet průchodů indexem (od minulé verze Postgres umí na jednou iterací indexu dohledat více hodnot). Čím méně je iterací, tím je samozřejmě vyhledávání rychlejší:
postgres=# EXPLAIN (ANALYZE, COSTS OFF) SELECT * FROM pg_class WHERE oid = ANY(ARRAY[2619, 1247]); ┌────────────────────────────────────────────────────────────────────────────────────────────┐ │ QUERY PLAN │ ╞════════════════════════════════════════════════════════════════════════════════════════════╡ │ Bitmap Heap Scan on pg_class (actual time=0.027..0.028 rows=2.00 loops=1) │ │ Recheck Cond: (oid = ANY ('{2619,1247}'::oid[])) │ │ Heap Blocks: exact=1 │ │ Buffers: shared hit=3 │ │ -> Bitmap Index Scan on pg_class_oid_index (actual time=0.013..0.013 rows=2.00 loops=1) │ │ Index Cond: (oid = ANY ('{2619,1247}'::oid[])) │ │ Index Searches: 1 │ │ Buffers: shared hit=2 │ │ Planning: │ │ Buffers: shared hit=30 read=2 │ │ Planning Time: 0.264 ms │ │ Execution Time: 0.051 ms │ └────────────────────────────────────────────────────────────────────────────────────────────┘ (12 rows) postgres=# EXPLAIN (ANALYZE, COSTS OFF) SELECT * FROM pg_class WHERE oid = 2619 OR oid = 1247; ┌────────────────────────────────────────────────────────────────────────────────────────────┐ │ QUERY PLAN │ ╞════════════════════════════════════════════════════════════════════════════════════════════╡ │ Bitmap Heap Scan on pg_class (actual time=0.047..0.048 rows=2.00 loops=1) │ │ Recheck Cond: ((oid = '2619'::oid) OR (oid = '1247'::oid)) │ │ Heap Blocks: exact=1 │ │ Buffers: shared hit=3 │ │ -> Bitmap Index Scan on pg_class_oid_index (actual time=0.035..0.035 rows=2.00 loops=1) │ │ Index Cond: (oid = ANY ('{2619,1247}'::oid[])) │ │ Index Searches: 1 │ │ Buffers: shared hit=2 │ │ Planning Time: 0.138 ms │ │ Execution Time: 0.078 ms │ └────────────────────────────────────────────────────────────────────────────────────────────┘ (10 rows)
Po upgrade databáze pomocí pg_upgrade
bylo nutné
aktualizovat sloupcové statistiky. Ty se z upgradované instance
nepřenesly. I s použitím volby --analyze-in-stages
aktualizace statistik prodlužovala dobu odstávky. Nově
pg_dump
exportuje i sloupcové statistiky:
-- Name: x; Type: TABLE; Schema: public; Owner: pavel -- CREATE TABLE public.x ( a integer ); ALTER TABLE public.x OWNER TO pavel; -- -- Data for Name: x; Type: TABLE DATA; Schema: public; Owner: pavel -- COPY public.x (a) FROM stdin; 10 \. -- -- Statistics for Name: x; Type: STATISTICS DATA; Schema: public; Owner: - -- SELECT * FROM pg_catalog.pg_restore_relation_stats( 'version', '180000'::integer, 'relation', 'public.x'::regclass, 'relpages', '1'::integer, 'reltuples', '1'::real, 'relallvisible', '0'::integer ); SELECT * FROM pg_catalog.pg_restore_attribute_stats( 'version', '180000'::integer, 'relation', 'public.x'::regclass, 'attname', 'a'::name, 'inherited', 'f'::boolean, 'null_frac', '0'::real, 'avg_width', '4'::integer, 'n_distinct', '-1'::real ); -- -- PostgreSQL database dump complete --
pg_upgrade
interně používá pg_dump
a tudíž
se přenesou i statistiky. vacuumdb
má novou volbu
--missing-stats-only
, která zajistí, že se budou
přepočítávat pouze chybějící statistiky (lze ji kombinovat s
--analyze-only
a --analyze-in-stages
.
Chybějící aktualizace statistik byl asi nejčastější problém upgradů
Postgresu, a z většího by měl být odstraněn).
pg_upgrade
nyní podporuje novou metodu - swap
(aktivováno volboy --swap
). Při této metodě se přesunou
celé adresáře databází do nového clusteru, a následně se upraví. Mělo by
se jednat o nejrychlejší způsob aktualizace, zejména pokud cluster
(instance) obsahuje velké množství tabulek. Nevýhodou je destruktivní
přístup k původní instanci. Po zahájení upgrade je originální instance
zničená, a v případě chyby během upgrade je nutné se vrátit k
záloze.
S novým přepínačem --no-policies
nebude
pg_dump
exportovat politiky row level security
(RLS).
autovacuum_max_workers
lze změnit bez nutnosti restartu.
Z minulých verzí známe parametr
autovacuum_vacuum_threshold
, který udává minimální počet
změněných řádků, aby se automaticky provedl příkaz VACUUM
nad tabulkou (u malých tabulek blokuje zbytečně časté vakuování). Nový
parametr autovacuum_vacuum_max_threshold
specifikuje
maximální počet změněných řádků, který vynutí vakuování tabulky. Tento
parametr dokáže vynutit častější vakuování u velkých tabulek, kde
výchozích 20% (autovacuum_vacuum_scale_factor
) je příliš.
Výchozí nastavení je sto miliónů řádků.
Pár menších i větších vylepšení doznala extenze
pg_stat_statements
. V sloupci
parallel_workers_to_launch
je vidět počet naplánovaných
workerů paralelní exekuce. V sloupci
parallel_workers_launched
pak skutečný počet workerů
použitý pro exekuci dotazu. Nově lze v pg_stat_statements
vidět i příkaz SET
, který je také normalizován (hodnota
parametru je nahrazena symbolem $n
). Po nastavení
query_id_squash_values
na true
bude
normalizace dotazu normalizovat i seznamy konstant:
-- stávající a výchozí zobrazení query | calls --------------------------------------------------------------------------------------+------- SELECT * FROM test_squash WHERE id IN ($1, $2, $3, $4, $5, $6, $7, $8, $9) | 1 SELECT * FROM test_squash WHERE id IN ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) | 1 SELECT * FROM test_squash WHERE id IN ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) | 1 -- po zapnutí query_id_squash_values query | calls ------------------------------------------------------------------------+------- SELECT * FROM test_squash WHERE id IN ($1 /*, ... */) | 3
V minulých verzích se tabulka identifikovala svým unikátním identifikačním číslem. Nově se tabulka bude identifikovat pouze nekvalifikovaným jménem (pro normalizaci - výpočet queryid). Na jednu stranu se tím vyřeší problémy s dočasnými tabulkami (které se nyní správně začnou grupovat). Naopak může se ztratit rozdíl mezi stejně pojmenovanými tabulkami v různých schématech (workaroundem může být použití různých aliasů).
S novou konfigurační volbou log_lock_failure
lze vynutit
zalogování selhání získání zámku. Aktuálně je podporován pouze příkaz
SELECT ... NOWAIT
.
Nejčastěji používaným indexem v Postgresu bude BTREE. Ostatní typy indexů jako GiST, BRIN nebo GIN se používají určitě mnohem méně, ale používají se. GIN index se používá pro fulltext, vyhledávání prvků v poli nebo pro vyhledávání v JSONB (používá se tam, kde se ptáme, jestli něco obsahuje nějakou hodnotu - článek slovo, polygon bod, dokument v JSONu nějaký klíč). Nepříjemnou vlastností GIN indexu je jeho náročnost při UPDATE. Vytvoření GIN indexu je také pomalejší než vytvoření BTREE indexu a navíc až doposud neumožňoval použít více procesů (více CPU) pro vytvoření indexu. V nové verzi Postgres může použít víc workerů pro vytvoření indexu (navíc některé interní operace jsou optimalizovány, takže vytvoření GIN indexu by mělo být rychlejší i bez paralelizace).
GIN index je nově podporován i v extenzi amcheck
(kontrola konzistence).
Příkaz VACUUM
(případně příkaz ANALYZE
),
aktivovaný automatem, běží přiškrceně. S aktivním sledováním volbou
track_cost_delay_timing
se v progres pohledech
pg_stat_progress_vacuum
a
pg_stat_progress_analyze
zobrazí součet časů
delay_time
(kdy příkaz čekal z důvodu přiškrcení). Tato
hodnota se může i zalogovat.
Příkaz pg_combinebackup
, který provádí obnovu z
inkrementálních záloh, má nový argument -k
nebo
--link
. S tímto argumentem se místo zkopírování souboru
vytvoří hardlink.
Příkaz pg_createsubscriber
(přidaný v minulé verzi) má
nový přepínač --all
. S touto volbou se na zdrojové instanci
vytvoří publikace pro všechny databáze, a na cílové databázi se zajistí
odběr (subskripce) těchto publikací (opět na všech databázích).
Kontroly (check) a omezení referenční integrity lze nyní vypnout
příkazem ALTER TABLE ALTER CONSTRAINT ... NOT ENFORCED
:
postgres=# CREATE TABLE foo_a(a int primary key); CREATE TABLE postgres=# CREATE TABLE foo_b(foo_a int REFERENCES foo_a(a) NOT ENFORCED); CREATE TABLE postgres=# INSERT INTO foo_b VALUES(10); INSERT 0 1 postgres=# INSERT INTO foo_b VALUES(20); INSERT 0 1 postgres=# \d foo_b Table "public.foo_b" ┌────────┬─────────┬───────────┬──────────┬─────────┐ │ Column │ Type │ Collation │ Nullable │ Default │ ╞════════╪═════════╪═══════════╪══════════╪═════════╡ │ foo_a │ integer │ │ │ │ └────────┴─────────┴───────────┴──────────┴─────────┘ Foreign-key constraints: "foo_b_foo_a_fkey" FOREIGN KEY (foo_a) REFERENCES foo_a(a) NOT ENFORCED postgres=# ALTER TABLE foo_b VALIDATE CONSTRAINT foo_b_foo_a_fkey; ERROR: cannot validate NOT ENFORCED constraint postgres=# ALTER TABLE foo_b ALTER CONSTRAINT foo_b_foo_a_fkey ENFORCED; ERROR: insert or update on table "foo_b" violates foreign key constraint "foo_b_foo_a_fkey" DETAIL: Key (foo_a)=(10) is not present in table "foo_a". postgres=# INSERT INTO foo_a VALUES(10),(20); INSERT 0 2 postgres=# ALTER TABLE foo_b ALTER CONSTRAINT foo_b_foo_a_fkey ENFORCED; ALTER TABLE
Nově lze vytvořit nevalidní omezení (constraint)
NOT NULL
, které lze dodatečně validovat. Důvodem, proč
vytvoření omezení rozložit do dvou kroků, jsou zámky.
ALTER TABLE ADD CONSTRAINT
vyžaduje exkluzivní zámek a
pokud má být omezení validní, je nutné přečíst celou tabulku (ve
výsledku to znamená u velkých tabulek nutnost odstávky, protože velice
dlouho DDL operace drží silný zámek). Pokud je omezení nevalidní, pak
nutnost full scanu padá. ALTER TABLE VALIDATE
exkluzivní
zámek nevyžaduje. Na implementaci se, opět, začalo dělat před několika
roky, a předpokladem byl refaktoring uložení metadat omezní typu
NOT NULL
(některé části implementace byly opakovaně
committované a revertované (jestli mne paměť neklame)).
Nově lze přidat nevalidní cizí klíč (s flagem NOT VALID
)
i na jednotlivé partišny (a později jej zvalidovat).
Alokace sdílené paměti je vidět v pohledu
pg_shmem_allocations
.
Pomocí funkce pg_stat_get_backend_io(pid)
lze získat
aktuální IO statistiky jiné než aktivní session. Druhou podobnou funkcí
je funkce pg_stat_get_backend_wal(pid)
, která vrací
statistiky zápisu do transakčního logu. S novými statistikami by mělo
být možné si vytvořit lepší představu o aktuální zátěži s většími
možnostmi filtrování, grupování per databáze, uživatel, (libovolná
hodnota z pg_stat_activity
). Konečně funkcí
pg_get_process_memory_contexts(pid)
lze získat popis
paměťových kontextů libovolného procesu Postgresu.
Nově také lze nastavit (skrze
idle_replication_slot_timeout
) invalidaci neaktivního
(nepoužívaného) replikačního slotu. Pozor, slot se invaliduje pouze v
rámci checkpointu, a tudíž invalidace může proběhnout o něco později,
než určuje zadaná hodnota (čeká se na checkpoint).
Export celé instance příkazem pg_dumpall
byl možný pouze
v textovém formátu. Od verze 18 bude pg_dumpall
podporovat
všechny formáty, které podporuje pg_dump
(v případě jiného
než textového formátu pro obnovu (import) použije
pg_restore
).
Defaultní práva lze nastavit i pro large objects:
GRANT { { SELECT | UPDATE } [, ...] | ALL [ PRIVILEGES ] } ON LARGE OBJECTS TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ]
Nový pohled pg_shmem_alloctions_numa
zobrazuje alokaci
sdílené paměti napříč NUMA uzly. Pro funkčnost tohoto pohledu je nutné
přeložit Postgres s podporou NUMA (--with-libnuma
(zatím
pouze pro Linux)).
psql
Možná většina backslashových příkazů (přidaná v posledních několika verzích) je navržena primárně pro psaní testů (postgresu, protokolu, různých proxy), případně nabízí možnost si jednoduše otestovat (nebo simulovat) specifické vlastnosti komunikačního protokolu (to se dá využít i pro benchmarking).
Komunikační protokol definuje komunikaci mezi klientem a serverem. Základní část protokolu v zásadě řeší připojení, vykonání příkazu a získání výsledku dotazu. Rozšířená část protokolu umožňuje pracovat s před připravenými pojmenovanými parametrizovanými (binding) dotazy. V předchozích verzích bylo možné spustit parametrizované dotazy:
postgres=# CREATE TABLE foo(a int, b int); CREATE TABLE INSERT INTO foo VALUES($1,$2) \bind 10 20 \g INSERT 0 1
Nově lze používat pojmenované (prepared):
postgres=# INSERT INTO foo VALUES($1,$2) \parse stmt postgres=# \bind_named stmt 10 20 \g INSERT 0 1
Binding funguje hezky i s psql
proměnnými.
Nově také lze simulovat režim pipeline pomocí příkazů
\startpipeline
, \endpipeline
,
\sendpipeline
, \flush
,
\flushrequest
, \getresult
(pipeline
režim redukuje komunikaci mezi klientem a serverem a tím zrychluje
provádění dávek příkazů v těch případech, kdy hrdlem je síť). Dovedu si
představit použití při demonstraci pipeline režimu. Je to
jednodušší, názornější a méně pracné než psát byť jednoduchého klienta
(musí se použít dedikované API, které prakticky nikdo nezná).
Příkaz \conninfo
zobrazí tabulku místo textu. Původně se
uvažovalo o pluskovém rozšíření, nakonec se ale rozhodlo o přepisu
zobrazení do tabulky v rozšířeném (extended) formátu. To by mělo umožnit
snadno přidávat další položky v budoucnu:
(2025-03-16 17:13:36) postgres=# \conninfo Connection Information ┌──────────────────────┬──────────┐ │ Parameter │ Value │ ╞══════════════════════╪══════════╡ │ Database │ postgres │ │ Client User │ pavel │ │ Socket Directory │ /tmp │ │ Server Port │ 5432 │ │ Options │ │ │ Protocol Version │ 3 │ │ Password Used │ false │ │ GSSAPI Authenticated │ false │ │ Backend PID │ 26657 │ │ TLS Connection │ false │ │ Superuser │ on │ │ Hot Standby │ off │ └──────────────────────┴──────────┘ (12 rows)
U pluskového zobrazení funkcí a operátorů se nově zobrazí hodnota
leakproof?
. Tento atribut funkce (operátoru) specifikuje,
jestli může nebo nebo nemůže dojít k nechtěnému zobrazení argumentů
funkce (např. v chybové hlášce). U pohledů vytvořených s volbou
security_barrier
Postgres garantuje, že nejdříve se
vyhodnotí filtry pohledu, a teprve poté funkce, které nemají flag
leakproof
. Nemůže tak dojít k nechtěnému (nebo i chtěnému)
úniku informace. Může to ale znamenat více úrovňový plán, jehož exekuce
bude pomalejší.
Příkazem \x
přepínáme mezi základním a rozšířeným
zobrazením (extended mode). Nově většina deskriptivních příkazů
dostala možnost použít x
a vynutit si zobrazení v
rozšířeném režimu (\dtx
, \dix
, …):
postgres=# \lx List of databases ┌─[ RECORD 1 ]──────┬───────────────────────┐ │ Name │ postgres │ │ Owner │ postgres │ │ Encoding │ UTF8 │ │ Locale Provider │ libc │ │ Collate │ cs_CZ.UTF-8 │ │ Ctype │ cs_CZ.UTF-8 │ │ Locale │ │ │ ICU Rules │ │ │ Access privileges │ │ ╞═[ RECORD 2 ]══════╪═══════════════════════╡ │ Name │ template0 │ │ Owner │ postgres │ │ Encoding │ UTF8 │ │ Locale Provider │ libc │ │ Collate │ cs_CZ.UTF-8 │ │ Ctype │ cs_CZ.UTF-8 │ │ Locale │ │ │ ICU Rules │ │ │ Access privileges │ =c/postgres ↵│ │ │ postgres=CTc/postgres │ ╞═[ RECORD 3 ]══════╪═══════════════════════╡ │ Name │ template1 │ │ Owner │ postgres │ │ Encoding │ UTF8 │ │ Locale Provider │ libc │ │ Collate │ cs_CZ.UTF-8 │ │ Ctype │ cs_CZ.UTF-8 │ │ Locale │ │ │ ICU Rules │ │ │ Access privileges │ =c/postgres ↵│ │ │ postgres=CTc/postgres │ └───────────────────┴───────────────────────┘
Optimalizace dotazů
Optimalizátor nyní může použít hash join pro right semijoin. Pro uživatele to znamená, že v určitých případech má optimalizátor možnost, kde dřív neměl, zvolit pro hashování menší relaci.
Eliminace self joinu (self join elimination (SJE)) je optimalizace, která se snaží odstranit zbytečná vnitřní spojení na tutéž tabulku. Návrh na tuto optimalizaci byl poslán v květnu roku 2018. Patch byl opakovaně commitovaný a revertovaný, nicméně poslední inkarnace patche už vypadá, že bude finální. Původně měli vývojáři Postgresu k optimalizacím tohoto typu dost negativní postoj. Převládal názor, že programátoři nebo ORM mají produkovat SQL příkazy bez zbytečných spojení nebo poddotazů. Jak se ale vývoj Postgresu komercializoval a do Postgresu se víc a víc začaly portovat aplikace z Oracle, tak došlo ke změně názoru. Navíc na moderním hardware je cena za tuto optimalizaci akceptovatelná:
postgres=# EXPLAIN SELECT * FROM t1 m JOIN t1 n ON m.x = n.x JOIN t2 o ON o.x = m.x; ┌──────────────────────────────────────────────────────────────────────────┐ │ QUERY PLAN │ ╞══════════════════════════════════════════════════════════════════════════╡ │ Hash Join (cost=26925.00..43975.01 rows=1000000 width=12) │ │ Hash Cond: (n.x = o.x) │ │ -> Seq Scan on t1 n (cost=0.00..14425.00 rows=1000000 width=4) │ │ -> Hash (cost=14425.00..14425.00 rows=1000000 width=4) │ │ -> Seq Scan on t2 o (cost=0.00..14425.00 rows=1000000 width=4) │ └──────────────────────────────────────────────────────────────────────────┘ (5 rows) postgres=# SET enable_self_join_elimination TO off; SET postgres=# EXPLAIN SELECT * FROM t1 m JOIN t1 n ON m.x = n.x JOIN t2 o ON o.x = m.x; ┌────────────────────────────────────────────────────────────────────────────────┐ │ QUERY PLAN │ ╞════════════════════════════════════════════════════════════════════════════════╡ │ Hash Join (cost=53850.00..73525.02 rows=1000000 width=12) │ │ Hash Cond: (m.x = o.x) │ │ -> Hash Join (cost=26925.00..43975.01 rows=1000000 width=8) │ │ Hash Cond: (m.x = n.x) │ │ -> Seq Scan on t1 m (cost=0.00..14425.00 rows=1000000 width=4) │ │ -> Hash (cost=14425.00..14425.00 rows=1000000 width=4) │ │ -> Seq Scan on t1 n (cost=0.00..14425.00 rows=1000000 width=4) │ │ -> Hash (cost=14425.00..14425.00 rows=1000000 width=4) │ │ -> Seq Scan on t2 o (cost=0.00..14425.00 rows=1000000 width=4) │ └────────────────────────────────────────────────────────────────────────────────┘ (9 rows)
Je to docela chytré. ORM knihovny občas produkují šílenosti (fakt jsem nikdy ORM nepřišel na chuť), k šíleným plánům se ale lze dostat i kombinací pohledů (takže tato optimalizace mi dává smysl):
postgres=# EXPLAIN SELECT * FROM t1 n JOIN t1 m ON n.id = m.id WHERE n.a = 100 AND m.b = 1000; ┌────────────────────────────────────────────────────────────────────────┐ │ QUERY PLAN │ ╞════════════════════════════════════════════════════════════════════════╡ │ Index Scan using t1_a_b_idx on t1 m (cost=0.42..8.45 rows=1 width=24) │ │ Index Cond: ((a = 100) AND (b = 1000)) │ └────────────────────────────────────────────────────────────────────────┘ (2 rows) postgres=# SET enable_self_join_elimination TO off; SET postgres=# EXPLAIN SELECT * FROM t1 n JOIN t1 m ON n.id = m.id WHERE n.a = 100 AND m.b = 1000; ┌──────────────────────────────────────────────────────────────────────────────────┐ │ QUERY PLAN │ ╞══════════════════════════════════════════════════════════════════════════════════╡ │ Nested Loop (cost=20.53..9981.25 rows=1 width=24) │ │ -> Bitmap Heap Scan on t1 n (cost=20.11..2548.11 rows=991 width=12) │ │ Recheck Cond: (a = 100) │ │ -> Bitmap Index Scan on t1_a_b_idx (cost=0.00..19.86 rows=991 width=0) │ │ Index Cond: (a = 100) │ │ -> Index Scan using t1_pkey on t1 m (cost=0.42..7.50 rows=1 width=12) │ │ Index Cond: (id = n.id) │ │ Filter: (b = 1000) │ └──────────────────────────────────────────────────────────────────────────────────┘ (8 rows)
Optimalizace také nyní dokáže transformovat filtr ve tvaru
sl = C1 OR sl = C2 ...
do tvaru
sl = ANY(ARRAY[C1, C2, ...])
. V tomto tvaru dokáže Postgres
v rámci jednoho průchodu indexu dohledat víc hodnot, a redukovat použití
operace BitmapOR. Opět se jedná o interně komplikovanou
funkcionalitu, kdy prototyp byl demonstrován v prosinci 2015. Na
historii tohoto patche je vidět, jak je komplikované a pracné pracovat
na optimalizaci dotazů. Aby bylo možné patch akceptovat - musel se
rozbít na menší kusy (mnohem mnohem menší kousky), které se postupně
commitují dva roky ve dvou “samostatných” větvích - planneru a načítání
dat z indexu. Napsat patch je legrace, dostat patch do upstreamu je
neskutečně práce. Samozřejmě, že nikdo nechce rozbít Postgres. To by
dnes už znamenalo velké problémy. Tudíž akceptace patchů, které mají
potenciálně větší dopad, je extrémně pečlivá, zoufale pomalá, určitě
často úmorná práce. Je to ale vidět pak na kvalitě (a spokojenosti
uživatelů).
Ostatní
Indexscan nyní podporuje tzv [https://wiki.postgresql.org/wiki/Loose_indexscan index skip scan]. V tomto případě Postgres dotahuje ostatní databáze, které index skip scan umí dekády (Když jsem pracoval v GoodData, tak na to že Postgres neumí index skip scan, jsme párkrát bolestivě narazili). Mohu se jen domýšlet proč - možná komplikovaná implementace (díky rozšiřitelnosti Postgresu), možná priorita jiných řešení (bitmap index scan, podpora prostorových indexů, atd). Dost možná to opravdu bylo složitostí implementace (implementace se rozdělila do Postgresu 17 a 18). Index skip scan se hodí v těch případech, kdy se používá vícesloupcový index a nefiltruje se podle počátečních sloupců:
postgres=# CREATE TABLE foo(a int, b int); CREATE TABLE postgres=# INSERT INTO foo SELECT random()*1000, random()*10000 FROM generate_series(1,1000000); INSERT 0 1000000 postgres=# CREATE INDEX ON foo(a,b); CREATE INDEX postgres=# VACUUM ANALYZE foo; VACUUM postgres=# EXPLAIN (ANALYZE, BUFFERS, COSTS OFF) SELECT * FROM foo WHERE b = 100; ┌────────────────────────────────────────────────────────────────────────────────────────┐ │ QUERY PLAN │ ╞════════════════════════════════════════════════════════════════════════════════════════╡ │ Index Only Scan using foo_a_b_idx on foo (actual time=0.055..6.631 rows=94.00 loops=1) │ │ Index Cond: (b = 100) │ │ Heap Fetches: 0 │ │ Index Searches: 1002 │ │ Buffers: shared hit=3007 │ │ Planning Time: 0.093 ms │ │ Execution Time: 6.680 ms │ └────────────────────────────────────────────────────────────────────────────────────────┘ (7 rows) -- Postgres 17 postgres=# EXPLAIN (ANALYZE, BUFFERS, COSTS OFF) SELECT * FROM foo WHERE b = 100; ┌────────────────────────────────────────────────────────────────────────────┐ │ QUERY PLAN │ ╞════════════════════════════════════════════════════════════════════════════╡ │ Gather (actual time=0.339..38.282 rows=94 loops=1) │ │ Workers Planned: 2 │ │ Workers Launched: 2 │ │ Buffers: shared hit=4432 │ │ -> Parallel Seq Scan on foo (actual time=0.444..31.454 rows=31 loops=3) │ │ Filter: (b = 100) │ │ Rows Removed by Filter: 333302 │ │ Buffers: shared hit=4432 │ │ Planning Time: 0.082 ms │ │ Execution Time: 38.320 ms │ └────────────────────────────────────────────────────────────────────────────┘ (10 rows)
Docela zásadní interní univerzální komponentou Postgresu je tzv
tuplestore. Tato komponenta je dynamicky se zvětšující paměťový
buffer, který po dosažení limitu (work_mem
) začne ukládat
data do dočasného souboru. Tuplestore v Postgresu zajišťuje
efektivní operace nad daty (pokud je to možné v paměti), a garantuje, že
tyto operace nevyčerpají operační paměť. Tuplestore nově
používá vlastní paměťový kontext s generačním alokátorem. Tento typ
alokátoru předpokládá, že paměť bude uvolňována ve stejném pořadí, jako
byla alokována nebo bude uvolněna hromadně (zrušením nebo resetem
paměťového kontextu).
- zbytečně neroste paměťový kontext executoru, který má delší životnost,
- použití generačního alokátoru vede k nižší fragmentaci paměti (sekvenční operace (v paměti) jsou pak o něco málo rychlejší).
Postupně se rozšiřuje použití SIMD instrukcí - nově například pro výstup JSONu. Zrychlit by se také měla transformace do hexakódu (cca o 30%).
Násobení hodnot typu numeric číslem s nízkým počtem číslic (do čtyř) by mělo být rychlejší.
Do mnoha letech experimentování se do Postgresu dostala podpora
asynchronního IO (AIO). Použití AIO je řízeno nastavením konfigurační
proměnné io_method
(sync
znamená synchronní
IO, worker
by měl používat AIO, io-uring
používá knihovnu liburing
(pouze na Linuxu)). AIO se zatím používá pouze v kódu background
writeru a checkpointeru. Výhodou by mělo být snížení
latencí IO operací a snížení režie CPU. Do budoucna by to měl být
způsob, jak elegantněji řešit direct IO (DIO). Myslím si, že na
“typického” uživatele Postresu to bude mít minimální dopad, jelikož
běžné problémy, které souvisely se zahlcením file systém cache, by měly
být relativně dlouho vyřešené. Postgres tím jak má realizovanou
multigenerační architekturu a indexy, tak zapisuje víc než ostatní
databáze. Efektivnější zápis může zvednout limity Postgresu, a pomoct
uživatelům, kteří mají aplikace, kde se opravdu hodně zapisuje do
databáze (což se u extrémů řešilo použitím jiných databází než
PostgreSQL).
Došlo ke změně algoritmu pro alokaci paměti hashjoinu. Mělo by se snížit riziko, že se při aktualizaci hash tabulky začne neefektivně ve velkém alokovat paměť. Dříve k tomu docházelo spíš výjimečně a nyní by k takové situaci mělo dojít ještě s menší pravděpodobností.
Nově se oddaluje zamykání tabulek (v tomto případě partišny) až do chvíle, kdy bude zřejmé, které partišny skutečně bude potřeba zamknout (protože budou použité v dotazu). V testech byla režie zámků více než znatelná už při 120 partišen (v testu o více než 50% horší výsledky (Postgres 17)). Nové řešení zvyšuje benchmark u nižšího počtu partišen (cca o 30%) a při vyšším počtu (např. 2000 partišen) je benchmark pouze o 25% nižší než benchmark u Postgresu 17 s nízkým počtem partišen).
Příkaz EXPLAIN
je nyní rozšiřitelný - extenze mohou
přidávat vlastní volby a zobrazovat jiné než stávající metriky. Zatím
ale žádná taková extenze není k dispozici. V diskuzi jako příklady
nasazení zazněla možnost návrhů na indexy nebo možnost zobrazit plán
vykonávaný na cizím serveru.
Došlo ke změně části vzorce, kterým se řídí autovacuum
.
autovacuum_vacuum_insert_threshold
se porovnával s poměrem
nově přidaných řádků vůči celkovému počtu řádků. Nově se porovnává s
poměrem nezafrízovaných stránek vůči celkovému počtu stránek. Výhodou by
mělo být častější vakuování tabulek do kterých se hodně přidává (a je
ještě šance je zachytit ve cache (share buffers)). Pomocí
konfigurační volby vacuum_truncate
lze vyblokovat poslední
fázi vakuování tabulky, kterou je redukce prázdných datových stránek od
konce souboru. U zatíženějších serverů popsané chování může působit
problémy. Dojde sice k redukci velikosti tabulky (většinou ale
zanedbatelné), nicméně krátkodobě, a za cenu exkluzivního zámku nad
tabulkou (byť na krátkou dobu).
Změnilo se omezení použití unikátního indexu pro partišnované klíče
(a klíče materializovaných pohledů). Místo nuceného použití BTREE indexu
je možné použít takový typ unikátního indexu, který podporuje operaci
COMPARE_EQ
.
Plánování dotazů s větším počtem partitions by mělo být rychlejší.
Na komunikační protokol se nesáhlo fůru let. Z části proto, že nebylo moc důvodu do něj sahat. Z části i proto, že každý zásah do protokolu má většinou dost negativní dopad na uživatele (je nutné aktualizovat všechny klienty a s tím jsou problémy). Návrh protokolu umožňoval jeho rozšíření, ale až doposud se nikdo o rozšíření protokolu nepokusil. V loňském roce se opět začalo víc pracovat na dalších funkcích pgbounceru (proxy), kde by se rozšíření protokolu hodilo, a ukázalo se, že stávající design rozšiřování protokolu není úplně domyšlený (a do určité míry je stávající stav zabetonovaný). V rámci refaktoringu té části protokolu, která řeší jeho rozšiřování, se implementovala možnost použít pro identifikaci procesu obsluhujícího dotaz, který chceme přerušit, delší token (původně 4 bajtový nově až 256 bajtový). Implementace je i jakýmsi vzorem, jak do budoucna rozšiřovat protokol (hlavně se odblokovala diskuze ohledně rozšiřování protokolu).
Co se připravuje
Pro mne asi největší bombou PostgreSQL 19 bude příkaz
REPACK
. Implementace se jen těsně nedostala do Postgresu
18. Autorem je náš kamarád Tonda Houska z Cybertecu. Jedná se o
reimplementaci příkazu VACUUM FULL
s volbou
CONCURRENTLY
. Tonda je autorem extenze
pg_squeeze
, která je ideový následník extenze
pg_repack
. Postgres už roky podporuje příkaz
REINDEX CONCURRENTLY
(případně
CREATE INDEX CONCURRENTLY
), takže principiálně by nemělo
nic bránit tomu, aby existoval příkaz
VACUUM FULL CONCURRENTLY
. Tonda napsal prototyp (na základě
pg_squeeze
) a ukázalo se, že největší problém je v
pojmenování příkazu. Osobně mi přišlo pojmenování
VACUUM FULL CONCURRENTLY
v pohodě, nicméně anglicky
nativním mluvčím nikoliv. Shoda se našla na názvu REPACK
případně REPACK CONCURRENTLY
. Když všechno běží jak má, tak
VACUUM FULL
není potřeba. Občas ale všechno neběží jak má,
a pak se možnost jednoduše zredukovat velikost tabulky za provozu
hodí.
Chybí dokončit implementaci JSON_TABLE
. Patch už je
připravený.
Připravuje se podpora SQL/PGQ, což je rozšíření SQL, které umožňuje pracovat s daty v databázi jako s grafy.
Závěr
Určitým motivem řady změn ve verzi 18 je dotahování funkcionalit (třeba i jen do podoby prvotního commitu v upstreamu), na kterých se pracovalo (a které se diskutovaly) posledních pět let (a někdy i déle). Dolaďuje se commitfest aplikace včetně propojení na testovací farmu a mailing list.
Mám pocit, že se už skoro neřeší téma migrací z Oracle a víc se řeší
cloud. Samozřejmě, že i v Postgresové komunitě rezonuje hype AI - viz
rozšíření extenze pgvector
.
Díky DuckDB
se snížila
motivace o implementaci analytiky do Postgresu (existuje několik různých
způsobů jak propojit DuckDB
s Postgresem).
DuckDB
dělá analytiku velice šikovně a dobře (a je to
analytický speciál).
Rozšíření Postgresu už dávno překonalo kritickou velikost, a v dnešní turbulentní době je velkou výhodou nezávislost Postgresu na konkrétní firmě, konkrétní zemi nebo společenství. Vývoj Postgresu nejde stopnout - zrovna tak nejde stopnout provoz Postgresu nebo zdražit případně změnit licenci (což se mi osobně i někdy líbit nemusí a v pár lidech jsem se zklamal (i když beru, že doba je opět zlá - nikdy by mne nenapadlo, že budu o Open Source uvažovat v tomto kontextu)). S vývojem Postgresu má dnes zkušenost vyšší stovky vývojářů (a vyšší desítky se aktuálně podílejí na vývoji). Teď se to ukazuje jako neskutečná výhoda vůči MariaDB, MySQL nebo Firebirdu, které měli po celou dobu výrazně uzavřenější vývoj navázaný na jednu firmu nebo jednu organizaci. Všechno má svoje - někdy bývá v Postgresu problém se na něčem domluvit, a některé věci trvají hrozně dlouho. Na druhou stranu u projektu který už existuje skoro 30 let (a kdyby se to počítalo od INGRESu) tak 50 let, pár roků není žádná doba, a výsledek stojí za to (nemluvě o tom, že můžete spolupracovat dost možná s nejlepšími programátory na planetě (a dívat se jim pod prsty)).