<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>FromDual: MySQL Tech-Feed (de)</title><link>https://www.fromdual.com/de/aggregator/categories/7/</link><description>FromDual: MySQL Tech-Feed in German</description><generator>Hugo</generator><language>de-CH</language><atom:link href="https://www.fromdual.com/de/aggregator/categories/7/index.xml" rel="self" type="application/rss+xml"/><item><title>Datenbank Index Optimierer</title><link>https://www.fromdual.com/de/blog/datenbank-index-optimierer/</link><pubDate>Wed, 15 Jul 2026 11:54:00 +0200</pubDate><guid>https://www.fromdual.com/de/blog/datenbank-index-optimierer/</guid><description>&lt;p&gt;Kürzlich hat mich ein Kunde gefragt, ob das &amp;ldquo;aufwändige&amp;rdquo; Index-Prüfen nicht einem Index-Optimierer überlassen werden könnte. Klar geht das&amp;hellip;&lt;/p&gt;
&lt;p&gt;Was wollen wir den genau überprüfen?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tabellen ohne Primary Key&lt;/li&gt;
&lt;li&gt;Doppelte Indices&lt;/li&gt;
&lt;li&gt;Teilweise redundante Indices&lt;/li&gt;
&lt;li&gt;Ungenutzte Indices&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="mariadb-mysql-und-percona-server"&gt;MariaDB, MySQL und Percona Server&lt;/h2&gt;
&lt;h3 id="tabellen-ohne-primary-key"&gt;Tabellen ohne Primary Key&lt;/h3&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;SQL&amp;gt; SELECT DISTINCT t.table_schema, t.table_name
 FROM information_schema.tables AS t
 LEFT JOIN information_schema.columns AS c ON t.table_schema = c.table_schema AND t.table_name = c.table_name
 AND c.column_key = &amp;#34;PRI&amp;#34;
 WHERE t.table_schema NOT IN (&amp;#39;information_schema&amp;#39;, &amp;#39;mysql&amp;#39;, &amp;#39;performance_schema&amp;#39;)
 AND c.table_name IS NULL AND t.table_type NOT IN(&amp;#39;VIEW&amp;#39;, &amp;#39;SEQUENCE&amp;#39;)
 AND t.table_schema = &amp;#39;testtest&amp;#39;
;
+--------------+------------+
| table_schema | table_name |
+--------------+------------+
| testtest | archived |
+--------------+------------+
1 row in set
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Quelle: &lt;a href="https://www.fromdual.com/blog/mysql-performance-schema-hints/#tables-without-primary-key" target="_blank"&gt;Tables without a Primary Key&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="doppelte-indices"&gt;Doppelte Indices&lt;/h3&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;SQL&amp;gt; SELECT table_name, redundant_index_name, redundant_index_columns, dominant_index_name, dominant_index_columns, sql_drop_index
 FROM sys.schema_redundant_indexes
 WHERE redundant_index_columns = dominant_index_columns
 AND table_schema = &amp;#39;testtest&amp;#39;
;
+------------+----------------------+-------------------------+---------------------+------------------------+------------------------------------------------------+
| table_name | redundant_index_name | redundant_index_columns | dominant_index_name | dominant_index_columns | sql_drop_index |
+------------+----------------------+-------------------------+---------------------+------------------------+------------------------------------------------------+
| archived | dupl2 | category_id | dupl1 | category_id | ALTER TABLE `testtest`.`archived` DROP INDEX `dupl2` |
+------------+----------------------+-------------------------+---------------------+------------------------+------------------------------------------------------+
1 row in set
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Quelle: &lt;a href="https://www.fromdual.com/blog/mysql-performance-schema-hints/#duplicate-and-redundant-indices" target="_blank"&gt;Duplicate and redundant indices&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="teilweise-redundante-indices"&gt;Teilweise redundante Indices&lt;/h3&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;SQL&amp;gt; SELECT table_name, redundant_index_name, redundant_index_columns, dominant_index_name, dominant_index_columns, sql_drop_index
 FROM sys.schema_redundant_indexes
 WHERE table_schema = &amp;#39;testtest&amp;#39;
;
+-------------------+----------------------+-------------------------+---------------------+---------------------------------+-------------------------------------------------------------------+
| table_name | redundant_index_name | redundant_index_columns | dominant_index_name | dominant_index_columns | sql_drop_index |
+-------------------+----------------------+-------------------------+---------------------+---------------------------------+-------------------------------------------------------------------+
| access | customer | customer | customer_2 | customer,callerid_internal | ALTER TABLE `testtest`.`access` DROP INDEX `customer` |
| access | customer | customer | customer_3 | customer,callerid_external | ALTER TABLE `testtest`.`access` DROP INDEX `customer` |
| active_customers | uniqueid | uniqueid | PRIMARY | uniqueid,scustomer | ALTER TABLE `testtest`.`active_customers` DROP INDEX `uniqueid` |
| analytics_include | analytics | analytics | PRIMARY | analytics,feature,dtype,dnumber | ALTER TABLE `testtest`.`analytics_include` DROP INDEX `analytics` |
| archived | dupl2 | category_id | dupl1 | category_id | ALTER TABLE `testtest`.`archived` DROP INDEX `dupl2` |
...
| texts_media | uniqueid | uniqueid | PRIMARY | uniqueid,filename | ALTER TABLE `testtest`.`texts_media` DROP INDEX `uniqueid` |
| unlimited_access | customer | customer | customer_2 | customer,callerid_internal | ALTER TABLE `testtest`.`unlimited_access` DROP INDEX `customer` |
| unlimited_access | customer | customer | customer_3 | customer,callerid_external | ALTER TABLE `testtest`.`unlimited_access` DROP INDEX `customer` |
+-------------------+----------------------+-------------------------+---------------------+---------------------------------+-------------------------------------------------------------------+
26 rows in set
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Quelle: &lt;a href="https://www.fromdual.com/blog/mysql-performance-schema-hints/#duplicate-and-redundant-indices" target="_blank"&gt;Duplicate and redundant indices&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="ungenutzte-indices"&gt;Ungenutzte Indices&lt;/h3&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;SQL&amp;gt; SELECT object_name, index_name
 FROM sys.schema_unused_indexes
 WHERE object_schema = &amp;#39;testtest&amp;#39;
;
+------------------------+------------------------+
| object_name | index_name |
+------------------------+------------------------+
| access | customer_3 |
| access | customer_2 |
| actions | class |
| actions | action |
| active | channel |
...
| urls | customer |
| voucher_batches | customer |
| vouchers | batch |
+------------------------+------------------------+
413 rows in set
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Achtung&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bei MariaDB muss das &lt;code&gt;PERFORMANCE_SCHEMA&lt;/code&gt; zuerst eingeschaltet werden.&lt;/li&gt;
&lt;li&gt;Die Informationen sind korrekt seit dem letzten Datenbank-Neustart. Wurde ein Index das letzte mal VOR dem letzten Neustart genutzt, wir er hier als ungenutzt angezeigt.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Quelle: &lt;a href="https://www.fromdual.com/blog/mysql-performance-schema-hints/#unused-indexes" target="_blank"&gt;Unused indexes&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="und-jetzt-mit-postgresql"&gt;Und jetzt mit PostgreSQL&lt;/h2&gt;
&lt;h3 id="tabellen-ohne-primary-key-1"&gt;Tabellen ohne Primary Key&lt;/h3&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;SQL&amp;gt; SELECT tab.table_schema, tab.table_name
 FROM information_schema.tables tab
 LEFT JOIN information_schema.table_constraints tco
 ON tab.table_schema = tco.table_schema
 AND tab.table_name = tco.table_name 
 AND tco.constraint_type = &amp;#39;PRIMARY KEY&amp;#39;
 WHERE tab.table_type = &amp;#39;BASE TABLE&amp;#39;
 AND tab.table_schema NOT IN (&amp;#39;pg_catalog&amp;#39;, &amp;#39;information_schema&amp;#39;)
 AND tco.constraint_name IS NULL
 ORDER BY table_schema, table_name
;
 table_schema | table_name 
--------------+------------
 public | archived
(1 row)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Quelle: &lt;a href="https://dataedo.com/kb/query/postgresql/find-tables-without-primary-keys" target="_blank"&gt;Find tables without primary keys (PKs) in PostgreSQL database&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="doppelte-indices-1"&gt;Doppelte Indices&lt;/h3&gt;
&lt;p&gt;Basierend auf dem MySQL &lt;code&gt;sys&lt;/code&gt; Schema:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;SQL&amp;gt; WITH schema_flattened_keys AS (
 SELECT sai.relid, sai.indexrelid
 , sai.schemaname AS table_schema, sai.relname AS table_name, sai.indexrelname AS index_name
 , CASE pi.indisunique WHEN &amp;#39;f&amp;#39; THEN 1 ELSE 0 END AS non_unique
 , index_columns.columns AS index_columns
 FROM pg_stat_all_indexes AS sai
 JOIN pg_index AS pi ON pi.indexrelid = sai.indexrelid
 JOIN (
 SELECT attrelid, string_agg(attname, &amp;#39;,&amp;#39; ORDER BY attnum ASC) AS columns
 FROM pg_attribute GROUP BY attrelid
 ) AS index_columns ON index_columns.attrelid = sai.indexrelid
 WHERE sai.schemaname NOT IN (&amp;#39;pg_toast&amp;#39;, &amp;#39;pg_catalog&amp;#39;)
)
SELECT redundant_keys.table_schema AS table_schema, redundant_keys.table_name AS table_name, redundant_keys.index_name AS redundant_index_name
 , redundant_keys.index_columns AS redundant_index_columns, redundant_keys.non_unique AS redundant_index_non_unique
 , dominant_keys.index_name AS dominant_index_name, dominant_keys.index_columns AS dominant_index_columns, dominant_keys.non_unique AS dominant_index_non_unique
 , CONCAT(&amp;#39;ALTER TABLE &amp;#39;, redundant_keys.table_schema, &amp;#39;.&amp;#39;, redundant_keys.table_name, &amp;#39; DROP INDEX &amp;#39;, redundant_keys.index_name, &amp;#39;&amp;#39;) AS sql_drop_index
 FROM schema_flattened_keys redundant_keys
 JOIN schema_flattened_keys dominant_keys ON redundant_keys.table_schema = dominant_keys.table_schema AND redundant_keys.table_name = dominant_keys.table_name
 WHERE (redundant_keys.index_name &amp;lt;&amp;gt; dominant_keys.index_name
 AND ((redundant_keys.index_columns = dominant_keys.index_columns)
 AND ((redundant_keys.non_unique &amp;gt; dominant_keys.non_unique) OR (redundant_keys.non_unique = dominant_keys.non_unique))
 )
 OR ((POSITION(CONCAT(redundant_keys.index_columns,&amp;#39;,&amp;#39;) IN dominant_keys.index_columns) = 1) AND (redundant_keys.non_unique = 1))
 OR ((POSITION(CONCAT(dominant_keys.index_columns,&amp;#39;,&amp;#39;) IN redundant_keys.index_columns) = 1) AND (dominant_keys.non_unique = 0))
 )
 AND redundant_keys.index_columns = dominant_keys.index_columns
;
 table_schema | table_name | redundant_index_name | redundant_index_columns | redundant_index_non_unique | dominant_index_name | dominant_index_columns | dominant_index_non_unique | sql_drop_index 
--------------+------------+----------------------+-------------------------+----------------------------+---------------------+------------------------+---------------------------+----------------------------------------------
 public | archived | dupl1 | category_id | 1 | dupl2 | category_id | 1 | ALTER TABLE public.archived DROP INDEX dupl1
 public | archived | dupl2 | category_id | 1 | dupl1 | category_id | 1 | ALTER TABLE public.archived DROP INDEX dupl2
(2 rows)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Quelle: &lt;a href="https://www.fromdual.com/blog/mysql-performance-schema-hints/#duplicate-and-redundant-indices" target="_blank"&gt;Duplicate and redundant indices&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="teilweise-redundante-indices-1"&gt;Teilweise redundante Indices&lt;/h3&gt;
&lt;p&gt;Basierend auf dem MySQL &lt;code&gt;sys&lt;/code&gt; Schema:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;SQL&amp;gt; WITH schema_flattened_keys AS (
 SELECT sai.relid, sai.indexrelid
 , sai.schemaname AS table_schema, sai.relname AS table_name, sai.indexrelname AS index_name
 , CASE pi.indisunique WHEN &amp;#39;f&amp;#39; THEN 1 ELSE 0 END AS non_unique
 , index_columns.columns AS index_columns
 FROM pg_stat_all_indexes AS sai
 JOIN pg_index AS pi ON pi.indexrelid = sai.indexrelid
 JOIN (
 SELECT attrelid, string_agg(attname, &amp;#39;,&amp;#39; ORDER BY attnum ASC) AS columns
 FROM pg_attribute GROUP BY attrelid
 ) AS index_columns ON index_columns.attrelid = sai.indexrelid
 WHERE sai.schemaname NOT IN (&amp;#39;pg_toast&amp;#39;, &amp;#39;pg_catalog&amp;#39;)
)
SELECT redundant_keys.table_schema AS table_schema, redundant_keys.table_name AS table_name, redundant_keys.index_name AS redundant_index_name
 , redundant_keys.index_columns AS redundant_index_columns, redundant_keys.non_unique AS redundant_index_non_unique
 , dominant_keys.index_name AS dominant_index_name, dominant_keys.index_columns AS dominant_index_columns, dominant_keys.non_unique AS dominant_index_non_unique
 , CONCAT(&amp;#39;ALTER TABLE &amp;#39;, redundant_keys.table_schema, &amp;#39;.&amp;#39;, redundant_keys.table_name, &amp;#39; DROP INDEX &amp;#39;, redundant_keys.index_name, &amp;#39;&amp;#39;) AS sql_drop_index
 FROM schema_flattened_keys redundant_keys
 JOIN schema_flattened_keys dominant_keys ON redundant_keys.table_schema = dominant_keys.table_schema AND redundant_keys.table_name = dominant_keys.table_name
 WHERE (redundant_keys.index_name &amp;lt;&amp;gt; dominant_keys.index_name
 AND ((redundant_keys.index_columns = dominant_keys.index_columns)
 AND ((redundant_keys.non_unique &amp;gt; dominant_keys.non_unique) OR (redundant_keys.non_unique = dominant_keys.non_unique))
 )
 OR ((POSITION(CONCAT(redundant_keys.index_columns,&amp;#39;,&amp;#39;) IN dominant_keys.index_columns) = 1) AND (redundant_keys.non_unique = 1))
 OR ((POSITION(CONCAT(dominant_keys.index_columns,&amp;#39;,&amp;#39;) IN redundant_keys.index_columns) = 1) AND (dominant_keys.non_unique = 0))
 )
;
 table_schema | table_name | redundant_index_name | redundant_index_columns | redundant_index_non_unique | dominant_index_name | dominant_index_columns | dominant_index_non_unique | sql_drop_index 
--------------+-----------------------+------------------------------------------+-------------------------+----------------------------+-------------------------------------------------+-----------------------------------------+---------------------------+---------------------------------------------------------------------------------------------
 public | numbers | numbers_customer_idx | customer | 1 | numbers_customer_text_dtype_text_dnumber_idx | customer,text_dtype,text_dnumber | 1 | ALTER TABLE public.numbers DROP INDEX numbers_customer_idx
 public | numbers | numbers_customer_idx | customer | 1 | numbers_customer_fax_dtype_fax_dnumber_idx | customer,fax_dtype,fax_dnumber | 1 | ALTER TABLE public.numbers DROP INDEX numbers_customer_idx
 public | numbers | numbers_customer_idx | customer | 1 | numbers_pkey | customer,stype,snumber | 0 | ALTER TABLE public.numbers DROP INDEX numbers_customer_idx
 public | numbers | numbers_dtype_idx | dtype | 1 | numbers_dtype_dnumber_idx | dtype,dnumber | 1 | ALTER TABLE public.numbers DROP INDEX numbers_dtype_idx
 public | number_callers | number_callers_dtype_idx | dtype | 1 | number_callers_dtype_dnumber_idx | dtype,dnumber | 1 | ALTER TABLE public.number_callers DROP INDEX number_callers_dtype_idx
 public | prefixes | prefixes_customer_idx | customer | 1 | prefixes_customer_dtype_dnumber_idx | customer,dtype,dnumber | 1 | ALTER TABLE public.prefixes DROP INDEX prefixes_customer_idx
 public | number_times | number_times_dtype_idx | dtype | 1 | number_times_dtype_dnumber_idx | dtype,dnumber | 1 | ALTER TABLE public.number_times DROP INDEX number_times_dtype_idx
 public | phones | phones_customer_idx | customer | 1 | phones_customer_callerid_location_idx | customer,callerid_location | 1 | ALTER TABLE public.phones DROP INDEX phones_customer_idx
 public | phones | phones_customer_idx | customer | 1 | phones_customer_callerid_external_idx | customer,callerid_external | 1 | ALTER TABLE public.phones DROP INDEX phones_customer_idx
 public | phones | phones_customer_idx | customer | 1 | phones_customer_callerid_internal_idx | customer,callerid_internal | 1 | ALTER TABLE public.phones DROP INDEX phones_customer_idx
 public | phones_hardware | phones_hardware_phone_idx | phone | 1 | phones_hardware_phone_hardware_address_idx | phone,hardware_address | 0 | ALTER TABLE public.phones_hardware DROP INDEX phones_hardware_phone_idx
 public | speeddials | speeddials_stype_idx | stype | 1 | speeddials_stype_snumber_idx | stype,snumber | 1 | ALTER TABLE public.speeddials DROP INDEX speeddials_stype_idx
 public | speeddials | speeddials_dtype_idx | dtype | 1 | speeddials_dtype_dnumber_idx | dtype,dnumber | 1 | ALTER TABLE public.speeddials DROP INDEX speeddials_dtype_idx
 public | mailbox_destinations | mailbox_destinations_context_mailbox_idx | context,mailbox | 1 | mailbox_destinations_pkey | context,mailbox,dcustomer,dtype,dnumber | 0 | ALTER TABLE public.mailbox_destinations DROP INDEX mailbox_destinations_context_mailbox_idx
 public | outgroup_times | outgroup_times_outgroup_idx | outgroup | 1 | outgroup_times_outgroup_name_idx | outgroup,name | 0 | ALTER TABLE public.outgroup_times DROP INDEX outgroup_times_outgroup_idx
 public | ingroup_times | ingroup_times_ingroup_idx | ingroup | 1 | ingroup_times_ingroup_name_idx | ingroup,name | 0 | ALTER TABLE public.ingroup_times DROP INDEX ingroup_times_ingroup_idx
 public | active_customers | active_customers_uniqueid_idx | uniqueid | 1 | active_customers_pkey | uniqueid,scustomer | 0 | ALTER TABLE public.active_customers DROP INDEX active_customers_uniqueid_idx
 public | access | access_customer_idx | customer | 1 | access_customer_callerid_external_idx | customer,callerid_external | 1 | ALTER TABLE public.access DROP INDEX access_customer_idx
 public | access | access_customer_idx | customer | 1 | access_customer_callerid_internal_idx | customer,callerid_internal | 1 | ALTER TABLE public.access DROP INDEX access_customer_idx
 public | unlimited_access | unlimited_access_customer_idx | customer | 1 | unlimited_access_customer_callerid_external_idx | customer,callerid_external | 1 | ALTER TABLE public.unlimited_access DROP INDEX unlimited_access_customer_idx
 public | unlimited_access | unlimited_access_customer_idx | customer | 1 | unlimited_access_customer_callerid_internal_idx | customer,callerid_internal | 1 | ALTER TABLE public.unlimited_access DROP INDEX unlimited_access_customer_idx
 public | texts | texts_dcustomer_idx | dcustomer | 1 | texts_dcustomer_dtype_dnumber_idx | dcustomer,dtype,dnumber | 1 | ALTER TABLE public.texts DROP INDEX texts_dcustomer_idx
 public | texts_media | texts_media_uniqueid_idx | uniqueid | 1 | texts_media_pkey | uniqueid,filename | 0 | ALTER TABLE public.texts_media DROP INDEX texts_media_uniqueid_idx
 public | number_calleridgroups | number_calleridgroups_dtype_idx | dtype | 1 | number_calleridgroups_dtype_dnumber_idx | dtype,dnumber | 1 | ALTER TABLE public.number_calleridgroups DROP INDEX number_calleridgroups_dtype_idx
 public | analytics_include | analytics_i | analytics | 1 | analytics_include_pkey | analytics,feature,dtype,dnumber | 0 | ALTER TABLE public.analytics_include DROP INDEX analytics_i
 public | archived | dupl1 | category_id | 1 | dupl2 | category_id | 1 | ALTER TABLE public.archived DROP INDEX dupl1
 public | archived | dupl2 | category_id | 1 | dupl1 | category_id | 1 | ALTER TABLE public.archived DROP INDEX dupl2
(27 rows)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Quelle: &lt;a href="https://www.fromdual.com/blog/mysql-performance-schema-hints/#duplicate-and-redundant-indices" target="_blank"&gt;Duplicate and redundant indices&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="ungenutzte-indices-1"&gt;Ungenutzte Indices&lt;/h3&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;SQL&amp;gt; SELECT relid::regclass AS table, indexrelid::regclass AS index
 , pg_size_pretty(pg_relation_size(indexrelid::regclass)) AS index_size
 , idx_tup_read, idx_tup_fetch, idx_scan
 FROM pg_stat_user_indexes 
 JOIN pg_index USING (indexrelid) 
 WHERE idx_scan = 0 
 AND indisunique IS FALSE
;
 table | index | index_size | idx_tup_read | idx_tup_fetch | idx_scan 
------------------------+-----------------------------------------------------------------+------------+--------------+---------------+----------
 customers | customers_prefix_idx | 16 kB | 0 | 0 | 0
 customers | customers_parent_idx | 16 kB | 0 | 0 | 0
 customers | customers_email_idx | 16 kB | 0 | 0 | 0
 customers | customers_affiliate_customer_idx | 16 kB | 0 | 0 | 0
 customers | customers_bill_ref_idx | 16 kB | 0 | 0 | 0
...
 analytics_include | analytics_i | 8192 bytes | 0 | 0 | 0
 archived | dupl1 | 8192 bytes | 0 | 0 | 0
 archived | dupl2 | 8192 bytes | 0 | 0 | 0
(413 rows)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Quellen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://wiki.postgresql.org/wiki/Index_Maintenance#Unused_Indexes" target="_blank"&gt;Unused Indexes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jmorano.moretrix.com/2014/02/postgresql-monitor-unused-indexes/" target="_blank"&gt;Postgresql: Monitor unused indexes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="pg-assistant"&gt;PG Assistant&lt;/h3&gt;
&lt;p&gt;An den &lt;a href="https://2026.pgday.ch/schedule/" target="_blank"&gt;Swiss PGDay2026&lt;/a&gt;(s) hat Bertrand Hartwig sein Tool &lt;a href="https://github.com/beh74/pgassistant-community" target="_blank"&gt;PG Assistant&lt;/a&gt; vorgestellt. In diesem Zusammenhang wollte ich es gleich mal ausprobieren&amp;hellip;&lt;/p&gt;
&lt;p&gt;Fehlende Primary Keys und doppelte Indices konnte PG Assistant finden. Teilweise redundante Indices oder ungenutzte Indices hat er mir nicht angezeigt, kann aber auch an mir liegen&amp;hellip;&lt;/p&gt;
&lt;figure&gt;
 &lt;a href="https://www.fromdual.com/images/pgAssistant_Screenshot_20260715_111018.png" title="volle Grösse"&gt;&lt;img src="https://www.fromdual.com/images/pgAssistant_Screenshot_20260715_111018_640x509.png" alt="pgAssistant-1"&gt;&lt;/a&gt;
 &lt;figcaption&gt;PG Assistant: Dashboard / Dev advisor&lt;/figcaption&gt;
&lt;/figure&gt;&lt;br&gt;
&lt;figure&gt;
 &lt;a href="https://www.fromdual.com/images/pgAssistant_Screenshot_20260715_111135.png" title="volle Grösse"&gt;&lt;img src="https://www.fromdual.com/images/pgAssistant_Screenshot_20260715_111135_640x533.png" alt="pgAssistant-2"&gt;&lt;/a&gt;
 &lt;figcaption&gt;PG Assistant: Global Advisor / Dev advisor&lt;/figcaption&gt;
&lt;/figure&gt;&lt;br&gt;
&lt;figure&gt;
 &lt;a href="https://www.fromdual.com/images/pgAssistant_Screenshot_20260715_111230.png" title="volle Grösse"&gt;&lt;img src="https://www.fromdual.com/images/pgAssistant_Screenshot_20260715_111230_640x532.png" alt="pgAssistant-3"&gt;&lt;/a&gt;
 &lt;figcaption&gt;PG Assistant: Strictly duplicate unused index&lt;/figcaption&gt;
&lt;/figure&gt;&lt;br&gt;
&lt;h4 id="intallation-von-pg-assistant"&gt;Intallation von PG Assistant&lt;/h4&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ apt update
$ apt install python3 python3.13-venv unzip pip
$ wget https://github.com/beh74/pgassistant-community/archive/refs/heads/main.zip
$ unzip main.zip 
$ cd pgassistant-community-main/
$ python3 -m venv env
$ source env/bin/activate
$ pip3 install -r requirements.txt
$ export FLASK_APP=run.py
$ flask run --host=0.0.0.0 --port=80
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Dann mit dem Web-Browser auf die angezeigte URL verbinden.&lt;/p&gt;
&lt;p&gt;In der Datenbank muss ein User angelegt:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;SQL&amp;gt; CREATE ROLE pgassistant WITH LOGIN SUPERUSER PASSWORD &amp;#39;secret&amp;#39;;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;sowie die &lt;code&gt;pg_hba.conf&lt;/code&gt; angepasst werden.&lt;/p&gt;
&lt;h2 id="nachtrag"&gt;Nachtrag&lt;/h2&gt;
&lt;p&gt;Ungenutzte Indices findet man mit dem PG Assitant wie folgt: Database Objects ➜ Indexes ➜ Status: Unused ➜ &amp;ldquo;NO INDEX ACTIVITY&amp;rdquo;&lt;/p&gt;
&lt;figure&gt;
 &lt;a href="https://www.fromdual.com/images/pgAssistant_Screenshot_20260716_093730.png" title="volle Grösse"&gt;&lt;img src="https://www.fromdual.com/images/pgAssistant_Screenshot_20260716_093730_640x459.png" alt="pgAssistant-4"&gt;&lt;/a&gt;
 &lt;figcaption&gt;PG Assistant: Unused Indexes&lt;/figcaption&gt;
&lt;/figure&gt;&lt;br&gt;</description></item><item><title>MariaDB verkürzt Wartungszeitraum von 5 auf 3 Jahre</title><link>https://www.fromdual.com/de/blog/mariadb-verkuerzt-wartungszeitraum-von-5-auf-3-jahre/</link><pubDate>Wed, 10 Jun 2026 21:37:00 +0200</pubDate><guid>https://www.fromdual.com/de/blog/mariadb-verkuerzt-wartungszeitraum-von-5-auf-3-jahre/</guid><description>&lt;p&gt;Irgendwie ist die Meldung an mir vorbei gegangen: MariaDB hat den Wartungszeitraum für die Long-Term Releases des MariaDB Community Servers von 5 auf 3 Jahre verkürzt. OK, ist ja nicht weiter verwunderlich, ich war ja gut einen Monat offline&amp;hellip;&lt;/p&gt;
&lt;h2 id="mariadb-server-lts-release-wartungszeiträume"&gt;MariaDB Server LTS Release Wartungszeiträume&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Release&lt;/th&gt;
					&lt;th&gt;GA Datum&lt;/th&gt;
					&lt;th&gt;EoL Datum&lt;/th&gt;
					&lt;th&gt;Dauer&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;12.3&lt;/td&gt;
					&lt;td&gt;28 May 2026&lt;/td&gt;
					&lt;td&gt;Jun 2029&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;3 Jahre&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;11.8&lt;/td&gt;
					&lt;td&gt;4 Jun 2025&lt;/td&gt;
					&lt;td&gt;4 Jun 2028&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;3 Jahre&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;11.4&lt;/td&gt;
					&lt;td&gt;29 May 2024&lt;/td&gt;
					&lt;td&gt;29 May 2029&lt;/td&gt;
					&lt;td&gt;5 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;10.11&lt;/td&gt;
					&lt;td&gt;16 Feb 2023&lt;/td&gt;
					&lt;td&gt;16 Feb 2028&lt;/td&gt;
					&lt;td&gt;5 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;10.6&lt;/td&gt;
					&lt;td&gt;6 Jul 2021&lt;/td&gt;
					&lt;td&gt;6 Jul 2026&lt;/td&gt;
					&lt;td&gt;5 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;10.5&lt;/td&gt;
					&lt;td&gt;24 Jun 2020&lt;/td&gt;
					&lt;td&gt;24 Jun 2025&lt;/td&gt;
					&lt;td&gt;5 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;10.4&lt;/td&gt;
					&lt;td&gt;18 Jun 2019&lt;/td&gt;
					&lt;td&gt;18 Jun 2024&lt;/td&gt;
					&lt;td&gt;5 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;10.3&lt;/td&gt;
					&lt;td&gt;25 May 2018&lt;/td&gt;
					&lt;td&gt;25 May 2023&lt;/td&gt;
					&lt;td&gt;5 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;10.2&lt;/td&gt;
					&lt;td&gt;23 May 2017&lt;/td&gt;
					&lt;td&gt;23 May 2022&lt;/td&gt;
					&lt;td&gt;5 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;10.1&lt;/td&gt;
					&lt;td&gt;17 Oct 2015&lt;/td&gt;
					&lt;td&gt;17 Oct 2020&lt;/td&gt;
					&lt;td&gt;5 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;10.0&lt;/td&gt;
					&lt;td&gt;31 Mar 2014&lt;/td&gt;
					&lt;td&gt;31 Mar 2019&lt;/td&gt;
					&lt;td&gt;5 Jahre&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Quelle: &lt;a href="https://mariadb.org/about/#maintenance-policy" target="_blank"&gt;MariaDB Server long-term release maintenance periods&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Ich bin ja mal gespannt, wie das die ganzen Distributionen lösen? Die haben nämlich einen deutlich längeren Wartungszeitraum.&lt;/p&gt;
&lt;p&gt;Und wie machen es denn die Mitbewerber, die anderen Datenbanken?&lt;/p&gt;
&lt;h2 id="debian"&gt;Debian&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Debian Long Term Support (LTS) is a project to extend the lifetime of all Debian stable releases to (at least) 5 years.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Quelle: &lt;a href="https://wiki.debian.org/LTS" target="_blank"&gt;Debian Long Term Support&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Version&lt;/th&gt;
					&lt;th&gt;Name&lt;/th&gt;
					&lt;th&gt;Release&lt;/th&gt;
					&lt;th&gt;ext-LTS&lt;/th&gt;
					&lt;th&gt;EoL&lt;/th&gt;
					&lt;th&gt;Dauer&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Debian 13&lt;/td&gt;
					&lt;td&gt;trixie&lt;/td&gt;
					&lt;td&gt;2025-08-09&lt;/td&gt;
					&lt;td&gt;2030-07-01&lt;/td&gt;
					&lt;td&gt;2035-06-30&lt;/td&gt;
					&lt;td&gt;5 / 10 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Debian 12&lt;/td&gt;
					&lt;td&gt;bookworm&lt;/td&gt;
					&lt;td&gt;2023-06-10&lt;/td&gt;
					&lt;td&gt;2028-07-01&lt;/td&gt;
					&lt;td&gt;2033-06-30&lt;/td&gt;
					&lt;td&gt;5 / 10 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Debian 11&lt;/td&gt;
					&lt;td&gt;bullseye&lt;/td&gt;
					&lt;td&gt;2021-08-14&lt;/td&gt;
					&lt;td&gt;2026-09-01&lt;/td&gt;
					&lt;td&gt;2031-06-30&lt;/td&gt;
					&lt;td&gt;5 / 10 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Debian 10&lt;/td&gt;
					&lt;td&gt;buster&lt;/td&gt;
					&lt;td&gt;2019-06-06&lt;/td&gt;
					&lt;td&gt;2024-07-01&lt;/td&gt;
					&lt;td&gt;2029-06-30&lt;/td&gt;
					&lt;td&gt;5 / 10 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Debian 9&lt;/td&gt;
					&lt;td&gt;stretch&lt;/td&gt;
					&lt;td&gt;2017-06-17&lt;/td&gt;
					&lt;td&gt;2022-07-01&lt;/td&gt;
					&lt;td&gt;2027-06-30&lt;/td&gt;
					&lt;td&gt;5 / 10 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Debian 8&lt;/td&gt;
					&lt;td&gt;jessie&lt;/td&gt;
					&lt;td&gt;2015-04-26&lt;/td&gt;
					&lt;td&gt;2020-07-01&lt;/td&gt;
					&lt;td&gt;2025-06-30&lt;/td&gt;
					&lt;td&gt;5 / 10 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Debian 7&lt;/td&gt;
					&lt;td&gt;wheezy&lt;/td&gt;
					&lt;td&gt;2013-05-04&lt;/td&gt;
					&lt;td&gt;2018-06-01&lt;/td&gt;
					&lt;td&gt;2020-06-30&lt;/td&gt;
					&lt;td&gt;5 / 7 Jahre&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Quelle: &lt;a href="https://wiki.debian.org/LTS/Extended" target="_blank"&gt;Extended Long Term Support&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="ubuntu"&gt;Ubuntu&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Version&lt;/th&gt;
					&lt;th&gt;Name&lt;/th&gt;
					&lt;th&gt;Release&lt;/th&gt;
					&lt;th&gt;End of Support&lt;/th&gt;
					&lt;th&gt;EoL&lt;/th&gt;
					&lt;th&gt;Dauer&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Ubuntu 26.04 LTS&lt;/td&gt;
					&lt;td&gt;Resolute Raccoon&lt;/td&gt;
					&lt;td&gt;23. April 2026&lt;/td&gt;
					&lt;td&gt;May 2031&lt;/td&gt;
					&lt;td&gt;April 2041&lt;/td&gt;
					&lt;td&gt;5 / 15 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Ubuntu 24.04 LTS&lt;/td&gt;
					&lt;td&gt;Noble Numbat&lt;/td&gt;
					&lt;td&gt;25. April 2024&lt;/td&gt;
					&lt;td&gt;June 2029&lt;/td&gt;
					&lt;td&gt;April 2039&lt;/td&gt;
					&lt;td&gt;5 / 15 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Ubuntu 22.04 LTS&lt;/td&gt;
					&lt;td&gt;Jammy Jellyfish&lt;/td&gt;
					&lt;td&gt;21. April 2022&lt;/td&gt;
					&lt;td&gt;June 2027&lt;/td&gt;
					&lt;td&gt;April 2037&lt;/td&gt;
					&lt;td&gt;5 / 15 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Ubuntu 20.04 LTS&lt;/td&gt;
					&lt;td&gt;Focal Fossa&lt;/td&gt;
					&lt;td&gt;23. April 2020&lt;/td&gt;
					&lt;td&gt;May 2025&lt;/td&gt;
					&lt;td&gt;April 2035&lt;/td&gt;
					&lt;td&gt;5 / 15 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Ubuntu 18.04 LTS&lt;/td&gt;
					&lt;td&gt;Bionic Beaver&lt;/td&gt;
					&lt;td&gt;26. April 2018&lt;/td&gt;
					&lt;td&gt;June 2023&lt;/td&gt;
					&lt;td&gt;April 2033&lt;/td&gt;
					&lt;td&gt;5 / 15 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Ubuntu 16.04 LTS&lt;/td&gt;
					&lt;td&gt;Xenial Xerus&lt;/td&gt;
					&lt;td&gt;21. April 2016&lt;/td&gt;
					&lt;td&gt;April 2021&lt;/td&gt;
					&lt;td&gt;April 2031&lt;/td&gt;
					&lt;td&gt;5 / 15 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Ubuntu 14.04 LTS&lt;/td&gt;
					&lt;td&gt;Trusty Tahr&lt;/td&gt;
					&lt;td&gt;17. April 2014&lt;/td&gt;
					&lt;td&gt;April 2019&lt;/td&gt;
					&lt;td&gt;April 2029&lt;/td&gt;
					&lt;td&gt;5 / 15 Jahre&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Quelle: &lt;a href="https://documentation.ubuntu.com/project/release-team/list-of-releases/" target="_blank"&gt;List of releases&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="rocky-linux"&gt;Rocky Linux&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Release&lt;/th&gt;
					&lt;th&gt;Codename&lt;/th&gt;
					&lt;th&gt;Release Date&lt;/th&gt;
					&lt;th&gt;Active Support End&lt;/th&gt;
					&lt;th&gt;End of Life&lt;/th&gt;
					&lt;th&gt;Dauer&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Rocky Linux 10&lt;/td&gt;
					&lt;td&gt;Red Quartz&lt;/td&gt;
					&lt;td&gt;June 11, 2025&lt;/td&gt;
					&lt;td&gt;May 31, 2030&lt;/td&gt;
					&lt;td&gt;May 31, 2035&lt;/td&gt;
					&lt;td&gt;5 / 10 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Rocky Linux 9&lt;/td&gt;
					&lt;td&gt;Blue Onyx&lt;/td&gt;
					&lt;td&gt;July 14, 2022&lt;/td&gt;
					&lt;td&gt;May 31, 2027&lt;/td&gt;
					&lt;td&gt;May 31, 2032&lt;/td&gt;
					&lt;td&gt;5 / 10 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Rocky Linux 8&lt;/td&gt;
					&lt;td&gt;Green Obsidian&lt;/td&gt;
					&lt;td&gt;May 1, 2021&lt;/td&gt;
					&lt;td&gt;May 31, 2024&lt;/td&gt;
					&lt;td&gt;May 31, 2029&lt;/td&gt;
					&lt;td&gt;3 / 8 Jahre&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Quelle: &lt;a href="https://wiki.rockylinux.org/rocky/version/#current-supported-releases" target="_blank"&gt;Rocky Linux Release and Version Guide&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="oracle--mysql-releases"&gt;Oracle / MySQL Releases&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Release&lt;/th&gt;
					&lt;th&gt;GA Date&lt;/th&gt;
					&lt;th&gt;Premier Support End&lt;/th&gt;
					&lt;th&gt;Extended Support End&lt;/th&gt;
					&lt;th&gt;Dauer&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;MySQL 9.7&lt;/td&gt;
					&lt;td&gt;Apr 2026&lt;/td&gt;
					&lt;td&gt;Apr 2031&lt;/td&gt;
					&lt;td&gt;Apr 2034&lt;/td&gt;
					&lt;td&gt;5 / 8 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;MySQL 8.4&lt;/td&gt;
					&lt;td&gt;Apr 2024&lt;/td&gt;
					&lt;td&gt;Apr 2029&lt;/td&gt;
					&lt;td&gt;Apr 2032&lt;/td&gt;
					&lt;td&gt;5 / 8 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;MySQL 8.0&lt;/td&gt;
					&lt;td&gt;Apr 2018&lt;/td&gt;
					&lt;td&gt;Apr 2025&lt;/td&gt;
					&lt;td&gt;Apr 2026&lt;/td&gt;
					&lt;td&gt;7 Jahre / 8 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;MySQL 5.7&lt;/td&gt;
					&lt;td&gt;Oct 2015&lt;/td&gt;
					&lt;td&gt;Oct 2020&lt;/td&gt;
					&lt;td&gt;Oct 2023&lt;/td&gt;
					&lt;td&gt;5 Jahre / 8 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;MySQL 5.6&lt;/td&gt;
					&lt;td&gt;Feb 2013&lt;/td&gt;
					&lt;td&gt;Feb 2018&lt;/td&gt;
					&lt;td&gt;Feb 2021&lt;/td&gt;
					&lt;td&gt;5 Jahre / 8 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;MySQL 5.5&lt;/td&gt;
					&lt;td&gt;Dec 2010&lt;/td&gt;
					&lt;td&gt;Dec 2015&lt;/td&gt;
					&lt;td&gt;Dec 2018&lt;/td&gt;
					&lt;td&gt;5 Jahre / 8 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;MySQL 5.1&lt;/td&gt;
					&lt;td&gt;Dec 2008&lt;/td&gt;
					&lt;td&gt;Dec 2013&lt;/td&gt;
					&lt;td&gt;Not Available&lt;/td&gt;
					&lt;td&gt;5 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;MySQL 5.0&lt;/td&gt;
					&lt;td&gt;Oct 2005&lt;/td&gt;
					&lt;td&gt;Dec 2011&lt;/td&gt;
					&lt;td&gt;Not Available&lt;/td&gt;
					&lt;td&gt;6 Jahre&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Quelle: &lt;a href="https://www.oracle.com/us/support/library/lifetime-support-technology-069183.pdf" target="_blank"&gt;Oracle Lifetime Support Policy&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="percona"&gt;Percona&lt;/h2&gt;
&lt;p&gt;Percona Distribution for PostgreSQL (PDPG) und Percona Server for MySQL (PS): Mindestens 5 Jahre, wenn ich die Support-Matrix richtig interpretiere&amp;hellip;&lt;/p&gt;
&lt;p&gt;Quelle: &lt;a href="https://www.percona.com/release-lifecycle-overview/" target="_blank"&gt;Percona Release Lifecycle Overview&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="oursql--villagesql"&gt;OurSQL / VillageSQL&lt;/h2&gt;
&lt;p&gt;Noch keine fertige Software vorhanden und somit noch keine Support Policies, sofern mir bekannt. Sofern das überhaupt mal vorgesehen ist?&lt;/p&gt;
&lt;p&gt;Quelle: &lt;a href="https://oursqlfoundation.org/" target="_blank"&gt;OurSQL&lt;/a&gt; und &lt;a href="https://villagesql.com/" target="_blank"&gt;VillageSQL&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="postgresql"&gt;PostgreSQL&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Version&lt;/th&gt;
					&lt;th&gt;First Release&lt;/th&gt;
					&lt;th&gt;Final Release&lt;/th&gt;
					&lt;th&gt;Dauer&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;18&lt;/td&gt;
					&lt;td&gt;September 25, 2025&lt;/td&gt;
					&lt;td&gt;November 14, 2030&lt;/td&gt;
					&lt;td&gt;5 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;17&lt;/td&gt;
					&lt;td&gt;September 26, 2024&lt;/td&gt;
					&lt;td&gt;November 8, 2029&lt;/td&gt;
					&lt;td&gt;5 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;16&lt;/td&gt;
					&lt;td&gt;September 14, 2023&lt;/td&gt;
					&lt;td&gt;November 9, 2028&lt;/td&gt;
					&lt;td&gt;5 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;15&lt;/td&gt;
					&lt;td&gt;October 13, 2022&lt;/td&gt;
					&lt;td&gt;November 11, 2027&lt;/td&gt;
					&lt;td&gt;5 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;14&lt;/td&gt;
					&lt;td&gt;September 30, 2021&lt;/td&gt;
					&lt;td&gt;November 12, 2026&lt;/td&gt;
					&lt;td&gt;5 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;13&lt;/td&gt;
					&lt;td&gt;September 24, 2020&lt;/td&gt;
					&lt;td&gt;November 13, 2025&lt;/td&gt;
					&lt;td&gt;5 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;12&lt;/td&gt;
					&lt;td&gt;October 3, 2019&lt;/td&gt;
					&lt;td&gt;November 21, 2024&lt;/td&gt;
					&lt;td&gt;5 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;11&lt;/td&gt;
					&lt;td&gt;October 18, 2018&lt;/td&gt;
					&lt;td&gt;November 9, 2023&lt;/td&gt;
					&lt;td&gt;5 Jahre&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;10&lt;/td&gt;
					&lt;td&gt;October 5, 2017&lt;/td&gt;
					&lt;td&gt;November 10, 2022&lt;/td&gt;
					&lt;td&gt;5 Jahre&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Quelle: &lt;a href="https://www.postgresql.org/support/versioning/" target="_blank"&gt;Versioning Policy&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="weitere-quellen"&gt;Weitere Quellen&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://mariadb.com/docs/release-notes/community-server/10.6/what-is-mariadb-106" target="_blank"&gt;MariaDB 10.6 Changes &amp;amp; Improvements&lt;/a&gt;&lt;br&gt;
&lt;em&gt;MariaDB 10.6 is a long-term maintenance stable version. The first stable release was in July 2021, and it will be maintained until July 2026.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mariadb.com/docs/release-notes/community-server/10.11/what-is-mariadb-1011" target="_blank"&gt;MariaDB 10.11 Changes &amp;amp; Improvements&lt;/a&gt;&lt;br&gt;
&lt;em&gt;MariaDB 10.11 is a long-term maintenance release series, maintained until February 2028.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mariadb.com/docs/release-notes/community-server/11.4/what-is-mariadb-114" target="_blank"&gt;MariaDB 11.4 Changes &amp;amp; Improvements&lt;/a&gt;&lt;br&gt;
&lt;em&gt;MariaDB 11.4 is a current long-term series, maintained until May 2029.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mariadb.com/docs/release-notes/community-server/11.8/what-is-mariadb-118" target="_blank"&gt;MariaDB 11.8 Changes &amp;amp; Improvements&lt;/a&gt;&lt;br&gt;
&lt;em&gt;MariaDB 11.8 is a long-term release, maintained until June 2028.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mariadb.com/docs/release-notes/community-server/12.3/mariadb-12.3-changes-and-improvements" target="_blank"&gt;MariaDB 12.3 Changes &amp;amp; Improvements&lt;/a&gt;&lt;br&gt;
&lt;em&gt;MariaDB 12.3 is a long term release, maintained until June 2029.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Wie lädt man Daten am schnellsten in die Datenbank?</title><link>https://www.fromdual.com/de/blog/daten-schnell-in-die-datenbank-laden/</link><pubDate>Tue, 10 Feb 2026 22:48:00 +0100</pubDate><guid>https://www.fromdual.com/de/blog/daten-schnell-in-die-datenbank-laden/</guid><description>&lt;p&gt;Beim letzten Kunden hatten wir wirklich ein paar spannende Fragen zu lösen! Insbesondere auch, weil die Datenbank nicht ganz klein war.&lt;/p&gt;
&lt;p&gt;Hier kurz einige Eckdaten: CPU: 2 Sockel x 24 Kerne x 2 Threads = 96 vCores, 756 G RAM, 2 x 10 Tbyte PCIe SSD im RAID-10 und 7 Tbyte Daten, einige Tausend Mandanten, stark wachsend.&lt;/p&gt;
&lt;p&gt;Der aktuelle Durchsatz: 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 gemittelt über 30 Tage. Tendenz stark steigend. Applikation und Queries nicht durchgängig optimiert. Datenbank Konfiguration: &amp;ldquo;state of the art&amp;rdquo; nicht mit Benchmarks verifiziert. CPU-Auslastung ca. 50% im Schnitt, in der Spitze mehr. I/O System hat noch Luft nach oben.&lt;/p&gt;
&lt;p&gt;Der Kunde sammelt Positions- und sonstige Gerätedaten ein und speichert diese in der Datenbank. Also ein klassisches IoT Problem (mit Zeitreihen, Index Clustered Table, etc.).&lt;/p&gt;
&lt;p&gt;Die Frage, die er gestellt hat, ist: Wie kriege ich am schnellsten Daten von einer Tabelle (pending data, eine Art Queue) in eine andere Tabelle (final data, pro Mandant) kopiert.&lt;/p&gt;
&lt;p&gt;Der Datenfluss sieht in etwa wie folgt aus:&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;3 verschiedene Varianten, die Daten zu kopieren, standen zur Auswahl.&lt;/p&gt;
&lt;h2 id="variante-1-insert-und-delete-einfachste-form"&gt;Variante 1: &lt;code&gt;INSERT&lt;/code&gt; und &lt;code&gt;DELETE&lt;/code&gt; (einfachste Form)&lt;/h2&gt;
&lt;p&gt;Die einfachste Variante ist simples &lt;code&gt;INSERT&lt;/code&gt; und &lt;code&gt;DELETE&lt;/code&gt;. Diese Variante ist insbesondere darum problematisch, weil MariaDB/MySQL und PostgreSQL per default &lt;code&gt;AUTOCOMMIT&lt;/code&gt; eingeschaltet haben (&lt;a href="https://dev.mysql.com/doc/refman/8.4/en/innodb-autocommit-commit-rollback.html" target="_blank"&gt;hier&lt;/a&gt;, &lt;a href="https://mariadb.com/docs/server/reference/sql-statements/transactions/start-transaction" target="_blank"&gt;hier&lt;/a&gt;, &lt;a href="https://www.postgresql.org/docs/current/ecpg-sql-set-autocommit.html" target="_blank"&gt;hier&lt;/a&gt; und &lt;a href="https://www.cybertec-postgresql.com/en/disabling-autocommit-in-postgresql-can-damage-your-health/" target="_blank"&gt;hier&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Damit man sich das ein bisschen besser vorstellen kann, hier etwas Pseudocode dazu:&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;Wenn wir also 20 k Zeilen umkopieren wollen, verursacht diese Variante: 40 k &lt;code&gt;COMMIT&lt;/code&gt;s (&lt;code&gt;fsync&lt;/code&gt;) und 42 k Netzwerk-Roundtrips!&lt;/p&gt;
&lt;h2 id="variante-2-start-transaction-und-insert-und-delete"&gt;Variante 2: &lt;code&gt;START TRANSACTION&lt;/code&gt; und &lt;code&gt;INSERT&lt;/code&gt; und &lt;code&gt;DELETE&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Diese Variante wird von versierteren Datenbank-Entwicklern verwendet. Hier der passende Pseudocode dazu:&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;Wenn wir in diesem Beispiel 20 k Zeilen umkopieren wollen verursacht diese Variante nur noch 2 k &lt;code&gt;COMMIT&lt;/code&gt;s (&lt;code&gt;fsync&lt;/code&gt;)! Also 20 mal weniger! Aber dafür 46 k Netzwerk-Roundtrips (10% mehr).&lt;/p&gt;
&lt;h2 id="variante-3-start-transaction-und-optimiertes-insert-und-delete"&gt;Variante 3: &lt;code&gt;START TRANSACTION&lt;/code&gt; und optimiertes &lt;code&gt;INSERT&lt;/code&gt; und &lt;code&gt;DELETE&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Diese Variante ist programmiertechnisch ein klein wenig anspruchsvoller. Sie wird verwendet, wenn man den Grenzen des Möglich etwas näher kommen will. Hier der Pseudocode dazu:&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;Und diese 3. Variante verursacht bei 20 k Zeilen ebenfalls nur noch 2 k &lt;code&gt;COMMIT&lt;/code&gt;&amp;rsquo;s (&lt;code&gt;fsync&lt;/code&gt;), spart sich aber die Schleife über die &lt;code&gt;INSERT&lt;/code&gt; und &lt;code&gt;DELETE&lt;/code&gt; Statements in der Datenbank. Wir sparen also einerseits Netzwerk Round-Trips (nur noch 10 k) und CPU-Cycles auf der Datenbank (welche sich schlecht skalieren lassen) zum Parsen der Queries.&lt;/p&gt;
&lt;h2 id="test-set-up"&gt;Test Set-up&lt;/h2&gt;
&lt;p&gt;Um das Ganze zu testen haben wir ein kleines Script vorbereitet: &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="vorbereitung-und-ausführung-mit-mariadbmysql"&gt;Vorbereitung und Ausführung mit MariaDB/MySQL&lt;/h3&gt;
&lt;p&gt;Mit folgenden Befehlen kann man diese Tests selber ausführen:&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;Die Messwerte kann sich jeder selbst mit dem entsprechenden Test-Skript erarbeiten.&lt;/p&gt;
&lt;h3 id="vorbereitung-und-ausführung-mit-postgresql"&gt;Vorbereitung und Ausführung mit PostgreSQL&lt;/h3&gt;
&lt;p&gt;Mit folgenden Befehlen kann man diese Tests selber ausführen:&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;Die Messwerte kann sich jeder selbst mit dem entsprechenden Test-Skript erarbeiten.&lt;/p&gt;
&lt;h2 id="resultate"&gt;Resultate&lt;/h2&gt;
&lt;p&gt;Um unnötige Diskussionen zu vermeiden, haben wir hier &amp;ldquo;nur&amp;rdquo; die relative Performance (Laufzeit) aufgeführt, wie es MarkC in letzter Zeit auch macht. Unsere Messwerte können gerne bilateral zur Verfügung stellen. Sie lassen sich aber einfach mit dem Test-Skript selber reproduzieren.&lt;/p&gt;
&lt;p&gt;Weniger ist besser:&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;Achtung&lt;/strong&gt;: Die Werte von MariaDB/MySQL und PostgreSQL lassen sich NICHT direkt vergleichen!&lt;/p&gt;
&lt;br&gt;
&lt;p&gt;Und hier noch die graphische Auswertung:&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="bemerkungen"&gt;Bemerkungen&lt;/h2&gt;
&lt;p&gt;Mit schnelleren Disks, wäre der Unterschied zwischen 1 und 2/3 wahrscheinlich nicht mehr ganz so gravierend ausgefallen. Tests beim Kunden haben &amp;ldquo;nur&amp;rdquo; ca. Faktor 5 (statt Faktor 9 bis 16) unterschied gezeigt.&lt;/p&gt;
&lt;p&gt;Es gibt sicher weitere Möglichkeiten, wie man diesen Lade-Vorgang noch optimieren kann. Hier kommen mir in den Sinn:&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;, wenn möglich&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 der Resultate?&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Vielleicht sollte ich mich mal um einen Profiler kümmern (&lt;a href="https://xdebug.org/" target="_blank"&gt;Xdebug&lt;/a&gt; oder &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="weitere-beiträge"&gt;Weitere Beiträge&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;</description></item><item><title>MariaDB hat das Konzept des dynamisch konfigurierbaren Buffer Pools kaputt gemacht!</title><link>https://www.fromdual.com/de/blog/mariadb-dynamisch-konfigurierbare-buffer-pool-kaputt/</link><pubDate>Sun, 08 Feb 2026 20:25:00 +0100</pubDate><guid>https://www.fromdual.com/de/blog/mariadb-dynamisch-konfigurierbare-buffer-pool-kaputt/</guid><description>&lt;h2 id="problembeschreibung"&gt;Problembeschreibung&lt;/h2&gt;
&lt;p&gt;MySQL hat mit 5.7.5 im September 2014 den dynamisch konfigurierbaren InnoDB Buffer Pool eingeführt (&lt;a href="https://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-5.html" target="_blank"&gt;hier&lt;/a&gt; und &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;hier&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 hat dieses Feature im September 2016 übernommen (&lt;a href="https://mariadb.com/docs/release-notes/community-server/old-releases/10.2/10.2.2#notable-changes" target="_blank"&gt;Quelle&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;Das Problematische ist einerseits, dass dieses Feature jetzt nicht mehr funktioniert wie bis anhin und nicht mehr funktioniert wie erwartet. Anderseits haben sie das Verhalten im Frühling 2025 innerhalb einer Major Release Serie (LTS) geändert, was meiner Meinung nach ein absolutes no-go ist (&lt;a href="https://mariadb.com/docs/server/server-usage/storage-engines/innodb/innodb-buffer-pool#buffer-pool-changes" target="_blank"&gt;Quelle&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;Und zudem ist die Beschreibung dazu recht dürftig (&lt;a href="https://mariadb.com/docs/release-notes/community-server/10.11/10.11.12#innodb" target="_blank"&gt;Quelle&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;Im entsprechende Worklog (&lt;a href="https://jira.mariadb.org/browse/MDEV-36197" target="_blank"&gt;MDEV-36197&lt;/a&gt;) beschreibt MarkoM zudem ein anderes Verhalten:&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;was meiner Meinung nach wesentlich sinnvoller gewesen wäre.&lt;/p&gt;
&lt;h2 id="wie-ging-es-früher"&gt;Wie ging es früher?&lt;/h2&gt;
&lt;p&gt;Wie ging es früher bei MariaDB und heute immer noch bei 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;Somit alles OK. Funktioniert wie erwartet und wie gewohnt.&lt;/p&gt;
&lt;h2 id="was-macht-mariadb-heute"&gt;Was macht MariaDB heute?&lt;/h2&gt;
&lt;p&gt;Was passiert heute bei 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;Also gar nichts! Nicht mal einen Fehler, nur eine Warnung. Und wenn ich nicht genau schaue, merke ich nicht mal, dass da was nicht geklappt hat.&lt;/p&gt;
&lt;p&gt;Im MariaDB Error Log steht:&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;Wenn man dann etwas auf die Suche geht, findet man heraus, das hier etwas geändert hat: &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; und versucht intuitiv:&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;Geht aber auch nicht.&lt;/p&gt;
&lt;p&gt;Das bedeutet, man muss die Datenbank neu starten! Und das möglicherweise genau in dem Augenblick, in welchem man den Neustart eigentlich gar nicht gebrauchen kann und dieses Feature nötig hätte&amp;hellip;&lt;/p&gt;
&lt;p&gt;In der Doku steht dann auch (&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;Quelle&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;und (&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;Quelle&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;Das heisst, ich muss mir wieder vorher Gedanken machen, wie gross ich &lt;code&gt;innodb_buffer_pool_size_max&lt;/code&gt; machen soll und kann erst dann nachträglich im Betrieb korrigieren, sollte ich es vergessen oder mich verschätzt haben.&lt;/p&gt;
&lt;p&gt;Dies ist meiner Meinung nach ein vollkommener Rückschritt aus betrieblicher Sicht. Wahrscheinlich ist das wieder eine Implementierung für irgendwelche Cloud-only as a Service Lösungen (Enterprise?).&lt;/p&gt;
&lt;p&gt;Mein Vorschlage ist: Entweder, wie im MDEV vorgeschlagen: 0 soll dieses Feature ausschalten und das Verhalten wie bisher sein oder aber, der Default-Wert soll auf 75% der RAM-Size gesetzt werden, so wie es &lt;code&gt;innodb_dedicated_server&lt;/code&gt; bei MySQL macht.&lt;/p&gt;
&lt;p&gt;Ich habe mich erdreistet hier mal einen Bug zu eröffnen: &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 hat mit verdankenswerter Weise noch folgen Link empfohlen: &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;. Ich schein nich der Einzige zu sein, dem dies Änderung auf diese Art sauer aufgestossen ist&amp;hellip;&lt;/p&gt;
&lt;h2 id="wie-macht-das-postgresql"&gt;Wie macht das PostgreSQL?&lt;/h2&gt;
&lt;p&gt;PostgreSQL ist zur Zeit (noch) nicht in der Lage, &lt;code&gt;shared_buffers&lt;/code&gt; dynamisch zu ändern. Der Default ist üblicherweise 128M. Hier gilt die Faustregel, ähnlich wie bei MyISAM, 25 - 40% vom RAM. Das Fehlen dieses Features ist bei PostgreSQL aber wahrscheinlich nicht so gravierend, da sich PostgreSQL stark auf den Filesystem Cache verlässt, ähnlich wie MyISAM.&lt;/p&gt;
&lt;p&gt;Quelle: &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="kommentare"&gt;Kommentare&lt;/h2&gt;
&lt;p&gt;Siehe &lt;a href="https://www.fromdual.com/blog/mariadb-dynamically-configurable-buffer-pool-broken/#mariadb-dynamically-configurable-buffer-pool-broken/%23comment-1049"&gt;hier&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Wie viel Platz braucht NULL?</title><link>https://www.fromdual.com/de/blog/wie-viel-platz-braucht-null/</link><pubDate>Sun, 08 Feb 2026 10:36:00 +0100</pubDate><guid>https://www.fromdual.com/de/blog/wie-viel-platz-braucht-null/</guid><description>&lt;p&gt;Beim letzten Beratungseinsatz beim Kunden, kam dieser freudestrahlend auf mich zu mit der Bemerkung: Er habe meinen Rat befolgt und sämtiche Primary Key Spalten von &lt;code&gt;BIGINT&lt;/code&gt; (8 byte) auf &lt;code&gt;INT&lt;/code&gt; (4 byte) geändert und das habe viel gebracht! Seine MySQL 8.4er Datenbank sei jetzt um 750 Gbyte kleiner geworden (von 5.5 Tbyte). Schön!&lt;/p&gt;
&lt;p&gt;Und ja, ich weiss, dass wiederspricht den Empfehlungen einiger meiner PostgreSQL Kollegen (&lt;a href="https://www.crunchydata.com/blog/postgres-serials-should-be-bigint-and-how-to-migrate" target="_blank"&gt;hier&lt;/a&gt; und &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;hier&lt;/a&gt;). In der MySQL-Welt wird eher auf solche Dinge Wert gelegt (&lt;a href="https://dev.mysql.com/doc/refman/8.4/en/data-size.html" target="_blank"&gt;Quelle&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;Zudem funktioniert InnoDB ein klein wening anders (Index Clusterd Table und Primary Key in allen Secondary Keys) als PostgreSQL (Heap Table, Indices mit Row Pointer (&lt;code&gt;ctid&lt;/code&gt;)).&lt;/p&gt;
&lt;p&gt;Aber das ist eigentlich nicht das Thema. Sofort im Anschluss kam er nämlich mit der Frage, ob das AusNULLen von Spalten vom Typ &lt;code&gt;DOUBLE&lt;/code&gt; (8 byte, in PostgreSQL-Sprech &lt;code&gt;DOUBLE PRECISION&lt;/code&gt;) auch Platzeinsparungen brächte oder ob er die Spalten lieber gleich droppen solle. Meine erste reflexartige Antwort bei &lt;code&gt;DOUBLE&lt;/code&gt; war: &lt;code&gt;NULL&lt;/code&gt; bringt was, gefolgt von &lt;code&gt;OPTIMIZE TABLE&lt;/code&gt; (in PostgreSQL-Sprech &lt;code&gt;VACUUM FULL&lt;/code&gt;). Der zweite Gedanke war aber, &lt;code&gt;DOUBLE&lt;/code&gt; ist ein Datentyp fixer länger, gilt das mit &lt;code&gt;NULL&lt;/code&gt; da auch oder nur bei Datentypen mit variabler Länge? Vorsicht ist die Mutter der Porzelankiste! Liebe zuerst mal das Manual konsultieren&amp;hellip;&lt;/p&gt;
&lt;p&gt;Und dort steht (&lt;a href="https://dev.mysql.com/doc/refman/8.4/en/data-size.html" target="_blank"&gt;Quelle&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;und (&lt;a href="https://dev.mysql.com/doc/refman/8.4/en/innodb-row-format.html" target="_blank"&gt;Quelle&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="versuch-mit-mariadbmysql"&gt;Versuch mit MariaDB/MySQL&lt;/h2&gt;
&lt;h3 id="versuchsaufbau"&gt;Versuchsaufbau&lt;/h3&gt;
&lt;p&gt;Irgendwie ist mir das etwas zu komplizert beschrieben. Eine kleine Skizze würde vielleicht helfen? Also machen wir einen Versuch:&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;Die Tabelle wird sowohl bei MariaDB als auch bei MySQL bei 16 M Rows ca. 1.8 Gbyte gross. Da dieses Informationen im &lt;code&gt;INFORMATION_SCHEMA&lt;/code&gt; nur sehr unpräzise angegeben werden schauen wir auf dem Filesystem nach:&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="defragementieren-der-tabelle"&gt;Defragementieren der Tabelle&lt;/h3&gt;
&lt;p&gt;Dann &amp;ldquo;defragmentieren&amp;rdquo; wir die Tabelle mit dem &lt;code&gt;OPTIMIZE TABLE&lt;/code&gt; Befehl:&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;Achtung&lt;/strong&gt;: Die Tabelle wir einmal umkopiert! Es braucht also kurzzeitig die doppelte Menge an Diskplatz! Dies können wird schön beobachten während der &lt;code&gt;OPTIMIZE TABLE&lt;/code&gt; Befehls läuft:&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;Das Resultat ist dann aber erstaunlich! Bei MariaDB ist die Tabelle in etwas gleich gross geblieben:&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;Bei MySQL hingegen ist die Tabelle nach dem &amp;ldquo;defragmentieren&amp;rdquo; sogar angewachsen und zwar um ca. 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;Wenn wir den &lt;code&gt;OPTIMIZE TABLE&lt;/code&gt; Befehl nochmal ausführen bleibt die Grösse sowohl bei MariaDB als auch MySQL konstant:&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="versuch-1-ausnullen"&gt;Versuch 1: AusNULLen&lt;/h3&gt;
&lt;p&gt;Jetzt &lt;code&gt;NULL&lt;/code&gt;en wir die Werte aus:&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;Nach diesem Schritt sind die Grössen der Dateien sogar noch etwas gewachsen:&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;Anschliessend defragemntieren wir die Tabelle erneut mit dem &lt;code&gt;OPTIMIZE TABLE&lt;/code&gt; Befehl. Die Tabellen schrumpfen wie erwartet.&lt;/p&gt;
&lt;p&gt;MariaDB (auf 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 (auf 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;Ein erneutes &lt;code&gt;OPTIMIZE TABLE&lt;/code&gt; bringt anschliessend KEINE Veränderung mehr in der Dateigrösse&amp;hellip;&lt;/p&gt;
&lt;h3 id="versuch-2-löschen-der-spalten"&gt;Versuch 2: Löschen der Spalten&lt;/h3&gt;
&lt;p&gt;Jetzt probieren wir das Ganze nochmal mit dem &lt;code&gt;DROP COLUMN&lt;/code&gt; Befehl. Die Ausganglage ist wieder die selbe wie oben beschrieben:&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;Nach dem &lt;code&gt;OPTIMIZE TABLE&lt;/code&gt; Befehl sehen die Werte ähnlich aus wie im ersten Versuch:&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;Ein erneutes &lt;code&gt;OPTIMIZE TABLE&lt;/code&gt; bring ebenfalls keine weiteren Veränderungen mehr, wie oben:&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;Und jetzt der eigentliche zweite Versuch mit Löschen der Spalten:&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;Als erstes stellen wir fest, dass der Befehl &lt;code&gt;INSTANTANEOUS&lt;/code&gt; ist, als keinerlei Änderung an den Daten vornimmt sondern nur Änderung an den Metadaten. Das ist auf der einen Seite gut, da der Einfluss auf die Applikation dadurch minimal gehalten wird.. Auf der anderen Seite bedeutet das ja auch: Keine Platzersparnis.&lt;/p&gt;
&lt;p&gt;Also rücken wir dem Ganzen wieder mit dem &lt;code&gt;OPTIMIZE TABLE&lt;/code&gt; Befehl zu Leibe:&lt;/p&gt;
&lt;p&gt;MariaDB (auf 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 (auf 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="fazit"&gt;Fazit&lt;/h3&gt;
&lt;p&gt;Sowohl das ausNULLen der Spalten als auch das Löschen der Spalten bringen signifikante Platzersparnis. Wobei das Löschen der Spalten nochmal ca. 7% mehr bringt als das ausNULLen. Wenn es applikatorisch möglich ist, sollte man daher nicht mehr benötigte Spalen löschen, wenn nicht möglich, diese zumindest ausNULLen.&lt;/p&gt;
&lt;h2 id="versuch-mit-postgresql"&gt;Versuch mit PostgreSQL&lt;/h2&gt;
&lt;p&gt;Und jetzt schauen wir uns das Ganze noch bei PostgreSQL 19devel an.&lt;/p&gt;
&lt;h3 id="versuchsaufbau-1"&gt;Versuchsaufbau&lt;/h3&gt;
&lt;p&gt;Der Versuchsaufbau erfolgt analog zu 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;Zuerst wollen wir mal wissen, wie gross die Tabelle eigentlich geworden ist. PostgreSQL scheint diese Informationen sehr genau zu kennen:&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;Dann wollen wir wissen, wo im Filesystem diese Dateien zu finden sind:&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;Tabellen- und Indexgrösse im 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; bedeutet &amp;ldquo;free space map&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*_vm&lt;/code&gt; bedeutet &amp;ldquo;visibility map&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*.1&lt;/code&gt; bedeutet 2. Segment des Objekts (Tabelle oder Index)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;PostgreSQL scheint per default mit Segmenten von 1 Gbyte Grösse zu arbeiten und im Unterschied zu MariaDB/MySQL (&lt;code&gt;INFORMATION_SCHEMA&lt;/code&gt;) sehr genau zu wissen, wie gross seine Dateien sind. Und die Diskrepanz von oben (zwischen &lt;code&gt;tot_rel_siz&lt;/code&gt; und &lt;code&gt;tab_and_idx_siz&lt;/code&gt;) lässt sich durch die &lt;code&gt;fsm&lt;/code&gt; und die &lt;code&gt;vm&lt;/code&gt;-Dateien erklären.&lt;/p&gt;
&lt;p&gt;Das PostgreSQL Equivalent zum MariaDB/MySQL &lt;code&gt;OPTIMIZE TABLE&lt;/code&gt; ist der Befehl &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;Das erste, was man feststellt, ist, dass PostgreSQL ganz viele Dateien anlangt und die &amp;ldquo;free space map&amp;rdquo; Datei ist verschwunden. Die Tabellen-Segmente sind dafür, im Unterschied zu MariaDB/MySQL gleich gross geblieben. Zudem sieht man, dass die alte Tabelle &amp;ldquo;verschwunden&amp;rdquo; (40965, 40970) und eine neue entstanden (40972 und 40975) ist. Der &lt;code&gt;VACUUM FULL&lt;/code&gt; Befehl bei PostgreSQL erstellt also ebenfalls, wie bei MariaDB/MySQL eine Kopie der Daten.&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;Um zu verstehen, welche Dateien/Objekte denn sonst noch so alles angelangt wurden, hilft folgendes 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="versuch-1-ausnullen-1"&gt;Versuch 1: AusNULLen&lt;/h3&gt;
&lt;p&gt;Dann &lt;code&gt;NULL&lt;/code&gt;en wir die Spalten bei PostgreSQL ebenfalls aus. Die Sicht aufs Datei-System sparen wir uns ab hier, da PostgreSQL die Dateigrössen ja genau zu kennen scheint, wie wir oben gesehen haben:&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;Hier sehen wir, dass die Tabellen-Segmente massiv anwachsen (+37%), was in der PostgreSQL Terminologie als &amp;ldquo;bloat&amp;rdquo; bezeichnet wird. Die MVCC Implementierung von PostgreSQL speichert sowohl die alte auch nie neue Version der Row &amp;ldquo;in-place&amp;rdquo; also direkt in der Tabelle im Gegensatz zu MariaDB/MySQL welche die alte Version im UNDO Space und die neue Row &amp;ldquo;in-place&amp;rdquo; speichert. Auch die Index-Datei vergrössert sich signifikant (+100%). Dazu müssen wir noch weiter forschen, warum das so ist. Zudem wird wieder eine &amp;ldquo;free space map&amp;rdquo; erstellt (Differenz zwischen &lt;code&gt;tot_rel_siz&lt;/code&gt; und &lt;code&gt;tab_and_idx_siz&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Ein anschliessendes &lt;code&gt;VACUUM FULL&lt;/code&gt; verkleinert die Tabelle (auf 28%) und den Index (auf 50%) wieder im Bezug auf die vorherige Grösse:&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;und auch in Bezug zur ursprünglichen Grösse wird die Tabelle (auf 38%) und der Index (auf 100%) wieder kleiner. Warum der Index gleich gross geblieben ist und nur die Tabelle geschrumpft ist, muss noch recherchiert werden&amp;hellip;&lt;/p&gt;
&lt;h3 id="versuch-2-löschen-der-spalten-1"&gt;Versuch 2: Löschen der Spalten&lt;/h3&gt;
&lt;p&gt;Anschliessend werden die Spalten noch mit &lt;code&gt;DROP COLUMN&lt;/code&gt; gelöscht.&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;Da die Rückmeldung sofort kam, kann davon ausgegangen werden, dass diese Operation ebenfalls instant erfolgt. Leider habe ich auf die Schnelle nichts in der PostgreSQL Dokumentation dazu gefunden.&lt;/p&gt;
&lt;p&gt;An der Grösse hat sich nichts signifikant geändert, was bei einer Instant-Operation ja eigentlich auch zu erwarten ist. Dass sich jedoch nach dem &lt;code&gt;VACUUM FULL&lt;/code&gt; Befehl die Grösse nicht mehr geändert hat, hat doch ein wenig erstaunt:&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="bemerkungen"&gt;Bemerkungen&lt;/h2&gt;
&lt;p&gt;Locking bei PostgreSQL funktioniert wie folgt:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;VACUUM&lt;/code&gt; Nebenläufige DML Befehle sind möglich ähnlich wie beim MariaDB/MySQL &lt;code&gt;OPTIMIZE TABLE&lt;/code&gt; Befehl. Das Resultat ist aber nicht ganz das Selbe.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;VACUUM FULL&lt;/code&gt; verursacht einen &lt;code&gt;ACCESS EXCLUSIVE&lt;/code&gt; Lock. Ähnlich wie bei MariaDB/MySQL 5.5 und älter beim &lt;code&gt;OPTIMIZE TABLE&lt;/code&gt; Befehl. DML und &lt;code&gt;SELECT&lt;/code&gt; Befehle sind NICHT zulässig.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="quellen"&gt;Quellen&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="zusatzversuche"&gt;Zusatzversuche&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Statt &lt;code&gt;0.0&lt;/code&gt; wurde &lt;code&gt;NULL&lt;/code&gt; in die Spalten &lt;code&gt;d0&lt;/code&gt; - &lt;code&gt;d9&lt;/code&gt; abgefüllt. Die Tabelle blieb klein (&lt;code&gt;tot_rel_siz_prtty = 1068 MB&lt;/code&gt;). Es lohnt sich also auch bei PostgreSQL &lt;code&gt;NULL&lt;/code&gt; statt Dummy-Werte zu speichern.&lt;/li&gt;
&lt;li&gt;Die Spalten &lt;code&gt;d0&lt;/code&gt; - &lt;code&gt;d9&lt;/code&gt; wurden mit &lt;code&gt;DOUBLE PRECISION NOT NULL&lt;/code&gt; angelegt und die Werte &lt;code&gt;0.0&lt;/code&gt; abgefüllt. Keinen Effekt: Die Tabelle blieb gross (&lt;code&gt;tot_rel_siz_prtty = 2232 MB&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>CSV Dateien in die Datenbank laden</title><link>https://www.fromdual.com/de/blog/csv-dateien-in-die-datenbank-laden/</link><pubDate>Fri, 06 Feb 2026 17:12:00 +0100</pubDate><guid>https://www.fromdual.com/de/blog/csv-dateien-in-die-datenbank-laden/</guid><description>&lt;p&gt;Kürzlich wollte ich für eine persönliche kleine Spielerei die Wohnorte der Vereinsmitlieder meines Vereins auf einer Karte darstellen (&lt;a href="https://www.shinguz.ch/computer/gis/igoc-mitglieder/" target="_blank"&gt;IGOC Mitglieder&lt;/a&gt;). Die Adressen der Vereinsmitglieder waren mir bekannt. Nicht aber die Koordinaten der Wohnorte.&lt;/p&gt;
&lt;p&gt;Also machte ich mich auf die Suche nach den Koordinaten und wurde beim Bundesamt für Landestopografie (&lt;a href="https://www.swisstopo.admin.ch/de" target="_blank"&gt;swisstopo&lt;/a&gt;) fündig.&lt;/p&gt;
&lt;p&gt;Die Daten werden dort als CSV Datei zur Verfügung gestellt. Details hier: &lt;a href="https://www.shinguz.ch/computer/gis/schweizer-ortschafts-koordinaten/" target="_blank"&gt;Schweizer Ortschafts-Koordinaten&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Wie lädt man jetzt diese Daten in eine Datenbank?&lt;/p&gt;
&lt;h2 id="laden-der-daten-mit-mariadbmysql"&gt;Laden der Daten mit MariaDB/MySQL&lt;/h2&gt;
&lt;p&gt;MariaDB und MySQL haben dafür den Befehl &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;Anschliessend kann man die Daten in der Datenbank abfragen:&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;Bzw. etwas präziser:&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;Das ausmisten der Duplikate überlasse ich der geneigten Lesering oder dem geneigten Leser&amp;hellip; :-)&lt;/p&gt;
&lt;p&gt;Soweit so gut, jetzt zu den Feinheiten:&lt;/p&gt;
&lt;h3 id="unterschiede-zwischen-mariadb-und-mysql"&gt;Unterschiede zwischen MariaDB und MySQL&lt;/h3&gt;
&lt;p&gt;Das oben beschriebene Verfahren funktioniert einwandfrei bei MariaDB 11.4 und 11.8. Bei MySQL 8.4 gibt es kleine Unterschiede:&lt;/p&gt;
&lt;p&gt;Die erste Fehlermeldung, die das Laden verhindert ist diese:&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;Sie kann relativ einfach umgangen werden mit dem Befehl:&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;Beim nächsten Versuch wird man wie folgt scheitern:&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;Dieses Problem kann gelöst werden, indem man den MySQL Client wie folgt startet:&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="quellen"&gt;Quellen&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="laden-der-daten-mit-postgresql"&gt;Laden der Daten mit PostgreSQL&lt;/h2&gt;
&lt;p&gt;PostgreSQL hat hierfür den Befehl &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;Auch hier erhalten wir das Resultat wie erwartet in der für PostgreSQL üblichen Form:&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="quellen-1"&gt;Quellen&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="kleine-unterschiede-zwischen-mariadbmysql-und-postgresql"&gt;Kleine Unterschiede zwischen MariaDB/MySQL und PostgreSQL&lt;/h2&gt;
&lt;p&gt;Grundsätzlich lautet der Ladebefehl in den beiden Datenbankwelten gänzlich unterschiedlich.&lt;/p&gt;
&lt;p&gt;Bei MariaDB und PostgreSQL laufen die Befehle &amp;lsquo;out-of-the-box&amp;rsquo;. MySQL hat hier noch zwei zusätzliche Sicherheitshürden eingebaut.&lt;/p&gt;
&lt;p&gt;PostgreSQL kennt keine &lt;code&gt;UNSIGNED&lt;/code&gt; Integer Datentypen, daher muss der nächstgrössere Datentyp (&lt;code&gt;INT&lt;/code&gt;) verwendet werden, was ein klein wenig weniger platzsparender ist als bei MariaDB/MySQL.&lt;/p&gt;
&lt;h2 id="bemerkungen"&gt;Bemerkungen&lt;/h2&gt;
&lt;p&gt;Als wir den selben Test vor ein paar wenigen Tagen gemacht hatten, gab es noch eine Fehler beim Laden. Es scheint also, dass sich die Datenquelle ebenfalls minimal geändert hat&amp;hellip;&lt;/p&gt;
&lt;p&gt;Ich habe auf die Schnelle nicht herausgefunden, ob es zu diesen Lade-Befehlen einen SQL-Standard gibt und wenn ja, ob MariaDB/MySQL oder PostgreSQL hier Standard konform sind.&lt;/p&gt;
&lt;p&gt;Und natürlich gibt es noch andere Möglichkeiten, wie man seine CSV Daten in die Datenbank rein kriegt&amp;hellip;&lt;/p&gt;
&lt;p&gt;Die Tools &lt;code&gt;mariadb-import&lt;/code&gt;/&lt;code&gt;mysqlimport&lt;/code&gt; verwendet man, wenn man das von der Kommandozeile aus machen möchte. Die CSV Storage Engine kann ebenfalls dazu missbraucht werden (Details siehe &lt;a href="https://www.fromdual.com/blog/csv-storage-engine/"&gt;hier&lt;/a&gt;). Eine offiziell unterstütze Variante ist ist die MariaDB CONNECT Storage Engine mit dem CSV Type (siehe &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;hier&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;So wie es aussieht, wird die CONNECT Storage Engine von MariaDB leider nicht mehr weiter supportet?!? Und auch das Tool &lt;code&gt;mydumper&lt;/code&gt;/&lt;code&gt;myloader&lt;/code&gt; scheint mit CSV Dateien umgehen zu können.&lt;/p&gt;
&lt;p&gt;Und natürlich kann das ganze auch applikatorisch gelöst werden&amp;hellip;&lt;/p&gt;
&lt;p&gt;Bei PostgreSQL gibt es folgende Möglichkeiten:&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;dann von der Shell aus:&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;Und die Variante über den Foreign Data Wrapper (FWD). Habe ich aber nicht ausprobiert:&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="nachtrag"&gt;Nachtrag&lt;/h2&gt;
&lt;p&gt;Der MariaDB/MySQL Datentyp &lt;code&gt;DOUBLE&lt;/code&gt; heist in ProsgreSQL &lt;code&gt;DOUBLE PRECISION&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Schulung Galera Cluster für MariaDB und MySQL im September 2019 in Berlin</title><link>https://www.fromdual.com/de/blog/schulung-galera-cluster-fuer-mariadb-und-mysql-im-september-2019-in-berlin/</link><pubDate>Sun, 18 Aug 2019 21:38:36 +0000</pubDate><guid>https://www.fromdual.com/de/blog/schulung-galera-cluster-fuer-mariadb-und-mysql-im-september-2019-in-berlin/</guid><description>&lt;p&gt;Die Sommerferien sind vorbei. Mit neuem Elan in den Herbst! Zeit für eine Weiterbildung?&lt;/p&gt;
&lt;p&gt;Vom 19. bis 20. September führt FromDual wieder die Galera Cluster Schulung &lt;a href="https://www.fromdual.com/de/galera-cluster-fuer-mysql-mariadb-schulung" title="Galera Cluster für MySQL und MariaDB Schulung"&gt;Galera Cluster für MySQL und MariaDB&lt;/a&gt; in Berlin durch. Siehe auch unsere weiteren &lt;a href="https://www.fromdual.com/de/mysql-mariadb-schulungstermine" title="MySQL und MariaDB Schulungstermine"&gt;Schulungstermine&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Es hat noch Plätze frei! Anmelden können Sie sich direkt bei unserem Schulungs-Partner, der &lt;a href="https://www.heinlein-support.de/schulung/galera-cluster-fuer-mysql-mariadb" target="_blank" title="Galera Cluster für MySQL und MariaDB"&gt;Heinlein Akademie&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Diese MariaDB/MySQL Weiterbildung richtet sich an alle DBAs, DevOps und System Administratoren, welche MariaDB und MySQL Datenbanken mit einem Galera Cluster zu betreuen haben und gerne besser verstehen wollen, wie Sie den Galera Cluster sicher und stabil betreiben.&lt;/p&gt;
&lt;p&gt;In dieser Schulung behandeln wir, wie Sie einen Galera Cluster richtig designen und aufsetzten, wie Sie ihn installieren, konfigurieren und betreiben. Zudem betrachten wir mögliche Load Balancing Mechanismen und besprechen Performance Fragen zu Galera.&lt;/p&gt;
&lt;p&gt;Das Ganze ist mit zahlreichen Übungen versehen, damit Sie das gelernte auch gleich praktisch anwenden können!&lt;/p&gt;
&lt;p&gt;Die Schulung findet in deutscher Sprache statt.&lt;/p&gt;
&lt;p&gt;Den detaillierten Inhalt dieser zweitägigen Galera Cluster Schulung finden Sie &lt;a href="https://www.fromdual.com/de/galera-cluster-fuer-mysql-mariadb-schulung" title="Galera Cluster für MySQL und MariaDB Schulung"&gt;hier&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Bei weiteren Fragen nehmen Sie bitte mit uns &lt;a href="https://www.fromdual.com/contact" title="FromDual kontaktieren"&gt;Kontakt&lt;/a&gt; auf.&lt;/p&gt;</description></item><item><title>Schulung MariaDB/MySQL für Fortgeschrittene im August 2019 in Köln</title><link>https://www.fromdual.com/de/blog/schulung-mariadb-mysql-fuer-fortgeschrittene-im-august-2019-in-koeln/</link><pubDate>Mon, 29 Jul 2019 14:29:01 +0000</pubDate><guid>https://www.fromdual.com/de/blog/schulung-mariadb-mysql-fuer-fortgeschrittene-im-august-2019-in-koeln/</guid><description>&lt;p&gt;Sommerferien-Zeit – für all die Daheimgebliebenen dürfte es jetzt hoffentlich etwas ruhiger zu und her gehen. Zeit für eine Weiterbildung? Es bleibt nicht mehr viel Zeit, das Jahres-Schulungs-Budget aufzubrauchen!&lt;/p&gt;
&lt;p&gt;Vom 19. bis 23. August führt FromDual wieder einmal die Schulung &lt;a href="https://www.fromdual.com/de/mysql-mariadb-fuer-fortgeschrittene-schulung" title="MySQL und MariaDB fortgeschrittene Schulung"&gt;MariaDB und MySQL für Fortgeschrittene&lt;/a&gt; in Köln durch. Siehe auch unsere &lt;a href="https://www.fromdual.com/de/mysql-mariadb-schulungstermine" title="MySQL und MariaDB Schulungstermine"&gt;Schulungstermine&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Es hat noch Plätze frei! Anmelden kann man sich direkt bei unserem Schulungs-Partner, der&lt;/p&gt;
&lt;p&gt;GFU Cyrus AG.&lt;/p&gt;
&lt;p&gt;Diese MariaDB/MySQL Weiterbildung richtet sich an alle DBAs, DevOps und System Administratoren, welche MariaDB und MySQL Datenbanken zu betreuen habe und gerne besser verstehen wollen, wie man das noch besser macht.&lt;/p&gt;
&lt;p&gt;In dieser Schulung behandeln wir Backup, Restore und Point-in-Time-Recovery sowohl einer kleinen wie auch einer grossen Datenbank. Aufsetzen von hochverfügbaren MariaDB und MySQL Datenbanken mittels der Master/Slave Replikation sowie dem Galera Cluster inklusive Switch-Over-Techniken. Schliesslich und endlich beschäftigen wir uns auch noch zwei Tage mit Datenbank Performance Tuning (Hardware, O/S, DB Konfiguration, Schema Tuning und SQL Query Tuning, etc.).&lt;/p&gt;
&lt;p&gt;Das Ganze ist mir zahlreichen Übungen versehen, damit man das gelernte auch gleich praktisch anwenden kann!&lt;/p&gt;
&lt;p&gt;Die Schulung findet in deutscher Sprache statt.&lt;/p&gt;
&lt;p&gt;Den detaillierten Inhalt dieser fünftägigen MySQL/MariaDB Schulung finden Sie &lt;a href="https://www.fromdual.com/de/mysql-mariadb-fuer-fortgeschrittene-schulung" title="MySQL und MariaDB fortgeschrittene Schulung"&gt;hier&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Bei weiteren Fragen nehmen Sie bitte mit uns &lt;a href="https://www.fromdual.com/contact" title="FromDual kontaktieren"&gt;Kontakt&lt;/a&gt; auf.&lt;/p&gt;</description></item><item><title>MariaDB/MySQL Schulungstermine 2019</title><link>https://www.fromdual.com/de/blog/mysql-mariadb-schulungstermine-2019/</link><pubDate>Fri, 05 Oct 2018 10:54:28 +0000</pubDate><guid>https://www.fromdual.com/de/blog/mysql-mariadb-schulungstermine-2019/</guid><description>&lt;p&gt;Jetzt sind auch noch die letzten &lt;a href="https://www.fromdual.com/de/mysql-mariadb-schulungstermine" title="MySQL und MariaDB Schulungstermine"&gt;MariaDB und MySQL Schulungstermine&lt;/a&gt; für 2019 festgelegt und veröffentlicht.&lt;/p&gt;
&lt;p&gt;Mit unseren drei &lt;a href="https://www.fromdual.com/de/partners" title="FromDual partner"&gt;Schulungspartnern in Essen, Köln und Berlin&lt;/a&gt; bietet FromDual zur Zeit insgesamt 12 öffentliche Schulungen zum Thema MariaDB und MySQL an.&lt;/p&gt;
&lt;p&gt;Es sind dies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;7 &lt;a href="https://www.fromdual.com/de/mysql-mariadb-fuer-fortgeschrittene-schulung" title="MySQL und MariaDB fortgeschrittene Schulung"&gt;MariaDB/MySQL für Fortgeschrittene&lt;/a&gt; (Januar, April, Mai, Juni, Juli, Oktober und November)&lt;/li&gt;
&lt;li&gt;1 &lt;a href="https://www.fromdual.com/de/mysql-mariadb-einsteiger-schulung" title="MySQL und MariaDB Einsteiger Schulung"&gt;MariaDB/MySQL für Einsteiger&lt;/a&gt; (Jan, diese Schulung findet sicher statt!)&lt;/li&gt;
&lt;li&gt;4 &lt;a href="https://www.fromdual.com/de/galera-cluster-fuer-mysql-mariadb-schulung" title="Galera Cluster für MySQL und MariaDB Schulung"&gt;Galera Cluster&lt;/a&gt; (2x im April, September und Oktober)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Diese Schulungen finden in deutsch statt. Auf Wunsch können auch Schulungen in englisch angeboten werden.&lt;/p&gt;
&lt;h2 id="was-bleibt-übrig-für-2018"&gt;Was bleibt übrig für 2018?&lt;/h2&gt;
&lt;p&gt;Noch im Jahr 2018 werden 5 weitere Schulungen durchgeführt. Diese finden alle sicher statt!&lt;/p&gt;
&lt;p&gt;Es sind dies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;4 &lt;a href="https://www.fromdual.com/de/mysql-mariadb-fuer-fortgeschrittene-schulung" title="MySQL und MariaDB fortgeschrittene Schulung"&gt;MariaDB/MySQL für Fortgeschrittene&lt;/a&gt; (Oktober und 3x November, wovon einer in englisch gehalten wird)&lt;/li&gt;
&lt;li&gt;1 &lt;a href="https://www.fromdual.com/de/galera-cluster-fuer-mysql-mariadb-schulung" title="Galera Cluster für MySQL und MariaDB Schulung"&gt;Galera Cluster&lt;/a&gt; Schulung (Dezember)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In diesen Schulungen sind noch vereinzelt Plätze frei. Habt Ihr Eure MariaDB/MySQL Schulung für 2018 schon bezogen?&lt;/p&gt;
&lt;p&gt;Bei Fragen zu den MariaDB oder MySQL Schulungen hilft Euch das [FromDual Schulungsteam](mailto:contact@fromdual.com?Subject=Frage zu MariaDB/MySQL Schulung &amp;ldquo;Mail an FromDual Schulungsteam&amp;rdquo;) gerne weiter!&lt;/p&gt;</description></item><item><title>Schulung MariaDB/MySQL für Fortgeschrittene vom 12.-16. 11. in Köln</title><link>https://www.fromdual.com/de/blog/mariadb-mysql-schulung-fuer-fortgeschrittene-2018-11/</link><pubDate>Mon, 10 Sep 2018 11:57:11 +0000</pubDate><guid>https://www.fromdual.com/de/blog/mariadb-mysql-schulung-fuer-fortgeschrittene-2018-11/</guid><description>&lt;p&gt;In unserer FromDual Schulung MariaDB/MySQL für Fortgeschrittene vom 12. bis 16. November in Köln hat es zur Zeit noch 3 Plätze frei.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.gfu.net/seminare-schulungen-kurse/datenbanken__db-sprachen__design_sk81/mysql-mariadb-fortgeschrittene-technike-betrieb-hochverfuegbarkeit-performance-tuning_s1872.html" target="_blank" title="Anmelden"&gt;Anmelden&lt;/a&gt; können Sie sich direkt bei unserem Schulungspartner GfU in Köln.&lt;/p&gt;
&lt;p&gt;Den Schulungsinhalt finden Sie auf der &lt;a href="https://www.fromdual.com/de/mysql-mariadb-fuer-fortgeschrittene-schulung" title="Schulungsinhalt MariaDB und MySQL für Fortgeschrittene"&gt;FromDual Webseite&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Wenn Sie spezifische in Haus Schulungen oder eine auf Sie zugeschnittene MariaDB oder MySQL Beratung benötigen, nehmen Sie mit uns &lt;a href="https://www.fromdual.com/de/kontakt" title="Kontaktaufnahme mit FromDual"&gt;Kontakt&lt;/a&gt; auf.&lt;/p&gt;</description></item></channel></rss>