PostgreSQL 8.3, uscito il 4 febbraio 2008, porta dentro al motore due funzioni che prima vivevano fuori — la ricerca full-text e il riuso aggressivo dello spazio sulle tuple morte. La scelta dice sulla direzione del progetto più di qualsiasi benchmark.

Contesto

Il codice di PostgreSQL nasce a Berkeley nel 1986, dal progetto POSTGRES di Michael Stonebraker, pensato come erede di Ingres per studiare un tema preciso: l’estensibilità. Tipi di dato definiti dall’utente, un sistema di regole, indici scrivibili su misura. Nel 1994 Andrew Yu e Jolly Chen sostituiscono l’interprete del linguaggio POSTQUEL con uno per SQL, e nasce Postgres95. Nel 1996 il progetto diventa PostgreSQL, passa a una comunità distribuita di sviluppatori e adotta la licenza permissiva BSD.

Da allora la linea è rimasta la stessa: aggiungere funzioni senza intaccare l’invariante centrale, cioè un motore dove un tipo di dato nuovo o un nuovo metodo di indicizzazione valgono quanto quelli forniti di serie. La 8.3 merita attenzione perché è la versione in cui una parte di ciò che la comunità aveva costruito come contrib esterno entra finalmente nel server installato di default.

Architettura: MVCC come fondamento

Il controllo di concorrenza di PostgreSQL poggia su MVCC (Multi-Version Concurrency Control). Ogni transazione vede uno snapshot dei dati così com’erano in un istante preciso, a prescindere dallo stato attuale delle righe sottostanti. La conseguenza pratica, descritta dalla documentazione 8.3, è che le letture non bloccano le scritture e le scritture non bloccano le letture, e i lock entrano in gioco solo quando due transazioni vogliono modificare la stessa riga.

Questo modello ha un costo strutturale. Un UPDATE non sovrascrive la riga: crea una nuova versione della tupla e lascia la vecchia al suo posto finché non diventa invisibile a tutte le transazioni ancora aperte. Una DELETE non libera lo spazio subito. Le tuple morte si accumulano e vanno recuperate dopo, di solito con VACUUM. Su tabelle con molti aggiornamenti il risultato è il bloat: spazio occupato, indici da riscrivere, e un I/O che sale man mano che le modifiche si fanno frequenti.

Il punto critico: HOT e full-text nel core

La 8.3 attacca il costo di MVCC con HOT (Heap-Only Tuples). Quando un UPDATE non tocca colonne indicizzate, PostgreSQL può scrivere la nuova versione della tupla sulla stessa pagina e collegarla alla vecchia senza aggiungere voci agli indici. Lo spazio delle tuple morte si recupera già al successivo INSERT o UPDATE, non solo durante un VACUUM. Su un carico fatto di aggiornamenti frequenti a colonne non indicizzate — una tabella di stato, una riga di sessione, un contatore — questo taglia sia l’I/O sia la crescita degli indici.

La seconda mossa riguarda la ricerca testuale. Fino alla 8.2 il full-text search stava nel modulo contrib/tsearch2: codice mantenuto dalla comunità, da installare a parte, con una sintassi tutta sua. Nella 8.3 le sue funzioni entrano nel server, installate di default, con qualche piccola modifica di sintassi. I tipi tsvector e tsquery diventano tipi del motore e non di un’estensione. Ed è qui che l’estensibilità paga: la ricerca full-text si appoggia a due metodi di indicizzazione che già esistono, GiST e GIN, che PostgreSQL offre come infrastruttura generica e non come codice scritto apposta per il testo.

La differenza tra i due indici è documentata senza ambiguità. GiST è lossy: può restituire falsi positivi da verificare sulla riga vera, ha lookup più lenti ma aggiornamenti più rapidi, e regge bene i dati dinamici. GIN è un indice invertito, in sostanza non lossy, con lookup più veloci (la documentazione 8.3 stima circa tre volte rispetto a GiST) ma costruzione e aggiornamento molto più lenti, adatto ai dati più statici. Scegliere tra i due dipende dal carico, non dalle funzionalità.

Implicazioni

Che tsearch2 passi da contrib al core non è una riga fra le tante nelle note di rilascio. È il segnale di un meccanismo: una funzione nasce come estensione, viene messa alla prova su carichi reali da chi la mantiene fuori dal server, e quando è abbastanza stabile migra dentro. L’estensibilità serve all’utente finale che si definisce un tipo su misura, e serve al progetto stesso come banco di prova per decidere cosa promuovere nel motore.

Lo stesso discorso vale per le altre novità della 8.3. Il commit asincrono rimanda le scritture sul WAL (Write-Ahead Log) durante il commit, scambiando durabilità per throughput sulle transazioni brevi — una scelta che il chiamante fa con cognizione di causa. Lo spreading dei checkpoint distribuisce le scritture su disco per smussare il picco di I/O. Il planner impara a ri-pianificare le query in cache quando cambiano le definizioni o le statistiche delle tabelle. Sono tutte modifiche al motore, non strati che gli si appoggiano sopra.

Per chi progetta un sistema la lezione pratica è semplice: PostgreSQL tende a internalizzare. Una funzione che oggi si risolve con un servizio esterno — l’indicizzazione testuale, in molti casi — può già stare dentro al database, con la stessa semantica transazionale del resto dei dati. Conviene controllare prima di aggiungere un componente.

Limiti

HOT aiuta solo quando l’UPDATE non tocca colonne indicizzate; con molti indici su colonne aggiornate spesso il vantaggio sparisce, e la pianificazione degli indici torna a pesare. Il full-text search nel core copre i casi comuni di ricerca su documenti, ma non rimpiazza un motore specializzato dove servano ranking sofisticati, analisi linguistica avanzata o scala su corpus enormi. E il modello MVCC resta quello che è: lo spazio va comunque recuperato, e una strategia di VACUUM tarata male produce bloat a prescindere da HOT. Nessuna di queste funzioni elimina il lavoro di tuning: lo sposta solo altrove.


https://www.postgresql.org/docs/8.3/release-8-3.html https://www.postgresql.org/docs/8.3/mvcc-intro.html https://www.postgresql.org/docs/8.3/textsearch-indexes.html https://www.postgresql.org/about/news/postgresql-83-released-918/ https://www.postgresql.org https://www.noze.it/insights/postgresql-open-source/

Immagine di copertina: Michael Stonebraker, l’informatico all’origine di Ingres e Postgres, in polo rossa — foto di Dcoetzee, CC0 — https://commons.wikimedia.org/wiki/File:Michael_Stonebraker_2.jpg