<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Postgresql on Ivan Luminaria</title><link>https://ivanluminaria.com/it/categories/postgresql/</link><description>Recent content in Postgresql on Ivan Luminaria</description><generator>Hugo</generator><language>it</language><lastBuildDate>Tue, 24 Mar 2026 08:03:00 +0100</lastBuildDate><atom:link href="https://ivanluminaria.com/it/categories/postgresql/index.xml" rel="self" type="application/rss+xml"/><item><title>VACUUM e autovacuum: perché PostgreSQL ha bisogno che qualcuno pulisca</title><link>https://ivanluminaria.com/it/posts/postgresql/vacuum-autovacuum-postgresql/</link><pubDate>Tue, 24 Mar 2026 08:03:00 +0100</pubDate><guid>https://ivanluminaria.com/it/posts/postgresql/vacuum-autovacuum-postgresql/</guid><description>&lt;p&gt;Un paio d&amp;rsquo;anni fa mi chiedono di guardare un PostgreSQL in produzione che
&amp;ldquo;rallenta ogni settimana&amp;rdquo;. Sempre lo stesso pattern: lunedì va bene,
venerdì è un disastro. Il weekend qualcuno riavvia il servizio e si
ricomincia.&lt;/p&gt;
&lt;p&gt;Database da circa 200 GB. Tabelle principali che occupavano quasi il
triplo dello spazio effettivo dei dati. Query che andavano in sequential
scan su tabelle dove non avrebbero dovuto. Tempi di risposta che
salivano di giorno in giorno.&lt;/p&gt;</description></item><item><title>Ruoli e utenti in PostgreSQL: perché è tutto (solo) un ROLE</title><link>https://ivanluminaria.com/it/posts/postgresql/postgresql_roles_and_users/</link><pubDate>Tue, 10 Feb 2026 08:03:00 +0100</pubDate><guid>https://ivanluminaria.com/it/posts/postgresql/postgresql_roles_and_users/</guid><description>&lt;p&gt;La prima volta che ho lavorato seriamente con PostgreSQL arrivavo da
anni di altri database. Cercavo il comando &lt;code&gt;CREATE USER&lt;/code&gt;. Lo trovavo.
Poi vedevo &lt;code&gt;CREATE ROLE&lt;/code&gt;. Poi &lt;code&gt;ALTER USER&lt;/code&gt;. Poi &lt;code&gt;ALTER ROLE&lt;/code&gt;.&lt;br&gt;
Per qualche minuto ho pensato: &amp;ldquo;Ok, qui qualcuno si diverte a confondere
le persone&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;In realtà no. PostgreSQL è molto più coerente di quanto sembri. Solo che
lo è a modo suo.&lt;/p&gt;
&lt;h2 id="in-postgresql-non-esistono-utenti-esistono-ruoli" class="relative group"&gt;In PostgreSQL non esistono utenti. Esistono ruoli. &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#in-postgresql-non-esistono-utenti-esistono-ruoli" aria-label="Ancora"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;La chiave è questa: &lt;strong&gt;in PostgreSQL tutto è un ROLE&lt;/strong&gt;.&lt;/p&gt;</description></item><item><title>Quando un LIKE '%valore%' rallenta tutto: un caso reale di ottimizzazione PostgreSQL</title><link>https://ivanluminaria.com/it/posts/postgresql/like-optimization-postgresql/</link><pubDate>Tue, 06 Jan 2026 08:03:00 +0100</pubDate><guid>https://ivanluminaria.com/it/posts/postgresql/like-optimization-postgresql/</guid><description>&lt;p&gt;Qualche settimana fa un cliente mi contatta con un problema molto
comune:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;La ricerca nella console amministrativa è lenta. A volte impiega
diversi secondi. Abbiamo già ridotto le JOIN, ma il problema non è
sparito.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ambiente: PostgreSQL su cloud managed.&lt;br&gt;
Tabella principale: &lt;code&gt;payment_report&lt;/code&gt; (~6 milioni di righe, 3 GB).&lt;br&gt;
Colonna ricercata: &lt;code&gt;reference_code&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Query incriminata:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reporting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;payment_report&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reporting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;payment_cart&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cart_id&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;service_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1001&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reference_code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LIKE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;%ABC123%&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;LIMIT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="-prima-osservazione-il-problema-non-erano-le-join" class="relative group"&gt;🧠 Prima osservazione: il problema non erano le JOIN &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#-prima-osservazione-il-problema-non-erano-le-join" aria-label="Ancora"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Ho confrontato:&lt;/p&gt;</description></item><item><title>EXPLAIN ANALYZE non basta: come leggere davvero un piano di esecuzione PostgreSQL</title><link>https://ivanluminaria.com/it/posts/postgresql/explain-analyze-postgresql/</link><pubDate>Tue, 28 Oct 2025 08:03:00 +0100</pubDate><guid>https://ivanluminaria.com/it/posts/postgresql/explain-analyze-postgresql/</guid><description>&lt;p&gt;L&amp;rsquo;altro giorno un collega mi manda uno screenshot su Teams. Una query che gira su una tabella da 2 milioni di righe, 45 secondi di esecuzione. Mi scrive:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Ho fatto EXPLAIN ANALYZE, ma non capisco cosa c&amp;rsquo;è che non va. Il piano sembra corretto.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Spoiler: il piano non era affatto corretto. L&amp;rsquo;optimizer aveva scelto un &lt;span class="glossary-tip" tabindex="0" data-glossary-desc="Nested Loop Join — strategia di join che scansiona la tabella interna per ogni riga della tabella esterna, ideale per dataset piccoli con indice." data-glossary-url="https://ivanluminaria.com/it/glossary/nested-loop/" data-glossary-more="Leggi di più →"&gt;nested loop&lt;/span&gt;
 join dove serviva un &lt;span class="glossary-tip" tabindex="0" data-glossary-desc="Hash Join — strategia di join ottimizzata per grandi volumi di dati, basata su una hash table costruita in memoria." data-glossary-url="https://ivanluminaria.com/it/glossary/hash-join/" data-glossary-more="Leggi di più →"&gt;hash join&lt;/span&gt;
, e la ragione era banale — statistiche non aggiornate. Ma per arrivarci ho dovuto leggere il piano riga per riga, e lì mi sono reso conto che la maggior parte dei DBA che conosco usa EXPLAIN ANALYZE come un oracolo binario: se il tempo è alto, la query è lenta. Fine dell&amp;rsquo;analisi.&lt;/p&gt;</description></item></channel></rss>