<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Jörg Brühe on FromDual GmbH</title><link>https://www.fromdual.com/it/aggregator/sources/12/</link><description>Recent content in Jörg Brühe on FromDual GmbH</description><generator>Hugo</generator><language>it-IT</language><managingEditor>oli.sennhauser@fromdual.com (Oli Sennhauser)</managingEditor><webMaster>oli.sennhauser@fromdual.com (Oli Sennhauser)</webMaster><copyright>© FromDual GmbH</copyright><atom:link href="https://www.fromdual.com/it/aggregator/sources/12/index.xml" rel="self" type="application/rss+xml"/><item><title>I blog di FromDual</title><link>https://www.fromdual.com/it/blog/</link><pubDate>Thu, 12 Feb 2026 20:30:00 +0100</pubDate><author>oli.sennhauser@fromdual.com (Oli Sennhauser)</author><guid>https://www.fromdual.com/it/blog/</guid><description>&lt;p&gt;In questa pagina troverete i vari blog dei dipendenti di FromDual e alcuni utili aggregatori di blog.&lt;/p&gt;
&lt;h2 id="blog-dei-dipendenti-fromdual"&gt;Blog dei dipendenti FromDual&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.fromdual.com/de/blog/oli/"&gt;Blog di Oli in tedesco&lt;/a&gt; &lt;img src="https://www.fromdual.com/images/de.png" alt="german" width="16" height="12"&gt; &lt;a href="https://www.fromdual.com/de/blog/oli/index.xml"&gt;&lt;img src="https://www.fromdual.com/images/feed.png" alt="RSS feed" width="12" height="12"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.fromdual.com/blog/shinguz/"&gt;Blog di Shinguz in inglese&lt;/a&gt; &lt;img src="https://www.fromdual.com/images/en.png" alt="english" width="16" height="12"&gt; &lt;a href="https://www.fromdual.com/blog/shinguz/index.xml"&gt;&lt;img src="https://www.fromdual.com/images/feed.png" alt="RSS feed" width="12" height="12"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.fromdual.com/blog/abdel-mawla/"&gt;Blog di Abdel-Mawla in inglese&lt;/a&gt; &lt;img src="https://www.fromdual.com/images/en.png" alt="english" width="16" height="12"&gt; &lt;a href="https://www.fromdual.com/blog/abdel-mawla/index.xml"&gt;&lt;img src="https://www.fromdual.com/images/feed.png" alt="RSS feed" width="12" height="12"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.fromdual.com/blog/joerg/"&gt;Blog di Jörg in inglese&lt;/a&gt; &lt;img src="https://www.fromdual.com/images/en.png" alt="english" width="16" height="12"&gt; &lt;a href="https://www.fromdual.com/blog/joerg/index.xml"&gt;&lt;img src="https://www.fromdual.com/images/feed.png" alt="RSS feed" width="12" height="12"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.fromdual.com/blog/cedric/"&gt;Blog di Cédric in inglese&lt;/a&gt; &lt;img src="https://www.fromdual.com/images/en.png" alt="english" width="16" height="12"&gt; &lt;a href="https://www.fromdual.com/blog/cedric/index.xml"&gt;&lt;img src="https://www.fromdual.com/images/feed.png" alt="RSS feed" width="12" height="12"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.fromdual.com/de/blog/sales_de/"&gt;Blog di vendita FromDual in tedesco&lt;/a&gt; &lt;img src="https://www.fromdual.com/images/de.png" alt="german" width="16" height="12"&gt; &lt;a href="https://www.fromdual.com/de/blog/sales_de/index.xml"&gt;&lt;img src="https://www.fromdual.com/images/feed.png" alt="RSS feed" width="12" height="12"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.fromdual.com/blog/sales_en/"&gt;Blog di vendita FromDual in inglese&lt;/a&gt; &lt;img src="https://www.fromdual.com/images/en.png" alt="english" width="16" height="12"&gt; &lt;a href="https://www.fromdual.com/blog/sales_en/index.xml"&gt;&lt;img src="https://www.fromdual.com/images/feed.png" alt="RSS feed" width="12" height="12"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="aggregati-di-blog-fromdual"&gt;Aggregati di blog FromDual&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.fromdual.com/de/aggregator/categories/10/"&gt;FromDual AllFeed in tedesco&lt;/a&gt; &lt;img src="https://www.fromdual.com/images/de.png" alt="german" width="16" height="12"&gt; &lt;a href="https://www.fromdual.com/aggregator/rss/10"&gt;&lt;img src="https://www.fromdual.com/images/feed.png" alt="RSS feed" width="12" height="12"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.fromdual.com/aggregator/categories/9/"&gt;FromDual AllFeed in inglese&lt;/a&gt; &lt;img src="https://www.fromdual.com/images/en.png" alt="english" width="16" height="12"&gt; &lt;a href="https://www.fromdual.com/aggregator/rss/9"&gt;&lt;img src="https://www.fromdual.com/images/feed.png" alt="RSS feed" width="12" height="12"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.fromdual.com/de/aggregator/categories/4/"&gt;FromDual TechFeed in tedesco&lt;/a&gt; &lt;img src="https://www.fromdual.com/images/de.png" alt="german" width="16" height="12"&gt; &lt;a href="https://www.fromdual.com/aggregator/rss/4"&gt;&lt;img src="https://www.fromdual.com/images/feed.png" alt="RSS feed" width="12" height="12"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.fromdual.com/aggregator/categories/5/"&gt;FromDual TechFeed in inglese&lt;/a&gt; &lt;img src="https://www.fromdual.com/images/en.png" alt="english" width="16" height="12"&gt; &lt;a href="https://www.fromdual.com/aggregator/rss/5"&gt;&lt;img src="https://www.fromdual.com/images/feed.png" alt="RSS feed" width="12" height="12"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.fromdual.com/de/aggregator/categories/8/"&gt;FromDual InfoFeed in tedesco&lt;/a&gt; &lt;img src="https://www.fromdual.com/images/de.png" alt="german" width="16" height="12"&gt; &lt;a href="https://www.fromdual.com/aggregator/rss/8"&gt;&lt;img src="https://www.fromdual.com/images/feed.png" alt="RSS feed" width="12" height="12"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.fromdual.com/aggregator/categories/6/"&gt;FromDual InfoFeed in inglese&lt;/a&gt; &lt;img src="https://www.fromdual.com/images/en.png" alt="english" width="16" height="12"&gt; &lt;a href="https://www.fromdual.com/aggregator/rss/6"&gt;&lt;img src="https://www.fromdual.com/images/feed.png" alt="RSS feed" width="12" height="12"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;MySQL Tech-Feed in tedesco &lt;img src="https://www.fromdual.com/images/de.png" alt="german" width="16" height="12"&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.fromdual.com/aggregator/categories/3/"&gt;MySQL Tech-Feed in inglese&lt;/a&gt; &lt;img src="https://www.fromdual.com/images/en.png" alt="english" width="16" height="12"&gt; &lt;a href="https://www.fromdual.com/aggregator/rss/3"&gt;&lt;img src="https://www.fromdual.com/images/feed.png" alt="RSS feed" width="12" height="12"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;PostgreSQL Tech-Feed in tedesco &lt;img src="https://www.fromdual.com/images/de.png" alt="german" width="16" height="12"&gt;&lt;/p&gt;
&lt;p&gt;PostgreSQL Tech-Feed in inglese &lt;img src="https://www.fromdual.com/images/en.png" alt="english" width="16" height="12"&gt;&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;br&gt;
Questa pagina è stata tradotta con &lt;a href="https://www.deepl.com/it/translator" target="_blank"&gt;deepl.com&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Qual è il modo più veloce per caricare dati nel database?</title><link>https://www.fromdual.com/it/blog/carica-rapidamente-i-dati-nel-database/</link><pubDate>Wed, 11 Feb 2026 10:04:00 +0100</pubDate><author>oli.sennhauser@fromdual.com (Oli Sennhauser)</author><guid>https://www.fromdual.com/it/blog/carica-rapidamente-i-dati-nel-database/</guid><description>&lt;p&gt;Con l&amp;rsquo;ultimo cliente abbiamo dovuto risolvere alcune questioni davvero interessanti! Soprattutto perché il database non era proprio piccolo.&lt;/p&gt;
&lt;p&gt;Ecco alcuni dati chiave: CPU: 2 socket x 24 core x 2 thread = 96 vCore, 756 G RAM, 2 x 10 Tbyte PCIe SSD in RAID-10 e 7 Tbyte di dati, diverse migliaia di clienti, in forte crescita.&lt;/p&gt;
&lt;p&gt;Il throughput attuale: 1 M &lt;code&gt;SELECT&lt;/code&gt;/min, 56 k &lt;code&gt;INSERT&lt;/code&gt;/min, 44 k &lt;code&gt;UPDATE&lt;/code&gt;/min , 7 k &lt;code&gt;DELETE&lt;/code&gt;/min in media su 30 giorni. Tendenza in forte aumento. Applicazione e query non ottimizzate in modo coerente. Configurazione del database: “state of the art” non verificata con benchmark. Utilizzo della CPU circa il 50% in media, con picchi superiori. Il sistema I/O ha ancora margini di miglioramento.&lt;/p&gt;
&lt;p&gt;Il cliente raccoglie dati di posizione e altri dati relativi ai dispositivi e li memorizza nel database. Si tratta quindi di un classico problema IoT (con serie temporali, tabella clusterizzata per indice, ecc.).&lt;/p&gt;
&lt;p&gt;La domanda che ha posto è: qual è il modo più veloce per copiare i dati da una tabella (dati in sospeso, una sorta di coda) a un&amp;rsquo;altra tabella (dati finali, per cliente)?&lt;/p&gt;
&lt;p&gt;Il flusso di dati è più o meno il seguente:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;+------------+
| IoT Device |--+
+------------+ \
 \ +-----+
+------------+ \ | AS | +--------------+ Processing +------------+
| IoT Device |------+--&amp;gt;| |--&amp;gt;| Pending data |-------------&amp;gt;| Final data |
+------------+ / | 400 | +--------------+ of data +------------+
 / +-----+
+------------+ /
| IoT Device |--+
+------------+
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Erano disponibili 3 diverse varianti per copiare i dati.&lt;/p&gt;
&lt;h2 id="variante-1-insert-e-delete-forma-più-semplice"&gt;Variante 1: &lt;code&gt;INSERT&lt;/code&gt; e &lt;code&gt;DELETE&lt;/code&gt; (forma più semplice)&lt;/h2&gt;
&lt;p&gt;La variante più semplice è &lt;code&gt;INSERT&lt;/code&gt; e &lt;code&gt;DELETE&lt;/code&gt;. Questa variante è particolarmente problematica perché MariaDB/MySQL e PostgreSQL hanno AUTOCOMMIT attivato di default (&lt;a href="https://dev.mysql.com/doc/refman/8.4/en/innodb-autocommit-commit-rollback.html" target="_blank"&gt;qui&lt;/a&gt;, &lt;a href="https://mariadb.com/docs/server/reference/sql-statements/transactions/start-transaction" target="_blank"&gt;qui&lt;/a&gt;, &lt;a href="https://www.postgresql.org/docs/current/ecpg-sql-set-autocommit.html" target="_blank"&gt;qui&lt;/a&gt; et &lt;a href="https://www.cybertec-postgresql.com/en/disabling-autocommit-in-postgresql-can-damage-your-health/" target="_blank"&gt;qui&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Per chiarire meglio il concetto, ecco un po&amp;rsquo; di pseudocodice:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// 20k rows
for (i = 1; i &amp;lt;= 2000; i++) {

 SELECT * FROM pending LIMIT 10;
 foreach ( row ) {
 INSERT INTO final;
 -- implicit COMMIT
 DELETE FROM pending WHERE id = row[id];
 -- implicit COMMIT
 }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Se vogliamo copiare 20 k righe, questa variante causa: 40 k &lt;code&gt;COMMIT&lt;/code&gt; (&lt;code&gt;fsync&lt;/code&gt;) e 42 k roundtrip di rete!&lt;/p&gt;
&lt;h2 id="variante-2-start-transaction-e-insert-e-delete"&gt;Variante 2: &lt;code&gt;START TRANSACTION&lt;/code&gt; e &lt;code&gt;INSERT&lt;/code&gt; e &lt;code&gt;DELETE&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Questa variante viene utilizzata dagli sviluppatori di database più esperti. Ecco il codice pseudocodice corrispondente:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// 20k rows
for (i = 1; i &amp;lt;= 2000; i++) {

 SELECT * FROM pending LIMIT 10;
 START TRANSACTION;
 foreach ( row ) {
 INSERT INTO final;
 DELETE FROM pending WHERE id = row[id];
 }
 COMMIT;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Se in questo esempio vogliamo copiare 20 k righe, questa variante genera solo 2 k &lt;code&gt;COMMIT&lt;/code&gt; (&lt;code&gt;fsync&lt;/code&gt;)! Quindi 20 volte meno! Ma in cambio 46 k roundtrip di rete (10% in più).&lt;/p&gt;
&lt;h2 id="variante-3-start-transaction-e-insert-e-delete-ottimizzati"&gt;Variante 3: &lt;code&gt;START TRANSACTION&lt;/code&gt; e &lt;code&gt;INSERT&lt;/code&gt; e &lt;code&gt;DELETE&lt;/code&gt; ottimizzati&lt;/h2&gt;
&lt;p&gt;Questa variante è leggermente più complessa dal punto di vista della programmazione. Viene utilizzata quando si desidera avvicinarsi il più possibile ai limiti del possibile. Ecco il pseudocodice corrispondente:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// 20k rows
for (i = 1; i &amp;lt;= 2000; i++) {

 SELECT * FROM pending LIMIT 10;
 START TRANSACTION;
 INSERT INTO final (), (), (), (), (), (), (), (), (), ();
 DELETE FROM pending WHERE id = IN (...);
 COMMIT;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Anche questa terza variante genera solo 2 k &lt;code&gt;COMMIT&lt;/code&gt; (&lt;code&gt;fsync&lt;/code&gt;) con 20 k righe, ma evita il ciclo delle istruzioni &lt;code&gt;INSERT&lt;/code&gt; e &lt;code&gt;DELETE&lt;/code&gt; nel database. Da un lato risparmiamo roundtrip di rete (solo 10 k) e cicli CPU sul database (che sono difficili da scalare) per l&amp;rsquo;analisi delle query.&lt;/p&gt;
&lt;h2 id="configurazione-del-test"&gt;Configurazione del test&lt;/h2&gt;
&lt;p&gt;Per testare il tutto, abbiamo preparato un piccolo script: &lt;a href="https://www.fromdual.com/code-examples/load_data.php.txt"&gt;load_data.php&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="preparazione-ed-esecuzione-con-mariadbmysql"&gt;Preparazione ed esecuzione con MariaDB/MySQL&lt;/h3&gt;
&lt;p&gt;Con i seguenti comandi è possibile eseguire questi test autonomamente:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;SQL&amp;gt; CREATE DATABASE test;
SQL&amp;gt; CREATE USER &amp;#39;app&amp;#39;@&amp;#39;127.0.0.1&amp;#39; IDENTIFIED BY &amp;#39;secret&amp;#39;;
SQL&amp;gt; GRANT ALL ON *.* TO &amp;#39;app&amp;#39;@&amp;#39;127.0.0.1&amp;#39;;

$ ./load_data.php --database-type=mysql --database=test --host=127.0.0.1 --port=3306 --user=app --password=secret --prepare

$ for i in $(seq 5) ; do
 ./load_data.php --database-type=mysql --database=test --host=127.0.0.1 --port=3306 --user=app --password=secret --run --variant=1
 ./load_data.php --database-type=mysql --database=test --host=127.0.0.1 --port=3306 --user=app --password=secret --run --variant=2
 ./load_data.php --database-type=mysql --database=test --host=127.0.0.1 --port=3306 --user=app --password=secret --run --variant=3
done

$ ./load_data.php --database-type=mysql --database=test --host=127.0.0.1 --port=3306 --user=app --password=secret --clean-up
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ognuno può elaborare i valori misurati con lo script di test corrispondente.&lt;/p&gt;
&lt;h3 id="preparazione-ed-esecuzione-con-postgresql"&gt;Preparazione ed esecuzione con PostgreSQL&lt;/h3&gt;
&lt;p&gt;È possibile eseguire questi test autonomamente utilizzando i seguenti comandi:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;postgres# CREATE DATABASE test;
postgres# CREATE USER app PASSWORD &amp;#39;secret&amp;#39;;
postgres# GRANT ALL ON DATABASE test TO app;
postgres# GRANT ALL ON SCHEMA public TO app;

$ ./load_data.php --database-type=postgresql --database=test --host=127.0.0.1 --port=5432 --user=app --password=secret --prepare

$ for i in $(seq 5) ; do
 ./load_data.php --database-type=postgresql --database=test --host=127.0.0.1 --port=5432 --user=app --password=secret --run --variant=1
 ./load_data.php --database-type=postgresql --database=test --host=127.0.0.1 --port=5432 --user=app --password=secret --run --variant=2
 ./load_data.php --database-type=postgresql --database=test --host=127.0.0.1 --port=5432 --user=app --password=secret --run --variant=3
done

$ ./load_data.php --database-type=postgresql --database=test --host=127.0.0.1 --port=5432 --user=app --password=secret --clean-up
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ognuno può elaborare i valori misurati utilizzando lo script di test corrispondente.&lt;/p&gt;
&lt;h2 id="risultati"&gt;Risultati&lt;/h2&gt;
&lt;p&gt;Per evitare discussioni inutili, abbiamo elencato qui “solo” le prestazioni relative (tempo di esecuzione), come fa ultimamente anche MarkC. I nostri valori misurati possono essere messi a disposizione bilateralmente. Tuttavia, possono essere facilmente riprodotti con lo script di test.&lt;/p&gt;
&lt;p&gt;Meno è meglio:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;Variante 1&lt;/th&gt;
 &lt;th&gt;Variante 2&lt;/th&gt;
 &lt;th&gt;Variante 3&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;MariaDB 11.8, avg(5)&lt;/td&gt;
 &lt;td&gt;100.0%&lt;/td&gt;
 &lt;td&gt;7.5%&lt;/td&gt;
 &lt;td&gt;6.2%&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;PostgreSQL 19dev, avg(5)&lt;/td&gt;
 &lt;td&gt;100.0%&lt;/td&gt;
 &lt;td&gt;11.2%&lt;/td&gt;
 &lt;td&gt;7.0%&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Attenzione&lt;/strong&gt;: i valori di MariaDB/MySQL e PostgreSQL NON sono direttamente comparabili!&lt;/p&gt;
&lt;br&gt;
&lt;p&gt;Ed ecco la valutazione grafica:&lt;/p&gt;
&lt;img src="https://www.fromdual.com/images/mariadb-data-load.png" alt="mariadb"&gt;
&lt;p&gt; &lt;/p&gt;
&lt;img src="https://www.fromdual.com/images/postgresql-data-load.png" alt="postgresql"&gt;
&lt;h2 id="osservazioni"&gt;Osservazioni&lt;/h2&gt;
&lt;p&gt;Con dischi più veloci, la differenza tra 1 e 2/3 probabilmente non sarebbe stata così significativa. I test effettuati presso il cliente hanno mostrato “solo” una differenza di circa 5 volte (anziché da 9 a 16 volte).&lt;/p&gt;
&lt;p&gt;Ci sono sicuramente altri modi per ottimizzare ulteriormente questo processo di caricamento. Mi vengono in mente:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;INSERT INTO ... SELECT * FROM&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LOAD DATA INFILE&lt;/code&gt;/&lt;code&gt;COPY&lt;/code&gt;, se possibile&lt;/li&gt;
&lt;li&gt;Prepared Statements&lt;/li&gt;
&lt;li&gt;Server side Stored Language (SQL/PSM, PL/pgSQL, &amp;hellip;) :-(&lt;/li&gt;
&lt;li&gt;PDO-Fetch dei risultati?&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Forse dovrei procurarmi un profiler (&lt;a href="https://xdebug.org/" target="_blank"&gt;Xdebug&lt;/a&gt; o &lt;a href="https://www.php.net/manual/en/book.xhprof.php" target="_blank"&gt;xhprof&lt;/a&gt;)?&lt;/p&gt;
&lt;h2 id="altri-contributi"&gt;Altri contributi&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.fromdual.com/blog/load-csv-files-into-the-database/"&gt;Load CSV files into the database&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.fromdual.com/blog/mariadb-prepared-statements-transactions-and-multi-row-inserts/"&gt;MariaDB Prepared Statements, Transactions and Multi-Row Inserts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.fromdual.com/blog/how-good-is-mysql-insert-trigger-performance/"&gt;How good is MySQL INSERT TRIGGER performance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Questa pagina è stata tradotta utilizzando &lt;a href="https://www.deepl.com/it/translator" target="_blank"&gt;deepl.com&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>MariaDB ha compromesso il concetto di buffer pool configurabile dinamicamente!</title><link>https://www.fromdual.com/it/blog/mariadb-buffer-pool-configurabile-dinamicamente-danneggiato/</link><pubDate>Mon, 09 Feb 2026 19:14:00 +0100</pubDate><author>oli.sennhauser@fromdual.com (Oli Sennhauser)</author><guid>https://www.fromdual.com/it/blog/mariadb-buffer-pool-configurabile-dinamicamente-danneggiato/</guid><description>&lt;h2 id="descrizione-del-problema"&gt;Descrizione del problema&lt;/h2&gt;
&lt;p&gt;MySQL ha introdotto il buffer pool InnoDB configurabile dinamicamente con la versione 5.7.5 nel settembre 2014 (&lt;a href="https://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-5.html" target="_blank"&gt;qui&lt;/a&gt; e &lt;a href="https://dev.mysql.com/doc/refman/5.7/en/innodb-buffer-pool-resize.html#innodb-buffer-pool-online-resize" target="_blank"&gt;qui&lt;/a&gt;):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The innodb_buffer_pool_size configuration option can be set dynamically using a SET statement, allowing you to resize the buffer pool without restarting the server. For example:&lt;br&gt;&lt;br&gt;
mysql&amp;gt; SET GLOBAL innodb_buffer_pool_size=402653184;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;MariaDB 10.2.2 ha adottato questa funzione nel settembre 2016 (&lt;a href="https://mariadb.com/docs/release-notes/community-server/old-releases/10.2/10.2.2#notable-changes" target="_blank"&gt;fonte&lt;/a&gt;):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;InnoDB was merged from MySQL-5.7.14 (XtraDB is disabled in MariaDB-10.2.2 pending a similar merge)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Il problema è, da un lato, che questa funzione ora non funziona più come prima e non funziona più come previsto. D&amp;rsquo;altra parte, nella primavera del 2025 hanno modificato il comportamento all&amp;rsquo;interno di una serie di release principali (LTS), cosa che a mio parere è assolutamente inaccettabile (&lt;a href="https://mariadb.com/docs/server/server-usage/storage-engines/innodb/innodb-buffer-pool#buffer-pool-changes" target="_blank"&gt;fonte&lt;/a&gt;):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;From MariaDB 10.11.12 / 11.4.6 / 11.8.2, there are significant changes to the InnoDB buffer pool behavior.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Inoltre, la descrizione è piuttosto scarsa (&lt;a href="https://mariadb.com/docs/release-notes/community-server/10.11/10.11.12#innodb" target="_blank"&gt;fonte&lt;/a&gt;):&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;decreasing innodb_buffer_pool_size at runtime does not release memory (MDEV-32339)&lt;/li&gt;
&lt;li&gt;reorganise innodb buffer pool (and remove buffer pool chunks) (MDEV-29445)&lt;/li&gt;
&lt;li&gt;The Linux memory pressure interface, which could previously not be disabled and could cause performance anomalies, was rewritten and is disabled by default. (MDEV-34863)&lt;/li&gt;
&lt;li&gt;Server crashes when resizing default innodb buffer pool after setting innodb-buffer-pool-chunk-size to 1M (MDEV-34677)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;Nel relativo worklog (&lt;a href="https://jira.mariadb.org/browse/MDEV-36197" target="_blank"&gt;MDEV-36197&lt;/a&gt;), MarkoM descrive anche un altro comportamento:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;innodb_buffer_pool_size_auto_max (my proposal for this task) would set the maximum for the automation (default: 0 to disable the logic).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;che a mio parere sarebbe stato molto più sensato.&lt;/p&gt;
&lt;h2 id="come-funzionava-prima"&gt;Come funzionava prima?&lt;/h2&gt;
&lt;p&gt;Come funzionava prima con MariaDB e come funziona ancora oggi con MySQL:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;SQL&amp;gt; SHOW GLOBAL VARIABLES LIKE &amp;#39;innodb_buffer_pool%size&amp;#39;;
+-------------------------------+-----------+
| Variable_name | Value |
+-------------------------------+-----------+
| innodb_buffer_pool_chunk_size | 2097152 |
| innodb_buffer_pool_size | 134217728 |
+-------------------------------+-----------+

SQL&amp;gt; SET GLOBAL innodb_buffer_pool_size = @@innodb_buffer_pool_chunk_size * 128;

SQL&amp;gt; SHOW GLOBAL VARIABLES LIKE &amp;#39;innodb_buffer_pool%size&amp;#39;;
+-------------------------------+-----------+
| Variable_name | Value |
+-------------------------------+-----------+
| innodb_buffer_pool_chunk_size | 2097152 |
| innodb_buffer_pool_size | 268435456 |
+-------------------------------+-----------+
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Quindi tutto OK. Funziona come previsto e come al solito.&lt;/p&gt;
&lt;h2 id="cosa-fa-mariadb-oggi"&gt;Cosa fa MariaDB oggi?&lt;/h2&gt;
&lt;p&gt;Cosa succede oggi con MariaDB:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;SQL&amp;gt; SHOW GLOBAL VARIABLES LIKE &amp;#39;innodb_buffer_pool%size%&amp;#39;;
+----------------------------------+-----------+
| Variable_name | Value |
+----------------------------------+-----------+
| innodb_buffer_pool_chunk_size | 0 |
| innodb_buffer_pool_size | 134217728 |
| innodb_buffer_pool_size_auto_min | 134217728 |
| innodb_buffer_pool_size_max | 134217728 |
+----------------------------------+-----------+

SQL&amp;gt; SET GLOBAL innodb_buffer_pool_size = 256*1024*1024;
Query OK, 0 rows affected, 1 warning (0.000 sec)

SQL&amp;gt; show warnings;
+---------+------+----------------------------------------------------------------+
| Level | Code | Message |
+---------+------+----------------------------------------------------------------+
| Warning | 1292 | Truncated incorrect innodb_buffer_pool_size value: &amp;#39;268435456&amp;#39; |
+---------+------+----------------------------------------------------------------+

SQL&amp;gt; SHOW GLOBAL VARIABLES LIKE &amp;#39;innodb_buffer_pool%size%&amp;#39;;
+----------------------------------+-----------+
| Variable_name | Value |
+----------------------------------+-----------+
| innodb_buffer_pool_chunk_size | 0 |
| innodb_buffer_pool_size | 134217728 |
| innodb_buffer_pool_size_auto_min | 134217728 |
| innodb_buffer_pool_size_max | 134217728 |
+----------------------------------+-----------+
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Niente! Nemmeno un errore, solo un avviso. E se non guardo attentamente, non mi accorgo nemmeno che qualcosa non ha funzionato.&lt;/p&gt;
&lt;p&gt;Nel log degli errori di MariaDB c&amp;rsquo;è scritto:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;[Note] InnoDB: Memory pressure event disregarded; innodb_buffer_pool_size=128m, innodb_buffer_pool_size_auto_min=128m
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Se poi si fa una ricerca, si scopre che qui è cambiato qualcosa: &lt;a href="https://mariadb.com/docs/server/server-usage/storage-engines/innodb/innodb-buffer-pool#buffer-pool-changes" target="_blank"&gt;Buffer Pool Changes&lt;/a&gt; e si prova intuitivamente:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;SQL&amp;gt; SET GLOBAL innodb_buffer_pool_size_max = 256*1024*1024;
ERROR 1238 (HY000): Variable &amp;#39;innodb_buffer_pool_size_max&amp;#39; is a read only variable
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ma non funziona.&lt;/p&gt;
&lt;p&gt;Ciò significa che è necessario riavviare il database! E questo forse proprio nel momento in cui non si ha affatto bisogno di riavviare e questa funzione sarebbe necessaria&amp;hellip;&lt;/p&gt;
&lt;p&gt;Nella documentazione si legge anche (&lt;a href="https://mariadb.com/docs/server/server-usage/storage-engines/innodb/innodb-system-variables#innodb_buffer_pool_size_max" target="_blank" title="innodb_buffer_pool_size_max"&gt;fonte&lt;/a&gt;):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Default Value: specified by the initial value of innodb_buffer_pool_size, rounded up to the block size of that variable. See the section about buffer pool changes in MariaDB 10.11.12, 11.4.6, and 11.8.2.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;e (&lt;a href="https://mariadb.com/docs/server/server-usage/storage-engines/innodb/innodb-buffer-pool#buffer-pool-changes" target="_blank" title="Buffer Pool Changes"&gt;fonte&lt;/a&gt;):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If innodb_buffer_pool_size_max is 0 or not specified, it defaults to the innodb_buffer_pool_size value.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ciò significa che devo riflettere in anticipo su quanto deve essere grande &lt;code&gt;innodb_buffer_pool_size_max&lt;/code&gt; e solo dopo posso correggere durante il funzionamento, se me ne sono dimenticato o ho valutato male.&lt;/p&gt;
&lt;p&gt;A mio parere, questo è un completo passo indietro dal punto di vista operativo. Probabilmente si tratta di nuovo di un&amp;rsquo;implementazione per alcune soluzioni cloud-only as a Service (Enterprise?).&lt;/p&gt;
&lt;p&gt;Il mio suggerimento è: o, come proposto in MDEV, 0 dovrebbe disattivare questa funzione e il comportamento dovrebbe rimanere come prima, oppure il valore predefinito dovrebbe essere impostato al 75% della dimensione della RAM, come fa &lt;code&gt;innodb_dedicated_server&lt;/code&gt; in MySQL.&lt;/p&gt;
&lt;p&gt;Mi sono permesso di aprire un bug qui: &lt;a href="https://jira.mariadb.org/browse/MDEV-38779" target="_blank"&gt;New InnoDB Buffer Pool autosize feature not so optimal implemented&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;FedericoR ha gentilmente consigliato il seguente link: &lt;a href="https://www.mail-archive.com/developers@lists.mariadb.org/msg00822.html" target="_blank" title="MariaDB developers mailing list"&gt;Issues with new buffer pool configuration in MariaDB Minors (10.11.12/13/14, 11.4.6/7/8, 11.8.2/3)&lt;/a&gt;. A quanto pare non sono l&amp;rsquo;unico a cui questa modifica ha dato fastidio&amp;hellip;&lt;/p&gt;
&lt;h2 id="come-funziona-postgresql"&gt;Come funziona PostgreSQL?&lt;/h2&gt;
&lt;p&gt;PostgreSQL al momento non è (ancora) in grado di modificare &lt;code&gt;shared_buffers&lt;/code&gt; in modo dinamico. Il valore predefinito è solitamente 128M. Qui vale la regola empirica, simile a MyISAM, del 25-40% della RAM. Tuttavia, la mancanza di questa funzione non è probabilmente così grave in PostgreSQL, poiché PostgreSQL fa molto affidamento sulla cache del filesystem, in modo simile a MyISAM.&lt;/p&gt;
&lt;p&gt;Fonte: &lt;a href="https://www.postgresql.org/docs/current/runtime-config-resource.html#RUNTIME-CONFIG-RESOURCE-MEMORY" target="_blank"&gt;Resource Consumption&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="commenti"&gt;Commenti&lt;/h2&gt;
&lt;p&gt;Vedi &lt;a href="https://www.fromdual.com/blog/mariadb-dynamically-configurable-buffer-pool-broken/#mariadb-dynamically-configurable-buffer-pool-broken/%23comment-1049"&gt;qui&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Questa pagina è stata tradotta utilizzando &lt;a href="https://www.deepl.com/it/translator" target="_blank"&gt;deepl.com&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Quanto spazio occupa NULL?</title><link>https://www.fromdual.com/it/blog/quanto-spazio-occupa-null/</link><pubDate>Sun, 08 Feb 2026 16:15:00 +0100</pubDate><author>oli.sennhauser@fromdual.com (Oli Sennhauser)</author><guid>https://www.fromdual.com/it/blog/quanto-spazio-occupa-null/</guid><description>&lt;p&gt;Durante l&amp;rsquo;ultima consulenza presso il cliente, questi mi è venuto incontro raggiante dicendomi che aveva seguito il mio consiglio e aveva modificato tutte le colonne Primary Key da &lt;code&gt;BIGINT&lt;/code&gt; (8 byte) a &lt;code&gt;INT&lt;/code&gt; (4 byte) e che questo aveva portato grandi vantaggi! Il suo database MySQL 8.4 era ora più piccolo di 750 Gbyte (da 5,5 Tbyte). Fantastico!&lt;/p&gt;
&lt;p&gt;E sì, so che questo contraddice le raccomandazioni di alcuni dei miei colleghi PostgreSQL (&lt;a href="https://www.crunchydata.com/blog/postgres-serials-should-be-bigint-and-how-to-migrate" target="_blank"&gt;qui&lt;/a&gt; e &lt;a href="https://www.cybertec-postgresql.com/en/uuid-serial-or-identity-columns-for-postgresql-auto-generated-primary-keys/#should-i-use-integerserial-or-bigintbigserial-for-my-auto-generated-primary-key" target="_blank"&gt;qui&lt;/a&gt;). Nel mondo MySQL si dà più importanza a queste cose (&lt;a href="https://dev.mysql.com/doc/refman/8.4/en/data-size.html" target="_blank"&gt;fonte&lt;/a&gt;):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Use the most efficient (smallest) data types possible. MySQL has many specialized types that save disk space and memory. For example, use the smaller integer types if possible to get smaller tables&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Inoltre, InnoDB funziona in modo leggermente diverso (tabella clusterizzata dell&amp;rsquo;indice e chiave primaria in tutte le chiavi secondarie) rispetto a PostgreSQL (tabella heap, indici con puntatore di riga (&lt;code&gt;ctid&lt;/code&gt;)).&lt;/p&gt;
&lt;p&gt;Ma in realtà non è questo il punto. Subito dopo mi ha chiesto se riempire le colonne di tipo &lt;code&gt;DOUBLE&lt;/code&gt; (8 byte, in PostgreSQL &lt;code&gt;DOUBLE PRECISION&lt;/code&gt;) con &lt;code&gt;NULL&lt;/code&gt; avrebbe portato a un risparmio di spazio o se fosse meglio eliminare subito le colonne. La mia prima risposta istintiva per &lt;code&gt;DOUBLE&lt;/code&gt; è stata: &lt;code&gt;NULL&lt;/code&gt; è utile, seguito da &lt;code&gt;OPTIMIZE TABLE&lt;/code&gt; (in PostgreSQL &lt;code&gt;VACUUM FULL&lt;/code&gt;). Il secondo pensiero è stato però: &lt;code&gt;DOUBLE&lt;/code&gt; è un tipo di dati fisso più lungo, questo vale anche con &lt;code&gt;NULL&lt;/code&gt; o solo per i tipi di dati con lunghezza variabile? La prudenza non è mai troppa! Prima di tutto, consulta il manuale&amp;hellip;&lt;/p&gt;
&lt;p&gt;E lì c&amp;rsquo;è scritto (&lt;a href="https://dev.mysql.com/doc/refman/8.4/en/data-size.html" target="_blank"&gt;fonte&lt;/a&gt;):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Declare columns to be NOT NULL if possible. It makes SQL operations faster, by enabling better use of indexes and eliminating overhead for testing whether each value is NULL. You also save some storage space, one bit per column. If you really need NULL values in your tables, use them. Just avoid the default setting that allows NULL values in every column.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;e (&lt;a href="https://dev.mysql.com/doc/refman/8.4/en/innodb-row-format.html" target="_blank"&gt;fonte&lt;/a&gt;):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The variable-length part of the record header contains a bit vector for indicating NULL columns. &amp;hellip; Columns that are NULL do not occupy space other than the bit in this vector. The variable-length part of the header also contains the lengths of variable-length columns. Each length takes one or two bytes, depending on the maximum length of the column. If all columns in the index are NOT NULL and have a fixed length, the record header has no variable-length part.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="prova-con-mariadbmysql"&gt;Prova con MariaDB/MySQL&lt;/h2&gt;
&lt;h3 id="configurazione-di-prova"&gt;Configurazione di prova&lt;/h3&gt;
&lt;p&gt;In qualche modo la descrizione mi sembra un po&amp;rsquo; troppo complicata. Forse un piccolo schizzo potrebbe aiutare? Allora facciamo una prova:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;SQL&amp;gt; -- DROP TABLE IF EXISTS tracking;

SQL&amp;gt; CREATE TABLE tracking (
 id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT
, d0 DOUBLE, d1 DOUBLE, d2 DOUBLE, d3 DOUBLE, d4 DOUBLE
, d5 DOUBLE, d6 DOUBLE, d7 DOUBLE, d8 DOUBLE, d9 DOUBLE
);

SQL&amp;gt; INSERT INTO tracking SELECT NULL, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0;
SQL&amp;gt; INSERT INTO tracking SELECT NULL, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 FROM tracking;
... bis 16 M rows
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Sia in MariaDB che in MySQL, la tabella ha una dimensione di circa 1,8 GB con 16 milioni di righe. Poiché queste informazioni sono indicate in modo molto impreciso in &lt;code&gt;INFORMATION_SCHEMA&lt;/code&gt;, controlliamo il file system:&lt;/p&gt;
&lt;p&gt;MariaDB 11.8:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;SQL&amp;gt; system ls -l tracking.ibd
-rw-rw---- 1 mysql mysql 1206 Feb 7 10:28 tracking.frm
-rw-rw---- 1 mysql mysql 1933574144 Feb 7 10:32 tracking.ibd
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;MySQL 8.4:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;SQL&amp;gt; system ls -l tracking.ibd
-rw-r----- 1 mysql mysql 1929379840 Feb 7 10:33 tracking.ibd
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="deframmentazione-della-tabella"&gt;Deframmentazione della tabella&lt;/h3&gt;
&lt;p&gt;Quindi “deframmentiamo” la tabella con il comando &lt;code&gt;OPTIMIZE TABLE&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;SQL&amp;gt; OPTIMIZE TABLE tracking;
+---------------+----------+----------+-------------------------------------------------------------------+
| Table | Op | Msg_type | Msg_text |
+---------------+----------+----------+-------------------------------------------------------------------+
| test.tracking | optimize | note | Table does not support optimize, doing recreate + analyze instead |
| test.tracking | optimize | status | OK |
+---------------+----------+----------+-------------------------------------------------------------------+
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Attenzione&lt;/strong&gt;: la tabella viene copiata una volta! Quindi è necessario il doppio dello spazio su disco per un breve periodo! Lo si può osservare chiaramente mentre il comando &lt;code&gt;OPTIMIZE TABLE&lt;/code&gt; è in esecuzione:&lt;/p&gt;
&lt;p&gt;MariaDB:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ watch -d -n 1 &amp;#39;ls -l trac* \#*&amp;#39;
-rw-rw---- 1 mysql mysql 1206 Feb 7 10:39 &amp;#39;#sql-alter-d57-8c.frm&amp;#39;
-rw-rw---- 1 mysql mysql 968884224 Feb 7 10:39 &amp;#39;#sql-alter-d57-8c.ibd&amp;#39;
-rw-rw---- 1 mysql mysql 1206 Feb 7 10:28 tracking.frm
-rw-rw---- 1 mysql mysql 1933574144 Feb 7 10:32 tracking.ibd
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;MySQL:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ watch -d -n 1 &amp;#39;ls -l trac* \#*&amp;#39;
-rw-r----- 1 mysql mysql 369098752 Feb 7 10:40 #sql-ib1594-4164062678.ibd
-rw-r----- 1 mysql mysql 1929379840 Feb 7 10:33 tracking.ibd
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Il risultato è però sorprendente! In MariaDB la tabella è rimasta più o meno della stessa dimensione:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;-rw-rw---- 1 mysql mysql 1206 Feb 7 10:39 tracking.frm
-rw-rw---- 1 mysql mysql 1912602624 Feb 7 10:39 tracking.ibd
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In MySQL, invece, dopo la “deframmentazione” la tabella è addirittura cresciuta di circa il 14%:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;-rw-r----- 1 mysql mysql 2197815296 Feb 7 10:41 tracking.ibd
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Se eseguiamo nuovamente il comando &lt;code&gt;OPTIMIZE TABLE&lt;/code&gt;, la dimensione rimane costante sia in MariaDB che in MySQL:&lt;/p&gt;
&lt;p&gt;MariaDB:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;-rw-rw---- 1 mysql mysql 1206 Feb 7 10:46 tracking.frm
-rw-rw---- 1 mysql mysql 1912602624 Feb 7 10:48 tracking.ibd
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;MySQL:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;-rw-r----- 1 mysql mysql 2197815296 Feb 7 10:48 tracking.ibd
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="prova-1-riempire-con-null"&gt;Prova 1: riempire con &lt;code&gt;NULL&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Ora sovrascriviamo i valori con NULL:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;SQL&amp;gt; UPDATE tracking
SET d0 = NULL, d1 = NULL, d2 = NULL, d3 = NULL, d4 = NULL
 , d5 = NULL, d6 = NULL, d7 = NULL, d8 = NULL, d9 = NULL
;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Dopo questo passaggio, le dimensioni dei file sono addirittura aumentate leggermente:&lt;/p&gt;
&lt;p&gt;MariaDB (+1.3%):&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;-rw-rw---- 1 mysql mysql 1206 Feb 7 10:49 tracking.frm
-rw-rw---- 1 mysql mysql 1937768448 Feb 7 11:04 tracking.ibd
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;MySQL (+0.2%):&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;-rw-r----- 1 mysql mysql 2202009600 Feb 7 11:04 tracking.ibd
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Successivamente, deframmentiamo nuovamente la tabella con il comando &lt;code&gt;OPTIMIZE TABLE&lt;/code&gt;. Come previsto, le tabelle si riducono.&lt;/p&gt;
&lt;p&gt;MariaDB (al 23%):&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;-rw-rw---- 1 mysql mysql 1206 Feb 7 11:09 tracking.frm
-rw-rw---- 1 mysql mysql 448790528 Feb 7 11:10 tracking.ibd
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;MySQL (al 24%):&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;-rw-r----- 1 mysql mysql 520093696 Feb 7 11:10 tracking.ibd
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Un nuovo &lt;code&gt;OPTIMIZE TABLE&lt;/code&gt; non apporta più alcuna modifica alla dimensione del file&amp;hellip;&lt;/p&gt;
&lt;h3 id="prova-2-cancellazione-delle-colonne"&gt;Prova 2: cancellazione delle colonne&lt;/h3&gt;
&lt;p&gt;Ora proviamo di nuovo con il comando &lt;code&gt;DROP COLUMN&lt;/code&gt;. La situazione di partenza è la stessa descritta sopra:&lt;/p&gt;
&lt;p&gt;MariaDB:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;-rw-rw---- 1 mysql mysql 1206 Feb 7 11:15 tracking.frm
-rw-rw---- 1 mysql mysql 1933574144 Feb 7 11:18 tracking.ibd
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;MySQL:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;-rw-r----- 1 mysql mysql 1929379840 Feb 7 11:19 tracking.ibd
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Dopo il comando &lt;code&gt;OPTIMIZE TABLE&lt;/code&gt;, i valori sono simili a quelli della prima prova:&lt;/p&gt;
&lt;p&gt;MariaDB:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;-rw-rw---- 1 mysql mysql 1206 Feb 7 11:20 tracking.frm
-rw-rw---- 1 mysql mysql 1912602624 Feb 7 11:21 tracking.ibd
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;MySQL:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;-rw-r----- 1 mysql mysql 2197815296 Feb 7 11:21 tracking.ibd
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Un nuovo &lt;code&gt;OPTIMIZE TABLE&lt;/code&gt; non apporta ulteriori modifiche, come sopra:&lt;/p&gt;
&lt;p&gt;MariaDB:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;-rw-rw---- 1 mysql mysql 1206 Feb 7 11:22 tracking.frm
-rw-rw---- 1 mysql mysql 1912602624 Feb 7 11:23 tracking.ibd
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;MySQL:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;-rw-r----- 1 mysql mysql 2197815296 Feb 7 11:24 tracking.ibd
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;E ora il secondo tentativo vero e proprio con l&amp;rsquo;eliminazione delle colonne:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;SQL&amp;gt; ALTER TABLE tracking
 DROP COLUMN d0, DROP COLUMN d1, DROP COLUMN d2, DROP COLUMN d3, DROP COLUMN d4
, DROP COLUMN d5, DROP COLUMN d6, DROP COLUMN d7, DROP COLUMN d8, DROP COLUMN d9
;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Per prima cosa notiamo che il comando è &lt;code&gt;INSTANTANEOUS&lt;/code&gt;, in quanto non apporta alcuna modifica ai dati, ma solo ai metadati. Da un lato questo è positivo, poiché l&amp;rsquo;influenza sull&amp;rsquo;applicazione è minima. D&amp;rsquo;altra parte, ciò significa anche che non si ottiene alcun risparmio di spazio.&lt;/p&gt;
&lt;p&gt;Quindi affrontiamo nuovamente il problema con il comando &lt;code&gt;OPTIMIZE TABLE&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;MariaDB (al 93%):&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;-rw-rw---- 1 mysql mysql 925 Feb 7 11:28 tracking.frm
-rw-rw---- 1 mysql mysql 415236096 Feb 7 11:29 tracking.ibd
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;MySQL (al 92%):&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;-rw-r----- 1 mysql mysql 478150656 Feb 7 11:28 tracking.ibd
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="conclusione"&gt;Conclusione&lt;/h3&gt;
&lt;p&gt;Sia la sostituzione dei valori delle colonne con &lt;code&gt;NULL&lt;/code&gt; che la cancellazione delle colonne consentono un notevole risparmio di spazio. La cancellazione delle colonne comporta un risparmio maggiore di circa il 7% rispetto alla sostituzione con &lt;code&gt;NULL&lt;/code&gt;. Se possibile dal punto di vista applicativo, è quindi consigliabile cancellare le colonne non più necessarie; se ciò non è possibile, sostituirle almeno con &lt;code&gt;NULL&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="prova-con-postgresql"&gt;Prova con PostgreSQL&lt;/h2&gt;
&lt;p&gt;Ora esaminiamo il tutto con PostgreSQL.&lt;/p&gt;
&lt;h3 id="configurazione-della-prova"&gt;Configurazione della prova&lt;/h3&gt;
&lt;p&gt;La configurazione della prova è analoga a quella di MariaDB/MySQL:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;postgres=# -- DROP TABLE IF EXISTS tracking;

postgres=# CREATE TABLE tracking (
 id SERIAL PRIMARY KEY
, d0 DOUBLE PRECISION, d1 DOUBLE PRECISION, d2 DOUBLE PRECISION, d3 DOUBLE PRECISION, d4 DOUBLE PRECISION
, d5 DOUBLE PRECISION, d6 DOUBLE PRECISION, d7 DOUBLE PRECISION, d8 DOUBLE PRECISION, d9 DOUBLE PRECISION
);

postgres=# \timing

postgres=# INSERT INTO tracking (d0, d1, d2, d3, d4, d5, d6, d7, d8, d9)
 SELECT 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0;
postgres=# INSERT INTO tracking (d0, d1, d2, d3, d4, d5, d6, d7, d8, d9)
 SELECT 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 FROM tracking;
... bis 16 M rows
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Per prima cosa vogliamo sapere quanto è diventata grande la tabella. PostgreSQL sembra conoscere queste informazioni con estrema precisione:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;postgres=# SELECT pg_relation_size(&amp;#39;tracking&amp;#39;) AS tab_siz
 , pg_size_pretty(pg_relation_size(&amp;#39;tracking&amp;#39;)) AS tab_siz_prtty
 , pg_indexes_size(&amp;#39;tracking&amp;#39;) AS idx_siz
 , pg_size_pretty(pg_indexes_size(&amp;#39;tracking&amp;#39;)) AS idx_siz_prtty
 , pg_relation_size(&amp;#39;tracking&amp;#39;) + pg_indexes_size(&amp;#39;tracking&amp;#39;) AS tab_and_idx_siz
 , pg_size_pretty(pg_relation_size(&amp;#39;tracking&amp;#39;) + pg_indexes_size(&amp;#39;tracking&amp;#39;)) AS tab_and_idx_siz_prtty
 , pg_total_relation_size(&amp;#39;tracking&amp;#39;) AS tot_rel_siz
 , pg_size_pretty(pg_total_relation_size(&amp;#39;tracking&amp;#39;)) AS tot_rel_siz_prtty
;
 tab_siz | tab_siz_prtty | idx_siz | idx_siz_prtty | tab_and_idx_siz | tab_and_idx_siz_prtty | tot_rel_siz | tot_rel_siz_prtty
------------+---------------+-----------+---------------+-----------------+-----------------------+-------------+-------------------
 1963417600 | 1872 MB | 376856576 | 359 MB | 2340274176 | 2232 MB | 2340798464 | 2232 MB
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Poi vogliamo sapere dove si trovano questi file nel filesystem:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;postgres=# SELECT oid AS db_oid FROM pg_database WHERE datname = current_database();
 db_oid
--------
 5

postgres=# SELECT oid AS table_oid, relname, relnamespace, relfilenode
 FROM pg_class WHERE relname = &amp;#39;tracking&amp;#39;;
 table_oid | relname | relnamespace | relfilenode
-----------+----------+--------------+-------------
 40965 | tracking | 2200 | 40965

postgres=# SELECT i.indexrelid::regclass as index_name, i.indexrelid as index_oid
 FROM pg_index i
 JOIN pg_class c ON i.indrelid = c.oid
 WHERE c.relname = &amp;#39;tracking&amp;#39;;
 index_name | index_oid
---------------+-----------
 tracking_pkey | 40970

postgres=# SELECT pg_relation_filepath(&amp;#39;tracking&amp;#39;);
 pg_relation_filepath
----------------------
 base/5/40965
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Dimensioni della tabella e dell&amp;rsquo;indice nel filesystem:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ ls -ltr 40965* 40970*
-rw------- 1 mysql mysql 40960 Feb 7 18:33 40965_vm
-rw------- 1 mysql mysql 499712 Feb 7 18:33 40965_fsm
-rw------- 1 mysql mysql 889675776 Feb 7 18:34 40965.1
-rw------- 1 mysql mysql 1073741824 Feb 7 18:34 40965
-rw------- 1 mysql mysql 376856576 Feb 7 18:35 40970
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;*_fsm&lt;/code&gt; significa “free space map”&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*_vm&lt;/code&gt; significa “visibility map”&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*.1&lt;/code&gt; significa 2° segmento dell&amp;rsquo;oggetto (tabella o indice)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;PostgreSQL sembra lavorare di default con segmenti di 1 GB e, a differenza di MariaDB/MySQL (&lt;code&gt;INFORMATION_SCHEMA&lt;/code&gt;), sembra conoscere con estrema precisione le dimensioni dei propri file. La discrepanza di cui sopra (tra &lt;code&gt;tot_rel_siz&lt;/code&gt; e &lt;code&gt;tab_and_idx_siz&lt;/code&gt;) può essere spiegata dai file &lt;code&gt;fsm&lt;/code&gt; e &lt;code&gt;vm&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;equivalente PostgreSQL di MariaDB/MySQL &lt;code&gt;OPTIMIZE TABLE&lt;/code&gt; è il comando &lt;code&gt;VACUUM FULL&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;postgres=# VACUUM FULL tracking;

$ ls -ltr
-rw------- 1 mysql mysql 40960 Feb 7 18:39 40965_vm
-rw------- 1 mysql mysql 499712 Feb 7 18:39 40965_fsm
-rw------- 1 mysql mysql 1073741824 Feb 7 18:39 40965
-rw------- 1 mysql mysql 889675776 Feb 7 18:39 40965.1
-rw------- 1 mysql mysql 1073741824 Feb 7 18:39 40972
-rw------- 1 mysql mysql 889675776 Feb 7 18:39 40972.1
-rw------- 1 mysql mysql 0 Feb 7 18:39 40975

...

-rw------- 1 mysql mysql 49152 Feb 7 18:39 2704
-rw------- 1 mysql mysql 32768 Feb 7 18:39 2703
-rw------- 1 mysql mysql 32768 Feb 7 18:39 2696
-rw------- 1 mysql mysql 65536 Feb 7 18:39 2674
-rw------- 1 mysql mysql 81920 Feb 7 18:39 2673
-rw------- 1 mysql mysql 98304 Feb 7 18:39 2659
-rw------- 1 mysql mysql 139264 Feb 7 18:39 2658
-rw------- 1 mysql mysql 24576 Feb 7 18:39 2619_fsm
-rw------- 1 mysql mysql 163840 Feb 7 18:39 2619
-rw------- 1 mysql mysql 106496 Feb 7 18:39 2608
-rw------- 1 mysql mysql 491520 Feb 7 18:39 1249
-rw------- 1 mysql mysql 122880 Feb 7 18:39 1247
-rw------- 1 mysql mysql 32768 Feb 7 18:39 2662
-rw------- 1 mysql mysql 114688 Feb 7 18:39 1259
-rw------- 1 mysql mysql 16384 Feb 7 18:39 3455
-rw------- 1 mysql mysql 49152 Feb 7 18:39 2663
-rw------- 1 mysql mysql 1073741824 Feb 7 18:39 40972
-rw------- 1 mysql mysql 889675776 Feb 7 18:39 40972.1
-rw------- 1 mysql mysql 376864768 Feb 7 18:39 40975
-rw------- 1 mysql mysql 0 Feb 7 18:39 40965
-rw------- 1 mysql mysql 0 Feb 7 18:39 40970
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;La prima cosa che si nota è che PostgreSQL crea molti file e il file “free space map” è scomparso. A differenza di MariaDB/MySQL, i segmenti della tabella sono rimasti della stessa dimensione. Si nota inoltre che la vecchia tabella è “scomparsa” (40965, 40970) e ne è stata creata una nuova (40972 e 40975). Il comando &lt;code&gt;VACUUM FULL&lt;/code&gt; in PostgreSQL crea quindi, come in MariaDB/MySQL, una copia dei dati.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;postgres=# SELECT pg_relation_size(&amp;#39;tracking&amp;#39;) AS tab_siz
 , pg_size_pretty(pg_relation_size(&amp;#39;tracking&amp;#39;)) AS tab_siz_prtty
 , pg_indexes_size(&amp;#39;tracking&amp;#39;) AS idx_siz
 , pg_size_pretty(pg_indexes_size(&amp;#39;tracking&amp;#39;)) AS idx_siz_prtty
 , pg_relation_size(&amp;#39;tracking&amp;#39;) + pg_indexes_size(&amp;#39;tracking&amp;#39;) AS tab_and_idx_siz
 , pg_size_pretty(pg_relation_size(&amp;#39;tracking&amp;#39;) + pg_indexes_size(&amp;#39;tracking&amp;#39;)) AS tab_and_idx_siz_prtty
 , pg_total_relation_size(&amp;#39;tracking&amp;#39;) AS tot_rel_siz
 , pg_size_pretty(pg_total_relation_size(&amp;#39;tracking&amp;#39;)) AS tot_rel_siz_prtty
;
 tab_siz | tab_siz_prtty | idx_siz | idx_siz_prtty | tab_and_idx_siz | tab_and_idx_siz_prtty | tot_rel_siz | tot_rel_siz_prtty
------------+---------------+-----------+---------------+-----------------+-----------------------+-------------+-------------------
 1963417600 | 1872 MB | 376864768 | 359 MB | 2340282368 | 2232 MB | 2340282368 | 2232 MB
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Per capire quali altri file/oggetti sono stati modificati, è utile la seguente query:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;postgres=# SELECT c.oid, c.relname, ns.nspname
FROM pg_class AS c
JOIN pg_namespace AS ns ON ns.oid = c.relnamespace
WHERE c.oid IN (2704, 2703, 2696, 2674, 2673, 2659, 2658, 2619, 2608, 1249, 1247, 40972, 2662, 1259, 3455, 2663, 40975, 40965, 40970)
;
 oid | relname | nspname
-------+-----------------------------------+------------
 40965 | tracking | public
 40970 | tracking_pkey | public
 2619 | pg_statistic | pg_catalog
 1247 | pg_type | pg_catalog
 2703 | pg_type_oid_index | pg_catalog
 2704 | pg_type_typname_nsp_index | pg_catalog
 2658 | pg_attribute_relid_attnam_index | pg_catalog
 2659 | pg_attribute_relid_attnum_index | pg_catalog
 2662 | pg_class_oid_index | pg_catalog
 2663 | pg_class_relname_nsp_index | pg_catalog
 3455 | pg_class_tblspc_relfilenode_index | pg_catalog
 2696 | pg_statistic_relid_att_inh_index | pg_catalog
 2673 | pg_depend_depender_index | pg_catalog
 2674 | pg_depend_reference_index | pg_catalog
 1249 | pg_attribute | pg_catalog
 1259 | pg_class | pg_catalog
 2608 | pg_depend | pg_catalog

postgres=# SELECT i.indexrelid::regclass as index_name, i.indexrelid as index_oid, ns.nspname
 FROM pg_index i
 JOIN pg_class c ON i.indrelid = c.oid
 JOIN pg_namespace AS ns ON ns.oid = c.relnamespace
 WHERE c.oid IN (2704, 2703, 2696, 2674, 2673, 2659, 2658, 2619, 2608, 1249, 1247, 40972, 2662, 1259, 3455, 2663, 40975, 40965, 40970)
;
 index_name | index_oid | nspname
-----------------------------------+-----------+------------
 pg_type_typname_nsp_index | 2704 | pg_catalog
 pg_attribute_relid_attnam_index | 2658 | pg_catalog
 tracking_pkey | 40970 | public
 pg_class_relname_nsp_index | 2663 | pg_catalog
 pg_class_tblspc_relfilenode_index | 3455 | pg_catalog
 pg_type_oid_index | 2703 | pg_catalog
 pg_attribute_relid_attnum_index | 2659 | pg_catalog
 pg_statistic_relid_att_inh_index | 2696 | pg_catalog
 pg_depend_depender_index | 2673 | pg_catalog
 pg_depend_reference_index | 2674 | pg_catalog
 pg_class_oid_index | 2662 | pg_catalog
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="tentativo-1-riempire-con-null"&gt;Tentativo 1: riempire con &lt;code&gt;NULL&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Quindi sovrascriviamo anche i valori delle colonne in PostgreSQL con &lt;code&gt;NULL&lt;/code&gt;. Da questo punto in poi non ci occuperemo più del file system, poiché PostgreSQL sembra conoscere con precisione le dimensioni dei file, come abbiamo visto sopra:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;postgres=# UPDATE tracking
SET d0 = NULL, d1 = NULL, d2 = NULL, d3 = NULL, d4 = NULL
 , d5 = NULL, d6 = NULL, d7 = NULL, d8 = NULL, d9 = NULL
;

postgres=# SELECT pg_relation_size(&amp;#39;tracking&amp;#39;) AS tab_siz
 , pg_size_pretty(pg_relation_size(&amp;#39;tracking&amp;#39;)) AS tab_siz_prtty
 , pg_indexes_size(&amp;#39;tracking&amp;#39;) AS idx_siz
 , pg_size_pretty(pg_indexes_size(&amp;#39;tracking&amp;#39;)) AS idx_siz_prtty
 , pg_relation_size(&amp;#39;tracking&amp;#39;) + pg_indexes_size(&amp;#39;tracking&amp;#39;) AS tab_and_idx_siz
 , pg_size_pretty(pg_relation_size(&amp;#39;tracking&amp;#39;) + pg_indexes_size(&amp;#39;tracking&amp;#39;)) AS tab_and_idx_siz_prtty
 , pg_total_relation_size(&amp;#39;tracking&amp;#39;) AS tot_rel_siz
 , pg_size_pretty(pg_total_relation_size(&amp;#39;tracking&amp;#39;)) AS tot_rel_siz_prtty
;
 tab_siz | tab_siz_prtty | idx_siz | idx_siz_prtty | tab_and_idx_siz | tab_and_idx_siz_prtty | tot_rel_siz | tot_rel_siz_prtty
------------+---------------+-----------+---------------+-----------------+-----------------------+-------------+-------------------
 2695716864 | 2571 MB | 753696768 | 719 MB | 3449413632 | 3290 MB | 3450101760 | 3290 MB
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Qui vediamo che i segmenti della tabella crescono in modo massiccio (+37%), ciò che nella terminologia PostgreSQL viene definito “bloat”. L&amp;rsquo;implementazione MVCC di PostgreSQL salva sia la versione vecchia che quella nuova della riga “in-place”, ovvero direttamente nella tabella, a differenza di MariaDB/MySQL che salva la versione vecchia nello spazio UNDO e la nuova riga “in-place”. Anche il file dell&amp;rsquo;indice aumenta in modo significativo (+100%). Dobbiamo ancora approfondire il motivo di questo fenomeno. Inoltre, viene nuovamente creata una “free space map” (differenza tra &lt;code&gt;tot_rel_siz&lt;/code&gt; e &lt;code&gt;tab_and_idx_siz&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Un successivo &lt;code&gt;VACUUM FULL&lt;/code&gt; riduce la tabella (al 28%) e l&amp;rsquo;indice (al 50%) rispetto alle dimensioni precedenti:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;postgres=# VACUUM FULL tracking;

postgres=# SELECT pg_relation_size(&amp;#39;tracking&amp;#39;) AS tab_siz
 , pg_size_pretty(pg_relation_size(&amp;#39;tracking&amp;#39;)) AS tab_siz_prtty
 , pg_indexes_size(&amp;#39;tracking&amp;#39;) AS idx_siz
 , pg_size_pretty(pg_indexes_size(&amp;#39;tracking&amp;#39;)) AS idx_siz_prtty
 , pg_relation_size(&amp;#39;tracking&amp;#39;) + pg_indexes_size(&amp;#39;tracking&amp;#39;) AS tab_and_idx_siz
 , pg_size_pretty(pg_relation_size(&amp;#39;tracking&amp;#39;) + pg_indexes_size(&amp;#39;tracking&amp;#39;)) AS tab_and_idx_siz_prtty
 , pg_total_relation_size(&amp;#39;tracking&amp;#39;) AS tot_rel_siz
 , pg_size_pretty(pg_total_relation_size(&amp;#39;tracking&amp;#39;)) AS tot_rel_siz_prtty
;
 tab_siz | tab_siz_prtty | idx_siz | idx_siz_prtty | tab_and_idx_siz | tab_and_idx_siz_prtty | tot_rel_siz | tot_rel_siz_prtty
-----------+---------------+-----------+---------------+-----------------+-----------------------+-------------+-------------------
 742916096 | 709 MB | 376864768 | 359 MB | 1119780864 | 1068 MB | 1119780864 | 1068 MB
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;e anche rispetto alle dimensioni originali, la tabella (al 38%) e l&amp;rsquo;indice (al 100%) diventano nuovamente più piccoli. Il motivo per cui l&amp;rsquo;indice è rimasto della stessa dimensione e solo la tabella si è ridotta deve ancora essere studiato&amp;hellip;&lt;/p&gt;
&lt;h3 id="tentativo-2-cancellazione-delle-colonne"&gt;Tentativo 2: cancellazione delle colonne&lt;/h3&gt;
&lt;p&gt;Successivamente, le colonne vengono cancellate con &lt;code&gt;DROP COLUMN&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;postgres=# ALTER TABLE tracking
 DROP COLUMN d0, DROP COLUMN d1, DROP COLUMN d2, DROP COLUMN d3, DROP COLUMN d4
, DROP COLUMN d5, DROP COLUMN d6, DROP COLUMN d7, DROP COLUMN d8, DROP COLUMN d9
;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Poiché la risposta è arrivata immediatamente, si può presumere che anche questa operazione sia istantanea. Purtroppo non ho trovato nulla al riguardo nella documentazione di PostgreSQL.&lt;/p&gt;
&lt;p&gt;Le dimensioni non sono cambiate in modo significativo, cosa che in realtà è prevedibile con un&amp;rsquo;operazione istantanea. Tuttavia, il fatto che le dimensioni non siano cambiate dopo il comando &lt;code&gt;VACUUM FULL&lt;/code&gt; è stato un po&amp;rsquo; sorprendente:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt; tab_siz | tab_siz_prtty | idx_siz | idx_siz_prtty | tab_and_idx_siz | tab_and_idx_siz_prtty | tot_rel_siz | tot_rel_siz_prtty
-----------+---------------+-----------+---------------+-----------------+-----------------------+-------------+-------------------
 742916096 | 709 MB | 376864768 | 359 MB | 1119780864 | 1068 MB | 1120010240 | 1068 MB

postgres=# VACUUM FULL tracking;

 tab_siz | tab_siz_prtty | idx_siz | idx_siz_prtty | tab_and_idx_siz | tab_and_idx_siz_prtty | tot_rel_siz | tot_rel_siz_prtty
-----------+---------------+-----------+---------------+-----------------+-----------------------+-------------+-------------------
 742916096 | 709 MB | 376864768 | 359 MB | 1119780864 | 1068 MB | 1119780864 | 1068 MB
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="osservazioni"&gt;Osservazioni&lt;/h2&gt;
&lt;p&gt;Il locking in PostgreSQL funziona come segue:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;VACUUM&lt;/code&gt; Sono possibili comandi DML concorrenti simili al comando &lt;code&gt;OPTIMIZE TABLE&lt;/code&gt; di MariaDB/MySQL. Il risultato, tuttavia, non è esattamente lo stesso.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;VACUUM FULL&lt;/code&gt; provoca un blocco &lt;code&gt;ACCESS EXCLUSIVE&lt;/code&gt;. Simile a MariaDB/MySQL 5.5 e versioni precedenti con il comando &lt;code&gt;OPTIMIZE TABLE&lt;/code&gt;. I comandi DML e &lt;code&gt;SELECT&lt;/code&gt; NON sono consentiti.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="fonti"&gt;Fonti&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://neon.com/postgresql/postgresql-administration/postgresql-database-indexes-table-size" target="_blank"&gt;How to Get Sizes of Database Objects in PostgreSQL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/current/storage-file-layout.html" target="_blank"&gt;Database File Layout&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/current/functions-admin.html" target="_blank"&gt;System Administration Functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/current/sql-cluster.html" target="_blank"&gt;CLUSTER&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/current/sql-vacuum.html" target="_blank"&gt;VACUUM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/current/explicit-locking.html" target="_blank"&gt;Explicit Locking&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="prove-aggiuntive"&gt;Prove aggiuntive&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Invece di &lt;code&gt;0.0&lt;/code&gt;, nelle colonne &lt;code&gt;d0&lt;/code&gt; - &lt;code&gt;d9&lt;/code&gt; è stato inserito &lt;code&gt;NULL&lt;/code&gt;. La tabella è rimasta piccola (&lt;code&gt;tot_rel_siz_prtty = 1068 MB&lt;/code&gt;). Vale quindi la pena salvare &lt;code&gt;NULL&lt;/code&gt; invece di valori fittizi anche in PostgreSQL.&lt;/li&gt;
&lt;li&gt;Le colonne &lt;code&gt;d0&lt;/code&gt; - &lt;code&gt;d9&lt;/code&gt; sono state create con &lt;code&gt;DOUBLE PRECISION NOT NULL&lt;/code&gt; e riempite con i valori &lt;code&gt;0.0&lt;/code&gt;. Nessun effetto: la tabella è rimasta grande (&lt;code&gt;tot_rel_siz_prtty = 2232 MB&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Questa pagina è stata tradotta utilizzando &lt;a href="https://www.deepl.com/it/translator" target="_blank"&gt;deepl.com&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Qualcuno sta cancellando i miei segmenti di memoria condivisa!</title><link>https://www.fromdual.com/it/blog/segmenti-di-memoria-condivisa-postgresql-eliminati/</link><pubDate>Sun, 08 Feb 2026 06:27:00 +0100</pubDate><author>oli.sennhauser@fromdual.com (Oli Sennhauser)</author><guid>https://www.fromdual.com/it/blog/segmenti-di-memoria-condivisa-postgresql-eliminati/</guid><description>&lt;p&gt;Quando lavoriamo con PostgreSQL su &lt;a href="https://www.fromdual.com/myenv/"&gt;myEnv&lt;/a&gt;, riceviamo regolarmente errori relativi al segmento di memoria condivisa. Esempio:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;psql: error: connection to server on socket &amp;#34;/tmp/.s.PGSQL.5433&amp;#34; failed:
FATAL: could not open shared memory segment &amp;#34;/PostgreSQL.4220847662&amp;#34;:
No such file or directory
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;oppure vediamo messaggi simili nel log degli errori di PostgreSQL:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ERROR: could not open shared memory segment &amp;#34;/PostgreSQL.4220847662&amp;#34;:
No such file or directory
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Essendo un amministratore MariaDB/MySQL, non ho molta familiarità con i problemi relativi alla memoria condivisa (MariaDB/MySQL non funziona con la memoria condivisa). Fortunatamente, una ricerca su Internet ci ha portato su una pista (&lt;a href="https://www.postgresql.org/message-id/56A52018.1030001%40gmx.net" target="_blank" title="Re: systemd deletes shared memory segment in /dev/shm/Postgresql.NNNNNN"&gt;fonte&lt;/a&gt;). Qui si legge:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The documentation of systemd states that this only happens for
non-system users. Can you check whether your &amp;ldquo;postgres&amp;rdquo; user (or
whatever you are using) is a system user?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="utente-di-sistema-linux"&gt;Utente di sistema Linux&lt;/h2&gt;
&lt;p&gt;Per prima cosa ho dovuto capire cosa fosse un utente di sistema in Linux. Ho trovato una risposta qui: &lt;a href="https://unix.stackexchange.com/questions/80277/whats-the-difference-between-a-normal-user-and-a-system-user" target="_blank"&gt;What&amp;rsquo;s the difference between a normal user and a system user?&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;That is not a technical difference but an organizational decision. E.g. it makes sense to show normal users in a login dialog (so that you can click them instead of having to type the user name) but it wouldn&amp;rsquo;t to show system accounts (the UIDs under which daemons and other automatic processes run) there.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Lo standard LSB dice: &lt;a href="https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/uidrange.html" target="_blank"&gt;User ID Ranges&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The system User IDs from 0 to 99 should be statically allocated by the system, and shall not be created by applications.&lt;br&gt;
The system User IDs from 100 to 499 should be reserved for dynamic allocation by system administrators and post install scripts using useradd.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Sul mio sistema Ubuntu appare come segue:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ grep SYS_ /etc/login.defs
#SYS_UID_MIN 100
#SYS_UID_MAX 999
#SYS_GID_MIN 100
#SYS_GID_MAX 999
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Per l&amp;rsquo;utente PostgreSQL sarebbe corretto:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ id postgres
uid=130(postgres) gid=142(postgres) groups=142(postgres),116(ssl-cert)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ma poiché l&amp;rsquo;istanza PostgreSQL interessata funziona sotto il nostro &lt;a href="https://www.fromdual.com/myenv/"&gt;myEnv&lt;/a&gt;, è diverso:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ id dba
uid=1001(dba) gid=1001(dba) groups=1001(dba)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ora abbiamo due possibilità:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Modifichiamo &lt;code&gt;SYS_UID_MAX&lt;/code&gt; e &lt;code&gt;SYS_GID_MAX&lt;/code&gt; su 1001 (variante semplice).&lt;/li&gt;
&lt;li&gt;Oppure modifichiamo l&amp;rsquo;&lt;code&gt;UID&lt;/code&gt; e il &lt;code&gt;GID&lt;/code&gt; del nostro utente &lt;code&gt;dba&lt;/code&gt; a meno di 1000.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="variante-semplice-modifica-di-sys_uid_max-e-sys_gid_max-a-1001"&gt;Variante semplice: modifica di &lt;code&gt;SYS_UID_MAX&lt;/code&gt; e &lt;code&gt;SYS_GID_MAX&lt;/code&gt; a 1001&lt;/h2&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# /etc/login.defs
SYS_UID_MAX 1001
SYS_GID_MAX 1001
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Per sicurezza, la macchina è stata riavviata. Tuttavia, ciò non è servito: dopo poco tempo si verificano nuovamente gli stessi errori.&lt;/p&gt;
&lt;h2 id="variante-più-complessa-modifica-delluid-da-1001-a-990"&gt;Variante più complessa: modifica dell&amp;rsquo;&lt;code&gt;UID&lt;/code&gt; da 1001 a 990&lt;/h2&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ cat /etc/passwd
...
polkitd:x:997:997:User for polkitd:/:/usr/sbin/nologin
systemd-coredump:x:998:998:systemd Core Dumper:/:/usr/sbin/nologin
tomcat:x:999:999:Apache Tomcat:/:/sbin/nologin
oli:x:1000:1000:Oli Sennhauser,,,:/home/oli:/bin/bash
dba:x:1001:1001:DBA user:/home/dba:/bin/bash
...

$ id dba
uid=1001(dba) gid=1001(dba) groups=1001(dba)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A tal fine, tutti i processi di questo utente devono essere arrestati!&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ usermod --uid 990 dba
$ groupmod --gid 990 dba

$ find / -user 1001 -exec chown --no-dereference dba {} \;
$ find / -group 1001 -exec chgrp --no-dereference dba {} \;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Il problema sembra essere risolto!&lt;/p&gt;
&lt;h2 id="informazioni-aggiuntive"&gt;Informazioni aggiuntive&lt;/h2&gt;
&lt;p&gt;Una fonte citata in precedenza consigliava di impostare i seguenti parametri in &lt;code&gt;systemd-logind&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# /etc/systemd/system/systemd-logind.service.d/override.conf
RemoveIPC=no
RuntimeDirectorySize=1%
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Tuttavia, questa misura non è più necessaria, poiché il sistema funziona anche senza questa modifica.&lt;/p&gt;
&lt;p&gt;Questa pagina è stata tradotta utilizzando &lt;a href="https://www.deepl.com/it/translator" target="_blank"&gt;deepl.com&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Caricare file CSV nel database</title><link>https://www.fromdual.com/it/blog/caricare-file-csv-nel-database/</link><pubDate>Fri, 06 Feb 2026 18:04:00 +0100</pubDate><author>oli.sennhauser@fromdual.com (Oli Sennhauser)</author><guid>https://www.fromdual.com/it/blog/caricare-file-csv-nel-database/</guid><description>&lt;p&gt;Recentemente, per un piccolo gioco personale, ho voluto rappresentare su una mappa i luoghi di residenza dei membri della mia associazione (&lt;a href="https://www.shinguz.ch/computer/gis/igoc-mitglieder/" target="_blank"&gt;IGOC Mitglieder&lt;/a&gt;). Conoscevo gli indirizzi dei membri dell&amp;rsquo;associazione, ma non le coordinate dei loro luoghi di residenza.&lt;/p&gt;
&lt;p&gt;Così mi sono messo alla ricerca delle coordinate e le ho trovate presso l&amp;rsquo;Ufficio federale di topografia (&lt;a href="https://www.swisstopo.admin.ch/it" target="_blank"&gt;swisstopo&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;I dati sono disponibili in formato CSV. Maggiori dettagli qui: &lt;a href="https://www.shinguz.ch/computer/gis/schweizer-ortschafts-koordinaten/" target="_blank"&gt;Coordinate delle località svizzere&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Come si caricano questi dati in un database?&lt;/p&gt;
&lt;h2 id="caricamento-dei-dati-con-mariadbmysql"&gt;Caricamento dei dati con MariaDB/MySQL&lt;/h2&gt;
&lt;p&gt;MariaDB e MySQL dispongono del comando &lt;code&gt;LOAD DATA INFILE&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;SQL&amp;gt; DROP TABLE IF EXISTS wgs84;

SQL&amp;gt; -- SET GLOBAL local_infile = ON; -- Only needed with MySQL

SQL&amp;gt; CREATE TABLE wgs84 (
 ortschaftsname VARCHAR(32)
, plz4 SMALLINT
, zusatzziffer SMALLINT
, zip_id SMALLINT UNSIGNED
, gemeindename VARCHAR(32)
, bfs_nr SMALLINT
, kantonskuerzel CHAR(2)
, adressenanteil varchar(8)
, e DOUBLE
, n DOUBLE
, sprache VARCHAR(8)
, validity VARCHAR(12)
);

SQL&amp;gt; -- TRUNCATE TABLE wgs84;

SQL&amp;gt; LOAD DATA LOCAL INFILE &amp;#39;/tmp/AMTOVZ_CSV_WGS84/AMTOVZ_CSV_WGS84.csv&amp;#39;
INTO TABLE wgs84
FIELDS TERMINATED BY &amp;#39;;&amp;#39;
LINES TERMINATED BY &amp;#39;\r\n&amp;#39;
IGNORE 1 LINES
;
Query OK, 5713 rows affected
Records: 5713 Deleted: 0 Skipped: 0 Warnings: 0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Successivamente è possibile interrogare i dati nel database:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;SQL&amp;gt; SELECT * FROM wgs84 ORDER BY ortschaftsname LIMIT 5;
+----------------+------+--------------+--------+--------------+--------+----------------+----------------+-------------------+--------------------+---------+------------+
| ortschaftsname | plz4 | zusatzziffer | zip_id | gemeindename | bfs_nr | kantonskuerzel | adressenanteil | e | n | sprache | validity |
+----------------+------+--------------+--------+--------------+--------+----------------+----------------+-------------------+--------------------+---------+------------+
| Aadorf | 8355 | 0 | 4672 | Aadorf | 4551 | TG | 96.802 % | 8.903193007810433 | 47.491079014637265 | de | 2008-07-01 |
| Aadorf | 8355 | 0 | 4672 | Elgg | 294 | ZH | 3.198 % | 8.89206766645808 | 47.4933781685032 | de | 2008-07-01 |
| Aarau | 5000 | 0 | 2913 | Aarau | 4001 | AG | 99.713 % | 8.048148371736266 | 47.38973523857376 | de | 2008-07-01 |
| Aarau | 5000 | 0 | 2913 | Suhr | 4012 | AG | 0.287 % | 8.059410934099922 | 47.383298214804334 | de | 2008-07-01 |
| Aarau | 5004 | 0 | 2932 | Aarau | 4001 | AG | 100 % | 8.060698546432551 | 47.400587704180744 | de | 2008-07-01 |
+----------------+------+--------------+--------+--------------+--------+----------------+----------------+-------------------+--------------------+---------+------------+
5 rows in set
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;O, per essere più precisi:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;SQL&amp;gt; SELECT ortschaftsname AS city, plz4 AS city_code, e AS lon, n AS lat
 FROM wgs84 WHERE plz4 IN (8280, 4663, 6043);
+-------------+-----------+-------------------+--------------------+
| city | city_code | lon | lat |
+-------------+-----------+-------------------+--------------------+
| Aarburg | 4663 | 7.904271716719409 | 47.321443418782955 |
| Aarburg | 4663 | 7.889249714098425 | 47.313536073562474 |
| Aarburg | 4663 | 7.880309179095798 | 47.31255194439023 |
| Adligenswil | 6043 | 8.364849060491428 | 47.07037816052481 |
| Kreuzlingen | 8280 | 9.173740257895282 | 47.64491046067056 |
| Kreuzlingen | 8280 | 9.159171428030783 | 47.654149879509134 |
| Kreuzlingen | 8280 | 9.204470741840725 | 47.639949130372145 |
+-------------+-----------+-------------------+--------------------+
7 rows in set (0.003 sec)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Lascio al gentile lettore o alla gentile lettrice il compito di eliminare i duplicati&amp;hellip; :-)&lt;/p&gt;
&lt;p&gt;Fin qui tutto bene, passiamo ora alle sottigliezze:&lt;/p&gt;
&lt;h3 id="differenze-tra-mariadb-e-mysql"&gt;Differenze tra MariaDB e MySQL&lt;/h3&gt;
&lt;p&gt;La procedura sopra descritta funziona perfettamente con MariaDB 11.4 e 11.8. Con MySQL 8.4 ci sono piccole differenze:&lt;/p&gt;
&lt;p&gt;Il primo messaggio di errore che impedisce il caricamento è questo:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ERROR 3948 (42000): Loading local data is disabled; this must be enabled on both the client and server sides
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Può essere aggirato in modo relativamente semplice con il comando:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;SQL&amp;gt; SET GLOBAL local_infile = ON;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Al prossimo tentativo si fallirà come segue:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ERROR 2068 (HY000): LOAD DATA LOCAL INFILE file request rejected due to restrictions on access.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Questo problema può essere risolto avviando il client MySQL come segue:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ mysql --local-infile=1 --user=root test
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="fonti"&gt;Fonti&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;MariaDB: &lt;a href="https://mariadb.com/docs/server/reference/sql-statements/data-manipulation/inserting-loading-data/load-data-into-tables-or-index/load-data-infile" target="_blank"&gt;LOAD DATA INFILE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;MySQL: &lt;a href="https://dev.mysql.com/doc/refman/8.4/en/load-data.html" target="_blank"&gt;LOAD DATA Statement&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="caricamento-dei-dati-con-postgresql"&gt;Caricamento dei dati con PostgreSQL&lt;/h2&gt;
&lt;p&gt;PostgreSQL dispone del comando &lt;code&gt;COPY ... FROM&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;postgres=# DROP TABLE IF EXISTS wgs84;

postgres=# CREATE TABLE wgs84 (
 ortschaftsname VARCHAR(32)
, plz4 SMALLINT
, zusatzziffer SMALLINT
, zip_id INT
, gemeindename VARCHAR(32)
, bfs_nr SMALLINT
, kantonskuerzel CHAR(2)
, adressenanteil varchar(8)
, e DOUBLE PRECISION
, n DOUBLE PRECISION
, sprache VARCHAR(8)
, validity VARCHAR(12)
);

postgres=# -- TRUNCATE TABLE wgs84;

postgres=# COPY wgs84
FROM &amp;#39;/tmp/AMTOVZ_CSV_WGS84/AMTOVZ_CSV_WGS84.csv&amp;#39;
DELIMITER &amp;#39;;&amp;#39;
CSV HEADER
;
COPY 5713
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Anche in questo caso otteniamo il risultato previsto nella forma usuale per PostgreSQL:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;postgres=# SELECT ortschaftsname AS city, plz4 AS city_code, e AS lon, n AS lat
 FROM wgs84 WHERE plz4 IN (8280, 4663, 6043);
 city | city_code | lon | lat
-------------+-----------+-------------------+--------------------
 Aarburg | 4663 | 7.904271716719409 | 47.321443418782955
 Aarburg | 4663 | 7.889249714098425 | 47.313536073562474
 Aarburg | 4663 | 7.880309179095798 | 47.31255194439023
 Adligenswil | 6043 | 8.36487538940682 | 47.07037794822416
 Kreuzlingen | 8280 | 9.173740257895282 | 47.64491046067056
 Kreuzlingen | 8280 | 9.159171428030783 | 47.654149879509134
 Kreuzlingen | 8280 | 9.204470741840725 | 47.639949130372145
(7 rows)
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="fonti-1"&gt;Fonti&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;PostgreSQL: &lt;a href="https://www.postgresql.org/docs/current/sql-copy.html" target="_blank"&gt;COPY&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="piccole-differenze-tra-mariadbmysql-e-postgresql"&gt;Piccole differenze tra MariaDB/MySQL e PostgreSQL&lt;/h2&gt;
&lt;p&gt;In linea di principio, il comando di caricamento è completamente diverso nei due mondi di database.&lt;/p&gt;
&lt;p&gt;In MariaDB e PostgreSQL i comandi funzionano “out-of-the-box”. MySQL ha integrato due ulteriori barriere di sicurezza.&lt;/p&gt;
&lt;p&gt;PostgreSQL non conosce i tipi di dati interi &lt;code&gt;USINGED&lt;/code&gt;, quindi è necessario utilizzare il tipo di dati immediatamente superiore (&lt;code&gt;INT&lt;/code&gt;), che occupa leggermente più spazio rispetto a MariaDB/MySQL.&lt;/p&gt;
&lt;h2 id="osservazioni"&gt;Osservazioni&lt;/h2&gt;
&lt;p&gt;Quando abbiamo eseguito lo stesso test pochi giorni fa, si è verificato un errore durante il caricamento. Sembra quindi che anche la fonte dei dati sia leggermente cambiata&amp;hellip;&lt;/p&gt;
&lt;p&gt;Non sono riuscito a capire rapidamente se esiste uno standard SQL per questi comandi di caricamento e, in caso affermativo, se MariaDB/MySQL o PostgreSQL sono conformi allo standard.&lt;/p&gt;
&lt;p&gt;E naturalmente ci sono anche altri modi per inserire i propri dati CSV nel database&amp;hellip;&lt;/p&gt;
&lt;p&gt;Gli strumenti &lt;code&gt;mariadb-import&lt;/code&gt;/&lt;code&gt;mysqlimport&lt;/code&gt; vengono utilizzati se si desidera eseguire l&amp;rsquo;operazione dalla riga di comando. Anche il CSV Storage Engine può essere utilizzato a questo scopo (per i dettagli vedere &lt;a href="https://www.fromdual.com/blog/csv-storage-engine/"&gt;qui&lt;/a&gt;). Una variante ufficialmente supportata è il motore di archiviazione MariaDB CONNECT con il tipo CSV (vedi &lt;a href="https://mariadb.com/docs/server/server-usage/storage-engines/connect/connect-table-types/connect-csv-and-fmt-table-types" target="_blank"&gt;qui&lt;/a&gt;):&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;SQL&amp;gt; INSTALL SONAME &amp;#39;ha_connect&amp;#39;;

SQL&amp;gt; CREATE TABLE wgs84_fdw
ENGINE = CONNECT
table_type = CSV
file_name=&amp;#39;/tmp/AMTOVZ_CSV_WGS84/AMTOVZ_CSV_WGS84.csv&amp;#39;
header = 1
sep_char = &amp;#39;;&amp;#39;
quoted = 0;

SQL&amp;gt; INSERT INTO wgs84 SELECT * FROM wgs84_fdw;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A quanto pare, il motore di archiviazione CONNECT di MariaDB non è più supportato?!? Anche lo strumento &lt;code&gt;mydumper&lt;/code&gt;/&lt;code&gt;myloader&lt;/code&gt; sembra essere in grado di gestire i file CSV.&lt;/p&gt;
&lt;p&gt;E naturalmente il tutto può essere risolto anche a livello applicativo&amp;hellip;&lt;/p&gt;
&lt;p&gt;Con PostgreSQL ci sono le seguenti possibilità:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;postgres=# \copy wgs84 FROM &amp;#39;/tmp/AMTOVZ_CSV_WGS84/AMTOVZ_CSV_WGS84.csv&amp;#39; DELIMITER &amp;#39;;&amp;#39; CSV HEADER
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;quindi dalla shell:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ psql --user=dba -c &amp;#34;\copy wgs84 FROM &amp;#39;/tmp/AMTOVZ_CSV_WGS84/AMTOVZ_CSV_WGS84.csv&amp;#39; DELIMITER &amp;#39;;&amp;#39; CSV HEADER&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;E la variante tramite Foreign Data Wrapper (FWD). Non l&amp;rsquo;ho però provata:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;postgres=# CREATE EXTENSION postgres_fdw;

postgres=# CREATE SERVER foreign_server
 FOREIGN DATA WRAPPER postgres_fdw
 OPTIONS (
 datasource &amp;#39;CSV:/tmp/AMTOVZ_CSV_WGS84/AMTOVZ_CSV_WGS84.csv&amp;#39;,
 format &amp;#39;CSV&amp;#39;
 )
;

postgres=# CREATE USER MAPPING FOR local_user
 SERVER foreign_server
 OPTIONS (user &amp;#39;foreign_user&amp;#39;, password &amp;#39;password&amp;#39;)
;

postgres=# CREATE FOREIGN TABLE foreign_table (
 id integer NOT NULL,
 data text
)
 SERVER foreign_server
 OPTIONS (schema_name &amp;#39;some_schema&amp;#39;, table_name &amp;#39;some_table&amp;#39;)
;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="aggiunta"&gt;Aggiunta&lt;/h2&gt;
&lt;p&gt;Il tipo di dati MariaDB/MySQL &lt;code&gt;DOUBLE&lt;/code&gt; in ProsgreSQL si chiama &lt;code&gt;DOUBLE PRECISION&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Questa pagina è stata tradotta utilizzando &lt;a href="https://www.deepl.com/it/translator" target="_blank"&gt;deepl.com&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>FromDual</title><link>https://www.fromdual.com/it/index.php</link><pubDate>Wed, 21 Jan 2026 11:36:36 +0100</pubDate><author>oli.sennhauser@fromdual.com (Oli Sennhauser)</author><guid>https://www.fromdual.com/it/index.php</guid><description>&lt;p&gt;&lt;br&gt;&lt;br&gt;&lt;/p&gt;
&lt;p style="font-size: 20px; text-align: center;"&gt;&lt;strong&gt;Servizi neutrali e indipendenti dal produttore&lt;br&gt;
per MariaDB, Galera Cluster e MySQL!&lt;/strong&gt;&lt;/p&gt;
&lt;br&gt;&lt;br&gt;
&lt;h2 id="servizi-per-mariadb-galera-e-mysql"&gt;Servizi per MariaDB, Galera e MySQL&lt;/h2&gt;
&lt;p&gt;Offriamo i seguenti &lt;a href="https://www.fromdual.com/it/i-nostri-servizi/"&gt;servizi&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.fromdual.com/mariadb-galera-and-mysql-consulting/"&gt;Consulenza&lt;/a&gt; neutrale e indipendente dal produttore per MySQL, Galera Cluster, MariaDB e Percona Server.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.fromdual.com/mariadb-galera-and-mysql-support/"&gt;Abbonamenti di assistenza Enterprise&lt;/a&gt; per MariaDB, Galera Cluster e MySQL.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.fromdual.com/mariadb-mysql-training/"&gt;Corsi di formazione/seminari&lt;/a&gt; su MariaDB, Galera e MySQL per amministratori di database (DBA) e sviluppatori.&lt;/li&gt;
&lt;li&gt;Con i nostri servizi &lt;a href="https://www.fromdual.com/mariadb-and-mysql-remote-dba/"&gt;remote-DBA&lt;/a&gt; vi aiutiamo a gestire i vostri database MariaDB e MySQL critici per l&amp;rsquo;azienda.&lt;/li&gt;
&lt;li&gt;I nostri prodotti software: &lt;a href="https://www.fromdual.com/ops-center/"&gt;OpsCenter&lt;/a&gt;, &lt;a href="https://www.fromdual.com/mysql-performance-monitor/"&gt;Performance Monitor&lt;/a&gt;, &lt;a href="https://www.fromdual.com/backup-and-recovery-manager/"&gt;Backup/Recovery Manager&lt;/a&gt; e &lt;a href="https://www.fromdual.com/myenv/"&gt;myEnv&lt;/a&gt; vi supportano nella gestione della vostra infrastruttura.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Per ulteriori informazioni, &lt;a href="https://www.fromdual.com/it/contatto/"&gt;contattateci&lt;/a&gt; &amp;hellip;
&lt;br&gt;&lt;/p&gt;
&lt;br&gt;
&lt;fieldset&gt;
&lt;br&gt;
&lt;p style="font-size: 20px; text-align: center;"&gt;&lt;strong&gt;Provate gratuitamente il nostro &lt;a href="https://www.fromdual.com/monitoring-as-a-service-maas"&gt;Monitoring as a Service&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;br&gt;
&lt;/fieldset&gt;
&lt;h2 id="corsi-di-formazione-mariadb-galera-e-mysql-in-programma"&gt;Corsi di formazione MariaDB, Galera e MySQL in programma&lt;/h2&gt;
&lt;p&gt;Per ulteriori informazioni sul nostro programma di formazione, consultate la pagina Corsi di &lt;a href="https://www.fromdual.com/mariadb-mysql-training/"&gt;formazione/seminari&lt;/a&gt; MariaDB, Galera e MySQL.
&lt;br&gt;&lt;/p&gt;
&lt;!-- Do NOT move he &lt;br&gt; here otherwise the page will break (500)!!! --&gt;
&lt;?php

$lang = 'en';
$file = '/home/shinguz/www/shinguz/tmp/fromdual_training_snippet_' . $lang . '.html';
if ( file_exists($file) ) {
 readfile($file);
}
else {
 echo '&lt;p style="color: red;"&gt;' . 'ERROR: Snipped currently unavailable!' . '&lt;/p&gt;' . "\n";
 error_log('File: ' . $file . ' does NOT exist.');
}

?&gt;
&lt;h2 id="notizie-fromdual"&gt;Notizie FromDual&lt;/h2&gt;
&lt;p&gt;Per le notizie da e su FromDual, seguiteci su a:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.linkedin.com/company/793494" target="_blank" title="LinkedIn"&gt;LinkedIn&lt;/a&gt;,
&lt;a href="https://x.com/fromdual" target="_blank" title="Twitter"&gt;X / Twitter&lt;/a&gt;,
&lt;a href="https://www.facebook.com/fromdual" target="_blank" title="Facebook"&gt;Facebook&lt;/a&gt;,
&lt;a href="https://bsky.app/profile/fromdual.bsky.social" target="_blank" title="Bluesky"&gt;Bluesky&lt;/a&gt; o
&lt;a href="https://mastodon.social/@fromdual" target="_blank" title="Mastodon"&gt;Mastodon&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;br&gt;
Questa pagina è stata tradotta con &lt;a href="https://www.deepl.com/it/translator" target="_blank"&gt;deepl.com&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Test</title><link>https://www.fromdual.com/it/test/</link><pubDate>Wed, 21 Jan 2026 11:28:01 +0100</pubDate><author>oli.sennhauser@fromdual.com (Oli Sennhauser)</author><guid>https://www.fromdual.com/it/test/</guid><description>&lt;p&gt;Una pagina di prova in tedesco&amp;hellip;&lt;/p&gt;</description></item><item><title>Contattare FromDual</title><link>https://www.fromdual.com/it/contatto/</link><pubDate>Fri, 16 Jan 2026 20:42:00 +0100</pubDate><author>oli.sennhauser@fromdual.com (Oli Sennhauser)</author><guid>https://www.fromdual.com/it/contatto/</guid><description>&lt;br&gt;
&lt;p&gt;Potete contattarci nei seguenti modi:&lt;/p&gt;
&lt;br&gt;
&lt;table&gt;
 &lt;colgroup&gt;
 &lt;col style="width: 33%"&gt;
 &lt;col style="width: 33%"&gt;
 &lt;col style="width: 33%"&gt;
 &lt;/colgroup&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td style="width: 192px; vertical-align: top;"&gt;Indirizzo:&lt;/td&gt;
 &lt;td&gt;FromDual GmbH&lt;br&gt;
 Rebenweg 6&lt;br&gt;
 CH - 8610 &lt;b&gt;Uster&lt;/b&gt;&lt;br&gt;
 Svizzera&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;E-mail:&lt;/td&gt;
 &lt;td&gt;&lt;a href="mailto:contact@fromdual.com"&gt;contact@fromdual.com&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Telefono:&lt;/td&gt;
 &lt;td&gt;&lt;a href="tel:+41445005820"&gt;+41 44 500 58 20&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;&lt;a href="tel:+492018536770"&gt;+49 201 853 67 70&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&amp;nbsp;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Partita IVA/UID:&lt;/td&gt;
 &lt;td&gt;CHE-301.341.221 MWST&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Numero DUNS:&lt;/td&gt;
 &lt;td&gt;485921436&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;</description></item><item><title>InnoDB deadlock con SELECT? Non è possibile! O forse sì?</title><link>https://www.fromdual.com/it/blog/innodb-deadlock-con-select-non-e-possibile-o-forse-si/</link><pubDate>Sat, 25 Nov 2023 18:05:25 +0000</pubDate><author>oli.sennhauser@fromdual.com (Oli Sennhauser)</author><guid>https://www.fromdual.com/it/blog/innodb-deadlock-con-select-non-e-possibile-o-forse-si/</guid><description>&lt;h2 id="introduzione"&gt;Introduzione&lt;/h2&gt;
&lt;p&gt;Due punti per iniziare:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Un deadlock è uno stato in cui due transazioni diverse non sono più in grado di continuare a lavorare perché ciascuna transazione detiene un blocco di cui l&amp;rsquo;altra transazione avrebbe bisogno. Poiché entrambe le transazioni stanno aspettando che l&amp;rsquo;altra transazione rilasci il proprio blocco, nessuna delle due rilascerà il proprio blocco. E questo durerebbe fino a dopodomani. Per evitare ciò, l&amp;rsquo;istanza di MariaDB interviene e uccide la transazione che ha svolto meno lavoro. L&amp;rsquo;applicazione riceve quindi un messaggio di errore di deadlock del tipo:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Il mantra generale nell&amp;rsquo;ecosistema MariaDB è che una &lt;code&gt;SELECT&lt;/code&gt; non causa blocchi (eccezione: &lt;code&gt;FOR UPDATE&lt;/code&gt; o &lt;code&gt;LOCK IN SHARE MODE&lt;/code&gt;) e quindi non può essere parte di un deadlock.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="il-problema"&gt;Il problema&lt;/h2&gt;
&lt;p&gt;Un cliente di lunga data si rivolge al team di DBA remoti di FromDual con la richiesta di spiegare una situazione di deadlock:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Salve team FromDual,
Ho bisogno della vostra competenza in materia di deadlock.
Quando vi farebbe comodo?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;La situazione è la seguente: La transazione 1 consiste in una semplice &lt;code&gt;INSERT&lt;/code&gt;. La transazione 2 consiste in una &lt;code&gt;SELECT&lt;/code&gt;. Questo non dovrebbe causare un deadlock!&lt;/p&gt;
&lt;p&gt;Per prima cosa controlliamo i seguenti punti:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tutte le tabelle interessate da queste query sono indicizzate correttamente? Sì, lo sono. Le query funzionano tutte perfettamente!&lt;/li&gt;
&lt;li&gt;La query &lt;code&gt;SELECT&lt;/code&gt; fa forse parte di una transazione più ampia (NON una transazione con auto-commit) e quindi non è la causa effettiva del deadlock? No, non lo è. Si tratta di transazioni con auto-commit.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;E adesso? Cosa c&amp;rsquo;è da dire per chiarire: La &lt;code&gt;SELECT&lt;/code&gt; viene inviata con una cadenza molto alta, cioè circa ogni 5 ms!&lt;/p&gt;
&lt;p&gt;È chiaro che l&amp;rsquo;&lt;code&gt;INSERT&lt;/code&gt; crea dei blocchi. Viene anche visualizzato. Ma perché il comando &lt;code&gt;SELECT&lt;/code&gt; genera dei blocchi? Anche questi vengono visualizzati!&lt;/p&gt;
&lt;p&gt;Cerchiamo quindi di scomporre il problema in singoli passaggi.&lt;/p&gt;
&lt;h2 id="modo-per-risolvere"&gt;Modo per risolvere&lt;/h2&gt;
&lt;p&gt;La query si presenta come segue:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SQL&amp;gt; SET @id = (SELECT id FROM test WHERE id = 3);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Se impacchettiamo questa query in una transazione esplicita, possiamo anche vedere i lock:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SQL&amp;gt; START TRANSACTION;
SQL&amp;gt; SET @id = (SELECT id FROM test WHERE id = 3);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;e in una seconda connessione:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SQL&amp;gt; SELECT * FROM information_schema.INNODB_TRX&amp;lt;br&amp;gt;G
*************************** 1. row ***************************
 trx_id: 0
 trx_state: RUNNING
 trx_started: 2023-11-19 15:27:09
 trx_requested_lock_id: NULL
 trx_wait_started: NULL
 trx_weight: 2
 trx_mysql_thread_id: 3765
 trx_query: NULL
 trx_operation_state:
 trx_tables_in_use: 0
 trx_tables_locked: 1
 trx_lock_structs: 2
 trx_lock_memory_bytes: 1128
 trx_rows_locked: 1
 trx_rows_modified: 0
 trx_concurrency_tickets: 0
 trx_isolation_level: REPEATABLE READ
 trx_unique_checks: 1
 trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
 trx_is_read_only: 0
trx_autocommit_non_locking: 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Purtroppo non è possibile vedere di che tipo di blocco (IS) si tratta perché la vista &lt;code&gt;INNODB_LOCKS&lt;/code&gt; è vuota.&lt;/p&gt;
&lt;h2 id="la-soluzione"&gt;La soluzione&lt;/h2&gt;
&lt;p&gt;Se facciamo lo stesso tentativo con delle &lt;code&gt;SELECT&lt;/code&gt; &amp;ldquo;normali&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SQL&amp;gt; START TRANSACTION; SELECT id FROM test WHERE id = 3;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;o&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SQL&amp;gt; START TRANSACTION; SELECT id INTO @id FROM test WHERE id = 3;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;NON vediamo nessun blocco:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SQL&amp;gt; SELECT * FROM information_schema.INNODB_TRX&amp;lt;br&amp;gt;G
*************************** 1. row ***************************
 trx_id: 0
 trx_state: RUNNING
 trx_started: 2023-11-19 15:31:35
 trx_requested_lock_id: NULL
 trx_wait_started: NULL
 trx_weight: 0
 trx_mysql_thread_id: 3765
 trx_query: NULL
 trx_operation_state:
 trx_tables_in_use: 0
 trx_tables_locked: 0
 trx_lock_structs: 0
 trx_lock_memory_bytes: 1128
 trx_rows_locked: 0
 trx_rows_modified: 0
 trx_concurrency_tickets: 0
 trx_isolation_level: REPEATABLE READ
 trx_unique_checks: 1
 trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
 trx_is_read_only: 0
trx_autocommit_non_locking: 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sembra quindi che il costrutto &lt;code&gt;SET @id = (...)&lt;/code&gt; causi questo blocco IS. Il cliente riscrive la sua applicazione e poco dopo riceviamo il seguente messaggio:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Salve team FromDual,
Il vostro consiglio è stato azzeccato.
Nessun blocco da venerdì a pranzo.
Grazie e buon fine settimana.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;img src="https://www.fromdual.com/sites/default/files/innodb_deadlocks.png" title="InnoDB Deadlocks" width="640" /&gt;
&lt;h2 id="ulteriori-domande-chiarificatrici"&gt;Ulteriori domande chiarificatrici&lt;/h2&gt;
&lt;p&gt;MySQL 8.0 si comporta allo stesso modo? Sì, esattamente lo stesso.&lt;/p&gt;
&lt;h2 id="addendum"&gt;Addendum&lt;/h2&gt;
&lt;p&gt;Il mio caro collega Matthias mi ha dato un&amp;rsquo;altra idea: che dire delle Stored Procedures e delle Stored Functions di MariaDB?&lt;/p&gt;
&lt;p&gt;I due test sono qui:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DELIMITER //

CREATE OR REPLACE PROCEDURE locktestsp (INOUT id INT)
BEGIN
 SELECT id INTO id FROM test WHERE id = id LIMIT 1;
END;
//

DELIMITER ;

SET @id = 3;
START TRANSACTION;
CALL locktestsp(@id);
SELECT @id;

SELECT trx_tables_locked, trx_lock_structs, trx_rows_locked FROM information_schema.INNODB_TRX;
+-------------------+------------------+-----------------+
| trx_tables_locked | trx_lock_structs | trx_rows_locked |
+-------------------+------------------+-----------------+
| 0 | 0 | 0 |
+-------------------+------------------+-----------------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;e qui:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DELIMITER //

CREATE OR REPLACE FUNCTION locktestsf (IN id INT)
RETURNS CHAR(50) DETERMINISTIC
BEGIN
 SELECT id INTO id FROM test WHERE id = id LIMIT 1;
 RETURN id;
END;
//

DELIMITER ;

START TRANSACTION;
SELECT locktestsf(3);

 SELECT trx_tables_locked, trx_lock_structs, trx_rows_locked FROM information_schema.INNODB_TRX;
+-------------------+------------------+-----------------+
| trx_tables_locked | trx_lock_structs | trx_rows_locked |
+-------------------+------------------+-----------------+
| 0 | 0 | 0 |
+-------------------+------------------+-----------------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Questa pagina è stata tradotta utilizzando &lt;a href="https://www.deepl.com/it/translator" target="_blank"&gt;deepl.com&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Ops! Pagina non trovata.</title><link>https://www.fromdual.com/it/404/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><author>oli.sennhauser@fromdual.com (Oli Sennhauser)</author><guid>https://www.fromdual.com/it/404/</guid><description>&lt;br&gt;
&lt;br&gt;
&lt;p&gt;Cosa stai cercando esattamente?&lt;/p&gt;
&lt;br&gt;
&lt;link href="https://www.fromdual.com/pagefind/pagefind-ui.css" rel="stylesheet"&gt;
&lt;script src="https://www.fromdual.com/pagefind/pagefind-ui.js"&gt;&lt;/script&gt;
&lt;div id="looking-for"&gt;&lt;/div&gt;
&lt;script&gt;
 window.addEventListener('DOMContentLoaded', (event) =&gt; {
 new PagefindUI({ element: "#looking-for", showSubResults: true });
 });
&lt;/script&gt;</description></item><item><title/><link>https://www.fromdual.com/it/i-nostri-servizi/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><author>oli.sennhauser@fromdual.com (Oli Sennhauser)</author><guid>https://www.fromdual.com/it/i-nostri-servizi/</guid><description/></item><item><title/><link>https://www.fromdual.com/it/blog/shinguz/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><author>oli.sennhauser@fromdual.com (Oli Sennhauser)</author><guid>https://www.fromdual.com/it/blog/shinguz/</guid><description/></item><item><title/><link>https://www.fromdual.com/it/blog/sales_en/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><author>oli.sennhauser@fromdual.com (Oli Sennhauser)</author><guid>https://www.fromdual.com/it/blog/sales_en/</guid><description/></item><item><title/><link>https://www.fromdual.com/it/blog/sales_de/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><author>oli.sennhauser@fromdual.com (Oli Sennhauser)</author><guid>https://www.fromdual.com/it/blog/sales_de/</guid><description/></item><item><title/><link>https://www.fromdual.com/it/blog/oli/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><author>oli.sennhauser@fromdual.com (Oli Sennhauser)</author><guid>https://www.fromdual.com/it/blog/oli/</guid><description/></item><item><title/><link>https://www.fromdual.com/it/blog/joerg/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><author>oli.sennhauser@fromdual.com (Oli Sennhauser)</author><guid>https://www.fromdual.com/it/blog/joerg/</guid><description/></item><item><title/><link>https://www.fromdual.com/it/blog/cedric/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><author>oli.sennhauser@fromdual.com (Oli Sennhauser)</author><guid>https://www.fromdual.com/it/blog/cedric/</guid><description/></item><item><title/><link>https://www.fromdual.com/it/blog/abdel-mawla/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><author>oli.sennhauser@fromdual.com (Oli Sennhauser)</author><guid>https://www.fromdual.com/it/blog/abdel-mawla/</guid><description/></item></channel></rss>