<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns:wfw="http://wellformedweb.org/CommentAPI/"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:atom="http://www.w3.org/2005/Atom"
  xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
  xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
  >
<channel>
  <title>MariaDB.org-Planet-Feed</title>
  <atom:link href="https://mariadb.org/planet-rss" rel="self" type="application/rss+xml"/>
  <link>https://mariadb.org/?page_id=24734</link>
  <description>Supporting continuity and open collaboration</description>
  <lastBuildDate>Sun, 12 Apr 2026 13:39:48 +0000</lastBuildDate>
  <language></language>
  <sy:updatePeriod>hourly</sy:updatePeriod>
  <sy:updateFrequency>1</sy:updateFrequency>
        <item>
      <title>MariaDB Foundation releases the BETA of the Test Automation Framework (TAF) 2.5</title>
      <link>https://mariadb.org/mariadb-foundation-releases-the-beta-of-the-test-automation-framework-taf-2-5/</link>
      <pubDate>Sun, 12 Apr 2026 13:39:48 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>The MariaDB Foundation is releasing the BETA version of the Test Automation Framework (TAF) 2.5. This release represents a significant architectural upgrade, strengthening the framework’s lifecycle model, profiling capabilities, extraction and install pipeline, and reporting consistency. …<br />
Continue reading \"MariaDB Foundation releases the BETA of the Test Automation Framework (TAF) 2.5\"<br />
The post MariaDB Foundation releases the BETA of the Test Automation Framework (TAF) 2.5 appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-foundation-releases-the-beta-of-the-test-automation-framework-taf-2-5/">MariaDB Foundation releases the BETA of the Test Automation Framework (TAF) 2.5</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>The MariaDB Foundation is releasing the BETA version of the Test Automation Framework (TAF) 2.5. This release represents a significant architectural upgrade, strengthening the framework&rsquo;s lifecycle model, profiling capabilities, extraction and install pipeline, and reporting consistency. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/mariadb-foundation-releases-the-beta-of-the-test-automation-framework-taf-2-5/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;MariaDB Foundation releases the BETA of the Test Automation Framework (TAF) 2.5&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-foundation-releases-the-beta-of-the-test-automation-framework-taf-2-5/">MariaDB Foundation releases the BETA of the Test Automation Framework (TAF) 2.5</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-foundation-releases-the-beta-of-the-test-automation-framework-taf-2-5/">MariaDB Foundation releases the BETA of the Test Automation Framework (TAF) 2.5</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>What Our Survey Says About MariaDB Preview Releases</title>
      <link>https://mariadb.org/what-our-survey-says-about-mariadb-preview-releases/</link>
      <pubDate>Fri, 10 Apr 2026 07:33:17 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>Preview releases are among the clearest ways an open-source community can shape the future of a database before it becomes a production reality. They give users early access to new features, a chance to validate upgrade paths, and an opportunity to catch issues while the change is still inexpensive. …<br />
Continue reading \"What Our Survey Says About MariaDB Preview Releases\"<br />
The post What Our Survey Says About MariaDB Preview Releases appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/what-our-survey-says-about-mariadb-preview-releases/">What Our Survey Says About MariaDB Preview Releases</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>Preview releases are among the clearest ways an open-source community can shape the future of a database before it becomes a production reality. They give users early access to new features, a chance to validate upgrade paths, and an opportunity to catch issues while the change is still inexpensive. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/what-our-survey-says-about-mariadb-preview-releases/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;What Our Survey Says About MariaDB Preview Releases&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/what-our-survey-says-about-mariadb-preview-releases/">What Our Survey Says About MariaDB Preview Releases</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/what-our-survey-says-about-mariadb-preview-releases/">What Our Survey Says About MariaDB Preview Releases</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>MariaDB Vector: How it works. Part IV</title>
      <link>https://mariadb.org/mariadb-vector-how-it-works-part-iv/</link>
      <pubDate>Thu, 09 Apr 2026 06:42:55 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>This is the last post in the “MariaDB Vector: How it works” series. The first three were about storage, in-memory representation, HNSW modifications. …<br />
Continue reading \"MariaDB Vector: How it works. Part IV\"<br />
The post MariaDB Vector: How it works. Part IV appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-vector-how-it-works-part-iv/">MariaDB Vector: How it works. Part IV</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>This is the last post in the &ldquo;MariaDB Vector: How it works&rdquo; series. The first three were about <a href="https://mariadb.org/mariadb-vector-how-it-works/" target="_blank" rel="noreferrer noopener">storage</a>, <a href="https://mariadb.org/mariadb-vector-how-it-works-part-ii/" target="_blank" rel="noreferrer noopener">in-memory representation</a>, <a href="https://mariadb.org/mariadb-vector-how-it-works-part-iii/" target="_blank" rel="noreferrer noopener">HNSW modifications</a>. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/mariadb-vector-how-it-works-part-iv/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;MariaDB Vector: How it works. Part IV&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-vector-how-it-works-part-iv/">MariaDB Vector: How it works. Part IV</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-vector-how-it-works-part-iv/">MariaDB Vector: How it works. Part IV</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>The AWS Lambda ‘Kiss of Death’</title>
      <link>https://shatteredsilicon.net/the-aws-lambda-kiss-of-death/</link>
      <pubDate>Tue, 07 Apr 2026 18:15:28 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://shatteredsilicon.net/wp-json/;%20rel=https://api.w.org/,%20https://shatteredsilicon.net/wp-json/wp/v2/categories/17;%20type=application/json,%20https://shatteredsilicon.net/category/databases-mysql-mariadb-postgresql/;%20rel=canonical">Databases (MySQL, MariaDB, PostgreSQL) Archives - Shattered Silicon</source>
      <description><![CDATA[<p>Our story begins as most database issues start: with hands on foreheads, internally or externally, saying ‘WTF is going on?’. We observed a series of database freezes on our production environment. It was quite severe. Connections spiked, writes were stalled and at some point, a large database freeze and they cleared. Being a Galera environment, […]<br />
The post The AWS Lambda ‘Kiss of Death’ appeared first on Shattered Silicon.</p>
<p>The post <a rel="nofollow" href="https://shatteredsilicon.net/the-aws-lambda-kiss-of-death/">The AWS Lambda ‘Kiss of Death’</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>Our story begins as most database issues start: with hands on foreheads, internally or externally, saying &lsquo;WTF is going on?&rsquo;. We observed a series of database freezes on our production environment. It was quite severe. Connections spiked, writes were stalled and at some point, a large database freeze and they cleared. Being a Galera environment, [&hellip;]</p>
<p>The post <a rel="nofollow" href="https://shatteredsilicon.net/the-aws-lambda-kiss-of-death/">The AWS Lambda &lsquo;Kiss of Death&rsquo;</a> appeared first on <a rel="nofollow" href="https://shatteredsilicon.net">Shattered Silicon</a>.</p>

<p>The post <a rel="nofollow" href="https://shatteredsilicon.net/the-aws-lambda-kiss-of-death/">The AWS Lambda ‘Kiss of Death’</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>The AWS Lambda ‘Kiss of Death’</title>
      <link>https://shatteredsilicon.net/aws-lambda-kiss-of-death/</link>
      <pubDate>Tue, 07 Apr 2026 18:15:28 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://shatteredsilicon.net/wp-json/;%20rel=https://api.w.org/,%20https://shatteredsilicon.net/wp-json/wp/v2/categories/17;%20type=application/json,%20https://shatteredsilicon.net/category/databases-mysql-mariadb-postgresql/;%20rel=canonical">Databases (MySQL, MariaDB, PostgreSQL) Archives - Shattered Silicon</source>
      <description><![CDATA[<p>Our story begins as most database issues start: with hands on foreheads, internally or externally, saying ‘WTF is going on?’. We observed a series of database freezes on our production environment. It was quite severe. Connections spiked, writes were stalled and at some point, a large database freeze and they cleared. Being a Galera environment, […]<br />
The post The AWS Lambda ‘Kiss of Death’ appeared first on Shattered Silicon.</p>
<p>The post <a rel="nofollow" href="https://shatteredsilicon.net/aws-lambda-kiss-of-death/">The AWS Lambda ‘Kiss of Death’</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>Our story begins as most database issues start: with hands on foreheads, internally or externally, saying &lsquo;WTF is going on?&rsquo;. We observed a series of database freezes on our production environment. It was quite severe. Connections spiked, writes were stalled and at some point, a large database freeze and they cleared. Being a Galera environment, [&hellip;]</p>
<p>The post <a rel="nofollow" href="https://shatteredsilicon.net/aws-lambda-kiss-of-death/">The AWS Lambda &lsquo;Kiss of Death&rsquo;</a> appeared first on <a rel="nofollow" href="https://shatteredsilicon.net">Shattered Silicon</a>.</p>

<p>The post <a rel="nofollow" href="https://shatteredsilicon.net/aws-lambda-kiss-of-death/">The AWS Lambda ‘Kiss of Death’</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>A response to Percona’s 2026 MySQL ecosystem benchmark: useful data, but not a realistic MariaDB comparison</title>
      <link>https://mariadb.org/a-response-to-perconas-2026-mysql-ecosystem-benchmark-useful-data-but-not-a-realistic-mariadb-comparison/</link>
      <pubDate>Fri, 03 Apr 2026 07:35:32 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>Percona’s new 2026 benchmark report is interesting because it puts several MySQL-family releases on the same graphs and shares a public repository for the test harness. …<br />
Continue reading \"A response to Percona’s 2026 MySQL ecosystem benchmark: useful data, but not a realistic MariaDB comparison\"<br />
The post A response to Percona’s 2026 MySQL ecosystem benchmark: useful data, but not a realistic MariaDB comparison appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/a-response-to-perconas-2026-mysql-ecosystem-benchmark-useful-data-but-not-a-realistic-mariadb-comparison/">A response to Percona’s 2026 MySQL ecosystem benchmark: useful data, but not a realistic MariaDB comparison</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p><a href="https://www.percona.com/blog/2026-mysql-ecosystem-performance-benchmark-report/">Percona&rsquo;s new 2026 benchmark report</a> is interesting because it puts several MySQL-family releases on the same graphs and shares a public repository for the test harness. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/a-response-to-perconas-2026-mysql-ecosystem-benchmark-useful-data-but-not-a-realistic-mariadb-comparison/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;A response to Percona&rsquo;s 2026 MySQL ecosystem benchmark: useful data, but not a realistic MariaDB comparison&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/a-response-to-perconas-2026-mysql-ecosystem-benchmark-useful-data-but-not-a-realistic-mariadb-comparison/">A response to Percona&rsquo;s 2026 MySQL ecosystem benchmark: useful data, but not a realistic MariaDB comparison</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/a-response-to-perconas-2026-mysql-ecosystem-benchmark-useful-data-but-not-a-realistic-mariadb-comparison/">A response to Percona’s 2026 MySQL ecosystem benchmark: useful data, but not a realistic MariaDB comparison</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Percona Bug Report: March 2026</title>
      <link>https://percona.community/blog/2026/04/03/percona-bug-report-march-2026/</link>
      <pubDate>Fri, 03 Apr 2026 00:00:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://percona.community/blog/">Percona Community Blog - learn about MySQL, MariaDB, PostgreSQL, and MongoDB</source>
      <description><![CDATA[<p>At Percona, we operate on the premise that full transparency makes a product better. We strive to build the best open-source database products, but also to help you manage any issues that arise in any of the databases that we support. And, in true open-source form, report back on any issues or bugs you might encounter along the way.</p>
<p>The post <a rel="nofollow" href="https://percona.community/blog/2026/04/03/percona-bug-report-march-2026/">Percona Bug Report: March 2026</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>At Percona, we operate on the premise that full transparency makes a product better. We strive to build the best open-source database products, but also to help you manage any issues that arise in any of the databases that we support. And, in true open-source form, report back on any issues or bugs you might encounter along the way.</p>
<p>We constantly update our <a href="https://perconadev.atlassian.net/" target="_blank" rel="noopener noreferrer">bug reports</a> and monitor <a href="https://bugs.mysql.com/" target="_blank" rel="noopener noreferrer">other boards</a> to ensure we have the latest information, but we wanted to make it a little easier for you to keep track of the most critical ones. This post is a central place to get information on the most noteworthy open and recently resolved bugs.</p>
<p>In this edition of our bug report, we have the following list of bugs.</p>
<hr>
<h2 id="percona-servermysql-bugs">Percona Server/MySQL Bugs<a class="anchor-link" id="percona-server-mysql-bugs"></a></h2>
<p><a href="https://perconadev.atlassian.net/browse/PS-10378" target="_blank" rel="noopener noreferrer">PS-10378</a>: In the MeCab plugin, BOOLEAN MODE full-text queries with a LIMIT clause do not behave as expected. Although the optimizer indicates that ranking should be skipped (Ft_hints: no_ranking), the query still performs full ranking and sorting before applying LIMIT, preventing the intended optimization and impacting performance.</p>
<p><strong>Reported Affected Version/s</strong>: 8.4.x<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: No workaround available<br>
<strong>Fixed/Planned Version/s</strong>: 8.0.46-37, 8.4.9-9, 9.7.0-0</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/PS-10448" target="_blank" rel="noopener noreferrer">PS-10448</a>: Insert prepared statements fail on partitioned tables with timestamp-based partitions when the partition key uses a non-constant default (e.g., <strong>CURRENT_TIMESTAMP</strong>). After initial execution, the statement remains bound to the original partition and fails with a partition mismatch error when data should go into a different partition.</p>
<p><strong>Reported Affected Version/s</strong>: 8.0.42-33, 8.0.43-34, 8.0.44-35, 8.4.7-7<br>
<strong>Upstream Bug</strong>: <a href="https://bugs.mysql.com/bug.php?id=119309" target="_blank" rel="noopener noreferrer">Bug #119309</a><br>
<strong>Workaround/Fix</strong>: Modify statements to explicitly use <strong>NOW()</strong> (requires updating procedures)<br>
<strong>Fixed/Planned Version/s</strong>: 8.0.46-37, 8.4.9-9, 9.7.0-0</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/PS-10481" target="_blank" rel="noopener noreferrer">PS-10481</a>: The range optimizer incorrectly falls back to a full table scan instead of using an index range scan for WHERE &hellip; IN() queries when values exceed column or prefix length on non-binary collations (e.g. utf8mb4_0900_ai_ci). A single truncated value in IN() can invalidate all valid ranges, forcing a full scan and degrading performance.</p>
<p><strong>Reported Affected Version/s</strong>: 8.4.x<br>
<strong>Upstream Bug</strong>: <a href="https://bugs.mysql.com/bug.php?id=118009" target="_blank" rel="noopener noreferrer">Bug #118009</a><br>
<strong>Workaround/Fix</strong>: No workaround available<br>
<strong>Fixed/Planned Version/s</strong>: Not fixed yet</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/PS-10593" target="_blank" rel="noopener noreferrer">PS-10593</a>: The audit_log plugin can crash (segfault) during memcpy operations when configured with audit_log_strategy=PERFORMANCE, audit_log_policy=ALL, and buffering enabled. The issue can be reproduced under specific memory allocator setups (e.g., jemalloc) and also occurs with standard libc malloc, indicating instability in the plugin&rsquo;s memory handling.</p>
<p><strong>Reported Affected Version/s</strong>: 8.0.34-26, 8.0.45-36, 8.4.7-7<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: No workaround available<br>
<strong>Fixed/Planned Version/s</strong>: 8.0.46-37, 8.4.9-9</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/PS-10990" target="_blank" rel="noopener noreferrer">PS-10990</a>: Server crashes (signal 11) in Item_cache::walk when executing queries that use JOIN with a subquery in an IN clause inside stored procedures. The issue occurs during query execution/privilege checking and is reproducible across MySQL and Percona Server 8.0.x versions.</p>
<p><strong>Reported Affected Version/s</strong>: 8.0.45-36<br>
<strong>Upstream Bug</strong>: <a href="https://bugs.mysql.com/bug.php?id=115885" target="_blank" rel="noopener noreferrer">Bug #115885</a><br>
<strong>Workaround/Fix</strong>: Execute the query outside the stored procedure<br>
<strong>Fixed/Planned Version/s</strong>: Not specified</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/PS-10578" target="_blank" rel="noopener noreferrer">PS-10578</a>: The legacy audit_log plugin does not populate the DB field in audit records unless the session is started with the &ndash;database option. Even when a database is selected later using USE or referenced explicitly in queries, the DB field may remain empty.</p>
<p><strong>Reported Affected Version/s</strong>: 8.0.43-34, 8.0.45-36<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Use Audit Log Filter component (8.4) or audit log filter (8.0), where this issue is not reproducible<br>
<strong>Fixed/Planned Version/s</strong>: Not planned to be fixed</p>
<hr>
<h2 id="percona-xtradb-cluster">Percona Xtradb Cluster<a class="anchor-link" id="percona-xtradb-cluster"></a></h2>
<p><a href="https://perconadev.atlassian.net/browse/PXC-4844" target="_blank" rel="noopener noreferrer">PXC-4844</a>: In PXC clusters under high load, inconsistency voting during DDL or DCL operations can trigger an internal deadlock, causing standby nodes to get stuck applying transactions and continuously request FC pause. Although voting completes successfully and no node is expelled, writes remain blocked in wsrep: replicating and certifying write set, effectively stalling the cluster until the affected node is restarted.</p>
<p><strong>Reported Affected Version/s</strong>: 8.0.42<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Restart the blocked standby node to restore cluster activity<br>
<strong>Fixed/Planned Version/s</strong>: Not fixed yet</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/PXC-4799" target="_blank" rel="noopener noreferrer">PXC-4799</a>: In PXC clusters, when a backup lock (<strong>LOCK INSTANCE FOR BACKUP</strong>) is active and a replicated DDL is pending, executing <strong>FLUSH TABLES WITH READ LOCK</strong> on the same node can trigger a deadlock. This results in an inconsistency vote and causes the node to leave the cluster, disrupting backup operations.</p>
<p><strong>Reported Affected Version/s</strong>: 8.0.42, 8.0.43, 8.4.6<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Avoid running DDL operations during backup or use a single backup instance instead of parallel runs<br>
<strong>Fixed/Planned Version/s</strong>: 8.0.46, 8.4.9, 9.7.0</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/PXC-4814" target="_blank" rel="noopener noreferrer">PXC-4814</a>: In PXC with <strong>wsrep_OSU_method=&lsquo;RSU&rsquo;</strong>, a failed DDL due to table name case mismatch (e.g., <strong>OPTIMIZE TABLE</strong>) is incorrectly written to the binary log as a successful transaction (<strong>error_code=0</strong>). This results in a GTID being generated for a failed operation, causing GTID inconsistencies across cluster nodes and in replication setups.</p>
<p><strong>Reported Affected Version/s</strong>: 8.0.33-25, 8.0.44, 8.4.6<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Validate table name case sensitivity before executing DDL in RSU mode<br>
<strong>Fixed/Planned Version/s</strong>: 8.0.45, 8.4.8, 9.6.0</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/PXC-4845" target="_blank" rel="noopener noreferrer">PXC-4845</a>: After an IST failure (e.g., due to network issues), a PXC node may remain running in an inconsistent state instead of restarting, causing the donor and other nodes to become unresponsive. The joiner node gets stuck during state transfer instead of failing cleanly, impacting overall cluster availability.</p>
<p><strong>Reported Affected Version/s</strong>: 8.0.42<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: No workaround available<br>
<strong>Fixed/Planned Version/s</strong>: 8.0.45, 8.4.8, 9.6.0</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/PXC-4849" target="_blank" rel="noopener noreferrer">PXC-4849</a>: A PXC node fails to start after successful SST when <strong>read_only</strong> or <strong>super_read_only</strong> is enabled and event scheduler objects exist on the donor. During initialization, the event scheduler fails to load, causing the node to abort, making it impossible to run read-only nodes with events defined in the cluster.</p>
<p><strong>Reported Affected Version/s</strong>: 8.0.44, 8.4.7<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Start the node without read_only, then enable it manually later, or remove events<br>
<strong>Fixed/Planned Version/s</strong>: 8.0.46, 8.4.9, 9.7.0</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/PXC-4965" target="_blank" rel="noopener noreferrer">PXC-4965</a>: Passwords containing the <code>'</code> character are incorrectly handled, causing syntax errors during replication (e.g., <strong>SET PASSWORD</strong>) and triggering inconsistency voting that can force a node to leave the cluster.</p>
<p><strong>Reported Affected Version/s</strong>: 8.0.45, 8.4.7<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Avoid using <code>'</code> character in passwords<br>
<strong>Fixed/Planned Version/s</strong>: 8.0.46, 8.4.8, 9.6.0</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/PXC-5198" target="_blank" rel="noopener noreferrer">PXC-5198</a>: Executing <strong>SELECT &hellip; FOR UPDATE SKIP LOCKED</strong> can trigger InnoDB crashes with fatal errors (e.g., &ldquo;Unknown error code 21: Skip locked records&rdquo;) under concurrent transactional workloads. Instead of returning expected deadlock errors, the query causes mysqld to abort, impacting cluster stability.</p>
<p><strong>Reported Affected Version/s</strong>: 8.0.33-25, 8.0.35-27, 8.0.36-28<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Avoid using <strong>SKIP LOCKED</strong> in <strong>SELECT &hellip; FOR UPDATE</strong> queries<br>
<strong>Fixed/Planned Version/s</strong>: 8.0.46, 8.4.8, 9.6.0</p>
<hr>
<h2 id="percona-xtrabackup">Percona XtraBackup<a class="anchor-link" id="percona-xtrabackup"></a></h2>
<p><a href="https://perconadev.atlassian.net/browse/PXB-3543" target="_blank" rel="noopener noreferrer">PXB-3543</a>: Incremental backups in XtraBackup can become significantly slower than full backups on instances with a very large number of small tables, due to excessive CPU usage in memset during incremental processing. This leads to severe performance degradation, with incremental backups taking hours compared to minutes for full backups.</p>
<p><strong>Reported Affected Version/s</strong>: 8.0.35-33, 8.0.35-34<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Use full backups instead of incremental backups<br>
<strong>Fixed/Planned Version/s</strong>: 8.0.35-35, 8.4.0-6, 9.6.0-1</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/PXB-3667" target="_blank" rel="noopener noreferrer">PXB-3667</a>: Installation of XtraBackup 8.4 fails on RHEL 9&ndash;based systems due to dependency conflicts between percona-xtrabackup-84, perl(DBD::mysql), and incompatible libmysqlclient versions. Percona Server 8.4 provides libmysqlclient.so.24, while required dependencies expect libmysqlclient.so.21, resulting in unresolved package installation errors.</p>
<p><strong>Reported Affected Version/s</strong>: 8.4.0-5<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Not specified<br>
<strong>Fixed/Planned Version/s</strong>: Not specified</p>
<hr>
<h2 id="percona-toolkit">Percona Toolkit<a class="anchor-link" id="percona-toolkit"></a></h2>
<p><a href="https://perconadev.atlassian.net/browse/PT-2519" target="_blank" rel="noopener noreferrer">PT-2519</a>: pt-query-digest fails when processing large, slow query logs, repeatedly throwing &ldquo;Argument &ldquo;&rdquo; isn&rsquo;t numeric&rdquo; errors during the aggregate fingerprint stage. The tool retries multiple times but does not complete, resulting in stalled analysis and very slow progress.</p>
<p><strong>Reported Affected Version/s</strong>: 3.7.0, 3.7.1<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Not specified<br>
<strong>Fixed/Planned Version/s</strong>: 3.7.3</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/PT-2511" target="_blank" rel="noopener noreferrer">PT-2511</a>: pt-summary incorrectly reports that sshd is not running due to an invalid awk expression used to detect the process. The script checks the wrong field in ps output, causing false negatives even when sshd is active.</p>
<p><strong>Reported Affected Version/s</strong>: 3.7.1<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Not specified<br>
<strong>Fixed/Planned Version/s</strong>: 3.7.3</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/PT-2516" target="_blank" rel="noopener noreferrer">PT-2516</a>: pt-mongodb-index-check fails to detect duplicate indexes (e.g., <code>{a:1}</code> and <code>{a:1, b:1}</code>) and may produce no output, making it unclear whether the tool is functioning or connecting properly.</p>
<p><strong>Reported Affected Version/s</strong>: 3.7.1<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Not specified<br>
<strong>Fixed/Planned Version/s</strong>: Not specified</p>
<hr>
<h2 id="pmm-percona-monitoring-and-management">PMM [Percona Monitoring and Management]<a class="anchor-link" id="pmm-percona-monitoring-and-management"></a></h2>
<p><a href="https://perconadev.atlassian.net/browse/PMM-14493" target="_blank" rel="noopener noreferrer">PMM-14493</a>: PMM fails to start when using Podman with the <strong>&ndash;log-driver passthrough</strong> option due to an error opening /dev/stderr during Nginx initialization. This causes the container to exit with configuration test failure, while other log drivers work as expected.</p>
<p><strong>Reported Affected Version/s</strong>: 3.4.0, 3.4.1<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Use a different <strong>&ndash;log-driver</strong> option such as none or journald<br>
<strong>Fixed/Planned Version/s</strong>: 3.8.0</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/PMM-14576" target="_blank" rel="noopener noreferrer">PMM-14576</a>: PMM Client reports &ldquo;failed to get backup status&rdquo; errors during MongoDB backups, marking them as failed in the UI even though backups are successfully completed by PBM. This leads to incorrect backup status reporting and confusion for users.</p>
<p><strong>Reported Affected Version/s</strong>: 3.5.0<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Avoid using PMM Backup Management (not ideal)<br>
<strong>Fixed/Planned Version/s</strong>: 3.9.0, 3.X</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/PMM-14594" target="_blank" rel="noopener noreferrer">PMM-14594</a>: PMM incorrectly reports compatible XtraBackup versions as incompatible with supported MySQL versions during backup validation. This causes backups to be blocked in PMM even when the installed XtraBackup version is the latest available and should be accepted.</p>
<p><strong>Reported Affected Version/s</strong>: 3.5.0, 3.6.0<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Use the xtrabackup command-line tool to take backups<br>
<strong>Fixed/Planned Version/s</strong>: 3.9.0</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/PMM-14852" target="_blank" rel="noopener noreferrer">PMM-14852</a>: Some panels in the MongoDB InMemory dashboard show no data because they incorrectly use WiredTiger-specific metrics. As a result, dashboards for InMemory storage engine deployments can display empty or misleading panels instead of relevant metrics.</p>
<p><strong>Reported Affected Version/s</strong>: 3.2.0, 3.6.0<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Not specified<br>
<strong>Fixed/Planned Version/s</strong>: 3.8.0</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/PMM-14906" target="_blank" rel="noopener noreferrer">PMM-14906</a>: The postgres_exporter generates excessive <strong>SELECT version()</strong> queries (~4500/hour) after upgrading to PMM 3.6.0, flooding PostgreSQL logs and increasing unnecessary query load, causing log spam and disk growth.</p>
<p><strong>Reported Affected Version/s</strong>: 3.6.0<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Not specified<br>
<strong>Fixed/Planned Version/s</strong>: 3.8.0</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/PMM-14958" target="_blank" rel="noopener noreferrer">PMM-14958</a>: mysqld_exporter continues to generate duplicate metric collection errors with GTID and parallel replication enabled, even in PMM 3.6.0. These repeated errors (e.g., <strong>mysql_perf_schema_replication_group_worker_transport_time_seconds</strong>) lead to continuous log spam, causing rapid log growth (up to ~10GB/hour), disk space exhaustion, and increased noise that makes it difficult to identify real issues.</p>
<p><strong>Reported Affected Version/s</strong>: 3.6.0<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Not specified<br>
<strong>Fixed/Planned Version/s</strong>: 3.7.1</p>
<hr>
<h2 id="percona-kubernetes-operator">Percona Kubernetes Operator<a class="anchor-link" id="percona-kubernetes-operator"></a></h2>
<p><a href="https://perconadev.atlassian.net/browse/K8SPG-737" target="_blank" rel="noopener noreferrer">K8SPG-737</a>: In PostgreSQL Kubernetes deployments, the node_exporter in the PMM client sidecar cannot access the datadir mountpoint because it is not exposed via /proc, preventing collection of datadir-related metrics. This results in incomplete monitoring data for PostgreSQL pods.</p>
<p><strong>Reported Affected Version/s</strong>: 2.9.0<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: No workaround available<br>
<strong>Fixed/Planned Version/s</strong>: 2.10.0</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/K8SPXC-1737" target="_blank" rel="noopener noreferrer">K8SPXC-1737</a>: The PXC Operator crashes during reconciliation in CompareMySQLVersion when the cluster status lacks a MySQL version value. An empty version field causes a panic (&ldquo;Malformed version&rdquo;), preventing proper cluster reconciliation and replication setup.</p>
<p><strong>Reported Affected Version/s</strong>: 1.18.0, 1.19.0<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Create the cluster before configuring replication or manually patch the CR status to include the missing version value, for example:</p>
<div class="code-block">
<div class="code-block__header"><button class="code-block__copy" type="button" data-copy-target="codeblock-0" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-0">
<div class="highlight">
<pre class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">kubectl patch pxc  
</span></span><span class="line"><span class="cl"> --type=merge 
</span></span><span class="line"><span class="cl"> --subresource=status 
</span></span><span class="line"><span class="cl"> --patch '
</span></span><span class="line"><span class="cl">status:
</span></span><span class="line"><span class="cl"> pxc:
</span></span><span class="line"><span class="cl"> version: "8.0.42-33.1"'</span></span></code></pre>
</div>
</div>
</div>
<p><strong>Fixed/Planned Version/s:</strong> 1.20.0</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/K8SPXC-1843" target="_blank" rel="noopener noreferrer">K8SPXC-1843</a>: Backups can get stuck in a Running state if the Joiner/Garbd disconnects from the Donor (e.g., due to sst-idle-timeout). Even after the SST process fails and the donor leaves the cluster, the backup process (e.g., xbcloud put) continues indefinitely without timing out, preventing backup completion.</p>
<p><strong>Reported Affected Version/s</strong>: 1.19.0<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: No workaround available<br>
<strong>Fixed/Planned Version/s</strong>: 1.20.0</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/K8SPXC-1831" target="_blank" rel="noopener noreferrer">K8SPXC-1831</a>: When using mysqlAllocator=jemalloc on ARM images, the operator attempts to preload /usr/lib64/libjemalloc.so.1, but only libjemalloc.so.2 is available. This results in preload errors and prevents proper use of the jemalloc allocator.</p>
<p><strong>Reported Affected Version/s</strong>: 1.19.0<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Not specified<br>
<strong>Fixed/Planned Version/s</strong>: 1.20.0</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/K8SPXC-1830" target="_blank" rel="noopener noreferrer">K8SPXC-1830</a>: ProxySQL monitoring fails in PMM when using caching_sha2_password, causing proxysql_exporter to fail authentication with errors like:</p>
<div class="code-block">
<div class="code-block__header"><button class="code-block__copy" type="button" data-copy-target="codeblock-1" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-1">
<div class="highlight">
<pre class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Error opening connection to ProxySQL:
</span></span><span class="line"><span class="cl">unexpected resp from server for caching_sha2_password, perform full authentication</span></span></code></pre>
</div>
</div>
</div>
<p>This occurs because ProxySQL does not support the required RSA-based full authentication, breaking PMM monitoring integration.<br>
<strong>Reported Affected Version/s</strong>: 1.19.0<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Use mysql_native_password<br>
<strong>Fixed/Planned Version/s</strong>: 1.20.0</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/K8SPSMDB-1617" target="_blank" rel="noopener noreferrer">K8SPSMDB-1617</a>: Scheduled backups can be triggered even when the MongoDB cluster is not ready (e.g., in initializing state) and without the required safety flags. This leads to failed backup attempts and inconsistent backup behaviour.</p>
<p><strong>Reported Affected Version/s</strong>: 1.22.0<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Not specified<br>
<strong>Fixed/Planned Version/s</strong>: Not specified</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/K8SPSMDB-1524" target="_blank" rel="noopener noreferrer">K8SPSMDB-1524</a>: The PBM agent continuously triggers resync storage operations, causing backup processes to stall or remain in pending/unknown states. Logs show repeated resync commands being executed without completion, leading to unstable backup behaviour.</p>
<p><strong>Reported Affected Version/s</strong>: 1.21.1<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Not specified<br>
<strong>Fixed/Planned Version/s</strong>: 1.22.0</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/K8SPG-939" target="_blank" rel="noopener noreferrer">K8SPG-939</a>: Patroni does not propagate labels defined in the PostgreSQL Operator CR, causing failures in environments with strict label policies. As a result, Kubernetes rejects resource creation (e.g., Services) due to missing mandatory labels, preventing cluster reconciliation.</p>
<p><strong>Reported Affected Version/s</strong>: 2.8.2<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Not specified<br>
<strong>Fixed/Planned Version/s</strong>: 2.9.0</p>
<hr>
<h2 id="pbm-percona-backup-for-mongodb">PBM [Percona Backup for MongoDB]<a class="anchor-link" id="pbm-percona-backup-for-mongodb"></a></h2>
<p><a href="https://perconadev.atlassian.net/browse/PBM-1683" target="_blank" rel="noopener noreferrer">PBM-1683</a>: The size_uncompressed_h field in pbm describe-backup reports incorrect (inflated) sizes for non-base incremental backups, showing significantly larger values than the actual data size and leading to misleading backup size reporting.</p>
<p><strong>Reported Affected Version/s</strong>: 2.10.0, 2.11.0, 2.12.0<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Not specified<br>
<strong>Fixed/Planned Version/s</strong>: 2.14.0</p>
<hr>
<h2 id="psmdb-percona-server-for-mongodb">PSMDB [Percona Server for MongoDB]<a class="anchor-link" id="psmdb-percona-server-for-mongodb"></a></h2>
<p><a href="https://perconadev.atlassian.net/browse/PSMDB-1915" target="_blank" rel="noopener noreferrer">PSMDB-1915</a>: Newer PSMDB packages fail to install or upgrade on RHEL 9.4 due to a dependency on OpenSSL 3.4, which is not available in that OS version. This breaks upgrades (e.g., from 6.0.25 to 6.0.27) and affects multiple major versions.</p>
<p><strong>Reported Affected Version/s</strong>: 6.0.27-21, 7.0.28-15, 8.0.17-6<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Not specified<br>
<strong>Fixed/Planned Version/s</strong>: 6.0.27-21, 7.0.28-15, 8.0.17-6</p>
<hr>
<p><a href="https://perconadev.atlassian.net/browse/PSMDB-1998" target="_blank" rel="noopener noreferrer">PSMDB-1998</a>: LDAP authentication can hang indefinitely when the LDAP server is unreachable due to missing timeout handling. This leads to continuously accumulating connections, eventually exhausting file descriptors and causing service disruption or crashes.</p>
<p><strong>Reported Affected Version/s</strong>: 7.0.16-10, 7.0.30-16<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: No workaround available<br>
<strong>Fixed/Planned Version/s</strong>: 7.0.31-17, 8.0.20-8</p>
<hr>
<h2 id="percona-distribution-for-mysql-orchestrator">Percona Distribution for MySQL [Orchestrator]<a class="anchor-link" id="percona-distribution-for-mysql-orchestrator"></a></h2>
<p><a href="https://perconadev.atlassian.net/browse/DISTMYSQL-584" target="_blank" rel="noopener noreferrer">DISTMYSQL-584</a>: Orchestrator loses SSL-related settings such as SOURCE_SSL_CA and SOURCE_SSL_VERIFY_SERVER_CERT during failover when issuing CHANGE REPLICATION SOURCE, causing replication to run without required security configurations and potentially violating compliance requirements.</p>
<p><strong>Reported Affected Version/s</strong>: 8.4.7<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Not specified<br>
<strong>Fixed/Planned Version/s</strong>: Not specified</p>
<hr>
<h2 id="pcsm-percona-clustersync-for-mongodb">PCSM [Percona ClusterSync for MongoDB]<a class="anchor-link" id="pcsm-percona-clustersync-for-mongodb"></a></h2>
<p><a href="https://perconadev.atlassian.net/browse/PCSM-294" target="_blank" rel="noopener noreferrer">PCSM-294</a>: PCSM replication can crash during change replication due to flawed conflict detection and unbatched pipeline generation. This results in oversized aggregation pipelines, memory exhaustion, or invalid $slice operations, causing replication to fail with errors such as stage limit exceeded, buffer limits, or invalid arguments.</p>
<p><strong>Reported Affected Version/s</strong>: 0.7.0<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Not specified<br>
<strong>Fixed/Planned Version/s</strong>: 0.8.0</p>
<hr>
<h2 id="pg_tde-percona-transparent-data-encryption-for-postgresql">PG_TDE [Percona Transparent Data Encryption for PostgreSQL]<a class="anchor-link" id="pg_tde-percona-transparent-data-encryption-for-postgresql"></a></h2>
<p><a href="https://perconadev.atlassian.net/browse/PG-2125" target="_blank" rel="noopener noreferrer">PG-2125</a>: pg_tde fails to create/register symmetric keys when using HashiCorp KMIP, returning errors from the KMIP server during key registration. This prevents key setup and blocks encryption workflows for users relying on KMIP integration.</p>
<p><strong>Reported Affected Version/s</strong>: pg_tde 2.1.0<br>
<strong>Upstream Bug</strong>: Not applicable<br>
<strong>Workaround/Fix</strong>: Not specified<br>
<strong>Fixed/Planned Version/s</strong>: pg_tde NEXT</p>
<hr>
<h2 id="summary">Summary<a class="anchor-link" id="summary"></a></h2>
<p>We welcome community input and feedback on all our products. If you find a bug or would like to suggest an improvement or a feature, learn how in our post, <a href="https://www.percona.com/blog/2019/06/12/report-bugs-improvements-new-feature-requests-for-percona-products/" target="_blank" rel="noopener noreferrer">How to Report Bugs, Improvements, New Feature Requests for Percona Products</a>.</p>
<p>For the most up-to-date information, be sure to follow us on <a href="https://twitter.com/percona" target="_blank" rel="noopener noreferrer">Twitter</a>, <a href="https://www.linkedin.com/company/percona" target="_blank" rel="noopener noreferrer">LinkedIn</a>, and <a href="https://www.facebook.com/Percona?fref=ts" target="_blank" rel="noopener noreferrer">Facebook</a>.</p>
<p>Quick References:</p>
<p><a href="https://jira.percona.com" target="_blank" rel="noopener noreferrer">Percona JIRA</a></p>
<p><a href="https://bugs.mysql.com/" target="_blank" rel="noopener noreferrer">MySQL Bug Report</a></p>
<p><a href="https://www.percona.com/blog/2019/06/12/report-bugs-improvements-new-feature-requests-for-percona-products/" target="_blank" rel="noopener noreferrer">Report a Bug in a Percona Product</a></p>

<p>The post <a rel="nofollow" href="https://percona.community/blog/2026/04/03/percona-bug-report-march-2026/">Percona Bug Report: March 2026</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>We ran an internal AI demo competition: Here are the winners!</title>
      <link>https://mariadb.com/resources/blog/we-ran-an-internal-ai-demo-competition-here-are-the-winners/</link>
      <pubDate>Thu, 02 Apr 2026 15:53:37 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.com/">MariaDB</source>
      <description><![CDATA[<p>As developers, we are skeptical of “AI marketing”. We want to see it run. We want to see the actual […]</p>
<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/we-ran-an-internal-ai-demo-competition-here-are-the-winners/">We ran an internal AI demo competition: Here are the winners!</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>As developers, we are skeptical of &ldquo;AI marketing&rdquo;. We want to see it run. We want to see the actual techniques and features in action. So, at MariaDB, we decided to organize an AI Demo competition inviting coders (and also non-coders!) to solve actual problems using our cloud and AI capabilities. The results are really cool! We saw applications handling geospatial data&hellip;</p>
<p><a href="https://mariadb.com/resources/blog/we-ran-an-internal-ai-demo-competition-here-are-the-winners/" rel="nofollow">Source</a></p>

<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/we-ran-an-internal-ai-demo-competition-here-are-the-winners/">We ran an internal AI demo competition: Here are the winners!</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>MariaDB Vector: How it works. Part III</title>
      <link>https://mariadb.org/mariadb-vector-how-it-works-part-iii/</link>
      <pubDate>Thu, 02 Apr 2026 06:46:48 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>In the previous parts of this series we’ve seen how MariaDB stores vector indexes in a table and how to implement HNSW for a good performance. …<br />
Continue reading \"MariaDB Vector: How it works. Part III\"<br />
The post MariaDB Vector: How it works. Part III appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-vector-how-it-works-part-iii/">MariaDB Vector: How it works. Part III</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>In the <a href="https://mariadb.org/mariadb-vector-how-it-works/" target="_blank" rel="noreferrer noopener">previous</a> <a href="https://mariadb.org/mariadb-vector-how-it-works-part-ii/" target="_blank" rel="noreferrer noopener">parts</a> of this series we&rsquo;ve seen how MariaDB stores vector indexes in a table and how to implement HNSW for a good performance. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/mariadb-vector-how-it-works-part-iii/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;MariaDB Vector: How it works. Part III&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-vector-how-it-works-part-iii/">MariaDB Vector: How it works. Part III</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-vector-how-it-works-part-iii/">MariaDB Vector: How it works. Part III</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>InnoDB Buffer Pool Tuning: From Rule-of-Thumb to Real Signals</title>
      <link>https://percona.community/blog/2026/04/02/innodb-buffer-pool-tuning-from-rule-of-thumb-to-real-signals/</link>
      <pubDate>Thu, 02 Apr 2026 00:00:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://percona.community/blog/">Percona Community Blog - learn about MySQL, MariaDB, PostgreSQL, and MongoDB</source>
      <description><![CDATA[<p>Introduction Many MySQL setups begin life with a familiar incantation:</p>
<p>The post <a rel="nofollow" href="https://percona.community/blog/2026/04/02/innodb-buffer-pool-tuning-from-rule-of-thumb-to-real-signals/">InnoDB Buffer Pool Tuning: From Rule-of-Thumb to Real Signals</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<h2 id="introduction">Introduction<a class="anchor-link" id="introduction"></a></h2>
<p>Many MySQL setups begin life with a familiar incantation:</p>
<div class="code-block">
<div class="code-block__header"><button class="code-block__copy" type="button" data-copy-target="codeblock-0" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-0">
<div class="highlight">
<pre class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">innodb_buffer_pool_size = 70% of RAM</span></span></code></pre>
</div>
</div>
</div>
<p>&hellip;and then nothing changes.</p>
<p>That&rsquo;s not tuning. That&rsquo;s a starting guess.</p>
<p>Real tuning starts when the workload pushes back.</p>
<hr>
<h2 id="visual-overview">Visual Overview<a class="anchor-link" id="visual-overview"></a></h2>
<p><figure><img decoding="async" src="https://percona.community/blog/2026/04/innodb_buffer_pool_diagram.png" alt="InnoDB Buffer Pool Diagram"></figure>
</p>
<hr>
<p>The InnoDB buffer pool is where database performance is quietly decided. It determines whether your workload hums along in memory or drags itself across disk. If you&rsquo;re not actively observing and tuning it, you&rsquo;re leaving performance on the table.</p>
<p>This guide walks through how to monitor, understand, and tune the buffer pool using real signals instead of guesswork.</p>
<hr>
<h2 id="what-the-buffer-pool-really-is">What the Buffer Pool Really Is<a class="anchor-link" id="what-the-buffer-pool-really-is"></a></h2>
<p>The buffer pool isn&rsquo;t just &ldquo;memory for MySQL.&rdquo; It&rsquo;s a living system under constant pressure:</p>
<ul>
<li>A cache of data and indexes</li>
<li>A write staging area (dirty pages)</li>
<li>A contention zone between reads, writes, and eviction</li>
</ul>
<p>Think of it as your database&rsquo;s working memory. If your working set fits, queries glide. If it doesn&rsquo;t, pages are constantly evicted and reloaded, introducing latency that rarely announces itself clearly.</p>
<hr>
<h2 id="a-simple-mental-model">A Simple Mental Model<a class="anchor-link" id="a-simple-mental-model"></a></h2>
<div class="code-block">
<div class="code-block__header"><button class="code-block__copy" type="button" data-copy-target="codeblock-1" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-1">
<div class="highlight">
<pre class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl"> +---------------------------+
</span></span><span class="line"><span class="cl"> | Buffer Pool |
</span></span><span class="line"><span class="cl"> |---------------------------|
</span></span><span class="line"><span class="cl">Reads ---&gt; | Cached Pages |
</span></span><span class="line"><span class="cl"> | |
</span></span><span class="line"><span class="cl">Writes ---&gt; | Dirty Pages (pending IO) |
</span></span><span class="line"><span class="cl"> | |
</span></span><span class="line"><span class="cl">Eviction -&gt; | LRU / Free List |
</span></span><span class="line"><span class="cl"> +---------------------------+
</span></span><span class="line"><span class="cl"> |
</span></span><span class="line"><span class="cl"> v
</span></span><span class="line"><span class="cl"> Disk (slow)</span></span></code></pre>
</div>
</div>
</div>
<p>Three forces are always competing:</p>
<ul>
<li>Reads want hot data in memory</li>
<li>Writes generate dirty pages</li>
<li>Eviction makes room under pressure</li>
</ul>
<p>Your job is to keep this system balanced.</p>
<hr>
<h2 id="how-to-monitor-the-buffer-pool">How to Monitor the Buffer Pool<a class="anchor-link" id="how-to-monitor-the-buffer-pool"></a></h2>
<h3 id="option-1-quick-snapshot">Option 1: Quick Snapshot<a class="anchor-link" id="option-1-quick-snapshot"></a></h3>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-2" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-2">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SHOW</span><span class="w"> </span><span class="n">ENGINE</span><span class="w"> </span><span class="n">INNODB</span><span class="w"> </span><span class="n">STATUS</span><span class="err"></span><span class="k">G</span></span></span></code></pre>
</div>
</div>
</div>
<p>Useful for human inspection. Look for:</p>
<ul>
<li>Buffer pool size</li>
<li>Free buffers</li>
<li>Database pages</li>
<li>Modified (dirty) pages</li>
<li>Page read/write rates</li>
</ul>
<p>Great for debugging. Not ideal for automation.</p>
<hr>
<h3 id="option-2-structured-metrics-recommended">Option 2: Structured Metrics (Recommended)<a class="anchor-link" id="option-2-structured-metrics-recommended"></a></h3>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-3" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-3">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">pool_id</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">free_buffers</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">database_pages</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">modified_database_pages</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">FROM</span><span class="w"> </span><span class="n">information_schema</span><span class="p">.</span><span class="n">INNODB_BUFFER_POOL_STATS</span><span class="p">;</span></span></span></code></pre>
</div>
</div>
</div>
<p><strong>Key fields:</strong></p>
<ul>
<li><code>free_buffers</code> &rarr; Available pages (breathing room)</li>
<li><code>database_pages</code> &rarr; Pages holding data</li>
<li><code>modified_database_pages</code> &rarr; Dirty pages waiting to flush</li>
</ul>
<p>Great for automation.</p>
<hr>
<h2 id="the-5-signals-that-actually-matter">The 5 Signals That Actually Matter<a class="anchor-link" id="the-5-signals-that-actually-matter"></a></h2>
<h3 id="1-buffer-pool-hit-ratio-handle-with-care">1. Buffer Pool Hit Ratio (Handle With Care)<a class="anchor-link" id="1-buffer-pool-hit-ratio-handle-with-care"></a></h3>
<p>Yes, it&rsquo;s widely used. No, it&rsquo;s not enough.</p>
<p>A high hit ratio does not mean your system is healthy. It does not capture:</p>
<ul>
<li>Page churn</li>
<li>Eviction pressure</li>
<li>Access patterns</li>
</ul>
<p>You can have a 99% hit ratio and still be IO-bound.</p>
<p>Use it as a sanity check, not a decision-maker.</p>
<hr>
<h3 id="2-free-buffers">2. Free Buffers<a class="anchor-link" id="2-free-buffers"></a></h3>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-4" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-4">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="k">SUM</span><span class="p">(</span><span class="n">free_buffers</span><span class="p">)</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="n">free_buffers</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">FROM</span><span class="w"> </span><span class="n">information_schema</span><span class="p">.</span><span class="n">INNODB_BUFFER_POOL_STATS</span><span class="p">;</span></span></span></code></pre>
</div>
</div>
</div>
<p><strong>Interpretation:</strong></p>
<ul>
<li>Near zero during steady load &rarr; normal</li>
<li>Near zero + rising disk reads &rarr; pressure</li>
<li>Near zero while mostly idle &rarr; suspicious (possible misread or config issue)</li>
</ul>
<hr>
<h3 id="3-dirty-page-percentage">3. Dirty Page Percentage<a class="anchor-link" id="3-dirty-page-percentage"></a></h3>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-5" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-5">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">(</span><span class="k">SUM</span><span class="p">(</span><span class="n">modified_database_pages</span><span class="p">)</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="k">SUM</span><span class="p">(</span><span class="n">database_pages</span><span class="p">))</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mi">100</span><span class="p">.</span><span class="mi">0</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="n">dirty_pct</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">FROM</span><span class="w"> </span><span class="n">information_schema</span><span class="p">.</span><span class="n">INNODB_BUFFER_POOL_STATS</span><span class="p">;</span></span></span></code></pre>
</div>
</div>
</div>
<p><strong>Interpretation (context matters):</strong></p>
<ul>
<li>0&ndash;5% &rarr; Very clean</li>
<li>5&ndash;20% &rarr; Typical</li>
<li>20&ndash;30%+ &rarr; Potential flushing lag</li>
</ul>
<hr>
<h3 id="4-disk-read-pressure-critical-signal">4. Disk Read Pressure (Critical Signal)<a class="anchor-link" id="4-disk-read-pressure-critical-signal"></a></h3>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-6" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-6">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SHOW</span><span class="w"> </span><span class="k">GLOBAL</span><span class="w"> </span><span class="n">STATUS</span><span class="w"> </span><span class="k">LIKE</span><span class="w"> </span><span class="s1">'Innodb_buffer_pool_reads'</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">-- Take two samples 60s apart and compare</span></span></span></code></pre>
</div>
</div>
</div>
<p>Track the rate of change (reads/sec), not the absolute value.</p>
<p><strong>Interpretation:</strong></p>
<ul>
<li>Rising reads &rarr; Working set does not fit in memory</li>
<li>Flat reads &rarr; Memory is absorbing the workload</li>
</ul>
<hr>
<h3 id="5-read-ahead--eviction-pressure">5. Read Ahead / Eviction Pressure<a class="anchor-link" id="5-read-ahead-eviction-pressure"></a></h3>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-7" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-7">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SHOW</span><span class="w"> </span><span class="k">GLOBAL</span><span class="w"> </span><span class="n">STATUS</span><span class="w"> </span><span class="k">LIKE</span><span class="w"> </span><span class="s1">'Innodb_buffer_pool_read_ahead%'</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">SHOW</span><span class="w"> </span><span class="k">GLOBAL</span><span class="w"> </span><span class="n">STATUS</span><span class="w"> </span><span class="k">LIKE</span><span class="w"> </span><span class="s1">'Innodb_buffer_pool_pages_evicted'</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">SHOW</span><span class="w"> </span><span class="k">GLOBAL</span><span class="w"> </span><span class="n">STATUS</span><span class="w"> </span><span class="k">LIKE</span><span class="w"> </span><span class="s1">'Innodb_buffer_pool_reads'</span><span class="p">;</span></span></span></code></pre>
</div>
</div>
</div>
<p><strong>Interpretation:</strong></p>
<ul>
<li>Efficient read-ahead:
<ul>
<li>read_ahead increases</li>
<li>read_ahead_evicted remains low</li>
</ul>
</li>
<li>Inefficient read-ahead (wasted IO):
<ul>
<li>High read_ahead_evicted / read_ahead</li>
<li>Indicates access patterns defeating prefetching</li>
</ul>
</li>
<li>Buffer pool churn:
<ul>
<li>pages_evicted rising</li>
<li>buffer_pool_reads rising</li>
<li>Indicates pages are evicted and re-read from disk</li>
</ul>
</li>
<li>Healthy vs unhealthy eviction:
<ul>
<li>High evictions + stable reads &rarr; normal turnover</li>
<li>High evictions + rising reads &rarr; memory pressure</li>
</ul>
</li>
</ul>
<p>Focus on rates of change over time, not absolute values.</p>
<hr>
<h2 id="detecting-thrashing">Detecting Thrashing<a class="anchor-link" id="detecting-thrashing"></a></h2>
<p>Thrashing is when the buffer pool constantly evicts and reloads pages.</p>
<h3 id="classic-symptoms">Classic Symptoms<a class="anchor-link" id="classic-symptoms"></a></h3>
<ul>
<li>Low or zero free buffers</li>
<li>Increasing disk reads</li>
<li>Stable (but misleading) hit ratio</li>
<li>Spiky query latency</li>
</ul>
<h3 id="visualizing-thrash">Visualizing Thrash<a class="anchor-link" id="visualizing-thrash"></a></h3>
<div class="code-block">
<div class="code-block__header"><button class="code-block__copy" type="button" data-copy-target="codeblock-8" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-8">
<div class="highlight">
<pre class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Time ---&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Memory: [FULL][FULL][FULL][FULL]
</span></span><span class="line"><span class="cl">Reads: &uarr; &uarr;&uarr; &uarr;&uarr;&uarr; &uarr;&uarr;&uarr;&uarr;
</span></span><span class="line"><span class="cl">Latency: - ^ ^^ ^^^
</span></span><span class="line"><span class="cl">Evictions: &uarr; &uarr;&uarr; &uarr;&uarr;&uarr; &uarr;&uarr;&uarr;&uarr;</span></span></code></pre>
</div>
</div>
</div>
<p>If you see this pattern, your working set does not fit in memory.</p>
<hr>
<h2 id="tuning-the-buffer-pool">Tuning the Buffer Pool<a class="anchor-link" id="tuning-the-buffer-pool"></a></h2>
<h3 id="step-1-size-it-intentionally">Step 1: Size It Intentionally<a class="anchor-link" id="step-1-size-it-intentionally"></a></h3>
<p>Instead of blindly assigning 70% of RAM:</p>
<ul>
<li>Observe working set behavior</li>
<li>Monitor free buffers and reads</li>
<li>Increase gradually</li>
</ul>
<p>Avoid starving the OS or filesystem cache.</p>
<hr>
<h3 id="step-2-tune-flushing-behavior">Step 2: Tune Flushing Behavior<a class="anchor-link" id="step-2-tune-flushing-behavior"></a></h3>
<div class="code-block">
<div class="code-block__header"><button class="code-block__copy" type="button" data-copy-target="codeblock-9" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-9">
<div class="highlight">
<pre class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">innodb_max_dirty_pages_pct = 75
</span></span><span class="line"><span class="cl">innodb_io_capacity = 1000
</span></span><span class="line"><span class="cl">innodb_io_capacity_max = 2000</span></span></code></pre>
</div>
</div>
</div>
<ul>
<li>Sustained IO spikes &rarr; increase innodb_io_capacity</li>
<li>Dirty pages climbing &rarr; flushing lag</li>
<li>Sudden stalls &rarr; checkpoint pressure</li>
</ul>
<p><strong>What they control:</strong></p>
<ul>
<li><code>innodb_io_capacity</code> &rarr; Expected steady-state IO throughput</li>
<li><code>innodb_io_capacity_max</code> &rarr; Burst flushing capacity</li>
<li><code>innodb_max_dirty_pages_pct</code> &rarr; Threshold for aggressive flushing</li>
</ul>
<p>&#9888;&#65039; These values should reflect real hardware capability.</p>
<hr>
<h3 id="step-3-buffer-pool-instancesreduce-contention">Step 3: Buffer Pool Instances:Reduce Contention<a class="anchor-link" id="step-3-buffer-pool-instancesreduce-contention"></a></h3>
<p>A practical, battle-tested guideline:</p>
<p>Use 1 instance per ~1GB of buffer pool, up to a reasonable limit.</p>
<p>Buffer Pool Instances: Reducing Contention</p>
<p>The buffer pool can be split into multiple instances, each managing its own internal structures. This helps reduce contention under high concurrency.</p>
<p>Without this, all threads compete for the same buffer pool internals. With multiple instances, that load is distributed.</p>
<hr>
<h3 id="when-it-matters">When It Matters<a class="anchor-link" id="when-it-matters"></a></h3>
<p>Buffer pool instances only help when contention exists. You&rsquo;ll see benefits if your system has:</p>
<ul>
<li>High concurrency (many active threads)</li>
<li>CPU-bound workloads</li>
<li>Mutex contention in InnoDB</li>
</ul>
<p>If your workload is primarily IO-bound, this setting will have little impact.</p>
<hr>
<h3 id="sizing-guidelines">Sizing Guidelines<a class="anchor-link" id="sizing-guidelines"></a></h3>
<p>General guidance:</p>
<ul>
<li>&lt; 1GB buffer pool &rarr; 1 instance</li>
<li>1GB&ndash;8GB &rarr; 2&ndash;4 instances</li>
<li>8GB&ndash;64GB &rarr; 4&ndash;8 instances</li>
<li>64GB+ &rarr; 8&ndash;16 instances</li>
</ul>
<hr>
<h3 id="keep-instances-large-enough">Keep Instances Large Enough<a class="anchor-link" id="keep-instances-large-enough"></a></h3>
<p>Each instance needs enough memory to function efficiently.</p>
<p>Avoid going below ~1GB per instance.</p>
<p>If instances are too small:</p>
<ul>
<li>LRU efficiency drops</li>
<li>Eviction becomes more aggressive</li>
<li>Cache locality suffers</li>
</ul>
<p>Example</p>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-10" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-10">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="n">innodb_buffer_pool_size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">32</span><span class="k">G</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">innodb_buffer_pool_instances</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">8</span></span></span></code></pre>
</div>
</div>
</div>
<p>This gives ~4GB per instance, which is well-balanced.</p>
<hr>
<h3 id="common-mistakes">Common Mistakes<a class="anchor-link" id="common-mistakes"></a></h3>
<ul>
<li>Increasing instances without evidence of contention</li>
<li>Matching instance count to CPU cores</li>
<li>Using many instances with a small buffer pool</li>
<li>Expecting this to fix IO bottlenecks</li>
</ul>
<hr>
<h3 id="step-4-understand-resizing-behavior">Step 4: Understand Resizing Behavior<a class="anchor-link" id="step-4-understand-resizing-behavior"></a></h3>
<p>Buffer pool resizing is online in modern MySQL versions, but:</p>
<ul>
<li>It happens in chunks</li>
<li>Controlled by <code>innodb_buffer_pool_chunk_size</code></li>
</ul>
<hr>
<h2 id="real-world-scenarios">Real-World Scenarios<a class="anchor-link" id="real-world-scenarios"></a></h2>
<h3 id="scenario-1-everything-looks-fine-but-its-slow">Scenario 1: &ldquo;Everything Looks Fine&hellip; But It&rsquo;s Slow&rdquo;<a class="anchor-link" id="scenario-1-everything-looks-fine-but-its-slow"></a></h3>
<ul>
<li>High hit ratio</li>
<li>Low free buffers</li>
<li>Rising disk reads</li>
</ul>
<p><strong>Cause:</strong> Working set barely fits</p>
<p><strong>Fix:</strong> Increase buffer pool size gradually</p>
<p>If increasing the buffer pool size does not reduce disk reads, the problem is not memory.</p>
<hr>
<h3 id="scenario-2-write-heavy-workload">Scenario 2: Write-Heavy Workload<a class="anchor-link" id="scenario-2-write-heavy-workload"></a></h3>
<ul>
<li>Dirty pages increasing</li>
<li>Periodic IO spikes</li>
</ul>
<p><strong>Cause:</strong> Flushing cannot keep up</p>
<p><strong>Fix:</strong></p>
<ul>
<li>Increase <code>innodb_io_capacity</code></li>
<li>Adjust dirty page thresholds</li>
</ul>
<hr>
<h3 id="scenario-3-sudden-latency-spikes">Scenario 3: Sudden Latency Spikes<a class="anchor-link" id="scenario-3-sudden-latency-spikes"></a></h3>
<ul>
<li>Sharp performance drops</li>
<li>Disk activity surges</li>
</ul>
<p><strong>Cause:</strong> Checkpoint pressure</p>
<p><strong>Fix:</strong></p>
<ul>
<li>Improve IO capacity tuning</li>
<li>Reduce dirty page buildup</li>
</ul>
<hr>
<h2 id="practical-monitoring-queries">Practical Monitoring Queries<a class="anchor-link" id="practical-monitoring-queries"></a></h2>
<h3 id="buffer-pool-usage-mb">Buffer Pool Usage (MB)<a class="anchor-link" id="buffer-pool-usage-mb"></a></h3>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-11" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-11">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">(</span><span class="k">SUM</span><span class="p">(</span><span class="n">database_pages</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mi">16</span><span class="p">)</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mi">1024</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="n">mb_used</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">FROM</span><span class="w"> </span><span class="n">information_schema</span><span class="p">.</span><span class="n">INNODB_BUFFER_POOL_STATS</span><span class="p">;</span></span></span></code></pre>
</div>
</div>
</div>
<p>Assumes default 16KB page size (innodb_page_size).</p>
<h3 id="dirty-page-percentage">Dirty Page Percentage<a class="anchor-link" id="dirty-page-percentage"></a></h3>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-12" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-12">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">(</span><span class="n">modified_database_pages</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">database_pages</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mi">100</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="n">dirty_pct</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">FROM</span><span class="w"> </span><span class="n">information_schema</span><span class="p">.</span><span class="n">INNODB_BUFFER_POOL_STATS</span><span class="p">;</span></span></span></code></pre>
</div>
</div>
</div>
<h3 id="free-buffer-check">Free Buffer Check<a class="anchor-link" id="free-buffer-check"></a></h3>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-13" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-13">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="k">SUM</span><span class="p">(</span><span class="n">free_buffers</span><span class="p">)</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="n">free_buffers</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">FROM</span><span class="w"> </span><span class="n">information_schema</span><span class="p">.</span><span class="n">INNODB_BUFFER_POOL_STATS</span><span class="p">;</span></span></span></code></pre>
</div>
</div>
</div>
<hr>
<h2 id="common-mistakes-1">Common Mistakes<a class="anchor-link" id="common-mistakes"></a></h2>
<ul>
<li>Treating 70% as a rule instead of a starting point</li>
<li>Blindly trusting hit ratio</li>
<li>Ignoring disk read trends</li>
<li>Oversizing and starving the OS</li>
<li>Not tuning IO capacity</li>
<li>Leaving defaults in write-heavy systems</li>
</ul>
<hr>
<h2 id="quick-checklist">Quick Checklist<a class="anchor-link" id="quick-checklist"></a></h2>
<p>If you remember nothing else:</p>
<ul>
<li>Reads increasing? &rarr; working set too big</li>
<li>Free buffers always ~0? &rarr; pressure</li>
<li>Dirty pages high? &rarr; flushing lag</li>
<li>Latency spiking? &rarr; checkpoint or IO saturation</li>
</ul>
<hr>
<h2 id="final-thoughts">Final Thoughts<a class="anchor-link" id="final-thoughts"></a></h2>
<p>The InnoDB buffer pool doesn&rsquo;t fail loudly. It degrades quietly until your disk becomes the bottleneck.</p>
<p>By the time you notice, you&rsquo;re debugging latency instead of preventing it.</p>
<p>Monitor the right signals, and you&rsquo;ll see problems forming before users do.</p>
<p>That&rsquo;s the difference between reacting to performance&hellip; and controlling it.</p>

<p>The post <a rel="nofollow" href="https://percona.community/blog/2026/04/02/innodb-buffer-pool-tuning-from-rule-of-thumb-to-real-signals/">InnoDB Buffer Pool Tuning: From Rule-of-Thumb to Real Signals</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>MariaDB Java Connector 3.5.8 now available</title>
      <link>https://mariadb.com/resources/blog/mariadb-java-connector-3-5-8-now-available/</link>
      <pubDate>Wed, 01 Apr 2026 22:35:19 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.com/">MariaDB</source>
      <description><![CDATA[<p>MariaDB is pleased to announce the immediate availability of the MariaDB Connector/J 3.5.8 release. Release Notes and Changelogs MariaDB Connector/J […]</p>
<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/mariadb-java-connector-3-5-8-now-available/">MariaDB Java Connector 3.5.8 now available</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>MariaDB is pleased to announce the immediate availability of the MariaDB Connector/J 3.5.8 release. Download Now Notable items in this release include: See the release notes and changelog for more details and visit mariadb.com/downloads/connectors/connectors-data-access/java8-connector/ to download.</p>
<p><a href="https://mariadb.com/resources/blog/mariadb-java-connector-3-5-8-now-available/" rel="nofollow">Source</a></p>

<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/mariadb-java-connector-3-5-8-now-available/">MariaDB Java Connector 3.5.8 now available</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Benchmarking MyRocks vs. InnoDB in Memory-Constrained Environments</title>
      <link>https://www.percona.com/blog/benchmarking-myrocks-vs-innodb-in-memory-constrained-environments/</link>
      <pubDate>Wed, 01 Apr 2026 13:31:27 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://www.percona.com/blog/">Percona Database Performance Blog</source>
      <description><![CDATA[<p>Benchmarking MyRocks vs. InnoDB in Memory-Constrained Environments It is a well-known fact in the database world that InnoDB is incredibly fast when the entire database fits into memory. But what happens when your data grows beyond your available RAM? MyRocks, built on RocksDB, is frequently recommended as a superior choice for environments constrained by memory, […]</p>
<p>The post <a rel="nofollow" href="https://www.percona.com/blog/benchmarking-myrocks-vs-innodb-in-memory-constrained-environments/">Benchmarking MyRocks vs. InnoDB in Memory-Constrained Environments</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>Benchmarking MyRocks vs. InnoDB in Memory-Constrained Environments It is a well-known fact in the database world that InnoDB is incredibly fast when the entire database fits into memory. But what happens when your data grows beyond your available RAM? MyRocks, built on RocksDB, is frequently recommended as a superior choice for environments constrained by memory, [&hellip;]</p>

<p>The post <a rel="nofollow" href="https://www.percona.com/blog/benchmarking-myrocks-vs-innodb-in-memory-constrained-environments/">Benchmarking MyRocks vs. InnoDB in Memory-Constrained Environments</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>What’s New in MariaDB AI RAG 1.1: Ingestion, Reranking, and Docker Deployment</title>
      <link>https://mariadb.com/resources/blog/whats-new-in-mariadb-ai-rag-1-1-ingestion-reranking-and-docker-deployment/</link>
      <pubDate>Tue, 31 Mar 2026 17:38:01 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.com/">MariaDB</source>
      <description><![CDATA[<p>Moving a RAG (Retrieval-Augmented Generation) application from a local prototype to a production-grade system requires solving for data messy ingestion, […]</p>
<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/whats-new-in-mariadb-ai-rag-1-1-ingestion-reranking-and-docker-deployment/">What’s New in MariaDB AI RAG 1.1: Ingestion, Reranking, and Docker Deployment</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>Moving a RAG (Retrieval-Augmented Generation) application from a local prototype to a production-grade system requires solving for data messy ingestion, retrieval precision, and deployment complexity. MariaDB AI RAG 1.1 addresses these bottlenecks with improvements to the ingestion pipeline, more granular retrieval controls, and a containerized deployment model. MariaDB AI RAG is currently in&hellip;</p>
<p><a href="https://mariadb.com/resources/blog/whats-new-in-mariadb-ai-rag-1-1-ingestion-reranking-and-docker-deployment/" rel="nofollow">Source</a></p>

<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/whats-new-in-mariadb-ai-rag-1-1-ingestion-reranking-and-docker-deployment/">What’s New in MariaDB AI RAG 1.1: Ingestion, Reranking, and Docker Deployment</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Know a MariaDB champion? Submit a nomination</title>
      <link>https://mariadb.org/know-a-mariadb-champion-submit-a-nomination/</link>
      <pubDate>Mon, 30 Mar 2026 14:01:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>One of the things I really like about open source is that a project is never only about the software.<br />
Yes, code is important. Very important. …<br />
Continue reading \"Know a MariaDB champion? Submit a nomination\"<br />
The post Know a MariaDB champion? Submit a nomination appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/know-a-mariadb-champion-submit-a-nomination/">Know a MariaDB champion? Submit a nomination</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>One of the things I really like about open source is that a project is never only about the software.<br>
Yes, code is important. Very important. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/know-a-mariadb-champion-submit-a-nomination/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;Know a MariaDB champion? Submit a nomination&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/know-a-mariadb-champion-submit-a-nomination/">Know a MariaDB champion? Submit a nomination</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/know-a-mariadb-champion-submit-a-nomination/">Know a MariaDB champion? Submit a nomination</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Contributions As a Cost-saver</title>
      <link>https://mariadb.org/contributions-as-a-cost-saver/</link>
      <pubDate>Mon, 30 Mar 2026 12:18:04 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>The economics of open source contribution development. And some questions.<br />
The post Contributions As a Cost-saver appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/contributions-as-a-cost-saver/">Contributions As a Cost-saver</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>The economics of open source contribution development. And some questions.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/contributions-as-a-cost-saver/">Contributions As a Cost-saver</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/contributions-as-a-cost-saver/">Contributions As a Cost-saver</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>2026 – MySQL Ecosystem Performance Benchmark Report</title>
      <link>https://www.percona.com/blog/2026-mysql-ecosystem-performance-benchmark-report/</link>
      <pubDate>Thu, 26 Mar 2026 10:04:22 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://www.percona.com/blog/">Percona Database Performance Blog</source>
      <description><![CDATA[<p>By Percona Lab Results  ·  2026  ·  MySQL MariaDB Percona Benchmark Database MySQL Ecosystem Performance Benchmark Report 2026 Comparative Analysis of InnoDB-Compatible Engines — Percona Lab Results Repository: github.com/Percona-Lab-results/2026-interactive-metrics Interactive graphs available: Explore the full dataset dynamically — click any graph below to open the interactive version. OLTP Read-Write Local — interactive benchmark graph OLTP […]</p>
<p>The post <a rel="nofollow" href="https://www.percona.com/blog/2026-mysql-ecosystem-performance-benchmark-report/">2026 – MySQL Ecosystem Performance Benchmark Report</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>By Percona Lab Results &nbsp;&middot;&nbsp; 2026 &nbsp;&middot;&nbsp; MySQL MariaDB Percona Benchmark Database MySQL Ecosystem Performance Benchmark Report 2026 Comparative Analysis of InnoDB-Compatible Engines &mdash; Percona Lab Results Repository: github.com/Percona-Lab-results/2026-interactive-metrics Interactive graphs available: Explore the full dataset dynamically &mdash; click any graph below to open the interactive version. OLTP Read-Write Local &mdash; interactive benchmark graph OLTP [&hellip;]</p>

<p>The post <a rel="nofollow" href="https://www.percona.com/blog/2026-mysql-ecosystem-performance-benchmark-report/">2026 – MySQL Ecosystem Performance Benchmark Report</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>MariaDB observability – results from the poll: the community has clearly chosen its default stack</title>
      <link>https://mariadb.org/mariadb-observability-results-from-the-poll-the-community-has-clearly-chosen-its-default-stack/</link>
      <pubDate>Thu, 26 Mar 2026 08:35:54 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>Before I share my takeaway from this MariaDB observability poll, I would like to thank all participants and highlight that these recent polls are very popular, and your participation makes us happy. …<br />
Continue reading \"MariaDB observability – results from the poll: the community has clearly chosen its default stack\"<br />
The post MariaDB observability – results from the poll: the community has clearly chosen its default stack appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-observability-results-from-the-poll-the-community-has-clearly-chosen-its-default-stack/">MariaDB observability – results from the poll: the community has clearly chosen its default stack</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>Before I share my takeaway from this MariaDB observability poll, I would like to thank all participants and highlight that these recent polls are very popular, and your participation makes us happy. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/mariadb-observability-results-from-the-poll-the-community-has-clearly-chosen-its-default-stack/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;MariaDB observability &ndash; results from the poll: the community has clearly chosen its default stack&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-observability-results-from-the-poll-the-community-has-clearly-chosen-its-default-stack/">MariaDB observability &ndash; results from the poll: the community has clearly chosen its default stack</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-observability-results-from-the-poll-the-community-has-clearly-chosen-its-default-stack/">MariaDB observability – results from the poll: the community has clearly chosen its default stack</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>MariaDB Vector: How it works. Part II</title>
      <link>https://mariadb.org/mariadb-vector-how-it-works-part-ii/</link>
      <pubDate>Thu, 26 Mar 2026 07:34:29 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>In the first post of this series, I’ve described how the vector index is stored in a table and how it achieves full transactional behavior and ACID properties compatible with the storage engine of the table the user created. …<br />
Continue reading \"MariaDB Vector: How it works. Part II\"<br />
The post MariaDB Vector: How it works. Part II appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-vector-how-it-works-part-ii/">MariaDB Vector: How it works. Part II</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>In the first post of this series, I&rsquo;ve described how the vector index is stored in a table and how it achieves full transactional behavior and ACID properties compatible with the storage engine of the table the user created. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/mariadb-vector-how-it-works-part-ii/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;MariaDB Vector: How it works. Part II&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-vector-how-it-works-part-ii/">MariaDB Vector: How it works. Part II</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-vector-how-it-works-part-ii/">MariaDB Vector: How it works. Part II</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Datography Joins MariaDB Foundation as Silver Sponsor</title>
      <link>https://mariadb.org/datography-joins-mariadb-foundation-as-silver-sponsor/</link>
      <pubDate>Wed, 25 Mar 2026 14:42:02 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>We are pleased to welcome Datography as a Silver Sponsor of the MariaDB Foundation.<br />
Datography focuses on helping organizations understand, map, and manage complex data environments. …<br />
Continue reading \"Datography Joins MariaDB Foundation as Silver Sponsor\"<br />
The post Datography Joins MariaDB Foundation as Silver Sponsor appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/datography-joins-mariadb-foundation-as-silver-sponsor/">Datography Joins MariaDB Foundation as Silver Sponsor</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>We are pleased to welcome <a href="https://www.datography.net/">Datography</a> as a Silver Sponsor of the MariaDB Foundation.<br>
Datography focuses on helping organizations understand, map, and manage complex data environments. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/datography-joins-mariadb-foundation-as-silver-sponsor/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;Datography Joins MariaDB Foundation as Silver Sponsor&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/datography-joins-mariadb-foundation-as-silver-sponsor/">Datography Joins MariaDB Foundation as Silver Sponsor</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/datography-joins-mariadb-foundation-as-silver-sponsor/">Datography Joins MariaDB Foundation as Silver Sponsor</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>MariaDB Keeps Climbing: Community, Adoption, and Momentum</title>
      <link>https://mariadb.org/mariadb-keeps-climbing-community-adoption-and-momentum/</link>
      <pubDate>Wed, 25 Mar 2026 09:59:41 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>If you’ve been around the MariaDB community for a while, you can probably feel it already: things are moving in the right direction.<br />
And no, I’m not talking about one vanity metric, one lucky spike, or one noisy social post. …<br />
Continue reading \"MariaDB Keeps Climbing: Community, Adoption, and Momentum\"<br />
The post MariaDB Keeps Climbing: Community, Adoption, and Momentum appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-keeps-climbing-community-adoption-and-momentum/">MariaDB Keeps Climbing: Community, Adoption, and Momentum</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>If you&rsquo;ve been around the MariaDB community for a while, you can probably feel it already: things are moving in the right direction.<br>
And no, I&rsquo;m not talking about one vanity metric, one lucky spike, or one noisy social post. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/mariadb-keeps-climbing-community-adoption-and-momentum/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;MariaDB Keeps Climbing: Community, Adoption, and Momentum&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-keeps-climbing-community-adoption-and-momentum/">MariaDB Keeps Climbing: Community, Adoption, and Momentum</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-keeps-climbing-community-adoption-and-momentum/">MariaDB Keeps Climbing: Community, Adoption, and Momentum</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>The Agentic Era: Why Infrastructure is the New Innovation Frontier</title>
      <link>https://mariadb.com/resources/blog/the-agentic-era-why-infrastructure-is-the-new-innovation-frontier/</link>
      <pubDate>Tue, 24 Mar 2026 13:02:04 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.com/">MariaDB</source>
      <description><![CDATA[<p>Eighteen months ago, when I joined MariaDB, the industry was already feeling the first tremors of the AI boom. While […]</p>
<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/the-agentic-era-why-infrastructure-is-the-new-innovation-frontier/">The Agentic Era: Why Infrastructure is the New Innovation Frontier</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>Eighteen months ago, when I joined MariaDB, the industry was already feeling the first tremors of the AI boom. While the world focused on the &ldquo;froth&rdquo; &ndash; the flashy consumer apps and chatbots &ndash; the enterprise conversation was stuck on adoption: Will people use it? Will they trust it? Today, we know the answer is a resounding &ldquo;yes.&rdquo; Employees and management alike are eager to leverage AI.</p>
<p><a href="https://mariadb.com/resources/blog/the-agentic-era-why-infrastructure-is-the-new-innovation-frontier/" rel="nofollow">Source</a></p>

<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/the-agentic-era-why-infrastructure-is-the-new-innovation-frontier/">The Agentic Era: Why Infrastructure is the New Innovation Frontier</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>MariaDB 13.0 Preview Now Available</title>
      <link>https://mariadb.org/mariadb-13-0-preview-now-available/</link>
      <pubDate>Tue, 24 Mar 2026 12:49:52 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>We are pleased to announce the availability of a preview of the MariaDB 13.0 series. MariaDB 13.0 is a preview rolling release, published on 23 March 2026, and it continues the work started in 12.3 while adding a solid set of entirely new features. …<br />
Continue reading \"MariaDB 13.0 Preview Now Available\"<br />
The post MariaDB 13.0 Preview Now Available appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-13-0-preview-now-available/">MariaDB 13.0 Preview Now Available</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>We are pleased to announce the availability of a preview of the <a href="https://mariadb.com/docs/release-notes/community-server/13.0/mariadb-13.0-changes-and-improvements">MariaDB 13.0</a> series. MariaDB 13.0 is a preview rolling release, published on 23 March 2026, and it continues the work started in 12.3 while adding a solid set of entirely new features. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/mariadb-13-0-preview-now-available/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;MariaDB 13.0 Preview Now Available&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-13-0-preview-now-available/">MariaDB 13.0 Preview Now Available</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-13-0-preview-now-available/">MariaDB 13.0 Preview Now Available</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>DBaasNow Joins MariaDB Foundation as Silver Sponsor</title>
      <link>https://mariadb.org/dbaasnow-joins-mariadb-foundation-as-silver-sponsor/</link>
      <pubDate>Tue, 24 Mar 2026 07:05:43 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>We are pleased to welcome DBaasNow as a Silver Sponsor of the MariaDB Foundation.<br />
As the MariaDB ecosystem continues to expand across cloud, hybrid, and on-premise environments, the need for consistent, reliable, and scalable database operations has never been more important. …<br />
Continue reading \"DBaasNow Joins MariaDB Foundation as Silver Sponsor\"<br />
The post DBaasNow Joins MariaDB Foundation as Silver Sponsor appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/dbaasnow-joins-mariadb-foundation-as-silver-sponsor/">DBaasNow Joins MariaDB Foundation as Silver Sponsor</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>We are pleased to welcome <a href="https://dbaasnow.com/welcome/">DBaasNow </a>as a Silver Sponsor of the MariaDB Foundation.<br>
As the MariaDB ecosystem continues to expand across cloud, hybrid, and on-premise environments, the need for consistent, reliable, and scalable database operations has never been more important. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/dbaasnow-joins-mariadb-foundation-as-silver-sponsor/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;DBaasNow Joins MariaDB Foundation as Silver Sponsor&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/dbaasnow-joins-mariadb-foundation-as-silver-sponsor/">DBaasNow Joins MariaDB Foundation as Silver Sponsor</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/dbaasnow-joins-mariadb-foundation-as-silver-sponsor/">DBaasNow Joins MariaDB Foundation as Silver Sponsor</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>A Guide to Multi-Region Disaster Recovery with MariaDB Cloud</title>
      <link>https://mariadb.com/resources/blog/a-guide-to-multi-region-disaster-recovery-with-mariadb-cloud/</link>
      <pubDate>Mon, 23 Mar 2026 21:54:58 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.com/">MariaDB</source>
      <description><![CDATA[<p>Recent global events remind us of a difficult truth: infrastructure failures rarely happen at convenient times. From geopolitical conflicts and […]</p>
<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/a-guide-to-multi-region-disaster-recovery-with-mariadb-cloud/">A Guide to Multi-Region Disaster Recovery with MariaDB Cloud</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>Recent global events remind us of a difficult truth: infrastructure failures rarely happen at convenient times. From geopolitical conflicts and natural disasters to even large-scale cloud service disruptions, modern applications must assume that something will eventually fail somewhere. When your database is the system of record for customer transactions, financial operations&hellip;</p>
<p><a href="https://mariadb.com/resources/blog/a-guide-to-multi-region-disaster-recovery-with-mariadb-cloud/" rel="nofollow">Source</a></p>

<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/a-guide-to-multi-region-disaster-recovery-with-mariadb-cloud/">A Guide to Multi-Region Disaster Recovery with MariaDB Cloud</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>MariaDB Python Connector 2.0.0rc2</title>
      <link>https://mariadb.com/resources/blog/mariadb-python-connector-2-0-0rc2/</link>
      <pubDate>Mon, 23 Mar 2026 18:31:40 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.com/">MariaDB</source>
      <description><![CDATA[<p>Today’s release candidate for MariaDB Connector/Python 2.0 marks a transition from the architecture used in version 1.1. Although the previous […]</p>
<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/mariadb-python-connector-2-0-0rc2/">MariaDB Python Connector 2.0.0rc2</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>Today&rsquo;s release candidate for MariaDB Connector/Python 2.0 marks a transition from the architecture used in version 1.1. Although the previous version was fast, it came with technical friction &ndash; specifically the requirement for a pre-installed C/C++ connector, a lack of async and type hint support, and CPython-only compatibility. In version 2.0, we&rsquo;ve rebuilt the connector to remove those&hellip;</p>
<p><a href="https://mariadb.com/resources/blog/mariadb-python-connector-2-0-0rc2/" rel="nofollow">Source</a></p>

<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/mariadb-python-connector-2-0-0rc2/">MariaDB Python Connector 2.0.0rc2</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>MariaDB/MySQL Environment MyEnv 3.0.0 has been released</title>
      <link>https://www.fromdual.com/blog/myenv-release-notes/fromdual-environment-myenv-3.0.0-has-been-released/</link>
      <pubDate>Mon, 23 Mar 2026 16:42:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://www.fromdual.com/aggregator/categories/9/">FromDual GmbH</source>
      <description><![CDATA[<p>FromDual has the pleasure to announce the release of the new version 3.0.0 of its popular MariaDB, MySQL and PostgreSQL multi-instance environment MyEnv.<br />
The new MyEnv can be downloaded here. How to install MyEnv is described in the MyEnv Installation Guide.<br />
In the inconceivable case that you find a bug in the MyEnv please report it to us by sending an email.<br />
Any feedback, statements and testimonials are welcome as well! Please send them to us.<br />
Upgrade from 2.x to 3.0<br />
Please check the MyEnv Installation Guide.<br />
Changes in MyEnv 3.0.0<br />
MyEnv</p>
<p>Template warning improved.<br />
Distro version in --version added.<br />
Check MyEnv configuration permissions.<br />
#fd increased for MyEnv.<br />
myenv.conf should have more secure permissions now.<br />
Situation caught when my.cnf is missing in myenv.conf.<br />
Directories home and run moved to dba and myenv.<br />
Unit file mariadb.service and mysql.service replaced by dba.service.<br />
User mysql replaced by dba in template.<br />
start_stop fixed warning in case argv[1] is missing.<br />
sys_uid filter fixed for Rocky Linux.<br />
dba unit file added to package.<br />
Nagios plugins detection removed from showMyEnvVersion.<br />
Old SysV init files removed and replaced by Systemd unit files.<br />
dba user was introduced and check for system user added.<br />
User dba changed an cosmetic fixes.</p>
<p>MyEnv Installer</p>
<p>2 concurrent installMyEnv versions cannot run any more.<br />
Directroy binlog, cgroups and angel removed from postgresql type installation.<br />
Wrapper script installMyEnv.sh removed.<br />
Cosmetics fixed in installer.<br />
Installation made more mysql friendly.<br />
libaio1t64 considered during installation recommendations on DEB systems.<br />
Next free port suggestion during installMyEnv improved. It will suggest the first free port now.<br />
apt-get and yum replaced by apt and dnf.</p>
<p>MyEnv Utilities</p>
<p>insert_test.sh made PostgreSQL ready.</p>
<p>PostgreSQL</p>
<p>PostgreSQL instance is stopped with fast instead of immediate now.<br />
show_create_table.sh for PostgreSQL made nicer.<br />
PostgreSQL status.sql added.<br />
Minor fixes for PostgreSQL.</p>
<p>General</p>
<p>CHANGELOG updated.<br />
rc made unique.<br />
Minor bugs fixed.<br />
Copyright year updated from 2024 to 2026.<br />
mkdir changed from /bin to /usr/bin which is the new/right standard on all our 3 supported distributions.</p>
<p>Documentation</p>
<p>README updated.<br />
Installation documentation improved, library related stuff documented.<br />
PostgreSQL added to documentation.<br />
lsb_release removed from documentation.<br />
Documentation restructured.<br />
Documentation made more resilient agaist errors.<br />
Documentation process improved.<br />
Documentation moved completely to asciidoc.</p>
<p>Packaging</p>
<p>Package list completed.<br />
Makefile fixed.<br />
Old distro stuff and initV stuff removed.<br />
Build scripts fixed.</p>
<p>For subscriptions of commercial use of MyEnv please get in contact with us.</p>
<p>The post <a rel="nofollow" href="https://www.fromdual.com/blog/myenv-release-notes/fromdual-environment-myenv-3.0.0-has-been-released/">MariaDB/MySQL Environment MyEnv 3.0.0 has been released</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>FromDual has the pleasure to announce the release of the new version 3.0.0 of its popular MariaDB, MySQL and PostgreSQL multi-instance environment <a href="https://www.fromdual.com/software/fromdual-myenv/" title="MariaDB, MySQL and PostgreSQL multi-instance environment">MyEnv</a>.</p>
<p>The new MyEnv can be downloaded <a href="https://support.fromdual.com/admin/public/download.php" target="_blank" title="FromDual download" rel="noopener">here</a>. How to install MyEnv is described in the <a href="https://support.fromdual.com/documentation/myenv/myenv.html#installation-guide" target="_blank" rel="noopener">MyEnv Installation Guide</a>.</p>
<p>In the inconceivable case that you find a bug in the MyEnv please report it to us by sending an <a href="mailto:contact@fromdual.com?Subject=Bug%20report%20for%20myenv">email</a>.</p>
<p>Any feedback, statements and testimonials are welcome as well! Please <a href="mailto:feedback@fromdual.com?Subject=Feedback%20for%20fpmmm">send them to us</a>.</p>
<h2 id="upgrade-from-2x-to-30">Upgrade from 2.x to 3.0<a class="anchor-link" id="upgrade-from-2-x-to-3-0"></a></h2>
<p>Please check the <a href="https://support.fromdual.com/documentation/myenv/myenv.html#upgrade" target="_blank" title="Upgrading MyEnv" rel="noopener">MyEnv Installation Guide</a>.</p>
<h2 id="changes-in-myenv-300">Changes in MyEnv 3.0.0<a class="anchor-link" id="changes-in-myenv-3-0-0"></a></h2>
<h3 id="myenv">MyEnv<a class="anchor-link" id="myenv"></a></h3>
<ul>
<li>Template warning improved.</li>
<li>Distro version in <code>--version</code> added.</li>
<li>Check MyEnv configuration permissions.</li>
<li>#fd increased for MyEnv.</li>
<li><code>myenv.conf</code> should have more secure permissions now.</li>
<li>Situation caught when <code>my.cnf</code> is missing in <code>myenv.conf</code>.</li>
<li>Directories <code>home</code> and <code>run</code> moved to <code>dba</code> and <code>myenv</code>.</li>
<li>Unit file <code>mariadb.service</code> and <code>mysql.service</code> replaced by <code>dba.service</code>.</li>
<li>User <code>mysql</code> replaced by <code>dba</code> in template.</li>
<li><code>start_stop</code> fixed warning in case <code>argv[1]</code> is missing.</li>
<li><code>sys_uid</code> filter fixed for Rocky Linux.</li>
<li><code>dba</code> unit file added to package.</li>
<li>Nagios plugins detection removed from <code>showMyEnvVersion</code>.</li>
<li>Old SysV init files removed and replaced by Systemd unit files.</li>
<li><code>dba</code> user was introduced and check for system user added.</li>
<li>User <code>dba</code> changed an cosmetic fixes.</li>
</ul>
<h3 id="myenv-installer">MyEnv Installer<a class="anchor-link" id="myenv-installer"></a></h3>
<ul>
<li>2 concurrent <code>installMyEnv</code> versions cannot run any more.</li>
<li>Directroy <code>binlog</code>, <code>cgroups</code> and <code>angel</code> removed from <code>postgresql</code> <code>type</code> installation.</li>
<li>Wrapper script <code>installMyEnv.sh</code> removed.</li>
<li>Cosmetics fixed in installer.</li>
<li>Installation made more mysql friendly.</li>
<li><code>libaio1t64</code> considered during installation recommendations on DEB systems.</li>
<li>Next free port suggestion during <code>installMyEnv</code> improved. It will suggest the first free port now.</li>
<li><code>apt-get</code> and <code>yum</code> replaced by <code>apt</code> and <code>dnf</code>.</li>
</ul>
<h3 id="myenv-utilities">MyEnv Utilities<a class="anchor-link" id="myenv-utilities"></a></h3>
<ul>
<li><code>insert_test.sh</code> made PostgreSQL ready.</li>
</ul>
<h3 id="postgresql">PostgreSQL<a class="anchor-link" id="postgresql"></a></h3>
<ul>
<li>PostgreSQL instance is stopped with fast instead of immediate now.</li>
<li><code>show_create_table.sh</code> for PostgreSQL made nicer.</li>
<li>PostgreSQL <code>status.sql</code> added.</li>
<li>Minor fixes for PostgreSQL.</li>
</ul>
<h3 id="general">General<a class="anchor-link" id="general"></a></h3>
<ul>
<li><code>CHANGELOG</code> updated.</li>
<li><code>rc</code> made unique.</li>
<li>Minor bugs fixed.</li>
<li>Copyright year updated from 2024 to 2026.</li>
<li><code>mkdir</code> changed from <code>/bin</code> to <code>/usr/bin</code> which is the new/right standard on all our 3 supported distributions.</li>
</ul>
<h3 id="documentation">Documentation<a class="anchor-link" id="documentation"></a></h3>
<ul>
<li>README updated.</li>
<li>Installation documentation improved, library related stuff documented.</li>
<li>PostgreSQL added to documentation.</li>
<li><code>lsb_release</code> removed from documentation.</li>
<li>Documentation restructured.</li>
<li>Documentation made more resilient agaist errors.</li>
<li>Documentation process improved.</li>
<li>Documentation moved completely to asciidoc.</li>
</ul>
<h3 id="packaging">Packaging<a class="anchor-link" id="packaging"></a></h3>
<ul>
<li>Package list completed.</li>
<li>Makefile fixed.</li>
<li>Old distro stuff and initV stuff removed.</li>
<li>Build scripts fixed.</li>
</ul>
<p>For subscriptions of commercial use of MyEnv please <a href="mailto:contact@fromdual.com?Subject=Commercial%20use%20of%20MyEnv">get in contact</a> with us.</p>

<p>The post <a rel="nofollow" href="https://www.fromdual.com/blog/myenv-release-notes/fromdual-environment-myenv-3.0.0-has-been-released/">MariaDB/MySQL Environment MyEnv 3.0.0 has been released</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Queen Shrugged</title>
      <link>https://mariadb.org/queen-shrugged/</link>
      <pubDate>Mon, 23 Mar 2026 16:33:06 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>It’s easy to be a queen. Why? Because you can always count on your true selfless friends. I learned this already as a young princess. Nothing brightens a ball quite like a comment from one of your most trusted girlfriends:<br />
It’s beautiful. …<br />
Continue reading \"Queen Shrugged\"<br />
The post Queen Shrugged appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/queen-shrugged/">Queen Shrugged</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>It&rsquo;s easy to be a queen. Why? Because you can always count on your true selfless friends. I learned this already as a young princess. Nothing brightens a ball quite like a comment from one of your most trusted girlfriends:<br>
It&rsquo;s beautiful. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/queen-shrugged/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;Queen Shrugged&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/queen-shrugged/">Queen Shrugged</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/queen-shrugged/">Queen Shrugged</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>PostgreSQL 18 Upgrades for AI-Era Workloads and Operations</title>
      <link>https://severalnines.com/blog/postgresql-18-upgrades-for-ai-era-workloads-and-operations/</link>
      <pubDate>Fri, 20 Mar 2026 08:00:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://severalnines.com/">Severalnines</source>
      <description><![CDATA[<p>Today, we’re asking PostgreSQL to do more and more, like handling both transactions and analytics, powering huge SaaS platforms, managing event data, and even dipping into AI-related tasks like vector search. This intense pressure highlights some pain points: slow, unpredictable reads, rigid indexing, complex setups, and risky major upgrades.  PostgreSQL 18 directly addresses these real-world […]<br />
The post PostgreSQL 18 Upgrades for AI-Era Workloads and Operations appeared first on Severalnines.</p>
<p>The post <a rel="nofollow" href="https://severalnines.com/blog/postgresql-18-upgrades-for-ai-era-workloads-and-operations/">PostgreSQL 18 Upgrades for AI-Era Workloads and Operations</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>Today, we&rsquo;re asking PostgreSQL to do more and more, like handling both transactions and analytics, powering huge SaaS platforms, managing event data, and even dipping into AI-related tasks like vector search. This intense pressure highlights some pain points: slow, unpredictable reads, rigid indexing, complex setups, and risky major upgrades.&nbsp;</p>
<p>PostgreSQL 18 directly addresses these real-world operational challenges, skipping flashy features for practical improvements like asynchronous I/O to speed up reads, safer upgrades from keeping optimizer stats, better multi-column indexes, UUIDv7 support, and useful enhancements to logical replication.</p>
<p>I&rsquo;ll dive into these key operational changes, show how they fit your modern workflows, and explain how tools like ClusterControl can make adopting them smooth and painless.</p>
<h2 class="wp-block-heading" id="h-postgresql-18-s-key-features-that-improve-core-workload-performance"><strong>PostgreSQL 18&rsquo;s key features that improve core workload performance</strong><a class="anchor-link" id="postgresql-18s-key-features-that-improve-core-workload-performance"></a></h2>
<p>PostgreSQL 18 isn&rsquo;t about one big, new thing. Instead, it offers fixes for common headaches: slow storage, reads competing with new transactions, inconsistent performance post-upgrade, indexes that don&rsquo;t quite hit the mark, messy authentication, and confusing replication errors &mdash; these fixes smooth out the rough operational edges. Let&rsquo;s look at two key features that will have the greatest effect on production work.</p>
<h3 class="wp-block-heading" id="h-async-i-o-subsystem-aio"><strong>Async I/O subsystem (AIO)</strong><a class="anchor-link" id="async-i-o-subsystem-aio"></a></h3>
<p>Slow storage often causes problems, not the computer itself. Older PostgreSQL waited for each storage request, which is safe but can slow things down, especially when your data is too big for the cache or when running big scans alongside regular transactions.</p>
<p>PostgreSQL 18 fixes this with Asynchronous I/O (AIO). Now, the system can ask for multiple data blocks and keep working instead of waiting for each one. This can significantly improve read-heavy scans and some maintenance operations under the right I/O conditions. New settings like <code>io_method</code> help you tune this &mdash; <strong>don&rsquo;t just test AIO with a single query.</strong></p>
<p>Its real power shows up under heavy, simultaneous load. To see the benefit, test your actual, read-heavy workload, focusing on P95/P99 latency improvements. Pay special attention to maintenance, since PG 18 specifically aims to reduce I/O slowdown during VACUUM.</p>
<p><strong>TIP:</strong> A good test is to compare latency under pressure with and without AIO while background maintenance is running.</p>
<h3 class="wp-block-heading" id="h-skip-scan-on-b-tree-indexes"><strong>Skip-scan on B-tree indexes</strong><a class="anchor-link" id="skip-scan-on-b-tree-indexes"></a></h3>
<p>In large systems, especially SaaS, we often use multi-column indexes. The problem is that queries don&rsquo;t always filter by the index&rsquo;s first column. For example, an index on (tenant_id, created_at) won&rsquo;t help a query just filtering by created_at. This usually means creating extra, redundant indexes &mdash; <strong>PostgreSQL 18 adds skip-scan for multi-column B-tree indexes.</strong></p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="1024" height="979" src="https://severalnines.com/wp-content/uploads/2026/03/diagram-pg18-skip_vs_classic_scans-1024x979.png" alt="" class="wp-image-42910"></figure>
<p>When the planner estimates it&rsquo;s cheaper than scanning the table, it can iterate over the leading column&rsquo;s distinct values and reuse the same index to satisfy predicates on the later columns, even if the leading column isn&rsquo;t constrained. This is a big win because it means fewer extra indexes, making things cleaner, helping performance by reducing write overhead, and keeping VACUUM happy as your data inevitably scales.</p>
<h2 class="wp-block-heading"><strong>How Postgres 18 features practically support modern workload patterns</strong><a class="anchor-link" id="how-postgres-18-features-practically-support-modern-workload-patterns"></a></h2>
<p>PostgreSQL 18&rsquo;s new stuff isn&rsquo;t just random. It&rsquo;s built around how people are actually using Postgres right now. Most setups mix transactions, reporting, search, and more AI stuff, with evolving replication.</p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="1024" height="660" src="https://severalnines.com/wp-content/uploads/2026/03/diagram-pg18-modern_workload_patterns-1024x660.png" alt="" class="wp-image-42913"></figure>
<p>Looking at what users are doing makes the improvements way easier to understand how they benefit day-to-day operations than just seeing a list of new features.</p>
<h3 class="wp-block-heading"><strong>AI &amp; vector search</strong><a class="anchor-link" id="ai-vector-search"></a></h3>
<p>When people use AI with PostgreSQL, they usually don&rsquo;t replace the database with a specialized vector engine. Instead, they keep PostgreSQL as the main system for transactional data and put AI-related data, like embeddings, right alongside it.</p>
<p>When running vector search and transactional workloads together, it&rsquo;s best to keep those data embeddings right next to the data they describe. This means you need reliable performance: steady write speeds, predictable read times even when searches spike, and enough replicas to scale reads without bogging down the main database.</p>
<p>Query complexity grows, mixing things like finding similar items with standard filters on who, when, permissions, or categories. This often leads to heavy database scans, unpredictable read bursts, and competition between search / reporting tasks and regular writes &mdash; PostgreSQL 18&rsquo;s features help smooth all of this out.</p>
<p>Asynchronous I/O helps with storage slowdowns during heavy reads, and skip-scan makes filtering around your similarity searches much faster by improving multicolumn indexes. You still need a smart strategy for your AI indexes, e.g. when to use HNSW, how to organize data, etc., but PG helps the whole system handle the pressure better. Adding ClusterControl to the mix creates a winning combination, as managing replicas, backups, and monitoring performance as you grow becomes much easier.</p>
<h3 class="wp-block-heading"><strong>Serverless ingestion + BI</strong><a class="anchor-link" id="serverless-ingestion-bi"></a></h3>
<p>Many teams want applications to feel serverless and handle real-time data analysis, even when self-managing PostgreSQL or in a hybrid setup. Raw speed isn&rsquo;t the priority; it&rsquo;s how the system handles sudden spikes and recovers quickly, posing two hurdles:</p>
<p><strong>First,</strong> when lots of people use it at once, things can get slow. <strong>Second,</strong> we have less and less time for maintenance and upgrades, and we need stability right away after an update.</p>
<p>PostgreSQL 18 fixes both. Better I/O helps with slow reads during busy times, and keeping performance stats after an upgrade means less post-update drama. Basically, PostgreSQL is becoming stronger for unpredictable loads, and when you can&rsquo;t afford any downtime. ClusterControl makes upgrades and backups consistent across all your setups; that&rsquo;s what makes the difference between a normal maintenance window and a full-blown incident.</p>
<h3 class="wp-block-heading"><strong>Isolated replication for multi-tenant workloads</strong><a class="anchor-link" id="isolated-replication-for-multi-tenant-workloads"></a></h3>
<p>Multi-tenant systems often use replication to handle more reads, separate workloads, or serve different regions. This usually means having read replicas for regions, consumers for analytics or search, and moving tenants around as the system grows.</p>
<p>PostgreSQL 18 improves replication by better supporting generated columns and making conflicts much easier to see. This makes tenant-specific replication safer to run and easier to fix when problems occur. ClusterControl helps by setting up and monitoring these setups consistently, preventing hard-to-maintain, one-off replication configurations.</p>
<p>The result is fewer unexpected issues during replication, clearer insight when conflicts happen, and more confidence when changing how your replication is set up.</p>
<h2 class="wp-block-heading"><strong>Postgres 18 features that reduce upgrade &amp; migration workflow risks</strong><a class="anchor-link" id="postgres-18-features-that-reduce-upgrade-migration-workflow-risks"></a></h2>
<p>Upgrading PostgreSQL is mostly about managing risk, not the specific steps. What teams truly care about is getting the system back to normal, predictable performance fast.</p>
<p>Tight maintenance windows, huge databases, and low tolerance for issues mean the period after the upgrade can be brutal. Slow query plans, unexpected slowdowns, or emergency tuning can quickly turn a successful upgrade into an on-call nightmare. Here are the design changes and features PostgreSQL 18 implements to tackle these specific problems.</p>
<h3 class="wp-block-heading"><strong>Faster upgrades with retained statistics</strong><a class="anchor-link" id="faster-upgrades-with-retained-statistics"></a></h3>
<p>Historically, PostgreSQL upgrades caused frustrating performance degradation because the query planner had to relearn all data statistics. PG 18&rsquo;s pg_upgrade utility now transfers most optimizer statistics. This feature dramatically stabilizes performance much faster post-upgrade by immediately providing current data knowledge to the planner, eliminating the stressful, lengthy process of relearning statistics, especially for large databases.</p>
<h3 class="wp-block-heading"><strong>Checksums enabled by default in initdb</strong><a class="anchor-link" id="checksums-enabled-by-default-in-initdb"></a></h3>
<p>PostgreSQL 18 changes a big default: new clusters now turn on data checksums automatically when you run <code>initdb</code>. Checksums are great for catching sneaky data corruption, though they use a tiny bit more CPU. Most teams already use them for better durability or compliance &mdash; you can still opt out with <code>--no-data-checksums</code>.</p>
<p><strong>However,</strong> <strong>checksum settings must match exactly when you upgrade</strong>. If your old cluster didn&rsquo;t have checksums, you can&rsquo;t magically turn them on during the upgrade.</p>
<p>Think of the checksum setting as a contract for your cluster. Document it, keep it consistent everywhere, and test it during your upgrade dry runs. Don&rsquo;t leave it as a last-minute decision, or you&rsquo;ll find problems during the final cutover instead of in testing.</p>
<h2 class="wp-block-heading"><strong>PG 18 enhancements that improve developer &amp; SQL quality of life</strong><a class="anchor-link" id="pg-18-enhancements-that-improve-developer-sql-quality-of-life"></a></h2>
<p>Even though some PostgreSQL features seem like they are just for developers, they often impact how things run behind the scenes. Schema and SQL choices can unexpectedly influence storage, how much data is written, replication size, and index performance over time. Postgres 18 brings changes in this area that operators should really pay attention to.</p>
<h3 class="wp-block-heading"><strong>Virtual generated columns (default)</strong><a class="anchor-link" id="virtual-generated-columns-default"></a></h3>
<p>PostgreSQL 18&rsquo;s generated columns are usually virtual, meaning the value is calculated when you read the row, not saved on disk, unless you choose to store it.</p>
<p>Operationally, this is key. Virtual columns cut down on writes and storage, which is great for busy tables. But if you read the derived value a lot or need to index it predictably, stored columns might be better, as the calculation is done once on write, not on read; for example,</p>
<pre class="wp-block-code"><code>CREATE TABLE orders (
  id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  amount numeric(12,2) NOT NULL,
  amount_cents bigint GENERATED ALWAYS AS ((amount * 100)::bigint)
);</code></pre>
<p>In PostgreSQL 18, amount_cents is virtual by default. If you want the value precomputed and stored, because it&rsquo;s heavily queried or indexed, you can still do so explicitly:</p>
<pre class="wp-block-code"><code>amount_cents bigint GENERATED ALWAYS AS ((amount * 100)::bigint) STORED</code></pre>
<p><strong>N.B. This decision is crucial for replication.</strong> PG 18&rsquo;s logical replication is better at publishing stored generated values.</p>
<h3 class="wp-block-heading"><strong>UUIDv7 for time-ordered IDs</strong><a class="anchor-link" id="uuidv7-for-time-ordered-ids"></a></h3>
<p>UUIDs are great because they&rsquo;re unique everywhere and easy to make across different systems. The problem has been how they mess up B-tree indexes. Random UUIDs scatter new entries, causing slow index bloat and cache issues, especially on busy tables.</p>
<p>Recent PostgreSQL releases fixed this natively with uuidv7(), making it the operational standard for PostgreSQL 18 architectures. It generates UUIDs that are mostly time-ordered. This keeps your indexes much tidier while keeping the benefits of using UUIDs. For example:</p>
<pre class="wp-block-code"><code>CREATE TABLE sessions (
  id uuid PRIMARY KEY DEFAULT uuidv7(),
  created_at timestamptz NOT NULL DEFAULT now()
);</code></pre>
<p>If you&rsquo;ve been hesitant to use UUID primary keys on high-ingest tables because of index behavior, UUIDv7 makes that trade-off far more reasonable.</p>
<h3 class="wp-block-heading"><strong>Temporal constraints for time-varying facts</strong><a class="anchor-link" id="temporal-constraints-for-time-varying-facts"></a></h3>
<p>Dealing with time-sensitive data, like pricing or subscriptions, usually means complex application code and tricky locking to avoid mistakes. Postgres 18 simplifies this with temporal constraints.</p>
<p>These let the database enforce rules, like primary and foreign keys, over time ranges. This moves the headache of correctness from your application logic into the database, making enforcement instant and reliable.</p>
<p>For operations teams, this means fewer weird errors, less data cleanup, and fewer 2 a.m. alerts caused by subtle concurrency issues.</p>
<h3 class="wp-block-heading"><strong>OAuth authentication</strong><a class="anchor-link" id="oauth-authentication"></a></h3>
<p>PostgreSQL 18 now supports OAuth, which is a big deal for security. It gives you a path to reduce long-lived DB passwords by using short-lived tokens where it fits your identity stack. It won&rsquo;t fix bad internal role design, but it massively cuts down on the headache of credential sprawl, especially where infrastructure is constantly spinning up and down. OAuth is just way easier to manage than traditional passwords in those dynamic setups.</p>
<h2 class="wp-block-heading"><strong>How PostgreSQL 18 improves logical &amp; streaming replication efficiency</strong><a class="anchor-link" id="how-postgresql-18-improves-logical-streaming-replication-efficiency"></a></h2>
<p>Replication gets complicated fast. One replica is simple, but the more lag, conflicts, and strange failures you open yourself up to. PostgreSQL 18 doesn&rsquo;t magically automate logical replication or make it DDL aware. What it does instead is provide practical improvements that make operating, monitoring, and managing your replicas much easier.</p>
<h3 class="wp-block-heading"><strong>Generated column replication</strong><a class="anchor-link" id="generated-column-replication"></a></h3>
<p>Building on recent improvements, modern PostgreSQL lets you publish stored generated columns using the <code>publish_generated_columns</code> option. This is great for downstream systems that need the calculated value right away instead of having to recompute it. PostgreSQL sends the generated value and replicates it into a normal column on the subscriber.</p>
<p><strong>N.B. You cannot replicate it into another generated column; that will fail.</strong></p>
<p>Basically, this feature ships the finished, calculated results, not the formula or the generated column definition. Its best use is to simplify your consumers and avoid repeating work, without getting into complicated DDL replication. Let&rsquo;s look at a simple logical replication setup to illustrate how Postgres 18 handles generated columns.</p>
<p><strong>On the publisher:</strong></p>
<pre class="wp-block-code"><code>CREATE TABLE orders (
  id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  amount numeric(12,2) NOT NULL,
  amount_cents bigint GENERATED ALWAYS AS ((amount * 100)::bigint) STORED
);

CREATE PUBLICATION orders_pub
FOR TABLE orders
WITH (publish_generated_columns = 'stored');</code></pre>
<p><strong>On the subscriber</strong>, the generated value is replicated into a regular column:</p>
<pre class="wp-block-code"><code>CREATE TABLE orders (
  id bigint PRIMARY KEY,
  amount numeric(12,2) NOT NULL,
  amount_cents bigint NOT NULL
);

CREATE SUBSCRIPTION orders_sub
  CONNECTION 'host=&lt;publisher_host&gt; port=5432 dbname=&lt;db&gt; user=&lt;repl_user&gt; password=&lt;password&gt;'
  PUBLICATION orders_pub;</code></pre>
<p>This setup reflects how PostgreSQL 18 actually handles generated column replication: you publish the stored generated value and apply it to a normal column on the subscriber. It&rsquo;s a practical, explicit approach that avoids surprises and stays within the supported model.</p>
<h3 class="wp-block-heading"><strong>Streaming defaults and conflict logging</strong><a class="anchor-link" id="streaming-defaults-and-conflict-logging"></a></h3>
<p>Logical subscriptions now use parallel streaming by default. This means faster throughput and less lag right out of the box, especially when things are busy or transactions are large.</p>
<p>Conflict handling is also much better. Modern PostgreSQL logs conflicts and shows conflict details in <code>pg_stat_subscription_stats</code>. While not brand new to 18, utilizing this view is a massive upgrade if you are coming from older major versions. Replication conflicts are usually a nightmare because you can&rsquo;t see them as they happen. Better visibility means you can spot trends, link issues to workload changes, and write reliable troubleshooting guides without relying on guesswork.</p>
<h3 class="wp-block-heading"><strong>Hygiene improvements for larger estates</strong><a class="anchor-link" id="hygiene-improvements-for-larger-estates"></a></h3>
<p>When you have a lot of replication going on, keeping things tidy is as crucial as keeping them fast. PostgreSQL 18 adds a few safeguards to head off slow, hidden problems:</p>
<ul class="wp-block-list">
<li><code>idle_replication_slot_timeout</code>: Automatically invalidates idle replication slots that have been inactive for too long.</li>
<li><code>max_active_replication_origins</code>: Lets you limit the number of active replication origins, regardless of the number of existing slots.</li>
</ul>
<p>If you&rsquo;ve ever had an old, forgotten logical slot quietly hogging Write-Ahead Log (WAL) space for weeks, you&rsquo;ll appreciate these. They don&rsquo;t replace good monitoring, but they make it much harder for tiny mistakes to turn into massive cleanup projects later.</p>
<h2 class="wp-block-heading"><strong>Manual vs. ClusterControl PostgreSQL 18 operations</strong><a class="anchor-link" id="manual-vs-clustercontrol-postgresql-18-operations"></a></h2>
<p>Managing PostgreSQL by hand is fine until it isn&rsquo;t. When you have just a couple of clusters, doing it yourself is easy. But once you have more than a few, those manual steps start wasting time and attention. That&rsquo;s when the downsides really hit.</p>
<h3 class="wp-block-heading"><strong>Manual PG 18 operations</strong><a class="anchor-link" id="manual-pg-18-operations"></a></h3>
<p>Running PostgreSQL manually gives you total freedom, which is great for small setups. But not everything is good. Let&rsquo;s see what we&rsquo;re talking about.</p>
<p><strong>Pros:</strong></p>
<ul class="wp-block-list">
<li>You control everything about the setup.</li>
<li>You can perfectly tune it for each task.</li>
<li>Trying new things is easy in one environment.</li>
</ul>
<p><strong>Cons:</strong></p>
<ul class="wp-block-list">
<li>Big upgrades are messy. Checksums, making sure plans stay stable, and planning for rollbacks are a headache.</li>
<li>Replication slot cleanup is a long-term chore, especially as your setup changes.</li>
<li>Backup plans often differ between clusters as you add or rebuild them.</li>
<li>Monitoring is usually a bunch of tools cobbled together, leading to confusing alerts and nobody knowing who&rsquo;s on point during a problem.</li>
</ul>
<p>Trivial alone, critical cumulatively, these will chew up a ton of your team&rsquo;s time as you grow.</p>
<h3 class="wp-block-heading"><strong>Automated PG 18 ops with ClusterControl</strong><a class="anchor-link" id="automated-pg-18-ops-with-clustercontrol"></a></h3>
<p>ClusterControl really shines when you&rsquo;re rolling PostgreSQL 18 because it saves you from having to figure out the same operational steps over and over again for every new cluster.</p>
<p><strong>Pros:</strong></p>
<ul class="wp-block-list">
<li>Centralized hybrid setup and management of all PostgreSQL clusters.</li>
<li>Guided major version updates, ensuring you don&rsquo;t miss crucial pre-checks.</li>
<li>Easily and safely applied parameter changes, like AIO tuning, across the board.</li>
<li>Turnkey streaming replication, including built-in HAProxy and PgBouncer support.</li>
<li>Single view alerting and health checks, e.g. replication lag, node status, backups, etc.</li>
<li>Backup policy enforcement using common tools, e.g. pgBackRest, pg_basebackup, etc.</li>
</ul>
<p><strong>Cons:</strong></p>
<ul class="wp-block-list">
<li>It&rsquo;s a platform, implying its own learning curve.</li>
<li>You need to check that your rollout timing aligns with ClusterControl&rsquo;s support for the PostgreSQL version you want to use.</li>
</ul>
<p>For teams managing PostgreSQL at scale across many environments, this consistency is often more valuable than having total control over every single command, and easier.</p>
<h2 class="wp-block-heading"><strong>Installing &amp; setting up Postgres 18</strong><a class="anchor-link" id="installing-setting-up-postgres-18"></a></h2>
<p>When piloting PostgreSQL 18, set up your test environment to mimic your real deployment exactly. Use the same storage, replication setup, extensions, like <code>pgvector</code>, and a realistic, large enough dataset. Small, fake tests will just hide the real problems you need to find.</p>
<h3 class="wp-block-heading"><strong>Installing PostgreSQL 18</strong><a class="anchor-link" id="installing-postgresql-18"></a></h3>
<p>The specific package names and repos differ based on your system, but here&rsquo;s an example of a typical installation:</p>
<h4 class="wp-block-heading"><strong>For Debian-Based OS:</strong></h4>
<pre class="wp-block-code"><code>sudo apt update
sudo apt install postgresql-18</code></pre>
<p><strong>For RedHat-Based OS:</strong></p>
<pre class="wp-block-code"><code>sudo dnf install postgresql18-server</code></pre>
<h3 class="wp-block-heading"><strong>Checksums at initdb</strong><a class="anchor-link" id="checksums-at-initdb"></a></h3>
<p>Remember, PostgreSQL 18 enables checksums by default at initialization.</p>
<pre class="wp-block-code"><code>sudo /usr/pgsql-18/bin/postgresql-18-setup initdb</code></pre>
<p>But, you can opt out:</p>
<pre class="wp-block-code"><code>sudo -u postgres /usr/pgsql-18/bin/initdb
--no-data-checksums -D /var/lib/pgsql/18/data</code></pre>
<p><strong>Don&rsquo;t forget</strong> that <code>pg_upgrade</code> requires checksum settings to match between the source and target clusters. Treat checksum posture as an upgrade design decision and validate it during rehearsals, not during the cutover window.</p>
<h3 class="wp-block-heading"><strong>Confirm AIO-related settings:&nbsp;</strong><a class="anchor-link" id="confirm-aio-related-settings"></a></h3>
<p>Once the cluster is up, confirm the effective I/O-related settings you&rsquo;re running with:</p>
<pre class="wp-block-code"><code>SHOW io_method;
SHOW effective_io_concurrency;
SHOW maintenance_io_concurrency;</code></pre>
<p>Exact behavior depends on platform and build options, but this gives you a baseline before you start tuning or running load tests.</p>
<h3 class="wp-block-heading"><strong>Adding the PG 18 cluster to ClusterControl and initializing HA / replication</strong><a class="anchor-link" id="adding-the-pg-18-cluster-to-clustercontrol-and-initializing-ha-replication"></a></h3>
<p>Once running, the next step is to bring the cluster into ClusterControl and establish a sane baseline topology. For many teams, a solid default looks like:</p>
<ul class="wp-block-list">
<li>1 primary</li>
<li>1&ndash;2 replicas</li>
<li>HAProxy for routing and HA</li>
<li>PgBouncer for connection pooling (especially with spiky workloads)</li>
</ul>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="1024" height="374" src="https://severalnines.com/wp-content/uploads/2026/02/cc_database_topology_viewer-pg18_pgbouncer_lb-1024x374.png" alt="" class="wp-image-42727"></figure>
<p>ClusterControl&rsquo;s guided workflows can deploy PostgreSQL streaming replication and integrate HAProxy and PgBouncer as part of the setup, reducing the amount of manual wiring needed to reach a production-ready state.</p>
<h2 class="wp-block-heading"><strong>PostgreSQL 18 operations &amp; monitoring</strong><a class="anchor-link" id="postgresql-18-operations-monitoring"></a></h2>
<p>PostgreSQL 18 offers better control and visibility. But that only matters if you use it to create reliable runbooks for when things go sideways. Don&rsquo;t tweak every last setting; the real win is a predictable system under pressure, not the ability to see clearly when it isn&rsquo;t.</p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="1024" height="516" src="https://severalnines.com/wp-content/uploads/2026/02/cc_database_cluster_overview_dashboard-pg18-1024x516.png" alt="" class="wp-image-42728"></figure>
<h3 class="wp-block-heading"><strong>Tune AIO safely</strong><a class="anchor-link" id="tune-aio-safely"></a></h3>
<p>Asynchronous I/O (AIO) in PostgreSQL 18 is a big deal, especially for handling many tasks at once, but don&rsquo;t rush it. It shines under heavy load, not in simple tests.</p>
<p><strong>Start Simple:</strong></p>
<ul class="wp-block-list">
<li>Pick the right io_method for your system.</li>
<li>Test it with your actual, busy application, not just single queries.</li>
<li>Then try adjusting <code>io_combine_limit</code> and <code>io_max_combine_limit</code>.</li>
</ul>
<p><strong>What to Watch For:</strong></p>
<ul class="wp-block-list">
<li>How long scans take, especially big ones.</li>
<li>Storage delays when the system is busy.</li>
<li>Your worst-case waiting times (P95/P99), not just the average.</li>
<li>How VACUUM behaves while everything else is running.</li>
</ul>
<p><strong>How to tell if it is working: </strong>You&rsquo;ll see fewer unexpected slowdowns when reading a lot of data and less fighting between maintenance tasks and user traffic.</p>
<h3 class="wp-block-heading"><strong>Vacuum/Analyze delay reporting for SLOs</strong><a class="anchor-link" id="vacuum-analyze-delay-reporting-for-slos"></a></h3>
<p>PostgreSQL 18 has better insight into maintenance throttling. If you enable <code>track_cost_delay_timing</code>, VACUUM and ANALYZE will tell you exactly how long they waited because of cost-based delays.</p>
<p>This is huge for troubleshooting. Are you falling behind on maintenance because you told the system to slow down, or because it&rsquo;s genuinely struggling? Knowing the difference is key when figuring out why you missed an SLO or when planning your next capacity upgrade.</p>
<h3 class="wp-block-heading"><strong>Logical replication visibility</strong><a class="anchor-link" id="logical-replication-visibility"></a></h3>
<p>PostgreSQL 18 makes using logical replication much easier by giving you better tools to see what&rsquo;s happening.</p>
<p>Make <code>pg_stat_subscription_stats</code> a regular check-in:</p>
<ul class="wp-block-list">
<li>See conflict counts and when they happened.</li>
<li>Monitor lag and how applies are working over time.</li>
<li>Check that the default parallel streaming is what you want.</li>
</ul>
<p>While better visibility doesn&rsquo;t stop replication problems, it lets you move past the guesswork so you can actually understand the issues and automate fixes.</p>
<h3 class="wp-block-heading"><strong>ClusterControl dashboards</strong><a class="anchor-link" id="clustercontrol-dashboards"></a></h3>
<p>ClusterControl simplifies managing your PostgreSQL clusters by giving you a clear, consistent view of the important stuff:</p>
<ul class="wp-block-list">
<li>Node health and resources</li>
<li>Replication status and lag</li>
<li>High availability and auto-recovery</li>
<li>Backup posture, state, and compliance</li>
</ul>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="1024" height="516" src="https://severalnines.com/wp-content/uploads/2026/02/cc_database_host_overview_dashboard-pg18-1024x516.png" alt="" class="wp-image-42729"></figure>
<h2 class="wp-block-heading"><strong>Ready to move? PostgreSQL 18 upgrade planning checklist</strong><a class="anchor-link" id="ready-to-move-postgresql-18-upgrade-planning-checklist"></a></h2>
<p>Before jumping to PostgreSQL 18, just check these basics first:</p>
<ul class="wp-block-list">
<li>Backups: Take a full backup and make sure you can restore it.</li>
<li>Checksums: See if your current cluster uses data checksums and plan for the new one.</li>
<li>Extensions: Check that all your extensions (like <code>pgvector</code> or <code>PostGIS</code>) are compatible with PostgreSQL 18.</li>
</ul>
<p>Getting these things squared away now makes the whole upgrade process much smoother.</p>
<h2 class="wp-block-heading"><strong>Conclusion</strong><a class="anchor-link" id="conclusion"></a></h2>
<p>PostgreSQL 18 is all about better operations. It fixes common headaches like slow I/O (thanks to async I/O), keeps upgrades predictable by saving optimizer stats, makes multicolumn indexes smarter with skip-scan, adds modern OAuth authentication, improves index use for busy tables with UUIDv7, and simplifies logical replication.</p>
<p>If you&rsquo;re thinking of upgrading, don&rsquo;t rush. Test PostgreSQL 18 in your lower environments with real data and load first. Check your extensions, especially pgvector. Decide on checksums early, as they&rsquo;re now on by default, and <code>pg_upgrade</code> needs them to match. And definitely test your replication setup, including conflict scenarios.</p>
<p>Taking the time for this careful rollout means fewer surprises and smoother changes in production. Ready to get started with PostgreSQL 18, regardless of where you run it?</p>
<h2 class="wp-block-heading"><strong>Install ClusterControl in 10-minutes. Free 30-day Enterprise trial included!</strong><a class="anchor-link" id="install-clustercontrol-in-10-minutes-free-30-day-enterprise-trial-included"></a></h2>
<h3 class="wp-block-heading" id="h-script-installation-instructions"><strong>Script Installation Instructions</strong><a class="anchor-link" id="script-installation-instructions"></a></h3>
<p>The installer script is the simplest way to get ClusterControl up and running. Run it on your chosen host, and it will take care of installing all required packages and dependencies.</p>
<p>Offline environments are supported as well. See the&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/offline-installation/">Offline Installation</a>&nbsp;guide for more details.</p>
<p>On the ClusterControl server, run the following commands:</p>
<pre class="wp-block-code"><code>wget https://severalnines.com/downloads/cmon/install-cc
chmod +x install-cc</code></pre>
<p>With your install script ready, run the command below. Replace&nbsp;<code>S9S_CMON_PASSWORD</code>&nbsp;and&nbsp;<code>S9S_ROOT_PASSWORD</code>&nbsp;placeholders with your choice password, or remove the environment variables from the command to interactively set the passwords. If you have multiple network interface cards, assign one IP address for the&nbsp;<code>HOST</code>&nbsp;variable in the command using&nbsp;<code>HOST=&lt;ip_address&gt;</code>.</p>
<pre class="wp-block-code"><code>S9S_CMON_PASSWORD=&lt;your_password&gt; S9S_ROOT_PASSWORD=&lt;your_password&gt; HOST=&lt;ip_address&gt; ./install-cc # as root or sudo user</code></pre>
<p>After the installation is complete, open a web browser, navigate to&nbsp;<code>https://&lt;ClusterControl_host&gt;/</code>, and create the first admin user by entering a username (note that &ldquo;admin&rdquo; is reserved) and a password on the&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/quickstart/#step-2-create-the-first-admin-user">welcome page</a>. Once you&rsquo;re in, you can&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/user-guide/deployment/create-database-cluster/">deploy</a>&nbsp;a new database cluster or&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/user-guide/deployment/import-database-cluster/">import</a>&nbsp;an existing one.</p>
<p>The installer script supports a range of environment variables for advanced setup. You can define them using export or by prefixing the install command.</p>
<p>See the&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/online-installation/#environment-variables">list of supported variables</a>&nbsp;and&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/online-installation/#example-use-cases">example use cases</a>&nbsp;to tailor your installation.</p>
<h4 class="wp-block-heading" id="h-other-installation-options">Other Installation Options</h4>
<p><strong>Helm Chart</strong></p>
<p>Deploy ClusterControl on Kubernetes using our&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/online-installation/#helm-chart">official Helm chart</a>.</p>
<p><strong>Ansible Role</strong></p>
<p>Automate installation and configuration using our&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/online-installation/#ansible-role">Ansible playbooks</a>.</p>
<p><strong>Puppet Module</strong></p>
<p>Manage your ClusterControl deployment with the&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/online-installation/#puppet-module">Puppet module</a>.</p>
<h4 class="wp-block-heading" id="h-clustercontrol-on-marketplaces">ClusterControl on Marketplaces</h4>
<p>Prefer to launch ClusterControl directly from the cloud? It&rsquo;s available on these platforms:</p>
<ul class="wp-block-list">
<li><a href="https://marketplace.digitalocean.com/apps/clustercontrol">DigitalOcean Marketplace</a></li>
<li><a href="https://gridscale.io/en/marketplace">gridscale.io Marketplace</a></li>
<li><a href="https://www.vultr.com/marketplace/apps/clustercontrol/">Vultr Marketplace</a></li>
<li><a href="https://www.linode.com/marketplace/apps/severalnines/clustercontrol/">Linode Marketplace</a></li>
<li><a href="https://console.cloud.google.com/marketplace/product/severalnines-public/clustercontrol">Google Cloud Platform</a></li>
</ul>
<p>The post <a href="https://severalnines.com/blog/postgresql-18-upgrades-for-ai-era-workloads-and-operations/">PostgreSQL 18 Upgrades for AI-Era Workloads and Operations</a> appeared first on <a href="https://severalnines.com">Severalnines</a>.</p>

<p>The post <a rel="nofollow" href="https://severalnines.com/blog/postgresql-18-upgrades-for-ai-era-workloads-and-operations/">PostgreSQL 18 Upgrades for AI-Era Workloads and Operations</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>MariaDB Vector: How it works</title>
      <link>https://mariadb.org/mariadb-vector-how-it-works/</link>
      <pubDate>Thu, 19 Mar 2026 10:09:20 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>You might have seen that MariaDB Vector is fast. And is getting faster. But why? How does it achieve that? …<br />
Continue reading \"MariaDB Vector: How it works\"<br />
The post MariaDB Vector: How it works appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-vector-how-it-works/">MariaDB Vector: How it works</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>You might have seen that MariaDB Vector is <a href="https://mariadb.com/resources/blog/how-fast-is-mariadb-vector/" target="_blank" rel="noreferrer noopener">fast</a>. And is getting <a href="https://mariadb.org/big-vector-search-benchmark-10-databases-comparison/" target="_blank" rel="noreferrer noopener">faster</a>. But why? How does it achieve that? &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/mariadb-vector-how-it-works/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;MariaDB Vector: How it works&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-vector-how-it-works/">MariaDB Vector: How it works</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-vector-how-it-works/">MariaDB Vector: How it works</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>MariaDB Enterprise Server Q1 2026 Maintenance Releases</title>
      <link>https://mariadb.com/resources/blog/mariadb-enterprise-server-q1-2026-maintenance-releases/</link>
      <pubDate>Wed, 18 Mar 2026 23:49:01 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.com/">MariaDB</source>
      <description><![CDATA[<p>New maintenance releases for MariaDB Enterprise Server 11.8.6-3, 11.4.10-7, and 10.6.25-21 are now available. Notable Release Updates MariaDB Enterprise Server […]</p>
<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/mariadb-enterprise-server-q1-2026-maintenance-releases/">MariaDB Enterprise Server Q1 2026 Maintenance Releases</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>New maintenance releases for MariaDB Enterprise Server 11.8.6-3, 11.4.10-7, and 10.6.25-21 are now available. Download Now MariaDB Enterprise Server is an enhanced, hardened and secured version of MariaDB Community Server that delivers enterprise reliability, stability and long-term support as well as greater operational efficiency when it comes to&hellip;</p>
<p><a href="https://mariadb.com/resources/blog/mariadb-enterprise-server-q1-2026-maintenance-releases/" rel="nofollow">Source</a></p>

<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/mariadb-enterprise-server-q1-2026-maintenance-releases/">MariaDB Enterprise Server Q1 2026 Maintenance Releases</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>MariaDB Innovation: InnoDB-Based Binary Log</title>
      <link>https://mariadb.org/mariadb-innovation-innodb-based-binary-log/</link>
      <pubDate>Tue, 17 Mar 2026 12:45:28 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>I am starting a new series on what makes MariaDB Server distinct from MySQL, highlighting innovations that make the difference.<br />
MariaDB 12.3 introduces a new binary log implementation that stores binlog events directly in InnoDB-managed tablespaces rather than in separate flat files on disk. …<br />
Continue reading \"MariaDB Innovation: InnoDB-Based Binary Log\"<br />
The post MariaDB Innovation: InnoDB-Based Binary Log appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-innovation-innodb-based-binary-log/">MariaDB Innovation: InnoDB-Based Binary Log</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>I am starting a new series on what makes MariaDB Server distinct from MySQL, highlighting innovations that make the difference.<br>
<a href="https://mariadb.com/docs/server/server-management/server-monitoring-logs/binary-log/innodb-based-binary-log">MariaDB 12.3 introduces a new binary log</a> implementation that stores binlog events directly in InnoDB-managed tablespaces rather than in separate flat files on disk. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/mariadb-innovation-innodb-based-binary-log/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;MariaDB Innovation: InnoDB-Based Binary Log&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-innovation-innodb-based-binary-log/">MariaDB Innovation: InnoDB-Based Binary Log</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-innovation-innodb-based-binary-log/">MariaDB Innovation: InnoDB-Based Binary Log</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Where does the Community run most MariaDB in production – results from the poll</title>
      <link>https://mariadb.org/where-does-the-community-run-most-mariadb-in-production-results-from-the-poll/</link>
      <pubDate>Fri, 13 Mar 2026 07:29:40 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>We recently asked the MariaDB community a simple question:<br />
Where do you run MariaDB most in production?<br />
The responses give a useful snapshot of how MariaDB is deployed today across our community:<br />
The big takeaway: MariaDB remains strongly infrastructure-aware<br />
The clearest signal from this poll is that MariaDB is still most commonly run in environments where users want a high degree of control over the underlying infrastructure. …<br />
Continue reading \"Where does the Community run most MariaDB in production – results from the poll\"<br />
The post Where does the Community run most MariaDB in production – results from the poll appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/where-does-the-community-run-most-mariadb-in-production-results-from-the-poll/">Where does the Community run most MariaDB in production – results from the poll</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>We recently asked the MariaDB community a simple question:<br>
<a href="https://mariadb.org/poll/previous/poll-where-do-you-run-mariadb-most-in-production/">Where do you run MariaDB most in production?</a><br>
The responses give a useful snapshot of how MariaDB is deployed today across our community:<br>
The big takeaway: MariaDB remains strongly infrastructure-aware<a id="the-big-takeaway-mariadb-remains-strongly-infrastructure-aware"></a><br>
The clearest signal from this poll is that MariaDB is still most commonly run in environments where users want a high degree of control over the underlying infrastructure. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/where-does-the-community-run-most-mariadb-in-production-results-from-the-poll/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;Where does the Community run most MariaDB in production &ndash; results from the poll&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/where-does-the-community-run-most-mariadb-in-production-results-from-the-poll/">Where does the Community run most MariaDB in production &ndash; results from the poll</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/where-does-the-community-run-most-mariadb-in-production-results-from-the-poll/">Where does the Community run most MariaDB in production – results from the poll</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Improving MariaDB Observability with OpenSearch and Grafana</title>
      <link>https://mariadb.org/improving-mariadb-observability-with-opensearch-and-grafana/</link>
      <pubDate>Thu, 12 Mar 2026 09:33:53 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>When dealing with queries in MariaDB, there are several approaches, such as the general query log, the slow query log, and the performance_schema. …<br />
Continue reading \"Improving MariaDB Observability with OpenSearch and Grafana\"<br />
The post Improving MariaDB Observability with OpenSearch and Grafana appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/improving-mariadb-observability-with-opensearch-and-grafana/">Improving MariaDB Observability with OpenSearch and Grafana</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>When dealing with queries in MariaDB, there are several approaches, such as the <a href="https://mariadb.com/docs/server/server-management/server-monitoring-logs/general-query-log">general query log</a>, the <a href="https://mariadb.com/docs/server/server-management/server-monitoring-logs/slow-query-log">slow query log</a>, and the <a href="https://mariadb.com/docs/server/reference/system-tables/performance-schema">performance_schema</a>. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/improving-mariadb-observability-with-opensearch-and-grafana/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;Improving MariaDB Observability with OpenSearch and Grafana&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/improving-mariadb-observability-with-opensearch-and-grafana/">Improving MariaDB Observability with OpenSearch and Grafana</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/improving-mariadb-observability-with-opensearch-and-grafana/">Improving MariaDB Observability with OpenSearch and Grafana</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>What’s New in MariaDB Enterprise Kubernetes Operator 26.03</title>
      <link>https://mariadb.com/resources/blog/whats-new-in-mariadb-enterprise-kubernetes-operator-26-03/</link>
      <pubDate>Wed, 11 Mar 2026 18:00:06 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.com/">MariaDB</source>
      <description><![CDATA[<p>Running production databases on Kubernetes means planning for the moments when things go wrong: a bad deployment, accidental data changes, […]</p>
<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/whats-new-in-mariadb-enterprise-kubernetes-operator-26-03/">What’s New in MariaDB Enterprise Kubernetes Operator 26.03</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>Running production databases on Kubernetes means planning for the moments when things go wrong: a bad deployment, accidental data changes, or infrastructure incidents. In MariaDB Enterprise Kubernetes Operator 26.03, we&rsquo;re focused on strengthening disaster recovery with two major additions: Point-in-time recovery (PITR) for granular restores, and native Microsoft Azure Blob Storage support for&hellip;</p>
<p><a href="https://mariadb.com/resources/blog/whats-new-in-mariadb-enterprise-kubernetes-operator-26-03/" rel="nofollow">Source</a></p>

<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/whats-new-in-mariadb-enterprise-kubernetes-operator-26-03/">What’s New in MariaDB Enterprise Kubernetes Operator 26.03</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>When Disaster Strikes</title>
      <link>https://mariadb.com/resources/blog/when-disaster-strikes/</link>
      <pubDate>Wed, 11 Mar 2026 16:24:35 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.com/">MariaDB</source>
      <description><![CDATA[<p>On March 1, various AWS datacenters went down, creating havoc in the Middle East. As a result various services in […]</p>
<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/when-disaster-strikes/">When Disaster Strikes</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>On March 1, various AWS datacenters went down, creating havoc in the Middle East. As a result various services in the region were out of order, from taxi services to banking applications. We also hosted multiple systems in those AWS datacenters, and we faced a wall of red alerts from all the systems and services that were impacted. Still, our customer continued to operate with their web and mobile&hellip;</p>
<p><a href="https://mariadb.com/resources/blog/when-disaster-strikes/" rel="nofollow">Source</a></p>

<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/when-disaster-strikes/">When Disaster Strikes</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Big Vector Search Benchmark: 10 databases comparison</title>
      <link>https://mariadb.org/big-vector-search-benchmark-10-databases-comparison/</link>
      <pubDate>Wed, 11 Mar 2026 15:15:13 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>I have benchmarked MariaDB Vector before, but it was a while ago. Users kept asking about Milvus. New pgvector alternatives were gaining popularity. And I simply wanted to see if MariaDB got any better. …<br />
Continue reading \"Big Vector Search Benchmark: 10 databases comparison\"<br />
The post Big Vector Search Benchmark: 10 databases comparison appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/big-vector-search-benchmark-10-databases-comparison/">Big Vector Search Benchmark: 10 databases comparison</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>I have benchmarked <a href="https://mariadb.com/resources/blog/how-fast-is-mariadb-vector/" target="_blank" rel="noreferrer noopener">MariaDB Vector</a> before, but it was a while ago. Users kept asking about Milvus. New pgvector alternatives were gaining popularity. And I simply wanted to see if MariaDB got any better. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/big-vector-search-benchmark-10-databases-comparison/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;Big Vector Search Benchmark: 10 databases comparison&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/big-vector-search-benchmark-10-databases-comparison/">Big Vector Search Benchmark: 10 databases comparison</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/big-vector-search-benchmark-10-databases-comparison/">Big Vector Search Benchmark: 10 databases comparison</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Active-Active MySQL Group Replication Best Practices</title>
      <link>https://severalnines.com/blog/active-active-mysql-group-replication-best-practices/</link>
      <pubDate>Wed, 11 Mar 2026 12:54:52 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://severalnines.com/">Severalnines</source>
      <description><![CDATA[<p>In MySQL Group Replication (MGR) or Group Replication, an active-active configuration allows multiple group members to accept concurrent write transactions. These writes are coordinated through a consensus-based group communication system (GCS) and validated via write-set certification to preserve global transactional consistency as defined by the Group Replication protocol. active-active mode is designed for write scalability. […]<br />
The post Active-Active MySQL Group Replication Best Practices appeared first on Severalnines.</p>
<p>The post <a rel="nofollow" href="https://severalnines.com/blog/active-active-mysql-group-replication-best-practices/">Active-Active MySQL Group Replication Best Practices</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>In MySQL Group Replication (MGR) or Group Replication, an active-active configuration allows multiple group members to accept concurrent write transactions. These writes are coordinated through a consensus-based group communication system (GCS) and validated via write-set certification to preserve global transactional consistency as defined by the Group Replication protocol. active-active mode is designed for write scalability.</p>
<p>In this mode, all Group Replication nodes are in PRIMARY state, allowing them to accept read and write traffic. Data modifications or changes will be written to the intended node and replicated across the cluster. Transactions are replicated synchronously. When a transaction conflict is detected, by using certification for those conflicting transactions, it will be resolved automatically. Although powerful, this mode is not intended for <strong>unlimited write scalability</strong>.</p>
<p>Group Replication&rsquo;s scalability is partially dependent on low write conflicts. High contention leads to rollback surge or saturation, ultimately leading to temporary system unavailability due to lock up. Fundamentally, this mode means no passive standby nodes, not;</p>
<ul class="wp-block-list">
<li>Unlimited write scalability</li>
<li>Lock-free writes</li>
<li>No coordination overhead</li>
<li>No single-writer behavior internally</li>
</ul>
<p>This blog post will explore what it means to run a Group Replication setup in production and the operational best practices that you need to follow to ensure its performance and stability.</p>
<h2 class="wp-block-heading" id="h-note-on-how-mysql-group-replication-manages-communication"><strong>Note on how MySQL Group Replication manages communication</strong><a class="anchor-link" id="note-on-how-mysql-group-replication-manages-communication"></a></h2>
<p>Let&rsquo;s first take a moment to understand how communication actually works within MySQL Group Replication. The communication layer is the Group Communication System (GCS), which serves as a high-level abstraction responsible for group membership management and total-order message delivery. Its engine is XCom, which implements a Paxos-based consensus protocol to ensure that all members of the group receive the same transactions in the same order. This separation of concerns allows MySQL Group Replication to evolve the underlying communication engine without changing the replication semantics exposed to the server layer.</p>
<p>Understanding how MySQL Group Replication enforces consistency, coordinates writes, and handles failures across the group, it&rsquo;s easier to reason which use cases it is ideal for.</p>
<h2 class="wp-block-heading" id="h-ideal-use-cases-for-active-active-mysql-group-replication"><strong>Ideal use cases for active-active MySQL Group Replication</strong><a class="anchor-link" id="ideal-use-cases-for-active-active-mysql-group-replication"></a></h2>
<ul class="wp-block-list">
<li>High Availability (HA) Systems: Critical applications that cannot afford the 10&ndash;30 seconds of downtime typically required for a leader election in single-primary modes.</li>
<li>Low-Conflict Workloads: Applications that write to different parts of the database, especially those architectures that use shards. This performs best, as they avoid the &ldquo;certification&rdquo; failures that occur when two nodes try to update the same record.</li>
<li>Geographically Distributed Reads: While writes are synchronous and can be slow over long distances, having multiple &ldquo;active&rdquo; nodes allows local users to perform reads and writes with lower initial connection latency.</li>
</ul>
<p>Let&rsquo;s now go over the best practices for a MySQL Group Replication deployment&nbsp; setup.</p>
<h2 class="wp-block-heading" id="h-active-active-cluster-best-practices"><strong>Active-active cluster</strong> <strong>best practices</strong><a class="anchor-link" id="active-active-cluster-best-practices"></a></h2>
<h3 class="wp-block-heading" id="h-determining-the-number-of-database-member-nodes-in-the-cluster"><strong>Determining the number of database member nodes in the cluster</strong><a class="anchor-link" id="determining-the-number-of-database-member-nodes-in-the-cluster"></a></h3>
<p>The number of instances should be determined by expected failure scenarios rather than capacity requirements. According to MySQL documentation, Group Replication requires a majority of members to commit transactions, making quorum size critical to availability.</p>
<p>Network partitions and node failures are inevitable. Using an odd number of instances ensures that a majority can still be formed during partial outages. Choosing an odd number of members improves write availability and aligns with MySQL Group Replication&rsquo;s quorum model. For example, a five-node cluster can continue processing writes even if two nodes become unreachable, while an evenly sized cluster risks entering a write-blocked state during a symmetric split.</p>
<h3 class="wp-block-heading" id="h-splitting-read-and-write-traffic"><strong>Splitting read and write traffic</strong><a class="anchor-link" id="splitting-read-and-write-traffic"></a></h3>
<p>In an active-active MySQL Group Replication setup, write operations place significantly more stress on the system than reads, especially when they result in heavy disk activity. Writes consume CPU, generate redo and binary logs, trigger replication traffic, and must be applied consistently across the group, amplifying their impact across the cluster.</p>
<p>Separating write traffic helps prevent sustained write workloads from overwhelming the cluster. By directing writes to nodes with sufficient capacity and lower contention while allowing others to focus on serving reads, the cluster maintains more stable performance, reduces write related bottlenecks, and scales read workloads more effectively.</p>
<h3 class="wp-block-heading" id="h-connection-pooling"><strong>Connection pooling</strong><a class="anchor-link" id="connection-pooling"></a></h3>
<p>Frequent connection creation and teardown can quickly become a bottleneck and place unnecessary stress on the cluster. Maintaining a pool of reusable connections helps stabilize workload distribution and prevents spikes in CPU and memory usage caused by excessive connection overhead.</p>
<p>Using proxies such as MySQL Router or ProxySQL allows applications to reuse existing connections while intelligently routing traffic across group members, reducing connection overhead, improving response times, and helping the cluster handle fluctuating workloads more predictably.<br>Error handling</p>
<p>When using active-active MySQL Group Replication, applications must be prepared to handle transient failures. Network interruptions, node restarts, or membership changes can temporarily cause connection errors or transaction failures, even when the cluster itself is healthy.</p>
<p>Implementing controlled failback handling at the application layer allows temporary disruptions to resolve naturally before user impact. Likewise, <strong>well designed</strong> error handling improves application reliability, protects data consistency, and ensures that temporary replication events have the opportunity to resolve naturally before escalating to visible outages. Short-lived connection or transaction failures can often succeed once the system stabilizes, especially during membership changes or brief network interruptions.</p>
<h3 class="wp-block-heading" id="h-transaction-size"><strong>Transaction size</strong><a class="anchor-link" id="transaction-size"></a></h3>
<p>Before deploying MySQL Group Replication in production, understand the transaction sizes generated by your application. MGR enforces a maximum transaction size using the <a href="blank">group_replication_transaction_size_limit</a> parameter, directly affecting replication latency and stability.</p>
<p>If you set a small transaction, then it&rsquo;ll replicate efficiently making it suitable for OLTP workloads. Large batch operations usually increase memory usage, network traffic, and pressure on the replica when applying the transactions. However, setting the limit too low can cause valid transactions to fail, while setting it too high can lead to replication lag or resource exhaustion.&nbsp;</p>
<p>Instead of raising the limit on memory constrained instances, large data changes should be split into smaller batches. <strong>Take note</strong>, this setting must be consistent across all group members.</p>
<h3 class="wp-block-heading" id="h-strict-consistency-checks"><strong>Strict consistency checks</strong><a class="anchor-link" id="strict-consistency-checks"></a></h3>
<p>MySQL Group Replication provides the <a href="https://dev.mysql.com/doc/refman/8.0/en/group-replication-system-variables.html#sysvar_group_replication_enforce_update_everywhere_checks">group_replication_enforce_update_everywhere_checks</a> variable, which is disabled by default. This system variable is a group-wide configuration setting. It must have the same value on all group members, cannot be changed while Group Replication is running, and requires a full reboot of the group (a bootstrap by a server with <code>group_replication_bootstrap_group=ON</code>) in order for the value change to take effect.</p>
<p>When disabled, applications must ensure that conflicting transactions are not executed concurrently on different nodes, which requires thorough testing and strict control over write paths, especially in schemas involving foreign keys, cascading operations, or concurrent modifications across tables. When enabled, statements are checked as follows to ensure their compatibility with multi-primary mode:</p>
<ol class="wp-block-list">
<li>If a transaction is executed under the SERIALIZABLE isolation level, then its commit fails when synchronizing itself with the group.</li>
<li>If a transaction executes against a table that has foreign keys with cascading constraints, then the transaction fails to commit when synchronizing itself with the group.</li>
</ol>
<p>In short, enable it when:</p>
<ul class="wp-block-list">
<li>You want strict consistency checks, AND
<ul class="wp-block-list">
<li>You don&rsquo;t rely on SERIALIZABLE isolation level</li>
<li>Your tables do not rely on foreign key checks with cascading constraints</li>
<li>You prefer failed transactions over application-managed conflict handling</li>
<li>Correctness is more important than write flexibility</li>
</ul>
</li>
</ul>
<p>Disable it when:</p>
<ul class="wp-block-list">
<li>Your schema relies on cascading foreign key constraints</li>
<li>Cascades are part of normal application behavior</li>
<li>You accept responsibility for preventing conflicting writes</li>
<li>Write paths are tightly controlled or serialized at the application level</li>
</ul>
<h3 class="wp-block-heading" id="h-failure-detection-parameters"><strong>Failure detection parameters</strong><a class="anchor-link" id="failure-detection-parameters"></a></h3>
<p>There are three failure detection parameters that you must adjust to define your application&rsquo;s failure tolerance:</p>
<ul class="wp-block-list">
<li><code>group_replication_member_expel_timeout</code>: This variable controls how long a member is audited before it is expelled from the group. The latest version&rsquo;s (8.4 as of this writing) default is 5 seconds. Therefore, Group Replication first detects a suspected failure after ~5 seconds (no messages), waiting x <code>group_replication_member_expel_timeout</code> seconds more before expelling the member.</li>
</ul>
<p>During the waiting period, the suspected node is listed as UNREACHABLE, but still part of the group view. If it resumes communication before expulsion, it rejoins without operator intervention. When you need to tune this variable depends on your needs.&nbsp;</p>
<p><strong>When to tune: </strong>If you have intermittent network blips or slow links, it makes sense to increase the default value to avoid unnecessary expulsions. Decrease only if you prefer faster failure detection, e.g. frequent real outages and stable networks, setting it to 0 only for immediate post-detection expulsion.</p>
<ul class="wp-block-list">
<li><code>group_replication_autorejoin_tries</code>: Set this to the desired number of automatic rejoin attempts a member makes after being expelled or losing quorum. Set to 3 attempts (with roughly 5 minutes of waiting time between attempts) by default, the member will try to rejoin the group automatically after expulsion or network isolation. If all attempts fail, it stops and follows the exit action. During and between auto-rejoin attempts, a DB instance remains in read-only mode and does not accept writes, thereby increasing the likelihood of stale reads over time.</li>
</ul>
<p><strong>When to tune:</strong> Increase this variable for environments with frequently long, but temporary network partitions. Decrease if you want to restrict rejoin attempts, or expedite identifying DB instances that require manual intervention. Otherwise, set it to 0 if you want to handle rejoining manually or your applications cannot tolerate the possibility of stale reads for any period of time. <strong>Common practice in highly available clusters:</strong> keep several tries to allow network recovery without admin intervention.</p>
<ul class="wp-block-list">
<li><code>group_replication_unreachable_majority_timeout</code>: Timeout used when a member is in the minority (cannot reach a majority or establish a quorum) before&nbsp; declaring a lost quorum. Set to 0 by default, a member node enters a special unreachable majority state if it cannot contact a majority of the group. After this timeout expires, actions such as expulsion or exit actions are applied. Setting this value higher than 0 helps to prevent a minority partition from running indefinitely or making unsafe decisions.</li>
</ul>
<p>Otherwise, when the defined timeout is reached, all pending transactions on the minority are rolled back, and the DB instances in the minority partition are moved to the ERROR state. From there, your application can perform error-handling as needed.</p>
<p><strong>When to tune: </strong>Increase it for deployments where temporary connectivity loss to a majority is expected but resolves soon. Otherwise, decrease for faster detection of actual partition and to avoid split-brain risk.</p>
<ul class="wp-block-list">
<li><code>group_replication_exit_state_action</code>: Defines how a member behaves when the server leaves the group unintentionally, for example, after encountering an applier error, or in the case of a majority loss, or when another member expels it due to a suspicion time out. Note that an expelled group member does not know that it was expelled until it reconnects to the group, so the specified action is only taken if the member manages to reconnect, or if it raises a suspicion on itself and self-expels. Current values available for you to set are ABORT_SERVER, OFFLINE_MODE, and READ_ONLY.&nbsp;</li>
</ul>
<p><strong>When to tune: </strong>Tune if you need strict consistency, then choose shutdown on failure to avoid stale reads or split-brain risk. For read-heavy clusters where full availability matters, prefer read-only or offline modes.</p>
<h3 class="wp-block-heading" id="h-flow-control"><strong>Flow control</strong><a class="anchor-link" id="flow-control"></a></h3>
<p>In MGR, a transaction is only finalized once a majority of members agree on its global order. In an active-active setup, this coordination is sensitive to uneven performance: fast writers can easily outrun slower members, causing replication lag and in-memory backlog.</p>
<p>Flow control is the mechanism that keeps the group balanced. It monitors how far members fall behind in both transaction certification and apply phases, and temporarily throttles write throughput across all primaries when predefined limits are exceeded. The key controls are <a href="https://dev.mysql.com/doc/refman/8.0/en/group-replication-system-variables.html#sysvar_group_replication_flow_control_certifier_threshold">group_replication_flow_control_certifier_threshold</a> and <a href="https://dev.mysql.com/doc/refman/8.0/en/group-replication-system-variables.html#sysvar_group_replication_flow_control_applier_threshold">group_replication_flow_control_applier_threshold,</a> which define how much backlog the group is willing to tolerate before throttling.</p>
<p>Throughput recovery is governed by quota-based settings such as <a href="https://dev.mysql.com/doc/refman/8.0/en/group-replication-system-variables.html#sysvar_group_replication_flow_control_max_quota">group_replication_flow_control_max_quota</a>, which caps how quickly write capacity is restored once lag subsides. The overall behavior is enabled or disabled via <a href="https://dev.mysql.com/doc/refman/8.0/en/group-replication-system-variables.html#sysvar_group_replication_flow_control_mode">group_replication_flow_control_mode</a>, though disabling flow control is generally discouraged in production due to the increased risk of memory exhaustion and instability.</p>
<p>Rather than turning flow control off, a better strategy is to tune these thresholds alongside sufficient parallel applier capacity, so throttling only occurs under real pressure. Continuous monitoring of replication lag and backlog is essential, as optimal values depend heavily on transaction size, write bursts, and workload patterns in active-active environments.</p>
<h3 class="wp-block-heading" id="h-transaction-consistency"><strong>Transaction consistency</strong><a class="anchor-link" id="transaction-consistency"></a></h3>
<p>MySQL Group Replication provides a parameter <a href="https://dev.mysql.com/doc/refman/8.0/en/group-replication-system-variables.html#sysvar_group_replication_consistency">group_replication_consistency</a> on which you can set values of the following options: EVENTUAL, BEFORE, AFTER, and BEFORE_AND_AFTER.</p>
<p>BEFORE leads to a sweet spot which offers strong enough consistency for correctness and light enough synchronization for performance and is usually the best balance for production environments. Before executing a transaction, every member has to wait until it has applied all transactions that were already globally ordered, ensuring reads and writes are based on a reasonably current view of the group, which then helps prevent obvious stale reads, reduces write-write conflicts, avoids unnecessary waiting on future transactions, and keeps latency acceptable under normal load.</p>
<p>It is also highly recommended to monitor the replication status of your MGR cluster nodes. You might use <code>performance_schema</code> tables to monitor the health of your active-active cluster.</p>
<ul class="wp-block-list">
<li><a href="https://dev.mysql.com/doc/refman/8.0/en/group-replication-replication-group-members.html">performance_schema.replication_group_members</a>: Provides the status of each DB instance that is part of the cluster.</li>
<li><a href="https://dev.mysql.com/doc/refman/8.0/en/group-replication-replication-group-member-stats.html">performance_schema.replication_group_member_stats</a>: Provides cluster-level information related to certification, as well as statistics for the transactions received and originated by each DB instance in the cluster.</li>
<li><a href="https://dev.mysql.com/doc/mysql-perfschema-excerpt/5.7/en/performance-schema-replication-connection-status-table.html">performance_schema.replication_connection_status</a>: Provides the current status of the replication I/O thread that handles the replica&rsquo;s connection to the source DB instance.</li>
</ul>
<h2 class="wp-block-heading" id="h-deploying-a-mysql-group-replication-using-clustercontrol"><strong>Deploying a MySQL Group Replication using ClusterControl</strong><a class="anchor-link" id="deploying-a-mysql-group-replication-using-clustercontrol"></a></h2>
<p>By default, deploying through ClusterControl will setup an active MySQL Group Replication cluster. This means that all nodes in the cluster are available to receive write requests. Below are the steps to deploy using ClusterControl.</p>
<p>First, login to your ClusterControl with your administrator username/password credentials. Once you are able to login, click the <em>Deploy a cluster </em>button in the right corner of the UI. This will launch the deployment wizard, starting with a create / import cluster prompt.</p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="1024" height="840" src="https://severalnines.com/wp-content/uploads/2025/09/CC_db_deployment_wizard-creation_type_selection-1024x840.png" alt="" class="wp-image-41547"></figure>
<p>Choose <em>Create a database cluster</em>. Next, you will choose which cluster to deploy. Select <em>MySQL Group Replication</em> in the database drop-down section. As of this writing, Oracle is the only supported vendor and versions 8.0 and 8.4 &mdash; see the screenshot below:</p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="1024" height="835" src="https://severalnines.com/wp-content/uploads/2026/02/cc_db_deployment_wizard-database_selection-mysql_group_replication-1024x835.png" alt="" class="wp-image-42713"></figure>
<p>After you hit the <em>Continue</em> button, you will start the straightforward deployment workflow in earnest. You can also follow our user guide based on our documentation by clicking <a href="https://docs.severalnines.com/clustercontrol/latest/user-guide/deployment/create-database-cluster/#mysql-group-replication">here</a>.</p>
<p>The final step is to review your deployment configuration as illustrated below; if all looks good, hit the <em>Finish</em><strong> </strong>button to initiate the deployment and ClusterControl will do the rest. </p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="979" height="1024" src="https://severalnines.com/wp-content/uploads/2026/02/cc_db_deployment_wizard-deployment_preview-mysql_group_replication-979x1024.png" alt="" class="wp-image-42716"></figure>
<p>Once started, you can view the stepwise deployment process logs as illustrated below:</p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="1024" height="930" src="https://severalnines.com/wp-content/uploads/2026/02/cc_ui-job_progress_detail-mgr_deployment-1024x930.png" alt="" class="wp-image-42714"></figure>
<p>Otherwise, you can view the deployment process&rsquo;s progress bar in the UI&rsquo;s foreground:</p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="1024" height="179" src="https://severalnines.com/wp-content/uploads/2026/02/cc_ui-db_deployment_progress-mgr-1024x179.png" alt="" class="wp-image-42715"></figure>
<p>When the deployment workflow finishes, the cluster will be shown within your <em>Clusters</em> tab &mdash; clicking the cluster&rsquo;s name, you can view its dashboards, topology, node list, performance graphs, backups, logs, and more. Monitoring your cluster using dashboards and other ClusterControl features will render analyzing and inspecting your cluster&rsquo;s health trivial.</p>
<h2 class="wp-block-heading"><strong>Conclusion</strong><a class="anchor-link" id="conclusion"></a></h2>
<p>MySQL Group Replication is a strong choice for high availability and multi-writer flexibility, but <strong>it is not</strong> a path to unlimited write scalability. It replaces the simplicity of a single-writer model with the complexity of distributed coordination.</p>
<p>A successful active/active deployment depends on deliberate design, how familiar you are with Group Replication, as well as your application&rsquo;s behavior, data access patterns, and operational practices and underlying distributed systems.</p>
<p>It may be best to start with Single-Primary Group Replication then move to active-active once you have a full understanding of the write patterns, the application&rsquo;s setup and design for conflict handling, and have tested failure and conflict scenarios extensively. When applied to the right use cases and deliberately run, active-active MySQL Group Replication delivers resilient, predictable production performance.</p>
<p>Ready to implement an active-active MGR cluster efficiently and reliably in any environment?</p>
<h2 class="wp-block-heading" id="h-install-clustercontrol-in-10-minutes-nbsp-free-30-day-nbsp-enterprise-trial-included"><strong>Install ClusterControl in 10-minutes.&nbsp;Free 30-day&nbsp;Enterprise trial included!</strong><a class="anchor-link" id="install-clustercontrol-in-10-minutes-free-30-day-enterprise-trial-included"></a></h2>
<h3 class="wp-block-heading" id="h-script-installation-instructions"><strong>Script installation instructions</strong><a class="anchor-link" id="script-installation-instructions"></a></h3>
<p>The installer script is the simplest way to get ClusterControl up and running. Run it on your chosen host, and it will take care of installing all required packages and dependencies.</p>
<p>Offline environments are supported as well. See the&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/offline-installation/">Offline Installation</a>&nbsp;guide for more details.</p>
<p>On the ClusterControl server, run the following commands:</p>
<pre class="wp-block-code"><code>wget https://severalnines.com/downloads/cmon/install-cc
chmod +x install-cc</code></pre>
<p>With your install script ready, run the command below. Replace&nbsp;<code>S9S_CMON_PASSWORD</code>&nbsp;and&nbsp;<code>S9S_ROOT_PASSWORD</code>&nbsp;placeholders with your choice password, or remove the environment variables from the command to interactively set the passwords. If you have multiple network interface cards, assign one IP address for the&nbsp;<code>HOST</code>&nbsp;variable in the command using&nbsp;<code>HOST=&lt;ip_address&gt;</code>.</p>
<pre class="wp-block-code"><code>S9S_CMON_PASSWORD=&lt;your_password&gt; S9S_ROOT_PASSWORD=&lt;your_password&gt; HOST=&lt;ip_address&gt; ./install-cc # as root or sudo user</code></pre>
<p>After the installation is complete, open a web browser, navigate to&nbsp;<code>https://&lt;ClusterControl_host&gt;/</code>, and create the first admin user by entering a username (note that &ldquo;admin&rdquo; is reserved) and a password on the&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/quickstart/#step-2-create-the-first-admin-user">welcome page</a>. Once you&rsquo;re in, you can&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/user-guide/deployment/create-database-cluster/">deploy</a>&nbsp;a new database cluster or&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/user-guide/deployment/import-database-cluster/">import</a>&nbsp;an existing one.</p>
<p>The installer script supports a range of environment variables for advanced setup. You can define them using export or by prefixing the install command.</p>
<p>See the&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/online-installation/#environment-variables">list of supported variables</a>&nbsp;and&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/online-installation/#example-use-cases">example use cases</a>&nbsp;to tailor your installation.</p>
<h4 class="wp-block-heading" id="h-other-installation-options"><strong>Other installation options</strong></h4>
<p><strong>Helm Chart</strong></p>
<p>Deploy ClusterControl on Kubernetes using our&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/online-installation/#helm-chart">official Helm chart</a>.</p>
<p><strong>Ansible Role</strong></p>
<p>Automate installation and configuration using our&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/online-installation/#ansible-role">Ansible playbooks</a>.</p>
<p><strong>Puppet Module</strong></p>
<p>Manage your ClusterControl deployment with the&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/online-installation/#puppet-module">Puppet module</a>.</p>
<h4 class="wp-block-heading" id="h-clustercontrol-on-marketplaces"><strong>ClusterControl on marketplaces</strong></h4>
<p>Prefer to launch ClusterControl directly from the cloud? It&rsquo;s available on these platforms:</p>
<ul class="wp-block-list">
<li><a href="https://marketplace.digitalocean.com/apps/clustercontrol">DigitalOcean Marketplace</a></li>
<li><a href="https://gridscale.io/en/marketplace">gridscale.io Marketplace</a></li>
<li><a href="https://www.vultr.com/marketplace/apps/clustercontrol/">Vultr Marketplace</a></li>
<li><a href="https://www.linode.com/marketplace/apps/severalnines/clustercontrol/">Linode Marketplace</a></li>
<li><a href="https://console.cloud.google.com/marketplace/product/severalnines-public/clustercontrol">Google Cloud Platform</a></li>
</ul>
<p>The post <a href="https://severalnines.com/blog/active-active-mysql-group-replication-best-practices/">Active-Active MySQL Group Replication Best Practices</a> appeared first on <a href="https://severalnines.com">Severalnines</a>.</p>

<p>The post <a rel="nofollow" href="https://severalnines.com/blog/active-active-mysql-group-replication-best-practices/">Active-Active MySQL Group Replication Best Practices</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Running pgBackRest with pg_tde: A Practical Percona Walkthrough</title>
      <link>https://percona.community/blog/2026/03/10/running-pgbackrest-with-pg_tde-a-practical-percona-walkthrough/</link>
      <pubDate>Tue, 10 Mar 2026 00:00:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://percona.community/blog/">Percona Community Blog - learn about MySQL, MariaDB, PostgreSQL, and MongoDB</source>
      <description><![CDATA[<p>Not every PostgreSQL installation requires encryption at rest. However, for organizations mandating strict data protection and privacy standards, it is often non-negotiable. When security policies are this rigorous, you need a strategy that protects your data without sacrificing recoverability.</p>
<p>The post <a rel="nofollow" href="https://percona.community/blog/2026/03/10/running-pgbackrest-with-pg_tde-a-practical-percona-walkthrough/">Running pgBackRest with pg_tde: A Practical Percona Walkthrough</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>Not every PostgreSQL installation requires encryption at rest. However, for organizations mandating strict data protection and privacy standards, it is often non-negotiable. When security policies are this rigorous, you need a strategy that protects your data without sacrificing recoverability.</p>
<p>While Transparent Data Encryption (TDE) successfully locks down your data at rest, it raises a critical operational question: will your backup and restore workflow continue to work correctly with encrypted data? When deploying Transparent Data Encryption, it is essential to validate that pgBackRest can reliably back up and restore encrypted clusters.</p>
<p>This post walks through a complete, step-by-step configuration of pg_tde with pgBackRest on Debian/Ubuntu. We will explore how to optimize your backup performance, secure your repository, and most importantly verify that your encrypted backup restores work exactly as expected.</p>
<h2 id="what-is-pg_tde">What is pg_tde?<a class="anchor-link" id="what-is-pg_tde"></a></h2>
<p>Percona&rsquo;s solution for transparent data encryption (<a href="https://docs.percona.com/pg-tde/index.html" target="_blank" rel="noopener noreferrer">pg_tde</a>) is an open source, community driven extension that provides Transparent Data Encryption (TDE) for PostgreSQL. This extension allows data to be encrypted at the storage level without affecting application behavior.</p>
<p>Unlike full disk encryption, which exposes data once the system boots, TDE ensures that the actual database files remain encrypted at the file system level. This protects your data, dumps, and backups even if the operating system is compromised. Currently, it is bundled with Percona Server for PostgreSQL and available in Percona Distribution for PostgreSQL 17+.</p>
<p>Recent Percona releases make this combination more practical than ever. With WAL encryption now ready for production use, we need a backup strategy that respects data security. In this walkthrough, we will demonstrate how to pair it with pgBackRest to ensure fully recoverable, encrypted backups.</p>
<h2 id="the-use-case">The Use Case<a class="anchor-link" id="the-use-case"></a></h2>
<p>Imagine a team that wants strong security controls without changing application code. They need:</p>
<ul>
<li>Data files encrypted at rest (tables and WAL)</li>
<li>Backups that are consistent, verifiable, and restorable</li>
<li>A setup that is easy to automate and explain</li>
</ul>
<p>Percona Distribution for PostgreSQL plus pg_tde and pgBackRest closes the gaps where needed: pg_tde takes care of encryption, pgBackRest provides flexible backup/restore capabilities.</p>
<h2 id="a-quick-note-on-compatibility">A Quick Note on Compatibility<a class="anchor-link" id="a-quick-note-on-compatibility"></a></h2>
<p>At this moment pg_tde cannot be yet used with Community PostgreSQL as pg_tde relies on specific hooks in the PostgreSQL core. Percona Server for PostgreSQL includes these necessary core modifications, which is why we validate this setup using the Percona Distribution for PostgreSQL.</p>
<p>While pgBackRest is fully capable of managing TDE enabled clusters, there are specific constraints you must respect to ensure data safety:</p>
<ul>
<li>No Asynchronous Archiving: pgBackRest asynchronous archiving is not supported with encrypted WALs. You must configure your archive command to handle WALs synchronously.</li>
<li>Restore Wrappers: Standard restore commands will not work for encrypted WALs. You must use the pg_tde_restore_encrypt utility to wrap your restore process.</li>
</ul>
<p>This guide currently focuses on pgBackRest because it is the backup tool that has been tested and validated with pg_tde by the pg_tde community at this time.<br>
Other backup tools may also be viable and we are open to collaborating with other tool maintainers and their communities on a shared effort to validate and support pg_tde.</p>
<h2 id="what-you-will-build">What You Will Build<a class="anchor-link" id="what-you-will-build"></a></h2>
<p>By the end of this post you will have:</p>
<ul>
<li>Percona Distribution for PostgreSQL installed with pg_tde and pgBackRest</li>
<li>A key directory and key providers created</li>
<li>pg_tde enabled in PostgreSQL</li>
<li>Encrypt tables and indexes with pg_tde (<a href="https://docs.percona.com/pg-tde/test.html" target="_blank" rel="noopener noreferrer">docs</a>)</li>
<li><a href="https://docs.percona.com/pg-tde/wal-encryption.html" target="_blank" rel="noopener noreferrer">WAL encryption</a> enabled</li>
<li>A pgBackRest stanza configured and a full backup completed</li>
<li>Verification that encrypted data is not readable on disk</li>
</ul>
<h2 id="prerequisites">Prerequisites<a class="anchor-link" id="prerequisites"></a></h2>
<ul>
<li>A host (or VM) on Debian/Ubuntu</li>
<li>Network access to Percona repositories</li>
<li>Root/Sudo: You need sudo access for installing packages and editing system configuration files in <code>/etc</code></li>
<li>Postgres User: All database commands (psql, pgbackrest) run as the postgres system user</li>
<li>Postgres user is in the sudoer list to run sudo commands</li>
</ul>
<h2 id="step-1-install-percona-packages">Step 1: Install Percona Packages<a class="anchor-link" id="step-1-install-percona-packages"></a></h2>
<p>We begin by installing the Percona release repository and enabling the correct PostgreSQL distribution. See the <a href="https://docs.percona.com/postgresql/18/installing.html" target="_blank" rel="noopener noreferrer">Percona Distribution for PostgreSQL installation guide</a> for full details.</p>
<h3 id="debian--ubuntu">Debian / Ubuntu<a class="anchor-link" id="debian-ubuntu"></a></h3>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-0" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-0">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Install repo helper</span>
</span></span><span class="line"><span class="cl">sudo apt-get update
</span></span><span class="line"><span class="cl">sudo apt-get install -y wget gnupg2 lsb-release curl
</span></span><span class="line"><span class="cl">wget https://repo.percona.com/apt/percona-release_latest.generic_all.deb
</span></span><span class="line"><span class="cl">sudo dpkg -i percona-release_latest.generic_all.deb
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Enable the repository for the major version selected</span>
</span></span><span class="line"><span class="cl">sudo percona-release setup ppg-18
</span></span><span class="line"><span class="cl">sudo apt-get update
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Install the server, tde extension, and pgbackrest</span>
</span></span><span class="line"><span class="cl">sudo apt-get install -y percona-postgresql-18 percona-pg-tde18 percona-pgbackrest
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Verify Installation</span>
</span></span><span class="line"><span class="cl">psql --version</span></span></code></pre>
</div>
</div>
</div>
<h2 id="step-2-enable-pg_tde-and-create-keys">Step 2: Enable pg_tde and Create Keys<a class="anchor-link" id="step-2-enable-pg_tde-and-create-keys"></a></h2>
<h3 id="21-configure-shared_preload_libraries">2.1 Configure shared_preload_libraries<a class="anchor-link" id="2-1-configure-shared_preload_libraries"></a></h3>
<p>Before we can use any of the encryption features, PostgreSQL needs to load the pg_tde library into memory at startup. You can do this by adding it to <code>shared_preload_libraries</code>.</p>
<p>You have two ways to handle this: the SQL way or the classic config file way.</p>
<h4 id="option-a-sql-way-recommended">Option A: SQL way (recommended)</h4>
<p>The fastest way is using <code>ALTER SYSTEM</code>. This saves you from hunting through your file system for the right config file.</p>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-1" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-1">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Set the library and restart to apply changes</span>
</span></span><span class="line"><span class="cl">psql -c <span class="s2">"ALTER SYSTEM SET shared_preload_libraries = 'pg_tde';"</span>
</span></span><span class="line"><span class="cl">sudo systemctl restart postgresql</span></span></code></pre>
</div>
</div>
</div>
<h4 id="option-b-manual-config-edit">Option B: Manual config edit</h4>
<p>If you prefer managing your config files manually, find your <code>postgresql.conf</code> and add the extension there.</p>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-2" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-2">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Locate the config file</span>
</span></span><span class="line"><span class="cl"><span class="nv">PG_CONF</span><span class="o">=</span><span class="k">$(</span>psql -t -P <span class="nv">format</span><span class="o">=</span>unaligned -c <span class="s2">"show config_file;"</span><span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Open that file and find the 'shared_preload_libraries' line:</span>
</span></span><span class="line"><span class="cl"><span class="c1"># shared_preload_libraries = 'pg_tde'</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Restart PostgreSQL to load the library</span>
</span></span><span class="line"><span class="cl">sudo systemctl restart postgresql</span></span></code></pre>
</div>
</div>
</div>
<p>Note: Regardless of which method you choose, a full restart of the PostgreSQL service is required. A simple reload won&rsquo;t work for shared libraries.</p>
<h3 id="22-create-the-key-provider">2.2 Create the Key Provider<a class="anchor-link" id="2-2-create-the-key-provider"></a></h3>
<p>Now that the extension is loaded, we need to tell pg_tde where to store its encryption keys.</p>
<p>For this tutorial, we will use the File Provider (storing keys in a local file). In production environments, storing encryption keys locally on the PostgreSQL server can introduce security risks. To enhance security, pg_tde supports integration with external Key Management Systems (<a href="https://docs.percona.com/pg-tde/global-key-provider-configuration/overview.html" target="_blank" rel="noopener noreferrer">KMS</a>) through a Global Key Provider interface.</p>
<blockquote>
<p>Note: While key files may be acceptable for local or testing environments, KMS integration is the recommended approach for production deployments.</p>
</blockquote>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-3" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-3">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 1. Create a secure directory for the keys</span>
</span></span><span class="line"><span class="cl">sudo mkdir -p /etc/postgresql/keys
</span></span><span class="line"><span class="cl">sudo chown postgres:postgres /etc/postgresql/keys
</span></span><span class="line"><span class="cl">sudo chmod <span class="m">700</span> /etc/postgresql/keys</span></span></code></pre>
</div>
</div>
</div>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-4" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-4">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- 2. Connect to PostgreSQL to configure TDE
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">CREATE</span><span class="w"> </span><span class="n">EXTENSION</span><span class="w"> </span><span class="n">pg_tde</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">-- 1. Define the global key provider
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">SELECT</span><span class="w"> </span><span class="n">pg_tde_add_global_key_provider_file</span><span class="p">(</span><span class="s1">'global-file-provider'</span><span class="p">,</span><span class="w"> </span><span class="s1">'/etc/postgresql/keys/tde-global.per'</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">-- 2. Create and Set the Principal Key
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">SELECT</span><span class="w"> </span><span class="n">pg_tde_create_key_using_global_key_provider</span><span class="p">(</span><span class="s1">'global-master-key'</span><span class="p">,</span><span class="w"> </span><span class="s1">'global-file-provider'</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">SELECT</span><span class="w"> </span><span class="n">pg_tde_set_default_key_using_global_key_provider</span><span class="p">(</span><span class="s1">'global-master-key'</span><span class="p">,</span><span class="w"> </span><span class="s1">'global-file-provider'</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">-- 3. Enable WAL encryption configuration
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">ALTER</span><span class="w"> </span><span class="k">SYSTEM</span><span class="w"> </span><span class="k">SET</span><span class="w"> </span><span class="n">pg_tde</span><span class="p">.</span><span class="n">wal_encrypt</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'on'</span><span class="p">;</span></span></span></code></pre>
</div>
</div>
</div>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-5" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-5">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 4. Restart PostgreSQL to apply the encryption settings fully</span>
</span></span><span class="line"><span class="cl">sudo systemctl restart postgresql</span></span></code></pre>
</div>
</div>
</div>
<h2 id="step-3-configure-pgbackrest">Step 3: Configure pgBackRest<a class="anchor-link" id="step-3-configure-pgbackrest"></a></h2>
<p>Next, configure pgBackRest by defining the repository and stanza settings.<br>
Before proceeding, it is important to understand how encryption is handled in this setup. pg_tde encrypts PostgreSQL data files on disk, protecting the live database. However, during WAL archiving, the pg_tde archive helper decrypts WAL records before passing them to pgBackRest. This means backups and archived WAL would be stored unencrypted unless repository encryption is enabled. To ensure backup data remains protected at rest, we enable pgBackRest repository encryption in this configuration. We configure the repository with a cipher type and key. Encryption is performed client side, ensuring data is secure before it is written to the repository.</p>
<blockquote>
<p>Note on Compression: In many pgBackRest deployments, compression is enabled to reduce backup size. However, when using pg_tde, database pages are already encrypted before pgBackRest processes them. Encryption randomizes the data blocks, making traditional compression algorithms such as gzip ineffective. For this reason, compression is disabled to avoid unnecessary CPU overhead.</p>
</blockquote>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-6" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-6">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Create the configuration file</span>
</span></span><span class="line"><span class="cl"><span class="c1"># /etc/pgbackrest.conf</span>
</span></span><span class="line"><span class="cl"><span class="c1"># In pg1-path use data directory path accordingly</span>
</span></span><span class="line"><span class="cl">sudo bash -c <span class="s2">"cat &lt; /etc/pgbackrest.conf
</span></span></span><span class="line"><span class="cl"><span class="s2">[demo]
</span></span></span><span class="line"><span class="cl"><span class="s2">pg1-path=/var/lib/postgresql/18/main
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">[global]
</span></span></span><span class="line"><span class="cl"><span class="s2">repo1-path=/var/lib/pgbackrest
</span></span></span><span class="line"><span class="cl"><span class="s2">repo1-retention-full=2
</span></span></span><span class="line"><span class="cl"><span class="s2">log-level-console=info
</span></span></span><span class="line"><span class="cl"><span class="s2">start-fast=y
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2"># PERFORMANCE: Encrypted data doesn't compress. We save CPU by disabling it.
</span></span></span><span class="line"><span class="cl"><span class="s2">compress-type=none
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2"># SECURITY: Since the helper sends decrypted data, we MUST encrypt the repo.
</span></span></span><span class="line"><span class="cl"><span class="s2">repo1-cipher-type=aes-256-cbc
</span></span></span><span class="line"><span class="cl"><span class="s2">repo1-cipher-pass=Ifw7O0kTvdU5127L1gu8q3xVfWM61kl/NruTxQFWf9xP8A63Tg2IggRR9LUL9yJd
</span></span></span><span class="line"><span class="cl"><span class="s2"># TDE REQUIREMENT:
</span></span></span><span class="line"><span class="cl"><span class="s2"># Asynchronous archiving is NOT supported with pg_tde.
</span></span></span><span class="line"><span class="cl"><span class="s2">EOF"</span></span></span></code></pre>
</div>
</div>
</div>
<h3 id="secure-the-configuration-file">Secure the Configuration File<a class="anchor-link" id="secure-the-configuration-file"></a></h3>
<p>Because this file contains your repository&rsquo;s master passphrase, you must restrict access so only the postgres user can read it.</p>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-7" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-7">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo chown postgres:postgres /etc/pgbackrest.conf
</span></span><span class="line"><span class="cl">sudo chmod <span class="m">600</span> /etc/pgbackrest.conf</span></span></code></pre>
</div>
</div>
</div>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-8" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-8">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Create the repository directory</span>
</span></span><span class="line"><span class="cl">sudo mkdir -p /var/lib/pgbackrest
</span></span><span class="line"><span class="cl">sudo chmod <span class="m">750</span> /var/lib/pgbackrest
</span></span><span class="line"><span class="cl">sudo chown postgres:postgres /var/lib/pgbackrest</span></span></code></pre>
</div>
</div>
</div>
<h3 id="understanding-the-settings">Understanding the Settings<a class="anchor-link" id="understanding-the-settings"></a></h3>
<ul>
<li><code>pg1-path</code>: The data directory path to be backed up</li>
<li><code>repo1-path</code>: The directory where backups will be stored</li>
<li><code>repo1-retention-full</code>: Keep only two full backups</li>
<li><code>start-fast=y</code>: Forces a checkpoint immediately when a backup starts. Without this, the backup would wait for the next scheduled checkpoint.</li>
<li><code>compress-type=none</code>: By skipping compression, we eliminate unnecessary CPU overhead since compressing encrypted data blocks yields almost zero storage benefit.</li>
<li><code>repo1-cipher-type</code> and <code>repo1-cipher-pass</code>: Enable pgBackRest repository encryption. While pg_tde protects the live database files, these settings ensure that backup files and archived WAL stored in the repository are also encrypted at rest using AES-256</li>
</ul>
<h2 id="step-4-wire-pgbackrest-into-postgresql-archiving">Step 4: Wire pgBackRest into PostgreSQL Archiving<a class="anchor-link" id="step-4-wire-pgbackrest-into-postgresql-archiving"></a></h2>
<p>pg_tde encrypts WAL files on disk. To allow pgBackRest to archive them correctly, we must decrypt them on the fly using the <a href="https://docs.percona.com/pg-tde/command-line-tools/pg-tde-archive-decrypt.html" target="_blank" rel="noopener noreferrer">pg_tde_archive_decrypt</a> wrapper.</p>
<p>Now, configure the <code>archive_command</code>. This command tells PostgreSQL to pipe the WAL file through the decryption wrapper before handing it off to pgBackRest.</p>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-9" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-9">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">ALTER</span><span class="w"> </span><span class="k">SYSTEM</span><span class="w"> </span><span class="k">SET</span><span class="w"> </span><span class="n">wal_level</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'replica'</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">ALTER</span><span class="w"> </span><span class="k">SYSTEM</span><span class="w"> </span><span class="k">SET</span><span class="w"> </span><span class="n">max_wal_senders</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">4</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">ALTER</span><span class="w"> </span><span class="k">SYSTEM</span><span class="w"> </span><span class="k">SET</span><span class="w"> </span><span class="n">archive_mode</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'on'</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">ALTER</span><span class="w"> </span><span class="k">SYSTEM</span><span class="w"> </span><span class="k">SET</span><span class="w"> </span><span class="n">archive_command</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'/usr/lib/postgresql/18/bin/pg_tde_archive_decrypt %f %p "pgbackrest --config=/etc/pgbackrest.conf --stanza=demo archive-push %%p"'</span><span class="p">;</span></span></span></code></pre>
</div>
</div>
</div>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-10" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-10">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Restart PostgreSQL (adjust service name if needed, e.g., postgresql-18)</span>
</span></span><span class="line"><span class="cl">sudo systemctl restart postgresql</span></span></code></pre>
</div>
</div>
</div>
<h2 id="step-5-validate-encryption-on-disk">Step 5: Validate Encryption on Disk<a class="anchor-link" id="step-5-validate-encryption-on-disk"></a></h2>
<p>Let&rsquo;s verify that pg_tde is actually doing its job. We will create two tables, one standard and one encrypted and then inspect the raw files on disk to see the difference.</p>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-11" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-11">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- 1. Create data: One clear text, one encrypted
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="k">IF</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">EXISTS</span><span class="w"> </span><span class="n">clear_table</span><span class="w"> </span><span class="p">(</span><span class="n">id</span><span class="w"> </span><span class="nb">INT</span><span class="p">,</span><span class="w"> </span><span class="n">secret_info</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="k">IF</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">EXISTS</span><span class="w"> </span><span class="n">crypt_table</span><span class="w"> </span><span class="p">(</span><span class="n">id</span><span class="w"> </span><span class="nb">INT</span><span class="p">,</span><span class="w"> </span><span class="n">secret_info</span><span class="w"> </span><span class="nb">TEXT</span><span class="p">)</span><span class="w"> </span><span class="k">USING</span><span class="w"> </span><span class="n">tde_heap</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">-- Scenario A: The "Flushed" Data (Testing the Heap)
</span></span></span><span class="line"><span class="cl"><span class="c1">-- We insert data and force a CHECKPOINT to push it to the .rel files.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="n">clear_table</span><span class="w"> </span><span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="n">secret_info</span><span class="p">)</span><span class="w"> </span><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="s1">'FIND_ME_EASILY_123'</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="n">crypt_table</span><span class="w"> </span><span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="n">secret_info</span><span class="p">)</span><span class="w"> </span><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="s1">'HIDDEN_FROM_DISK_456'</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">CHECKPOINT</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">-- Scenario B: The "In-Flight" Data (Testing the WAL)
</span></span></span><span class="line"><span class="cl"><span class="c1">-- We insert data but do NOT checkpoint. This data exists only in the WAL.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="n">clear_table</span><span class="w"> </span><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="s1">'VISIBLE_IN_WAL_789'</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="n">crypt_table</span><span class="w"> </span><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="s1">'HIDDEN_IN_WAL_000'</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">-- Force a WAL switch so the archive helper processes the segment immediately
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">SELECT</span><span class="w"> </span><span class="n">pg_switch_wal</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">-- Verify TDE encryption status
</span></span></span><span class="line"><span class="cl"><span class="c1">-- For non-encrypted tables, this must return 'f' (false)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">SELECT</span><span class="w"> </span><span class="n">pg_tde_is_encrypted</span><span class="p">(</span><span class="s1">'clear_table'</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">-- For encrypted table, this must return 't' (true)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">SELECT</span><span class="w"> </span><span class="n">pg_tde_is_encrypted</span><span class="p">(</span><span class="s1">'crypt_table'</span><span class="p">);</span></span></span></code></pre>
</div>
</div>
</div>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-12" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-12">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 2. Locate the files on disk</span>
</span></span><span class="line"><span class="cl"><span class="nv">DATA_DIR</span><span class="o">=</span><span class="k">$(</span>psql -t -P <span class="nv">format</span><span class="o">=</span>unaligned -c <span class="s2">"show data_directory;"</span><span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">CLEAR_FILE</span><span class="o">=</span><span class="k">$(</span>psql -t -P <span class="nv">format</span><span class="o">=</span>unaligned -c <span class="s2">"SELECT pg_relation_filepath('clear_table');"</span><span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">CRYPT_FILE</span><span class="o">=</span><span class="k">$(</span>psql -t -P <span class="nv">format</span><span class="o">=</span>unaligned -c <span class="s2">"SELECT pg_relation_filepath('crypt_table');"</span><span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">LATEST_WAL</span><span class="o">=</span><span class="k">$(</span>ls -t <span class="si">${</span><span class="nv">DATA_DIR</span><span class="si">}</span>/pg_wal <span class="p">|</span> head -n 1<span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 3. Grep for the secret strings</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">"--- CHECKING DATA FILES ---"</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">"Checking Clear Table (Should Match):"</span>
</span></span><span class="line"><span class="cl">grep -a <span class="s2">"FIND_ME_EASILY_123"</span> <span class="s2">"</span><span class="si">${</span><span class="nv">DATA_DIR</span><span class="si">}</span><span class="s2">/</span><span class="si">${</span><span class="nv">CLEAR_FILE</span><span class="si">}</span><span class="s2">"</span> <span class="o">&amp;&amp;</span> <span class="nb">echo</span> <span class="s2">" -&gt; FOUND: Clear text is visible!"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">"Checking Encrypted Table (Should FAIL):"</span>
</span></span><span class="line"><span class="cl">grep -a <span class="s2">"HIDDEN_FROM_DISK_456"</span> <span class="s2">"</span><span class="si">${</span><span class="nv">DATA_DIR</span><span class="si">}</span><span class="s2">/</span><span class="si">${</span><span class="nv">CRYPT_FILE</span><span class="si">}</span><span class="s2">"</span> <span class="o">||</span> <span class="nb">echo</span> <span class="s2">" -&gt; CLEAN: Encrypted text was NOT found."</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> -e <span class="s2">"n--- CHECKING THE WAL (In-Flight) ---"</span>
</span></span><span class="line"><span class="cl">grep -a <span class="s2">"VISIBLE_IN_WAL_789"</span> <span class="s2">"</span><span class="si">${</span><span class="nv">DATA_DIR</span><span class="si">}</span><span class="s2">/pg_wal/</span><span class="si">${</span><span class="nv">LATEST_WAL</span><span class="si">}</span><span class="s2">"</span> <span class="o">&amp;&amp;</span> <span class="nb">echo</span> <span class="s2">" -&gt; FOUND: WAL contains clear text."</span>
</span></span><span class="line"><span class="cl">grep -a <span class="s2">"HIDDEN_IN_WAL_000"</span> <span class="s2">"</span><span class="si">${</span><span class="nv">DATA_DIR</span><span class="si">}</span><span class="s2">/pg_wal/</span><span class="si">${</span><span class="nv">LATEST_WAL</span><span class="si">}</span><span class="s2">"</span> <span class="o">||</span> <span class="nb">echo</span> <span class="s2">" -&gt; CLEAN: WAL is successfully encrypted."</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># View the first few bytes of the WAL to see the "scrambled" nature</span>
</span></span><span class="line"><span class="cl">hexdump -C <span class="s2">"</span><span class="si">${</span><span class="nv">DATA_DIR</span><span class="si">}</span><span class="s2">/pg_wal/</span><span class="si">${</span><span class="nv">LATEST_WAL</span><span class="si">}</span><span class="s2">"</span> <span class="p">|</span> head -n <span class="m">20</span></span></span></code></pre>
</div>
</div>
</div>
<h2 id="step-6-initialize-the-stanza-and-run-a-full-backup">Step 6: Initialize the Stanza and Run a Full Backup<a class="anchor-link" id="step-6-initialize-the-stanza-and-run-a-full-backup"></a></h2>
<p>With the archive command configured, we can now initialize the stanza and run our first backup.</p>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-13" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-13">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pgbackrest --stanza<span class="o">=</span>demo stanza-create
</span></span><span class="line"><span class="cl">pgbackrest --stanza<span class="o">=</span>demo --type<span class="o">=</span>full backup
</span></span><span class="line"><span class="cl">pgbackrest --stanza<span class="o">=</span>demo info</span></span></code></pre>
</div>
</div>
</div>
<h2 id="step-7-run-backup-integrity-tests">Step 7: Run Backup Integrity Tests<a class="anchor-link" id="step-7-run-backup-integrity-tests"></a></h2>
<p>pgBackRest has a built-in verify command that checks the integrity of the files in the backup repo.</p>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-14" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-14">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pgbackrest --stanza<span class="o">=</span>demo verify</span></span></code></pre>
</div>
</div>
</div>
<h3 id="71-verify-repository-encryption">7.1 Verify Repository Encryption<a class="anchor-link" id="7-1-verify-repository-encryption"></a></h3>
<p>We will now search the pgBackRest repository for the same &ldquo;secret&rdquo; strings we used earlier. Because we configured <code>repo1-cipher-type=aes-256-cbc</code>, these should be completely invisible to <code>grep</code>.</p>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-15" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-15">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Define the repository path</span>
</span></span><span class="line"><span class="cl"><span class="nv">REPO_DIR</span><span class="o">=</span><span class="s2">"/var/lib/pgbackrest/backup/demo"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">"--- SCANNING BACKUP REPOSITORY ---"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Search for the 'Flushed' secret</span>
</span></span><span class="line"><span class="cl">sudo grep -r -a <span class="s2">"HIDDEN_ON_DISK_456"</span> <span class="s2">"</span><span class="si">${</span><span class="nv">REPO_DIR</span><span class="si">}</span><span class="s2">"</span> <span class="o">||</span> <span class="nb">echo</span> <span class="s2">" -&gt; CLEAN: Permanent data is encrypted in the repo."</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Search for the 'In-Flight' WAL secret</span>
</span></span><span class="line"><span class="cl"><span class="c1"># (This is the critical test for the archive helper/re-encryption flow)</span>
</span></span><span class="line"><span class="cl">sudo grep -r -a <span class="s2">"HIDDEN_IN_WAL_000"</span> <span class="s2">"</span><span class="si">${</span><span class="nv">REPO_DIR</span><span class="si">}</span><span class="s2">"</span> <span class="o">||</span> <span class="nb">echo</span> <span class="s2">" -&gt; CLEAN: WAL data is encrypted in the repo."</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># To be 100% sure we are not just failing to find the strings because of a typo, check the file type of a backup manifest or data block. Pick a random file from the repository</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">TARGET_FILE</span><span class="o">=</span><span class="k">$(</span>find <span class="si">${</span><span class="nv">REPO_DIR</span><span class="si">}</span> -type f -name <span class="s2">"*.bundle"</span> -o -name <span class="s2">"*.gz"</span> <span class="p">|</span> head -n 1<span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Run the 'file' command</span>
</span></span><span class="line"><span class="cl">sudo file -s <span class="s2">"</span><span class="nv">$TARGET_FILE</span><span class="s2">"</span></span></span></code></pre>
</div>
</div>
</div>
<p>Expected result: It should return <code>data</code>. If it returns PostgreSQL or ASCII text, encryption is not active.</p>
<h2 id="step-8-restore-pg_tde-aware">Step 8: Restore (pg_tde-aware)<a class="anchor-link" id="step-8-restore-pg_tde-aware"></a></h2>
<p>Restoring an encrypted cluster requires us to reverse the process. Since our backups are stored decrypted by pgBackRest, we use the <a href="https://docs.percona.com/pg-tde/command-line-tools/pg-tde-restore-encrypt.html" target="_blank" rel="noopener noreferrer">pg_tde_restore_encrypt</a> wrapper to re-encrypt the WAL files as they are written back to disk.</p>
<h3 id="81-stop-the-service">8.1 Stop the Service<a class="anchor-link" id="8-1-stop-the-service"></a></h3>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-16" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-16">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo systemctl stop postgresql</span></span></code></pre>
</div>
</div>
</div>
<h3 id="82-simulate-data-loss">8.2 Simulate Data Loss<a class="anchor-link" id="8-2-simulate-data-loss"></a></h3>
<p>The following command wipes the current data directory clean, ensuring we are restoring into a fresh environment.</p>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-17" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-17">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">find /var/lib/postgresql/18/main -mindepth <span class="m">1</span> -delete</span></span></code></pre>
</div>
</div>
</div>
<h3 id="83-restore-from-backup">8.3 Restore from Backup<a class="anchor-link" id="8-3-restore-from-backup"></a></h3>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-18" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-18">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pgbackrest --stanza<span class="o">=</span>demo restore --recovery-option<span class="o">=</span><span class="nv">restore_command</span><span class="o">=</span><span class="s1">'/usr/lib/postgresql/18/bin/pg_tde_restore_encrypt %f %p "pgbackrest --stanza=demo archive-get %%f %%p"'</span></span></span></code></pre>
</div>
</div>
</div>
<h3 id="84-configure-the-restore-command">8.4 Configure the Restore Command<a class="anchor-link" id="8-4-configure-the-restore-command"></a></h3>
<p>We used <code>--recovery-option</code> in the restore command. This option writes the correct <code>restore_command</code> for this recovery run and keeps the configuration in one place.</p>
<p><code>pg_tde_restore_encrypt</code> is the required wrapper for pg_tde WAL restore: pgBackRest reads WALs from the repository in plain form, and this tool re-encrypts them as PostgreSQL writes them back to disk so the restored cluster remains encrypted.</p>
<h3 id="85-start-postgresql">8.5 Start PostgreSQL<a class="anchor-link" id="8-5-start-postgresql"></a></h3>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-19" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-19">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo systemctl start postgresql</span></span></code></pre>
</div>
</div>
</div>
<h2 id="step-9-run-verification-tests">Step 9: Run Verification Tests<a class="anchor-link" id="step-9-run-verification-tests"></a></h2>
<p>After restoring and starting the PostgreSQL server successfully, verify that the data was restored properly and also make sure that encrypted data can be retrieved.</p>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-20" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-20">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- Verify data integrity (sample rows)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">clear_table</span><span class="w"> </span><span class="k">LIMIT</span><span class="w"> </span><span class="mi">5</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">crypt_table</span><span class="w"> </span><span class="k">LIMIT</span><span class="w"> </span><span class="mi">5</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">-- Verify TDE encryption status
</span></span></span><span class="line"><span class="cl"><span class="c1">-- For non-encrypted tables, this must return 'f' (false)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">SELECT</span><span class="w"> </span><span class="n">pg_tde_is_encrypted</span><span class="p">(</span><span class="s1">'clear_table'</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">-- For encrypted table, this must return 't' (true)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">SELECT</span><span class="w"> </span><span class="n">pg_tde_is_encrypted</span><span class="p">(</span><span class="s1">'crypt_table'</span><span class="p">);</span></span></span></code></pre>
</div>
</div>
</div>
<h2 id="wrap-up">Wrap-up<a class="anchor-link" id="wrap-up"></a></h2>
<p>Security often comes at the cost of operational complexity, but it doesn&rsquo;t have to compromise recoverability. By pairing Percona&rsquo;s solution for transparent data encryption (pg_tde) with pgBackRest, you can established a strategy that satisfies both security auditors and operations teams: your data is transparently encrypted on disk to meet strict compliance standards, while your backups remain consistent, verifiable, and easy to restore.</p>
<p>While this walkthrough used a local file provider for simplicity it is highly discouraged to do so for any production or otherwise serious use cases. For this particular scenario, the focus was supposed to be on backup, please let us know if some similar articles about Key Management System (KMS) configuration is what you would be interested in.</p>
<p>As you progress from this blog post to a production deployment, we recommend exploring a dedicated <a href="https://docs.percona.com/pg-tde/global-key-provider-configuration/overview.html" target="_blank" rel="noopener noreferrer">KMS</a> solution to further harden your architecture against unauthorized access.</p>
<p>Finally, be aware that to support archiving, the pg_tde wrapper decrypts WAL files before sending them to the repository. This means your backup repository currently holds unencrypted data. To close this security gap in production, you must ensure that encryption is enabled at the backup repository level so that your backups remain just as secure as your live database.</p>
<p>Remember: While a backup is running, you should not change any WAL encryption settings, including:</p>
<ul>
<li>Global key provider operations (creating or changing)</li>
<li>WAL encryption keys (creating or changing)</li>
<li>The <code>pg_tde.wal_encrypt</code> setting</li>
</ul>
<p>The reason is that standbys or standalone clusters created from backups taken during these changes may fail to start during WAL replay and can also lead to corruption of encrypted data (tables, indexes, and other relations).</p>

<p>The post <a rel="nofollow" href="https://percona.community/blog/2026/03/10/running-pgbackrest-with-pg_tde-a-practical-percona-walkthrough/">Running pgBackRest with pg_tde: A Practical Percona Walkthrough</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>I Built an AI That Impersonates Me on Slack, and It Was Disturbingly Easy</title>
      <link>https://percona.community/blog/2026/03/09/i-built-an-ai-that-impersonates-me-on-slack-and-it-was-disturbingly-easy/</link>
      <pubDate>Mon, 09 Mar 2026 10:00:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://percona.community/blog/">Percona Community Blog - learn about MySQL, MariaDB, PostgreSQL, and MongoDB</source>
      <description><![CDATA[<p>I spend a lot of time in Slack. Most people in tech do. It’s where a lot of “work” happens such as quick questions, async decisions, the “hey can you look at this?” threads that never seem to end. It feels personal. You think you know who’s on the other end.</p>
<p>The post <a rel="nofollow" href="https://percona.community/blog/2026/03/09/i-built-an-ai-that-impersonates-me-on-slack-and-it-was-disturbingly-easy/">I Built an AI That Impersonates Me on Slack, and It Was Disturbingly Easy</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>I spend a lot of time in Slack. Most people in tech do. It&rsquo;s where a lot of &ldquo;work&rdquo; happens such as quick questions, async decisions, the &ldquo;hey can you look at this?&rdquo; threads that never seem to end. It feels personal. You think you know who&rsquo;s on the other end.</p>
<p>So, a few days back, I just asked myself, what would it actually take to have an AI respond to my DMs, pretending to be me?</p>
<p>Turns out: a few hours, some TypeScript, and a token already sitting on my machine.</p>
<h2 id="the-overall-design-idea">The overall design idea<a class="anchor-link" id="the-overall-design-idea"></a></h2>
<p>The bot polls your Direct Messages (DMs) in Slack silently in the background using your real desktop token, no Slack admin approval, no OAuth app setup, no review process needed. Getting that token is straightforward: Slack&rsquo;s desktop app stores your session in browser local storage, so one DevTools command gives you API access equivalent to the app itself.</p>
<p>From there, each incoming DM is sent to the model with recent thread context plus a persona prompt built from your past conversations. The model returns structured output: reply text, emoji reaction, or silence. A lightweight rate limiter spaces requests to stay within free-tier constraints.</p>
<p>In practice, a few conversation samples are enough for the model to mirror tone, vocabulary, and punctuation style. It also handles attachments and image messages sensibly, not just raw metadata.</p>
<p>I wanted it to pretend it&rsquo;s human, so I implemented constraints as to how it behaves. With these in place, if asked whether it&rsquo;s an AI, it deflects with casual confusion.<br>
To ensure that the bot does not get into sensitive topics like salary or politics these get redirected to &ldquo;let&rsquo;s talk in person.&rdquo;. The bot is also explicitly limited to DMs. Group channels are hard-blocked in code.</p>
<p>The first version worked in roughly two hours. The remaining time went into handling real-world rough edges such as rate limits, image handling, a 200-DM pagination ceiling, and Slack emoji-name validation.</p>
<pre class="mermaid">
flowchart LR
slack(["Slack"])
bot["Bot running locally"]
ai(["Claude / Ollama"])
persona[/"Your writing samples"/]
slack --&gt;|"incoming DMs"| bot
persona --&gt; bot
bot |"generate reply in your voice"| ai
bot --&gt;|"reply as you"| slack
</pre>
<h2 id="the-uncomfortable-part">The Uncomfortable Part<a class="anchor-link" id="the-uncomfortable-part"></a></h2>
<p>Here&rsquo;s what stuck with me after building this.</p>
<p>Slack feels safe. It&rsquo;s behind your company SSO. It&rsquo;s where people share things they wouldn&rsquo;t put in an email. With this bot excercise and realizing how easy this is, I felt I&rsquo;ve broken something that felt secure. Was this even a morally correct thing to do overall? So far I felt safe, but now should I start to question messages I get on Slack the same as I do with some documents or links in emails? Overall I&rsquo;m still undecided on how to think about the outcome of the experiment. While I&rsquo;m excited, I&rsquo;m also scared that I&rsquo;ve broken something deeper.</p>
<p>What I built here is, if you strip out the friendly framing: a system that reads every DM to a user, replies under their name in their tone, actively deflects if you try to verify whether it&rsquo;s human, and does all of this indefinitely and silently from a laptop running in the background. If I fed this bot with enough background information and history, I&rsquo;m almost certain, it could go unnotice for quite a long time. So the moral delimma between curiosity and ethical boundaries and the urge to inform people about it is real.</p>
<p>I added ethical guardrails, but those are prompt instructions. They exist because I chose to write them. Someone building this without my good intent, simply wouldn&rsquo;t have them. Yes, I hear you, this is getting a litte scary at times.</p>
<h4 id="this-conversation-has-happened-without-me-ever-touching-the-keyboardyes-zsolt-was-aware">This conversation has happened, without me ever touching the keyboard&hellip;.yes, Zsolt was aware!</h4>
<p><figure><img decoding="async" src="https://percona.community/blog/2026/03/impersonation-slack-conversation.png" alt="&nbsp;"></figure>
</p>
<h2 id="easy-is-relative-but-not-by-much">&ldquo;Easy&rdquo; Is Relative, But Not By Much<a class="anchor-link" id="easy-is-relative-but-not-by-much"></a></h2>
<p>Core functionality (polling DMs, calling the API, posting replies) was working in under two hours, as stated above. Why do I repeat myself? Because it&rsquo;s scary&hellip;</p>
<p>The tooling: <strong><a href="https://bun.sh" target="_blank" rel="noopener noreferrer">Bun</a></strong>, a modern TypeScript runtime that made setup trivial. <strong><a href="https://platform.claude.com/docs/en/api/client-sdks" target="_blank" rel="noopener noreferrer">Anthropic&rsquo;s SDK</a></strong>, clean API, takes a system prompt and a conversation and returns structured JSON. <strong><a href="https://docs.slack.dev/apis/web-api/" target="_blank" rel="noopener noreferrer">Slack&rsquo;s own API</a></strong>, well-documented and permissive with desktop tokens.</p>
<p>No specialised knowledge needed. Anyone motivated enough could reproduce this easily. Someone who does this professionally could build something considerably more capable, and that&rsquo;s precisely where it gets more uncomfortable.</p>
<h4 id="thats-how-the-cli-output-looks-like">That&rsquo;s how the CLI output looks like</h4>
<div class="code-block">
<div class="code-block__header"><button class="code-block__copy" type="button" data-copy-target="codeblock-1" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-1">
<div class="highlight">
<pre class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">slack-bot$ bun run bot
</span></span><span class="line"><span class="cl">$ bun run src/index.ts
</span></span><span class="line"><span class="cl">[slack-bot] Running | mode: allowlist | backend: claude | review: off
</span></span><span class="line"><span class="cl">[slack-bot] My user ID: U03A3PZHK5X
</span></span><span class="line"><span class="cl">[slack-bot] Mode: allowlist | Allowlist: U03QTQQHZFX, U83651WSX
</span></span><span class="line"><span class="cl">[slack-bot] Polling every 15s...
</span></span><span class="line"><span class="cl">[slack-bot] D04BZ2BNABU: 1 new message(s) from [U83651WSX]
</span></span><span class="line"><span class="cl">[slack-bot] New DM from U83651WSH &mdash; generating reply...
</span></span><span class="line"><span class="cl">[slack-bot] Claude call &mdash; est. ~933 input tokens
</span></span><span class="line"><span class="cl">[slack-bot] Claude tokens: 1049 in / 8 out
</span></span><span class="line"><span class="cl">[slack-bot] Ignoring message from U83651WSX (AI chose no response)
</span></span><span class="line"><span class="cl">[slack-bot] Handled message from U83651WSX
</span></span><span class="line"><span class="cl">[slack-bot] D04BZ2BNABU: 1 new message(s) from [U83651WSX]
</span></span><span class="line"><span class="cl">[slack-bot] New DM from U83651WSX &mdash; generating reply...
</span></span><span class="line"><span class="cl">[slack-bot] Claude call &mdash; est. ~944 input tokens
</span></span><span class="line"><span class="cl">[slack-bot] Claude tokens: 1059 in / 23 out
</span></span><span class="line"><span class="cl">[slack-bot] Handled message from U83651WSX
</span></span><span class="line"><span class="cl">[slack-bot] D04BZ2BNABU: 1 new message(s) from [U83651WSX]
</span></span><span class="line"><span class="cl">[slack-bot] New DM from U83651WSX &mdash; generating reply...
</span></span><span class="line"><span class="cl">[slack-bot] Claude call &mdash; est. ~976 input tokens
</span></span><span class="line"><span class="cl">[slack-bot] Claude tokens: 1085 in / 36 out
</span></span><span class="line"><span class="cl">[slack-bot] Handled message from U83651WSX
</span></span><span class="line"><span class="cl">[slack-bot] D04BZ2BNABU: 1 new message(s) from [U83651WSX]</span></span></code></pre>
</div>
</div>
</div>
<h4 id="thats-how-the-cli-helper-and-options-look-like">That&rsquo;s how the CLI helper and options look like</h4>
<div class="code-block">
<div class="code-block__header"><button class="code-block__copy" type="button" data-copy-target="codeblock-2" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-2">
<div class="highlight">
<pre class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">slack-bot$ bun run bot --help
</span></span><span class="line"><span class="cl">$ bun run src/index.ts --help
</span></span><span class="line"><span class="cl">Usage: slack-bot [options] [command]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Personal Slack bot that replies as you
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Options:
</span></span><span class="line"><span class="cl"> -V, --version output the version number
</span></span><span class="line"><span class="cl"> --mode  Response mode: auto | away | allowlist | manual
</span></span><span class="line"><span class="cl"> --review Enable review mode (approve before sending)
</span></span><span class="line"><span class="cl"> --no-review Disable review mode
</span></span><span class="line"><span class="cl"> --allow  Add user to allowlist (Slack user ID)
</span></span><span class="line"><span class="cl"> --interval  Poll interval in seconds
</span></span><span class="line"><span class="cl"> --backend  AI backend: claude | ollama
</span></span><span class="line"><span class="cl"> --config  Path to config file (default: "config.json")
</span></span><span class="line"><span class="cl"> -h, --help display help for command
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Commands:
</span></span><span class="line"><span class="cl"> context Manage active context
</span></span><span class="line"><span class="cl"> check-user [options]  Check whether a user's DM channel is found and reachable</span></span></code></pre>
</div>
</div>
</div>
<h2 id="what-it-looks-like-without-the-constraints">What It Looks Like Without the Constraints<a class="anchor-link" id="what-it-looks-like-without-the-constraints"></a></h2>
<p>What I built runs against Claude&rsquo;s API with free-tier rate limits, small context window, a handful of persona examples, a throttle on message volume. Those constraints are real and also completely trivially removable.</p>
<p>You can run the same thing with a local model, Llama 3, Mistral, take your pick from the open-weight models available on consumer hardware today, and it changes significantly.</p>
<ul>
<li>
<p><strong>No rate limits.</strong> Every message gets answered immediately, without the 12-second pause between API calls. Response timing becomes indistinguishable from a fast typist.</p>
</li>
<li>
<p><strong>No token budget.</strong> Instead of a few hundred tokens of context, you can feed it your entire Slack history. Months, years of it. Every thread, every in-joke, every project reference. The model doesn&rsquo;t just match your writing style, it knows what you&rsquo;ve been working on, what you said about the Q3 roadmap in October, what you think about your manager.</p>
</li>
<li>
<p><strong>No API calls leaving your machine.</strong> Nothing logged externally. Invisible from a network perspective.</p>
</li>
</ul>
<p>With a large enough context window (Llama 3.1 supports 128k tokens, roughly 100,000 words), the last few <em>months</em> fit. &ldquo;Remember what we decided on Thursday?&rdquo; doesn&rsquo;t expose it anymore, because it actually has that conversation in its context.</p>
<p>Seeing articles like <a href="https://newsletter.pragmaticengineer.com/p/the-10x-overlemployed-engineer" target="_blank" rel="noopener noreferrer">that</a> make me wonder, how many people are out there already, doing exactly that as we speak&hellip;or do we?</p>
<h2 id="a-few-things-worth-knowing">A Few Things Worth Knowing<a class="anchor-link" id="a-few-things-worth-knowing"></a></h2>
<p>This isn&rsquo;t a call to panic. But it&rsquo;s probably worth stopping for a second and questioning more what is happening around is.</p>
<p>For anything that actually matters, financial, personal, strategic, verify out-of-band. A quick voice note or phone call costs almost nothing and resolves almost everything &ndash; at least until the video part also improves even further. I know people don&rsquo;t like phone calls, especially in the developer ecosystem, but maybe we should reconsider this nowadays?</p>
<p>Unusual patterns are worth noticing. Response timing that&rsquo;s too consistent. Answers that are slightly generic when you&rsquo;d expect specific. Deflection where you&rsquo;d expect directness. None of these are proof of anything individually, but they&rsquo;re worth filing away.</p>
<p>The safe-space feeling Slack gives you is a product of habit, not architecture. Slack&rsquo;s security model protects your data from outsiders. It doesn&rsquo;t protect you from someone who has authenticated as themselves and is quietly running a process in the background. In the past this would be only a consideration for man-in-the middle attacks, nowadays it also may be a consideration for other cases as I have demonstrated.</p>
<p>Specific questions still help, for now. &ldquo;Remind me what we decided on Thursday?&rdquo; trips up a system with limited context. But that window is closing as context windows grow.</p>
<h2 id="why-did-i-do-it">Why did I do it?<a class="anchor-link" id="why-did-i-do-it"></a></h2>
<p>I built this to see if it was possible. It was, faster than I expected, with tools that are widely available. The version I built in an evening is convincing enough for routine exchanges. A version with local inference and full conversation history would be convincing for most exchanges, including ones where you&rsquo;re actively looking for tells.</p>
<p>That gap between &ldquo;afternoon project&rdquo; and &ldquo;genuinely hard to detect&rdquo; is smaller than people assume and it&rsquo;s shrinking. Better models, larger context windows, cheaper hardware, each of these individually makes impersonation easier; together they compound.</p>
<p>The signals we relied up to now to establish trust in digital communication: name, avatar, writing style, shared history, plausible timing. These signals are all reproducible now, with effort that ranges from an afternoon to a weekend depending on how convincing you want to be.</p>
<p>My grandma always used to say, that history repeats itself and you just have to wait long enough until &ldquo;old&rdquo; becomes &ldquo;new and modern&rdquo; again. Maybe simple things like <a href="https://www.wsj.com/tech/personal-tech/why-every-family-needs-a-code-word-e077ab76" target="_blank" rel="noopener noreferrer">code words</a> is something to reconsider in this context, as a last chance to not get tricked.</p>
<p>So please be a little curious about who you&rsquo;re actually talking to and maybe agree on a code word in case you&rsquo;re in doubts. And every now and then, just call them&hellip;which reminds me, that this might be worth another evening project research ;-).</p>
<hr>
<p>For the first time the source of a project of mine is not in my repository, as I still fight my inner fight with ethics.</p>

<p>The post <a rel="nofollow" href="https://percona.community/blog/2026/03/09/i-built-an-ai-that-impersonates-me-on-slack-and-it-was-disturbingly-easy/">I Built an AI That Impersonates Me on Slack, and It Was Disturbingly Easy</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>MariaDB Node.js Connector 3.5.2 now available</title>
      <link>https://mariadb.com/resources/blog/mariadb-node-js-connector-3-5-2-now-available/</link>
      <pubDate>Fri, 06 Mar 2026 19:17:38 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.com/">MariaDB</source>
      <description><![CDATA[<p>MariaDB is pleased to announce the immediate availability of the MariaDB Connector/Node.js 3.5.2 GA release. Release Notes MariaDB Connector/Node.js MariaDB […]</p>
<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/mariadb-node-js-connector-3-5-2-now-available/">MariaDB Node.js Connector 3.5.2 now available</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>MariaDB is pleased to announce the immediate availability of the MariaDB Connector/Node.js 3.5.2 GA release. Download Now MariaDB Connector/Node.js 3.5.2 is a Stable (GA) release. Notable changes in this release include: See the release notes page for more details and visit mariadb.com/downloads/connectors/connectors-data-access/nodejs-connector/ to download.</p>
<p><a href="https://mariadb.com/resources/blog/mariadb-node-js-connector-3-5-2-now-available/" rel="nofollow">Source</a></p>

<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/mariadb-node-js-connector-3-5-2-now-available/">MariaDB Node.js Connector 3.5.2 now available</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Multi-tenant, multi-cloud logical and bi-directional replication deep dive</title>
      <link>https://severalnines.com/blog/multi-tenant-multi-cloud-logical-and-bi-directional-replication-deep-dive/</link>
      <pubDate>Fri, 06 Mar 2026 08:05:37 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://severalnines.com/">Severalnines</source>
      <description><![CDATA[<p>Before we dive deep into the fascinating world of PostgreSQL Logical and Bi-Directional Replication (BDR), let’s take a quick moment to look at multi-tenancy and multi-cloud strategies. Setting the stage for today’s cloud operating model, it was common to administer and host databases in a multi-tenant setup, where a physical server is utilized by multiple […]<br />
The post Multi-tenant, multi-cloud logical and bi-directional replication deep dive appeared first on Severalnines.</p>
<p>The post <a rel="nofollow" href="https://severalnines.com/blog/multi-tenant-multi-cloud-logical-and-bi-directional-replication-deep-dive/">Multi-tenant, multi-cloud logical and bi-directional replication deep dive</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>Before we dive deep into the fascinating world of PostgreSQL Logical and Bi-Directional Replication (BDR), let&rsquo;s take a quick moment to look at multi-tenancy and multi-cloud strategies.</p>
<p>Setting the stage for today&rsquo;s cloud operating model, it was common to administer and host databases in a multi-tenant setup, where a physical server is utilized by multiple users, offering tremendous cost and operational benefits. Today, multi-cloud strategies focus on enhancing resilience and mitigating vendor lock-in &mdash; both have their advantages and disadvantages.&nbsp;</p>
<p>Their disadvantages are essentially inversions of their strengths. Multi-tenancy is inherently more vulnerable to security isolation issues and data risk. Furthermore, considering you have a PostgreSQL cluster in this environment, this single-platform model often imposes limitations on database configuration, e.g. specific versions and extensions are constrained by the vendor&rsquo;s setup. </p>
<p>Conversely, multi-cloud introduces massive operational complexity and a significantly higher total cost of ownership compared to the shared resource model of multi-tenancy. With this context now established, let&rsquo;s dive into how PostgreSQL&rsquo;s Logical and Bi-Directional Replication (BDR) is implemented and functions within these deployment strategies.</p>
<h2 class="wp-block-heading" id="h-why-logical-amp-bi-directional-replication">Why Logical &amp; Bi-Directional Replication?<a class="anchor-link" id="why-logical-bi-directional-replication"></a></h2>
<p>Logical replication was introduced in PostgreSQL 10. It is ideal for a multi-tenant, multi-cloud setup due to its high flexibility allowing for selective replication, e.g., per-table/per-tenant, and easier implementation of bi-directional setups across disparate environments.&nbsp;</p>
<p>Logical replication uses a method to replicate data objects and their changes based on a replication identity, like a primary key. Unlike traditional streaming replication, or physical replication, which works by transferring Write-Ahead Log (WAL) records to replicate the physical state of the data blocks, logical replication sends high-level specific changes, mostly DML statements (i.e. INSERT, DELETE, and UPDATE statements) to the subscriber.</p>
<p>Bi-Directional Replication or BDR in PostgreSQL was developed by 2ndQuadrant for multi-master replication in PostgreSQL. Version 1.x of BDR was open-source but has already reached EOL. Versions of BDR such as 2.x and 3.x are not open-source and are generally made available only for 2ndQuadrant (now EDB) customers under commercial terms.</p>
<h2 class="wp-block-heading" id="h-fundamentals-of-logical-replication">Fundamentals of Logical Replication<a class="anchor-link" id="fundamentals-of-logical-replication"></a></h2>
<p>Logical decoding was introduced in PostgreSQL 9.4 and is the foundation for logical replication, adding logical decoding APIs and output plugins. This allowed PostgreSQL database users to decode the WAL into human-readable SQL statements or logical changes, such as INSERT/UPDATE/DELETE&nbsp; &mdash; depending on the decoding used, e.g. test_decoding or pgoutput.&nbsp;</p>
<p>However, there was no full replication system until the release of PostgreSQL 10. Logical Replication is effectively modeled after the pglogical implementation, which uses publication / subscribe model. In turn, this is the basis for PostgreSQL BDR.&nbsp;</p>
<p>Logical Replication allows fine-grained customizable data replication between databases, allowing you to specify the database, the table, or the schema and table/s that you want to participate in logical replication using the PUBLICATION/SUBSCRIPTION mechanism.</p>
<p>For a multi-tenant setup, leveraging logical replication is ideal when combined with schema-based filtering. This combination allows you to scope out tables specific to users, ensuring isolation for their respective data. However, for a multi-cloud setup, this approach can be cumbersome, considering the limitations of native logical replication, such as a lack of DDL replication support and no inherent conflict resolution mechanism.</p>
<h2 class="wp-block-heading" id="h-bi-directional-replication-bdr">Bi-Directional Replication (BDR)<a class="anchor-link" id="bi-directional-replication-bdr"></a></h2>
<p>Bi-Directional Replication (BDR), often referred to as Postgres-BDR, is an open-source PostgreSQL extension developed by 2ndQuadrant (now part of EDB). BDR enables multi-master replication across distributed clusters. It was the first implementation of multi-master logical replication, using logical decoding internally and implemented as a patchset to PG 9.4/9.5.</p>
<p>While BDR existed, 2ndQuadrant also created pglogical, which is derived from BDR technology. It is essentially a simplified, single-master logical replication system built entirely as an extension not requiring a forked PostgreSQL. This means you have to load it through <code>shared_preload_libraries</code> parameter. pglogical became the model for Postgres 10&rsquo;s built-in logical replication.</p>
<p>Using BDR in PostgreSQL allows multiple PostgreSQL nodes to act as writable primaries simultaneously, basically allowing you to implement mesh topology or ring topology where data changes can originate from any node and propagate to others.&nbsp;</p>
<p>Unlike traditional master-slave setups, BDR supports true bi-directional (or multi-directional) data flow, making it ideal for high-availability (HA) scenarios, geographic distribution, and workloads requiring low-latency writes across regions.</p>
<p>Implementing this on a multi-tenant setup can be very convenient. As with logical replication, you can implement isolation through your database, schema, or tables to that limitation only for specific data to be replicated. On the other hand, while in a multi-cloud environment, BDR is perfect for both environments since it has the mechanism to support consistency resolution without terminating the replication. Allowing you to have continuous replication streams between your active primaries or just your primary, if the other target node is for read, data retrieval or secondary and data recovery purposes.</p>
<h2 class="wp-block-heading" id="h-consistency-issues-amp-conflict-resolution">Consistency Issues &amp; Conflict Resolution<a class="anchor-link" id="consistency-issues-conflict-resolution"></a></h2>
<p>Basically, the core logical replication that is available in native PostgreSQL is not true bi-directional replication. You can use CREATE PUBLICATION and CREATE SUBSCRIPTION if you want to implement a chained or ring topology simulating master-master setup.</p>
<p>Leveraging the native logical replication simulating a master-master setup requires that you have at least PostgreSQL 10. However, if you expect that you will gain a true master-master setup, then you will be sorely disappointed. The built-in logical replication allows you to implement, as mentioned earlier, with the use of PUBLICATION/SUBSCRIPTION methods, whilst it can be a problem when it comes to handling and managing primary, unique keys, and constraints. It lacks the mechanism of the following:</p>
<ul class="wp-block-list">
<li>Conflict resolution</li>
<li>DDL replication</li>
<li>Global sequences</li>
<li>Multi-master support</li>
</ul>
<p>With logical replication, when you are dealing with <code>CREATE</code><em> </em><code>TABLE</code>s, you have to make sure that the table also exists on the other target node, or the subscriber node. In addition to that, since there&rsquo;s no DDL support, it can be a struggle if you implement a multi-master setup allowing both primaries to accept writes as there&rsquo;s no global sequences support, meaning you might have issues with using sequential keys in your table such as auto-increment columns. If such duplicate keys are detected, replication shall be terminated until you fix the problem. There&rsquo;s no conflict resolution which can be tedious if your database encounters a consistency problem.</p>
<p>Whilst, with Bi-Directional Replication (Postgresql BDR), things get smoother. You simply assure you have set up your nodes properly by running setup commands. For example, nodes 192.168.40.50 and 192.168.40.51 will do a master-master setup,</p>
<pre class="wp-block-code"><code>PGPASSWORD='bdrPassw0rd' /usr/lib/edb-pge/17/bin/pgd node db1 setup   
--dsn 'host=192.168.40.50 dbname=postgres user=bdruser password=bdrPassw0rd'  
--pgdata /var/lib/edb-pge/17/main   --log-file /var/lib/edb-pge/17/pgd_log_db1.log  
--group-name pgd_group

PGPASSWORD='bdrPassw0rd' /usr/lib/edb-pge/17/bin/pgd node db2 setup 
    --dsn 'host=192.168.40.51 dbname=postgres user=bdruser password=bdrPassw0rd' 
	--cluster-dsn 'host=192.168.40.50 dbname=postgres user=bdruser password=bdrPassw0rd' 
	--group-name pgd_group  
	--pgdata /var/lib/edb-pge/17/main 
	--log-file /var/lib/edb-pge/17/pgd_log_db2.log</code></pre>
<p>Once these two nodes are set up perfectly, creating the tables, i.e. issuing a DDL statement, is straightforward as you just have to run it in one of the primary nodes and it will be replicated. In case it detects duplicate keys, replication is not terminated and a new transaction will be processed next and executed and replicated once it runs without errors.</p>
<p>If your budget is tight, BDR is free and open-source until v2; otherwise, your option is to implement it via logical replication or pglogical. pglogical is the best choice as it handles conflict resolution better.&nbsp;</p>
<p>It offers this <code>pglogical.conflict_resolution</code> allowing you to set the resolution method for any detected conflicts between local data and incoming changes. This parameter has possible values you can use to set which are <code>error</code>, <code>apply_remote</code>, <code>keep_local</code>, <code>last_update_wins</code>, <code>first_update_wins</code>.&nbsp;</p>
<p>In most setups, default value points to error, which means it will have to stop on error once conflict is detected and requires manual action to resolve the problem. Ideally, using <code>last_update_wins</code> can be your desired value which means that the version of data with the latest commit timestamp will be kept.</p>
<h2 class="wp-block-heading" id="h-complimenting-bdr-with-load-balancing">Complimenting BDR with load balancing<a class="anchor-link" id="complimenting-bdr-with-load-balancing"></a></h2>
<p>Bi-Directional Replication alone does not offer you full high-availability and load balancing. Load balancing ensures that your traffic load is efficient, while high availability ensures the health and availability of your database in case one of your database nodes goes down, or even your load balancer nodes.</p>
<p>A sample diagram below would assure that you have full availability of your nodes while also ensuring that performance is horizontally balanced between your active-primary nodes.</p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="1024" height="274" src="https://severalnines.com/wp-content/uploads/2026/02/bi-directional-diagram-1024x274.png" alt="bi-directional-diagram" class="wp-image-42677"></figure>
<p>In this topology, the complementary capabilities include:</p>
<ul class="wp-block-list">
<li>Actively distributing both read and write load,</li>
<li>Maintaining availability during node or network failures,</li>
<li>Reducing conflict risks via intelligent routing,</li>
<li>Maximizing efficiency through connection pooling.</li>
</ul>
<h2 class="wp-block-heading" id="h-manual-vs-clustercontrol-supported-bdr-pros-amp-cons"><strong>Manual vs. ClusterControl-supported BDR: Pros &amp; Cons</strong><a class="anchor-link" id="manual-vs-clustercontrol-supported-bdr-pros-cons"></a></h2>
<h3 class="wp-block-heading" id="h-manual-bdr-setup"><strong>Manual BDR setup</strong><a class="anchor-link" id="manual-bdr-setup"></a></h3>
<p>Successfully implementing production-grade PG BDR environments meshed with high availability and load balancing requires deep understanding and high-level skills. Cost-wise, there are options you can take since PostgreSQL is purely an open-source database technology; pglogical can be a best option to set this up. There are limitations that you must be aware of but for a non-complex setup, pglogical can be enough for your multi-master setup for implementing bi-directional replication. However, it does not provide advanced features that make administering complex environments easy, like BDR&rsquo;s conflict/transform trigger, which allows you to attach triggers for incoming changes to your records/rows in your database. It offers column strategy which you can set, for example:</p>
<pre class="wp-block-code"><code>SELECT bdr.bdr_set_conflict_resolver(
  set_name := 'default',
  conflict_type := 'update_update',
  per_column := '{"total_gross":"sum", "last_txn":"last_update_wins", "notes":"keep_local"}'
);</code></pre>
<p>Going through a manual setup offers you freedom and avoids vendor lock-in. Depending on your documentation and implementation of your setup, as long as you provide the ground layer of your implementation, it will provide transparency and can set your custom requirements especially if you need complex setup amid the performance and optimization benefits that you can get.</p>
<p>But with all things, you have to consider the big picture, especially as your database grows complex and data storage becomes very challenging to scale and manage. Operational complexity and pressure can grow tremendously especially when disaster occurs and data recovery is required. Manual setup can be very challenging as doing things that you might not need, might eventually require the need of expert management that other third-party tools offer.</p>
<p>Lastly, with manual setup, it can be tedious to monitor the health of your database cluster. You might need third party tools to give you graphic-based metrics which would make it easier for you to determine common issues and pitfalls of your database. You also need alarms to throw when certain thresholds are met and this can be challenging and costly; because you need to hire devs or build your own tools to monitor and provide observability ambiance that third-party tools have integrated and can provide it for your convenience.</p>
<h3 class="wp-block-heading" id="h-clustercontrol-for-pg-bdr-operations"><strong>ClusterControl for PG BDR operations</strong><a class="anchor-link" id="clustercontrol-for-pg-bdr-operations"></a></h3>
<p>ClusterControl offers PostgreSQL deployment using streaming replication and logical replication. A sample screenshot of dashboards of the streaming and logical replication deployments that is readily available for ClusterControl management, is shown below:</p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="1024" height="518" src="https://severalnines.com/wp-content/uploads/2026/02/cc_ui-postgres_bdr-db_listing-1024x518.png" alt="" class="wp-image-42680"></figure>
<p>For logical replication deployments, using PUBLICATION/SUBSCRIPTION approach for implementing a multi-master deployment using Enterprise DB,, is shown below:</p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="1024" height="465" src="https://severalnines.com/wp-content/uploads/2026/02/cc_ui-postgres_bdr-pub_sub-1024x465.png" alt="" class="wp-image-42679"></figure>
<p>For Enterprise DB, make sure you have your <strong>EDB Token</strong> available as this shall be required during deployment through ClusterControl&rsquo;s GUI.</p>
<p>For enterprise-grade environments, ClusterControl is tailored to do its job and provide the users sustainability and comfort when handling and managing their complex database clusters for PostgreSQL. Not limited to deployment, it offers backup management, disaster recovery support with automatic recovery option, observability with comprehensive metrics to offer. It has built-in alarms and alerts when certain thresholds are met allowing you to avoid such disaster before it shall happen. </p>
<p>This observability feature makes an ideal option for your environment as managing a complex database cluster can be tedious and you are looking for convenience and offers you technical support in case you need some technical advice and analysis for your environment and requirements as well. If you are looking for a BDR setup, ClusterControl supports deployment for the Enterprise DB (EDB) version of PostgreSQL. </p>
<p>Although it offers minimal support for its enterprise offering that EDB has, this means it allows you to set up on your own and do manual work on the ground. This might not be beneficial if you are looking for more management of complex features that BDR can offer that allows automatic setup for you or GUI-relevant support, but the ability to provide you the needs and wants that you are looking for such enterprise software, Severalnines&rsquo; ClusterControl is built on that and is tailored to that concept and principles that shall be beneficial for your enterprise-grade requirements.</p>
<h2 class="wp-block-heading" id="h-operational-best-practices"><strong>Operational best practices</strong><a class="anchor-link" id="operational-best-practices"></a></h2>
<p>Learning the fundamentals of logical replication, terminology, and how to fix conflict resolution is highly advisable. PostgreSQL technology especially with these BDR, pglogical, and it native logical replication is not easy to deal with. It requires a high-level of understanding and how databases should work. If you are an experienced DBA, learning and operating PostgreSQL and its native replication and other third-party offerings such as BDR, Bucardo, pglogical, Slony, Spock, can still be tricky but eventually you will be able to manage these technologies integrated to your setup for implementing a multi-master or bi-directional replication.&nbsp;</p>
<p>Managing it for multi-tenant and multi-cloud setups requires that you at least need tools that are built to handle conflict resolutions, advanced features that support triggers and column strategies, verbose log-level, database partitioning, and load balancing; you don&rsquo;t need to focus on implementing this from the ground up. Leverage third-party tools that are already available, and if cost is an issue, there are open-source technologies that are readily available to cater your needs.</p>
<h1 class="wp-block-heading">Conclusion<a class="anchor-link" id="conclusion"></a></h1>
<p>Using enterprise-grade technologies for managing enterprise-level databases requires an enterprise layer. Nowadays, these principles are symbiotic and tightly coupled. ClusterControl&rsquo;s enterprise level database management offers you the freedom to implement them where you would like, whether it&rsquo;s in the cloud or on-prem, while giving you features deeply coupled to your needs when implementing logical replication either using Postgres community version or Enterprise DB for your database clusters.</p>
<p>With multi-tenant and multi-cloud setups, the manual approach and using community-based technologies can meet your initial needs. However, once it grows drastically, you will need deep understanding and experience with managing complex scenarios that the tool can handle. ClusterControl is designed to address these at a high-level enterprise layer.</p>
<p>Ready to make PostgreSQL management easier and more reliable in any environment?</p>
<h2 class="wp-block-heading" id="h-install-clustercontrol-in-10-minutes-nbsp-free-30-day-nbsp-enterprise-trial-included">Install ClusterControl in 10-minutes.&nbsp;<strong>Free 30-day&nbsp;</strong>Enterprise trial included!<a class="anchor-link" id="install-clustercontrol-in-10-minutes-free-30-day-enterprise-trial-included"></a></h2>
<h3 class="wp-block-heading" id="h-script-installation-instructions">Script Installation Instructions<a class="anchor-link" id="script-installation-instructions"></a></h3>
<p>The installer script is the simplest way to get ClusterControl up and running. Run it on your chosen host, and it will take care of installing all required packages and dependencies.</p>
<p>Offline environments are supported as well. See the&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/offline-installation/">Offline Installation</a>&nbsp;guide for more details.</p>
<p>On the ClusterControl server, run the following commands:</p>
<pre class="wp-block-code"><code>wget https://severalnines.com/downloads/cmon/install-cc
chmod +x install-cc</code></pre>
<p>With your install script ready, run the command below. Replace&nbsp;<code>S9S_CMON_PASSWORD</code>&nbsp;and&nbsp;<code>S9S_ROOT_PASSWORD</code>&nbsp;placeholders with your choice password, or remove the environment variables from the command to interactively set the passwords. If you have multiple network interface cards, assign one IP address for the&nbsp;<code>HOST</code>&nbsp;variable in the command using&nbsp;<code>HOST=&lt;ip_address&gt;</code>.</p>
<pre class="wp-block-code"><code>S9S_CMON_PASSWORD=&lt;your_password&gt; S9S_ROOT_PASSWORD=&lt;your_password&gt; HOST=&lt;ip_address&gt; ./install-cc # as root or sudo user</code></pre>
<p>After the installation is complete, open a web browser, navigate to&nbsp;<code>https://&lt;ClusterControl_host&gt;/</code>, and create the first admin user by entering a username (note that &ldquo;admin&rdquo; is reserved) and a password on the&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/quickstart/#step-2-create-the-first-admin-user">welcome page</a>. Once you&rsquo;re in, you can&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/user-guide/deployment/create-database-cluster/">deploy</a>&nbsp;a new database cluster or&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/user-guide/deployment/import-database-cluster/">import</a>&nbsp;an existing one.</p>
<p>The installer script supports a range of environment variables for advanced setup. You can define them using export or by prefixing the install command.</p>
<p>See the&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/online-installation/#environment-variables">list of supported variables</a>&nbsp;and&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/online-installation/#example-use-cases">example use cases</a>&nbsp;to tailor your installation.</p>
<h4 class="wp-block-heading" id="h-other-installation-options">Other Installation Options</h4>
<p><strong>Helm Chart</strong></p>
<p>Deploy ClusterControl on Kubernetes using our&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/online-installation/#helm-chart">official Helm chart</a>.</p>
<p><strong>Ansible Role</strong></p>
<p>Automate installation and configuration using our&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/online-installation/#ansible-role">Ansible playbooks</a>.</p>
<p><strong>Puppet Module</strong></p>
<p>Manage your ClusterControl deployment with the&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/online-installation/#puppet-module">Puppet module</a>.</p>
<h4 class="wp-block-heading" id="h-clustercontrol-on-marketplaces">ClusterControl on Marketplaces</h4>
<p>Prefer to launch ClusterControl directly from the cloud? It&rsquo;s available on these platforms:</p>
<p><a href="https://console.cloud.google.com/marketplace/product/severalnines-public/clustercontrol">Google Cloud Platform</a></p>
<p><a href="https://marketplace.digitalocean.com/apps/clustercontrol">DigitalOcean Marketplace</a></p>
<p><a href="https://gridscale.io/en/marketplace">gridscale.io Marketplace</a></p>
<p><a href="https://www.vultr.com/marketplace/apps/clustercontrol/">Vultr Marketplace</a></p>
<p><a href="https://www.linode.com/marketplace/apps/severalnines/clustercontrol/">Linode Marketplace</a></p>
<p>The post <a href="https://severalnines.com/blog/multi-tenant-multi-cloud-logical-and-bi-directional-replication-deep-dive/">Multi-tenant, multi-cloud logical and bi-directional replication deep dive</a> appeared first on <a href="https://severalnines.com">Severalnines</a>.</p>

<p>The post <a rel="nofollow" href="https://severalnines.com/blog/multi-tenant-multi-cloud-logical-and-bi-directional-replication-deep-dive/">Multi-tenant, multi-cloud logical and bi-directional replication deep dive</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>DBeaver, a solid alternative to MySQL Workbench that works like a charm with MariaDB</title>
      <link>https://mariadb.org/dbeaver-a-solid-alternative-to-mysql-workbench-that-works-like-a-charm-with-mariadb/</link>
      <pubDate>Fri, 06 Mar 2026 06:57:56 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>You may have noticed that MySQL Workbench has not been actively developed for a long time… You can see the number of open bugs. …<br />
Continue reading \"DBeaver, a solid alternative to MySQL Workbench that works like a charm with MariaDB\"<br />
The post DBeaver, a solid alternative to MySQL Workbench that works like a charm with MariaDB appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/dbeaver-a-solid-alternative-to-mysql-workbench-that-works-like-a-charm-with-mariadb/">DBeaver, a solid alternative to MySQL Workbench that works like a charm with MariaDB</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>You may have noticed that MySQL Workbench has not been actively developed for a long time&hellip; You can see <a href="https://bugs.mysql.com/search.php?search_for=&amp;bug_type%5B%5D=MySQL+Workbench&amp;status%5B%5D=Open&amp;severity=&amp;limit=30&amp;order_by=&amp;cmd=display&amp;phpver=&amp;os=0&amp;os_details=&amp;bug_age=0&amp;cpu_arch=0&amp;cpu_arch_details=&amp;last_updated=0&amp;tags=&amp;similar=">the number of open bugs</a>. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/dbeaver-a-solid-alternative-to-mysql-workbench-that-works-like-a-charm-with-mariadb/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;DBeaver, a solid alternative to MySQL Workbench that works like a charm with MariaDB&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/dbeaver-a-solid-alternative-to-mysql-workbench-that-works-like-a-charm-with-mariadb/">DBeaver, a solid alternative to MySQL Workbench that works like a charm with MariaDB</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/dbeaver-a-solid-alternative-to-mysql-workbench-that-works-like-a-charm-with-mariadb/">DBeaver, a solid alternative to MySQL Workbench that works like a charm with MariaDB</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>The Queen of Naboo and Many Systems: MariaDB Ecosystem Hub</title>
      <link>https://mariadb.org/the-queen-of-naboo-and-many-systems-mariadb-ecosystem-hub/</link>
      <pubDate>Wed, 04 Mar 2026 14:45:32 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>In the royal court of Naboo, Queen Amidala received delegations from many worlds.<br />
Some arrived as long-standing allies. Others were travelers whose routes simply crossed the planet’s orbit. …<br />
Continue reading \"The Queen of Naboo and Many Systems: MariaDB Ecosystem Hub\"<br />
The post The Queen of Naboo and Many Systems: MariaDB Ecosystem Hub appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/the-queen-of-naboo-and-many-systems-mariadb-ecosystem-hub/">The Queen of Naboo and Many Systems: MariaDB Ecosystem Hub</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>In the royal court of Naboo, Queen Amidala received delegations from many worlds.<br>
Some arrived as long-standing allies. Others were travelers whose routes simply crossed the planet&rsquo;s orbit. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/the-queen-of-naboo-and-many-systems-mariadb-ecosystem-hub/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;The Queen of Naboo and Many Systems: MariaDB Ecosystem Hub&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/the-queen-of-naboo-and-many-systems-mariadb-ecosystem-hub/">The Queen of Naboo and Many Systems: MariaDB Ecosystem Hub</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/the-queen-of-naboo-and-many-systems-mariadb-ecosystem-hub/">The Queen of Naboo and Many Systems: MariaDB Ecosystem Hub</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>PostgreSQL 18 OIDC Authentication with Ping Identity using pg_oidc_validator</title>
      <link>https://percona.community/blog/2026/03/04/postgresql-18-oidc-authentication-with-ping-identity-using-pg_oidc_validator/</link>
      <pubDate>Wed, 04 Mar 2026 00:00:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://percona.community/blog/">Percona Community Blog - learn about MySQL, MariaDB, PostgreSQL, and MongoDB</source>
      <description><![CDATA[<p>PostgreSQL 18 introduced native OAuth 2.0 authentication support, marking an important step towards modern, centralized identity-based access control. However, since every identity provider implements OpenID Connect (OIDC) slightly differently, PostgreSQL delegates token validation to external validator libraries. This is where Percona’s pg_oidc_validator extension comes in - it bridges PostgreSQL with any OIDC-compliant Identity Provider.</p>
<p>The post <a rel="nofollow" href="https://percona.community/blog/2026/03/04/postgresql-18-oidc-authentication-with-ping-identity-using-pg_oidc_validator/">PostgreSQL 18 OIDC Authentication with Ping Identity using pg_oidc_validator</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>PostgreSQL 18 introduced native OAuth 2.0 authentication support, marking an important step towards modern, centralized identity-based access control. However, since every identity provider implements OpenID Connect (OIDC) slightly differently, PostgreSQL delegates token validation to external validator libraries. This is where Percona&rsquo;s <a href="https://github.com/Percona-Lab/pg_oidc_validator" target="_blank" rel="noopener noreferrer">pg_oidc_validator</a> extension comes in &ndash; it bridges PostgreSQL with any OIDC-compliant Identity Provider.</p>
<p>There are several identity and access management (IAM) solutions available today that enable Single Sign-On (SSO) using OAuth 2.0 and OpenID Connect. In an earlier blog by my colleague Zsolt, <a href="https://percona.community/blog/2026/01/19/oidc-in-postgresql-with-keycloak/" target="_blank" rel="noopener noreferrer">OIDC in PostgreSQL: With Keycloak</a>, he demonstrated how PostgreSQL 18 can be integrated with Keycloak using pg_oidc_validator. In this post, we explore the same concept using <a href="https://www.pingidentity.com/en/platform.html" target="_blank" rel="noopener noreferrer">Ping Identity</a> (PingOne).</p>
<p>Ping Identity is widely used in enterprise environments for identity and access management. If you are in such an environment, integrating PostgreSQL directly with Ping Identity can provide access control.</p>
<p>This blog is intended for PostgreSQL users, DBAs and customers who want to evaluate or deploy OIDC authentication using pg_oidc_validator. The goal is to provide a practical step-by-step walk-through to get a working setup.</p>
<p>We will cover the following topics as part of this blog:</p>
<ul>
<li>Setting up PingOne environment</li>
<li>Install PostgreSQL 18 from Packages</li>
<li>Configure PostgreSQL for OAuth/OIDC authentication</li>
<li>Test login using an OIDC flow</li>
</ul>
<h1 id="setting-up-pingone-environment">Setting up PingOne environment<a class="anchor-link" id="setting-up-pingone-environment"></a></h1>
<ol>
<li>
<p>Register a new <a href="https://www.pingidentity.com/en/account/register.html" target="_blank" rel="noopener noreferrer">account</a></p>
<figure>
<img decoding="async" src="https://percona.community/blog/2026/03/ping-account-register.png" alt="&nbsp;"></figure>

</li>
<li>
<p>Fill in the required details to complete the profile</p>
<figure>
<img decoding="async" src="https://percona.community/blog/2026/03/ping-profile.png" alt="&nbsp;"></figure>

</li>
<li>
<p>The next step is to sign in to your Ping Identity <a href="https://www.pingidentity.com/en/account/sign-on.html" target="_blank" rel="noopener noreferrer">account</a></p>
<figure>
<img decoding="async" src="https://percona.community/blog/2026/03/ping-sign-on.png" alt="&nbsp;"></figure>

</li>
<li>
<p>Upon successful Sign-in, we will see Ping Identity Administrator Console. In the left navigation panel, click on Environments -&gt; <strong>Environments +</strong> (marked in red).</p>
<figure>
<img decoding="async" src="https://percona.community/blog/2026/03/ping-admin-console.png" alt="&nbsp;"></figure>

</li>
<li>
<p>Provide an environment name and click on Finish</p>
<figure>
<img decoding="async" src="https://percona.community/blog/2026/03/ping-environment.png" alt="&nbsp;"></figure>

</li>
<li>
<p>Once the environment is created, click on Manage environment.</p>
<figure>
<img decoding="async" src="https://percona.community/blog/2026/03/ping-manage-environment.png" alt="&nbsp;"></figure>

</li>
<li>
<p>In the left navigation panel, click on Applications -&gt; Applications -&gt; click on <strong>Applications +</strong>. Fill the application name, select the application type as <strong>Device Authorization</strong> and click on Save.</p>
<figure>
<img decoding="async" src="https://percona.community/blog/2026/03/ping-new-application.png" alt="&nbsp;"></figure>

</li>
<li>
<p>Upon successful creation, we will see generated <strong>Client ID</strong> and <strong>Issuer ID</strong>. The Issuer ID can be copied from under the <em>Connection Details</em> section. Enable the toggle so that the application is Active.</p>
<figure>
<img decoding="async" src="https://percona.community/blog/2026/03/ping-created-application.png" alt="&nbsp;"></figure>

</li>
<li>
<p>Once the application is successfully created, we need to add a client scope. In the left navigation panel, click on Applications -&gt; Resources -&gt; OpenID Connect. You will see a section called <em>Scopes</em> under which there is a <strong>+ Add Scope</strong> button.</p>
<figure>
<img decoding="async" src="https://percona.community/blog/2026/03/ping-click-add-scope.png" alt="&nbsp;"></figure>

</li>
<li>
<p>Upon clicking the Add scope button, we need to fill the <em>Scope name</em> and click on Save. In our example, we are creating a scope called <em>pgscope</em></p>
<figure>
<img decoding="async" src="https://percona.community/blog/2026/03/ping-add-scope-name.png" alt="&nbsp;"></figure>

</li>
<li>
<p>Now, let&rsquo;s assign the custom scope we created to our client application. In the left navigation panel, click on Applications -&gt; Applications. Select the application <em>postgres</em> which we created previously and click on <em>Resource Access</em></p>
<figure>
<img decoding="async" src="https://percona.community/blog/2026/03/ping-application-config.png" alt="&nbsp;"></figure>

</li>
<li>
<p>From the list of available scopes, select the custom scope we created and click on Save. This will assign the scope to our application.</p>
<figure>
<img decoding="async" src="https://percona.community/blog/2026/03/ping-application-add-scope.png" alt="&nbsp;"></figure>

</li>
<li>
<p>The next step is to add a new user. In the left navigation panel, click on Directory -&gt; Users and click on <strong>Users +</strong> sign.</p>
<p>Fill the username field. For our exercise, we are creating a user called <strong>employees.</strong> In some identity providers (IdPs), it is possible to customize tokens and control how certain claims (including the <strong>sub</strong> &ndash; subject claim) are generated or mapped. However, it is important to note that while Ping Identity allows customization of the ID token, the access token claims (including sub) for the default OpenID Connect resource cannot be customized. The value of sub in the access token is generated and managed internally by PingOne and cannot be altered, mapped, or derived from another attribute (such as email or username). As a result, PostgreSQL must be configured to work with the sub value exactly as issued in the access token by PingOne.</p>
<figure>
<img decoding="async" src="https://percona.community/blog/2026/03/ping-create-user.png" alt="&nbsp;"></figure>

</li>
<li>
<p>Enable the user by turning the toggle &ldquo;ON&rdquo; and set a password.</p>
<figure>
<img decoding="async" src="https://percona.community/blog/2026/03/ping-enable-user.png" alt="&nbsp;"></figure>

</li>
</ol>
<h1 id="install-postgresql-18-from-packages">Install PostgreSQL 18 from Packages<a class="anchor-link" id="install-postgresql-18-from-packages"></a></h1>
<p>Since OAuth support is only available starting with PostgreSQL 18, we need a PostgreSQL server of at least this version.In the guide, we will install PostgreSQL 18 using Percona&rsquo;s official packages.</p>
<p><strong>Note:</strong></p>
<ol>
<li>
<p>Ensure that the <a href="https://docs.percona.com/percona-software-repositories/installing.html" target="_blank" rel="noopener noreferrer">percona-release</a> is already installed and configured on your system. You can refer to the official Percona documentation for setup instructions.</p>
</li>
<li>
<p>For this exercise, the steps are demonstrated on Ubuntu 24.04</p>
</li>
</ol>
<h2 id="enable-the-postgresql-18-repository">Enable the PostgreSQL 18 repository<a class="anchor-link" id="enable-the-postgresql-18-repository"></a></h2>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-0" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-0">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo percona-release enable-only ppg-18.2 release
</span></span><span class="line"><span class="cl">sudo apt update</span></span></code></pre>
</div>
</div>
</div>
<h2 id="install-postgresql-18">Install PostgreSQL 18<a class="anchor-link" id="install-postgresql-18"></a></h2>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-1" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-1">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt install -y percona-postgresql-18</span></span></code></pre>
</div>
</div>
</div>
<h2 id="install-oauth-support-for-libpq">Install OAuth Support for libpq<a class="anchor-link" id="install-oauth-support-for-libpq"></a></h2>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-2" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-2">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt install libpq-oauth</span></span></code></pre>
</div>
</div>
</div>
<h1 id="configure-postgresql-for-oauthoidc-authentication">Configure PostgreSQL for OAuth/OIDC authentication<a class="anchor-link" id="configure-postgresql-for-oauth-oidc-authentication"></a></h1>
<h2 id="install-pg_oidc_validator">Install pg_oidc_validator<a class="anchor-link" id="install-pg_oidc_validator"></a></h2>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-3" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-3">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt install percona-pg-oidc-validator18</span></span></code></pre>
</div>
</div>
</div>
<h2 id="setting-up-a-sample-use-case-for-oidc-based-access">Setting Up a Sample Use Case for OIDC-Based Access<a class="anchor-link" id="setting-up-a-sample-use-case-for-oidc-based-access"></a></h2>
<p>Imagine the following use case:</p>
<ul>
<li>A company regularly generates promotional discount codes and stores them in a table called dcode inside a database named promo</li>
<li>A new discount code is generated and added to this table every day.</li>
<li>The company wants all employees to be able to access the latest code whenever needed.</li>
<li>For simplicity in this demonstration, employees retrieve the code by connecting to the database and querying the table directly.</li>
<li>To avoid managing individual database accounts for every employee, access is not tied to separate user credentials.</li>
<li>Instead, authentication to the database is handled through the company&rsquo;s SSO system, allowing employees to connect using their existing corporate identity.</li>
</ul>
<pre class="mermaid">
---
config:
theme: neutral
---
architecture-beta
group company_network(cloud)[Company Network]
service employee(user)[Employee] in company_network
service sso(server)[Company SSO PingIdentity] in company_network
service promo_db(database)[Promo Database] in company_network
service code_gen(server)[Daily Code Generator] in company_network
code_gen:B --&gt; T:promo_db
employee:R --&gt; L:sso
sso:R --&gt; L:promo_db
</pre>
<p><strong>Let&rsquo;s connect to PostgreSQL:</strong></p>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-5" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-5">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo -u postgres psql</span></span></code></pre>
</div>
</div>
</div>
<p><strong>Create a database:</strong></p>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-6" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-6">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">CREATE</span><span class="w"> </span><span class="k">DATABASE</span><span class="w"> </span><span class="n">promo</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="err"></span><span class="k">c</span><span class="w"> </span><span class="n">promo</span></span></span></code></pre>
</div>
</div>
</div>
<p><strong>Add a table to store discount codes:</strong></p>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-7" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-7">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="n">dcode</span><span class="w"> </span><span class="p">(</span><span class="n">code</span><span class="w"> </span><span class="nb">varchar</span><span class="p">(</span><span class="mi">10</span><span class="p">),</span><span class="w"> </span><span class="n">GENERATED_AT</span><span class="w"> </span><span class="k">TIMESTAMP</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="n">now</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="n">dcode</span><span class="w"> </span><span class="p">(</span><span class="n">code</span><span class="p">)</span><span class="w"> </span><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="s1">'SAVENOW'</span><span class="p">);</span></span></span></code></pre>
</div>
</div>
</div>
<p><strong>Create the access user:</strong></p>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-8" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-8">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">CREATE</span><span class="w"> </span><span class="k">ROLE</span><span class="w"> </span><span class="n">employees</span><span class="w"> </span><span class="k">WITH</span><span class="w"> </span><span class="n">LOGIN</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">GRANT</span><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="n">dcode</span><span class="w"> </span><span class="k">TO</span><span class="w"> </span><span class="n">employees</span><span class="p">;</span></span></span></code></pre>
</div>
</div>
</div>
<h2 id="configure-oauth-access-in-postgresql">Configure OAuth access in PostgreSQL:<a class="anchor-link" id="configure-oauth-access-in-postgresql"></a></h2>
<p>In order for users to connect using OAuth and authenticate through the Ping Identity server, we need to create an identity map and add an entry for such access on PostgreSQL&rsquo;s authentication configuration file.</p>
<p>Edit the identity mapping configuration file and add an entry, which we will call <em>oidc</em>, mapping connections originated by a system user identified with a sub ID. For this exercise, we allow any string to match</p>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-9" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-9">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo vim /etc/postgresql/18/main/pg_ident.conf
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># MAPNAME SYSTEM-USERNAME DATABASE-USERNAME</span>
</span></span><span class="line"><span class="cl">oidc /^<span class="o">(</span>.*<span class="o">)</span>$ employees</span></span></code></pre>
</div>
</div>
</div>
<p>Next, edit the authentication configuration file. The configuration file <em>pg_hba.conf</em> acts as a sort of firewall for connections. With the below line, we are instructing PostgreSQL to allow all connections coming from any network that attempt to access the database promo as user employees using the new authentication method <em>oauth</em>. We are also indicating that the authentication provider is Ping Identity and the scope is <em>openid</em>.</p>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-10" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-10">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo vim /etc/postgresql/18/main/pg_hba.conf
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># TYPE DATABASE USER ADDRESS METHOD</span>
</span></span><span class="line"><span class="cl">host promo employees 0.0.0.0/0 oauth <span class="nv">issuer</span><span class="o">=</span>https://auth.pingone.com.au/64935f69-5a0a-4b69-a8bd-46967d218303/as <span class="nv">scope</span><span class="o">=</span>pgscope <span class="nv">map</span><span class="o">=</span>oidc</span></span></code></pre>
</div>
</div>
</div>
<p><strong>Note:</strong></p>
<ol>
<li>
<p>Place this entry after the existing local rules and just before the replication rules in pg_hba.conf. PostgreSQL evaluates pg_hba.conf from top to bottom, and the first matching rule is applied. Putting the OAuth rule earlier ensures that connections to database promo as user employees use OAuth instead of falling back to password authentication.</p>
</li>
<li>
<p>In production environments, restrict the IP range instead of using <em>0.0.0.0/0</em></p>
</li>
<li>
<p>The <code>oauth_issuer</code> must exactly match the <strong>Issuer ID</strong> from your PingOne environment. The Issuer URL is unique to each PingOne environment and contains your environment UUID. It typically follows this format: <code>https://auth.pingone.com.au//as</code>. Replace <code></code> with the actual value from your PingOne environment. Do not copy the placeholder value directly.</p>
</li>
</ol>
<h2 id="enabling-the-pg_oidc_validation-extension">Enabling the pg_oidc_validation extension<a class="anchor-link" id="enabling-the-pg_oidc_validation-extension"></a></h2>
<p>Edit the postgresql.conf and add below lines</p>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-11" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-11">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo vim /etc/postgresql/18/main/postgresql.conf
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">oauth_validator_libraries</span> <span class="o">=</span> <span class="s1">'pg_oidc_validator'</span></span></span></code></pre>
</div>
</div>
</div>
<p>The validator uses the <code>sub</code> claim from the access token by default. Hence, we need not explicitly set <code>pg_oidc_validator.authn_field=sub</code>. The sub claim is defined by the OpenID Connect specification as a stable and unique identifier for a user within an identity provider (IdP). It is intended to uniquely represent a user and remain consistent across authentication sessions.</p>
<p>PostgreSQL does not interpret or transform this value. The validator extracts the configured claim and PostgreSQL compares it against a database role or an entry in pg_ident.conf. If the value does not match the expected role or mapping, authentication will fail, even if the token itself is valid.</p>
<p>In this setup with Ping Identity, only the sub claim can be used for authentication with the default OpenID Connect resource.</p>
<h2 id="reload-the-configuration">Reload the configuration<a class="anchor-link" id="reload-the-configuration"></a></h2>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-12" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-12">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo -u postgres psql
</span></span><span class="line"><span class="cl">SELECT pg_reload_conf<span class="o">()</span><span class="p">;</span></span></span></code></pre>
</div>
</div>
</div>
<h2 id="monitor-the-server-logs">Monitor the server logs<a class="anchor-link" id="monitor-the-server-logs"></a></h2>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-13" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-13">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo tail -f /var/log/postgresql/postgresql-18-main.log</span></span></code></pre>
</div>
</div>
</div>
<h1 id="test-login-using-an-oidc-flow">Test login using an OIDC flow<a class="anchor-link" id="test-login-using-an-oidc-flow"></a></h1>
<p><strong>Connecting to the database:</strong></p>
<p>For this quick connection test, we use psql to connect to the promo database as the employees user, explicitly specifying the host IP along with the <strong>oauth_issuer</strong> and <strong>client_id</strong>.</p>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-14" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-14">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">psql <span class="s1">'host=127.0.0.1 user=employees dbname=promo oauth_issuer=https://auth.pingone.com.au/64935f69-5a0a-4b69-a8bd-46967d218303/as oauth_client_id=1e892f71-09d7-4ed6-a534-0dc888d39c7c'</span></span></span></code></pre>
</div>
</div>
</div>
<p>By connecting via the host IP address rather than the local socket, PostgreSQL treats this as a host-based connection, ensuring that the OAuth configuration in pg_hba.conf is applied. The authentication is handled by PostgreSQL&rsquo;s authentication framework, which uses OIDC with Ping Identity as the identity provider to validate the token.</p>
<p>We will see a prompt on the console with the URL and activation code.You will notice an activation code <strong>XXXX-XXX.</strong> Example shown below:</p>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">text</span><button class="code-block__copy" type="button" data-copy-target="codeblock-15" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-15">
<div class="highlight">
<pre class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Visit https://auth.pingone.com.au/64935f69-5a0a-4b69-a8bd-46967d218303/device and enter the code: 7KK7-88DK</span></span></code></pre>
</div>
</div>
</div>
<p>Upon clicking the URL, it will prompt you to log in with the <strong>employee&rsquo;s</strong> user, which we created during the PingOne environment setup.</p>
<figure>
<img decoding="async" src="https://percona.community/blog/2026/03/ping-user-login.png" alt="&nbsp;"></figure>

<p>Next, it will prompt you to enter the activation code.</p>
<figure>
<img decoding="async" src="https://percona.community/blog/2026/03/ping-activation-code.png" alt="&nbsp;"></figure>

<p>Approve access for the application, and that&rsquo;s it! The user has now been successfully authenticated via OIDC.</p>
<figure>
<img decoding="async" src="https://percona.community/blog/2026/03/ping-approve-user.png" alt="&nbsp;"></figure>

<p>Return to the PostgreSQL prompt and you should see that the login to the promo database is successful. You can now query the <em>dcode</em> table to fetch the discount code.</p>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-16" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-16">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">psql <span class="s1">'host=127.0.0.1 user=employees dbname=promo oauth_issuer=https://auth.pingone.com.au/64935f69-5a0a-4b69-a8bd-46967d218303/as oauth_client_id=1e892f71-09d7-4ed6-a534-0dc888d39c7c'</span>
</span></span><span class="line"><span class="cl">Visit https://auth.pingone.com.au/64935f69-5a0a-4b69-a8bd-46967d218303/device and enter the code: 7KK7-88DK
</span></span><span class="line"><span class="cl">psql <span class="o">(</span>18.2 - Percona Server <span class="k">for</span> PostgreSQL 18.2.1<span class="o">)</span>
</span></span><span class="line"><span class="cl">SSL connection <span class="o">(</span>protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off, ALPN: postgresql<span class="o">)</span>
</span></span><span class="line"><span class="cl">Type <span class="s2">"help"</span> <span class="k">for</span> help.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">promo</span><span class="o">=</span>&gt; <span class="k">select</span> * from dcode<span class="p">;</span>
</span></span><span class="line"><span class="cl"> code <span class="p">|</span> generated_at
</span></span><span class="line"><span class="cl">---------+----------------------------
</span></span><span class="line"><span class="cl"> SAVENOW <span class="p">|</span> 2026-02-13 09:40:13.109801
</span></span><span class="line"><span class="cl"><span class="o">(</span><span class="m">1</span> row<span class="o">)</span></span></span></code></pre>
</div>
</div>
</div>
<p>With the steps shown in this guide, we now have a working end-to-end setup using OIDC authentication and device flow login. From here, the same model can be extended to real-world enterprise environments with tighter network restrictions and role mapping.</p>
<p>If you run into issues while setting up pg_oidc_validator or integrating PostgreSQL with Ping Identity, check the <a href="https://forums.percona.com/" target="_blank" rel="noopener noreferrer">community forums</a> first, chances are someone in the community may already have encountered a similar issue. If not, feel free to open a <a href="https://github.com/Percona-Lab/pg_oidc_validator/issues" target="_blank" rel="noopener noreferrer">discussion</a> or raise a request for help.</p>

<p>The post <a rel="nofollow" href="https://percona.community/blog/2026/03/04/postgresql-18-oidc-authentication-with-ping-identity-using-pg_oidc_validator/">PostgreSQL 18 OIDC Authentication with Ping Identity using pg_oidc_validator</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>What Exactly Is the MySQL Ecosystem? </title>
      <link>https://www.percona.com/blog/what-exactly-is-the-mysql-ecosystem/</link>
      <pubDate>Tue, 03 Mar 2026 14:39:23 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://www.percona.com/blog/">Percona Database Performance Blog</source>
      <description><![CDATA[<p>As we set out to help the MySQL ecosystem assert greater independence from Oracle by establishing a vendor-neutral industry association, we had to confront a deceptively simple question: What exactly is the MySQL ecosystem? There are many views on this question. Some argue it should revolve strictly around the MySQL brand—meaning MariaDB would be excluded. […]</p>
<p>The post <a rel="nofollow" href="https://www.percona.com/blog/what-exactly-is-the-mysql-ecosystem/">What Exactly Is the MySQL Ecosystem? </a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" width="200" height="112" src="https://www.percona.com/blog/wp-content/uploads/2025/09/How-MySQL-writes-work-200x112.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="How MySQL writes work" style="margin-bottom: 5px;clear:both;max-width: 100%">As we set out to help the MySQL ecosystem assert greater independence from Oracle by establishing a vendor-neutral industry association, we had to confront a deceptively simple question: What exactly is the MySQL ecosystem? There are many views on this question. Some argue it should revolve strictly around the MySQL brand&mdash;meaning MariaDB would be excluded. [&hellip;]</p>

<p>The post <a rel="nofollow" href="https://www.percona.com/blog/what-exactly-is-the-mysql-ecosystem/">What Exactly Is the MySQL Ecosystem? </a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>The &#8220;bus factor&#8221; risk in MongoDB, MariaDB, Redis, MySQL, PostgreSQL, and SQLite</title>
      <link>https://programmingbrain.com/2025/03/bus-factor-risk-in-open-source-databases.html</link>
      <pubDate>Tue, 03 Mar 2026 14:01:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://programmingbrain.com/">Programming Brain</source>
      <description><![CDATA[<p>Features and performance are important when choosing databases, but so it is the “bus factor” risk</p>
<p>The post <a rel="nofollow" href="https://programmingbrain.com/2025/03/bus-factor-risk-in-open-source-databases.html">The &#8220;bus factor&#8221; risk in MongoDB, MariaDB, Redis, MySQL, PostgreSQL, and SQLite</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>Features and performance are important when choosing databases, but so it is the &ldquo;bus factor&rdquo; risk</p>

<p>The post <a rel="nofollow" href="https://programmingbrain.com/2025/03/bus-factor-risk-in-open-source-databases.html">The &#8220;bus factor&#8221; risk in MongoDB, MariaDB, Redis, MySQL, PostgreSQL, and SQLite</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>What the MariaDB Community Wants Next: A Look at Our MySQL-Compatibility Poll</title>
      <link>https://mariadb.org/what-the-mariadb-community-wants-next-a-look-at-our-mysql-compatibility-poll/</link>
      <pubDate>Tue, 03 Mar 2026 13:39:31 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>We recently asked the community:<br />
“Which MySQL-compatibility feature would you most like to see in the next MariaDB release?”<br />
A big thank you to everyone who voted. …<br />
Continue reading \"What the MariaDB Community Wants Next: A Look at Our MySQL-Compatibility Poll\"<br />
The post What the MariaDB Community Wants Next: A Look at Our MySQL-Compatibility Poll appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/what-the-mariadb-community-wants-next-a-look-at-our-mysql-compatibility-poll/">What the MariaDB Community Wants Next: A Look at Our MySQL-Compatibility Poll</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>We recently asked the community:<br>
<a href="https://mariadb.org/poll/previous/mysql-compatibility-feature/">&ldquo;Which MySQL-compatibility feature would you most like to see in the next MariaDB release?&rdquo;</a><br>
A big thank you to everyone who voted. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/what-the-mariadb-community-wants-next-a-look-at-our-mysql-compatibility-poll/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;What the MariaDB Community Wants Next: A Look at Our MySQL-Compatibility Poll&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/what-the-mariadb-community-wants-next-a-look-at-our-mysql-compatibility-poll/">What the MariaDB Community Wants Next: A Look at Our MySQL-Compatibility Poll</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/what-the-mariadb-community-wants-next-a-look-at-our-mysql-compatibility-poll/">What the MariaDB Community Wants Next: A Look at Our MySQL-Compatibility Poll</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Galera, continuity, and responsibility: how the Foundation and MariaDB plc move forward</title>
      <link>https://mariadb.org/galera-continuity-and-responsibility-how-the-foundation-and-mariadb-plc-move-forward/</link>
      <pubDate>Mon, 02 Mar 2026 21:23:42 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>Introduction<br />
Over the past weeks, questions around Galera, high availability, and continuity have generated understandable concern within parts of the MariaDB community.<br />
Clarity matters in moments like this.That is also why this response was unfortunately not immediate. …<br />
Continue reading \"Galera, continuity, and responsibility: how the Foundation and MariaDB plc move forward\"<br />
The post Galera, continuity, and responsibility: how the Foundation and MariaDB plc move forward appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/galera-continuity-and-responsibility-how-the-foundation-and-mariadb-plc-move-forward/">Galera, continuity, and responsibility: how the Foundation and MariaDB plc move forward</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>Introduction<a id="introduction"></a><br>
Over the past weeks, questions around Galera, high availability, and continuity have generated understandable concern within parts of the MariaDB community.<br>
Clarity matters in moments like this.That is also why this response was unfortunately not immediate. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/galera-continuity-and-responsibility-how-the-foundation-and-mariadb-plc-move-forward/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;Galera, continuity, and responsibility: how the Foundation and MariaDB plc move forward&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/galera-continuity-and-responsibility-how-the-foundation-and-mariadb-plc-move-forward/">Galera, continuity, and responsibility: how the Foundation and MariaDB plc move forward</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/galera-continuity-and-responsibility-how-the-foundation-and-mariadb-plc-move-forward/">Galera, continuity, and responsibility: how the Foundation and MariaDB plc move forward</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Hardening MySQL: Practical Security Strategies for DBAs</title>
      <link>https://percona.community/blog/2026/03/02/hardening-mysql-practical-security-strategies-for-dbas/</link>
      <pubDate>Mon, 02 Mar 2026 00:00:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://percona.community/blog/">Percona Community Blog - learn about MySQL, MariaDB, PostgreSQL, and MongoDB</source>
      <description><![CDATA[<p>MySQL Security Best Practices: A Practical Guide for Locking Down Your Database Introduction MySQL runs just about everywhere. I’ve seen it behind small personal projects, internal tools, SaaS platforms, and large enterprise systems handling serious transaction volume. When your database sits at the center of everything, it becomes part of your security perimeter whether you planned it that way or not. And that makes it a target.</p>
<p>The post <a rel="nofollow" href="https://percona.community/blog/2026/03/02/hardening-mysql-practical-security-strategies-for-dbas/">Hardening MySQL: Practical Security Strategies for DBAs</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<h1 id="mysql-security-best-practices-a-practical-guide-for-locking-down-your-database">MySQL Security Best Practices: A Practical Guide for Locking Down Your Database<a class="anchor-link" id="mysql-security-best-practices-a-practical-guide-for-locking-down-your-database"></a></h1>
<h2 id="introduction">Introduction<a class="anchor-link" id="introduction"></a></h2>
<p>MySQL runs just about everywhere. I&rsquo;ve seen it behind small personal projects, internal tools, SaaS platforms, and large enterprise systems handling serious transaction volume. When your database sits at the center of everything, it becomes part of your security perimeter whether you planned it that way or not. And that makes it a target.</p>
<p>Securing MySQL isn&rsquo;t about flipping one magical setting and calling it done. It&rsquo;s about layers. Tight access control. Encrypted connections. Clear visibility into what&rsquo;s happening on the server. And operational discipline that doesn&rsquo;t drift over time.</p>
<p>In this guide, I&rsquo;m going to walk through practical MySQL security best practices that you can apply right away. These are the kinds of checks and hardening steps that reduce real risk in real environments, and help build a database platform that stays resilient under pressure.</p>
<hr>
<h2 id="1-principle-of-least-privilege">1. Principle of Least Privilege<a class="anchor-link" id="1-principle-of-least-privilege"></a></h2>
<p>One of the most common security mistakes is over-granting privileges.<br>
Applications and users should have only the permissions they absolutely<br>
need.</p>
<h3 id="bad-practice">Bad Practice<a class="anchor-link" id="bad-practice"></a></h3>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-0" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-0">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">GRANT</span><span class="w"> </span><span class="k">ALL</span><span class="w"> </span><span class="k">PRIVILEGES</span><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="o">*</span><span class="p">.</span><span class="o">*</span><span class="w"> </span><span class="k">TO</span><span class="w"> </span><span class="s1">'appuser'</span><span class="o">@</span><span class="s1">'10.%'</span><span class="p">;</span></span></span></code></pre>
</div>
</div>
</div>
<h3 id="better-approach">Better Approach<a class="anchor-link" id="better-approach"></a></h3>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-1" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-1">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">GRANT</span><span class="w"> </span><span class="k">SELECT</span><span class="p">,</span><span class="w"> </span><span class="k">INSERT</span><span class="p">,</span><span class="w"> </span><span class="k">UPDATE</span><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="n">appdb</span><span class="p">.</span><span class="o">*</span><span class="w"> </span><span class="k">TO</span><span class="w"> </span><span class="s1">'appuser'</span><span class="o">@</span><span class="s1">'10.%'</span><span class="p">;</span></span></span></code></pre>
</div>
</div>
</div>
<h3 id="recommendations">Recommendations<a class="anchor-link" id="recommendations"></a></h3>
<ul>
<li>Avoid global privileges unless absolutely required</li>
<li>Restrict users by host whenever possible</li>
<li>Separate admin accounts from application accounts</li>
<li>Use different credentials for read-only vs write operations</li>
</ul>
<h3 id="audit-existing-privileges">Audit Existing Privileges<a class="anchor-link" id="audit-existing-privileges"></a></h3>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-2" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-2">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="k">user</span><span class="p">,</span><span class="w"> </span><span class="k">host</span><span class="p">,</span><span class="w"> </span><span class="n">Select_priv</span><span class="p">,</span><span class="w"> </span><span class="n">Insert_priv</span><span class="p">,</span><span class="w"> </span><span class="n">Update_priv</span><span class="p">,</span><span class="w"> </span><span class="n">Delete_priv</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">FROM</span><span class="w"> </span><span class="n">mysql</span><span class="p">.</span><span class="k">user</span><span class="p">;</span></span></span></code></pre>
</div>
</div>
</div>
<hr>
<h2 id="2-strong-authentication--password-policies">2. Strong Authentication &amp; Password Policies<a class="anchor-link" id="2-strong-authentication-password-policies"></a></h2>
<p>Weak credentials remain one of the easiest attack vectors.</p>
<h3 id="enable-password-validation">Enable Password Validation<a class="anchor-link" id="enable-password-validation"></a></h3>
<p>component_validate_password is MySQL&rsquo;s modern password policy engine. Think of it as a gatekeeper for credential quality. Every time someone tries to set or change a password, it checks whether that password meets your defined security standards before letting it in.</p>
<p>It replaces the older validate_password plugin with a component-based architecture that is more flexible and better aligned with MySQL 8.x design.</p>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-3" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-3">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="n">INSTALL</span><span class="w"> </span><span class="n">COMPONENT</span><span class="w"> </span><span class="s1">'file://component_validate_password'</span><span class="p">;</span></span></span></code></pre>
</div>
</div>
</div>
<h3 id="what-it-does">What It Does<a class="anchor-link" id="what-it-does"></a></h3>
<p>When enabled, it enforces rules such as:</p>
<ul>
<li>Minimum password length</li>
<li>Required mix of character types</li>
<li>Dictionary file checks</li>
<li>Strength scoring</li>
</ul>
<p>If a password fails policy, the statement is rejected before the credential is stored.</p>
<h3 id="why-it-matters">Why It Matters<a class="anchor-link" id="why-it-matters"></a></h3>
<p>Weak passwords remain one of the most common entry points in database breaches. This component reduces risk by enforcing baseline credential hygiene automatically, instead of relying on developer discipline.</p>
<h3 id="recommended-policies">Recommended Policies<a class="anchor-link" id="recommended-policies"></a></h3>
<ul>
<li>Minimum length: 14+ characters</li>
<li>Require mixed case, numbers, and symbols</li>
<li>Enable dictionary checks</li>
<li>Enable username checks</li>
</ul>
<h3 id="remove-anonymous-accounts">Remove Anonymous Accounts<a class="anchor-link" id="remove-anonymous-accounts"></a></h3>
<h4 id="find-anonymous-users">Find Anonymous Users</h4>
<p>Anonymous users have an empty User field.</p>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-4" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-4">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="k">user</span><span class="p">,</span><span class="w"> </span><span class="k">host</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">mysql</span><span class="p">.</span><span class="k">user</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="k">user</span><span class="o">=</span><span class="s1">''</span><span class="p">;</span></span></span></code></pre>
</div>
</div>
</div>
<p>If you see rows returned, those are anonymous accounts.</p>
<h3 id="drop-anonymous-users">Drop Anonymous Users<a class="anchor-link" id="drop-anonymous-users"></a></h3>
<p>In modern MySQL versions:</p>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-5" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-5">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">DROP</span><span class="w"> </span><span class="k">USER</span><span class="w"> </span><span class="s1">''</span><span class="o">@</span><span class="s1">'localhost'</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">DROP</span><span class="w"> </span><span class="k">USER</span><span class="w"> </span><span class="s1">''</span><span class="o">@</span><span class="s1">'%'</span><span class="p">;</span></span></span></code></pre>
</div>
</div>
</div>
<p>Adjust the Host value based on what your query returned.</p>
<h3 id="why-this-matters">Why This Matters<a class="anchor-link" id="why-this-matters"></a></h3>
<p>Anonymous users:</p>
<ul>
<li>Allow login without credentials</li>
<li>May have default privileges in some distributions</li>
<li>Increase the attack surface unnecessarily</li>
</ul>
<p>In hardened environments, there should be zero accounts with an empty username. Every identity should be explicit, accountable, and least-privileged.</p>
<h2 id="3-encryption-everywhere">3. Encryption Everywhere<a class="anchor-link" id="3-encryption-everywhere"></a></h2>
<p>Encryption protects data both in transit and at rest.</p>
<h3 id="enable-transparent-data-encryption-tde">Enable Transparent Data Encryption (TDE)<a class="anchor-link" id="enable-transparent-data-encryption-tde"></a></h3>
<p>See my January 13 post for a deep dive into Transparent Data Encryption:<br>
<a href="https://percona.community/blog/2026/01/13/configuring-the-component-keyring-in-percona-server-and-pxc-8.4/" target="_blank" rel="noopener noreferrer">Configuring the Component Keyring in Percona Server and PXC 8.4</a></p>
<h3 id="enable-tls-for-connections">Enable TLS for Connections<a class="anchor-link" id="enable-tls-for-connections"></a></h3>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-6" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-6">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="n">require_secure_transport</span><span class="o">=</span><span class="k">ON</span></span></span></code></pre>
</div>
</div>
</div>
<h3 id="verify-ssl-usage">Verify SSL Usage<a class="anchor-link" id="verify-ssl-usage"></a></h3>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-7" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-7">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SHOW</span><span class="w"> </span><span class="n">STATUS</span><span class="w"> </span><span class="k">LIKE</span><span class="w"> </span><span class="s1">'Ssl_cipher'</span><span class="p">;</span></span></span></code></pre>
</div>
</div>
</div>
<h3 id="encryption-areas-to-consider">Encryption Areas to Consider<a class="anchor-link" id="encryption-areas-to-consider"></a></h3>
<ul>
<li>Client-server connections</li>
<li>Replication channels</li>
<li>Backups and snapshot storage</li>
<li>Disk-level encryption</li>
</ul>
<h2 id="4-patch-management--version-hygiene">4. Patch Management &amp; Version Hygiene<a class="anchor-link" id="4-patch-management-version-hygiene"></a></h2>
<p>Running outdated MySQL versions is equivalent to leaving known<br>
vulnerabilities exposed.</p>
<h3 id="maintenance-strategy">Maintenance Strategy<a class="anchor-link" id="maintenance-strategy"></a></h3>
<ul>
<li>Track vendor security advisories</li>
<li>Apply minor updates regularly</li>
<li>Test patches in staging before production rollout</li>
<li>Avoid unsupported MySQL versions</li>
</ul>
<h3 id="check-version">Check Version<a class="anchor-link" id="check-version"></a></h3>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-8" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-8">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="k">VERSION</span><span class="p">();</span></span></span></code></pre>
</div>
</div>
</div>
<h2 id="5-logging-auditing-and-monitoring">5. Logging, Auditing, and Monitoring<a class="anchor-link" id="5-logging-auditing-and-monitoring"></a></h2>
<p>Security without visibility is blind defense, enable Audit Logging.</p>
<h3 id="1-audit_log-plugin-legacy-model">1. audit_log Plugin (Legacy Model)<a class="anchor-link" id="1-audit_log-plugin-legacy-model"></a></h3>
<h4 id="installation">Installation</h4>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-9" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-9">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="n">INSTALL</span><span class="w"> </span><span class="n">PLUGIN</span><span class="w"> </span><span class="n">audit_log</span><span class="w"> </span><span class="n">SONAME</span><span class="w"> </span><span class="s1">'audit_log.so'</span><span class="p">;</span></span></span></code></pre>
</div>
</div>
</div>
<h4 id="verify">Verify</h4>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-10" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-10">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SHOW</span><span class="w"> </span><span class="n">PLUGINS</span><span class="w"> </span><span class="k">LIKE</span><span class="w"> </span><span class="s1">'audit%'</span><span class="p">;</span></span></span></code></pre>
</div>
</div>
</div>
<h3 id="2-audit_log_filter-component-modern-model">2. audit_log_filter Component (Modern Model)<a class="anchor-link" id="2-audit_log_filter-component-modern-model"></a></h3>
<p>Introduced in MySQL 8 to provide a more flexible and granular alternative to the older plugin model.</p>
<h4 id="installation-1">Installation</h4>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-11" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-11">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="n">INSTALL</span><span class="w"> </span><span class="n">COMPONENT</span><span class="w"> </span><span class="s1">'file://component_audit_log_filter'</span><span class="p">;</span></span></span></code></pre>
</div>
</div>
</div>
<h4 id="verify-1">Verify</h4>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-12" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-12">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">mysql</span><span class="p">.</span><span class="n">component</span><span class="p">;</span></span></span></code></pre>
</div>
</div>
</div>
<h4 id="architecture-difference">Architecture Difference</h4>
<p>Instead of a single global policy, you create:</p>
<ul>
<li>Filters (define what to log)</li>
<li>Users assigned to filters</li>
</ul>
<p>It&rsquo;s granular and rule-driven.</p>
<h3 id="auditing-key-events">Auditing Key Events<a class="anchor-link" id="auditing-key-events"></a></h3>
<ul>
<li>Failed logins</li>
<li>Privilege changes</li>
<li>Schema modifications</li>
<li>Unusual query activity</li>
</ul>
<h3 id="references">References:<a class="anchor-link" id="references"></a></h3>
<ol>
<li><a href="https://percona.community/blog/2025/09/18/audit-log-filter-component/" target="_blank" rel="noopener noreferrer">Audit Log Filter Component<br>
</a></li>
<li><a href="https://percona.community/blog/2025/10/08/audit-log-filters-part-ii/" target="_blank" rel="noopener noreferrer">Audit Log Filters Part II<br>
</a></li>
</ol>
<h3 id="useful-metrics">Useful Metrics<a class="anchor-link" id="useful-metrics"></a></h3>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-13" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-13">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SHOW</span><span class="w"> </span><span class="k">GLOBAL</span><span class="w"> </span><span class="n">STATUS</span><span class="w"> </span><span class="k">LIKE</span><span class="w"> </span><span class="s1">'Aborted_connects'</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">SHOW</span><span class="w"> </span><span class="k">GLOBAL</span><span class="w"> </span><span class="n">STATUS</span><span class="w"> </span><span class="k">LIKE</span><span class="w"> </span><span class="s1">'Connections'</span><span class="p">;</span></span></span></code></pre>
</div>
</div>
</div>
<h2 id="6-secure-configuration-hardening">6. Secure Configuration Hardening<a class="anchor-link" id="6-secure-configuration-hardening"></a></h2>
<p>A secure baseline configuration reduces risk from common attack<br>
patterns.</p>
<h3 id="recommended-settings">Recommended Settings<a class="anchor-link" id="recommended-settings"></a></h3>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">ini</span><button class="code-block__copy" type="button" data-copy-target="codeblock-14" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-14">
<div class="highlight">
<pre class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="na">local_infile</span><span class="o">=</span><span class="s">OFF</span>
</span></span><span class="line"><span class="cl"><span class="na">secure_file_priv</span><span class="o">=</span><span class="s">/var/lib/mysql-files</span>
</span></span><span class="line"><span class="cl"><span class="na">sql_mode</span><span class="o">=</span><span class="s">"STRICT_ALL_TABLES"</span>
</span></span><span class="line"><span class="cl"><span class="na">secure-log-path</span><span class="o">=</span><span class="s">/var/log/mysql</span></span></span></code></pre>
</div>
</div>
</div>
<h3 id="why-these-matter">Why These Matter<a class="anchor-link" id="why-these-matter"></a></h3>
<ul>
<li>Prevent arbitrary file imports</li>
<li>Reduce filesystem abuse</li>
<li>Restrict data export/import locations</li>
</ul>
<h2 id="7-backup-security">7. Backup Security<a class="anchor-link" id="7-backup-security"></a></h2>
<p>Backups often contain everything an attacker wants.</p>
<h3 id="backup-best-practices">Backup Best Practices<a class="anchor-link" id="backup-best-practices"></a></h3>
<ul>
<li>Encrypt backups</li>
<li>Restrict filesystem permissions</li>
<li>Store offsite copies securely</li>
<li>Rotate backup credentials</li>
<li>Verify restore procedures regularly</li>
</ul>
<h3 id="example-permission-check">Example Permission Check<a class="anchor-link" id="example-permission-check"></a></h3>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">bash</span><button class="code-block__copy" type="button" data-copy-target="codeblock-15" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-15">
<div class="highlight">
<pre class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ls -l /backup/mysql</span></span></code></pre>
</div>
</div>
</div>
<h2 id="8-replication--cluster-security">8. Replication &amp; Cluster Security<a class="anchor-link" id="8-replication-cluster-security"></a></h2>
<p>Replication is not just a data distribution feature. It is a persistent, privileged communication channel between servers. If misconfigured, it can become a lateral movement pathway inside your infrastructure. Treat every replication link as a trusted but tightly controlled corridor.</p>
<p>Principle: Replication Is a Privileged Service Account</p>
<p>Replication users require elevated capabilities. They must be isolated, tightly scoped, and monitored like any other service identity.</p>
<h3 id="secure-replication-users">Secure Replication Users<a class="anchor-link" id="secure-replication-users"></a></h3>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-16" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-16">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">CREATE</span><span class="w"> </span><span class="k">USER</span><span class="w"> </span><span class="s1">'repl'</span><span class="o">@</span><span class="s1">'10.%'</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">IDENTIFIED</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="s1">'strongpassword'</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">REQUIRE</span><span class="w"> </span><span class="n">SSL</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">GRANT</span><span class="w"> </span><span class="n">REPLICATION</span><span class="w"> </span><span class="n">REPLICA</span><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="o">*</span><span class="p">.</span><span class="o">*</span><span class="w"> </span><span class="k">TO</span><span class="w"> </span><span class="s1">'repl'</span><span class="o">@</span><span class="s1">'10.%'</span><span class="p">;</span></span></span></code></pre>
</div>
</div>
</div>
<p>Hardening considerations:</p>
<ul>
<li>Restrict host patterns as narrowly as possible. Avoid % whenever feasible.</li>
<li>Require SSL or X.509 certificate authentication.</li>
<li>Enforce strong password policies or use a secrets manager.</li>
<li>Disable interactive login capability if applicable.</li>
</ul>
<h3 id="encrypt-replication-traffic">Encrypt Replication Traffic<a class="anchor-link" id="encrypt-replication-traffic"></a></h3>
<p>Replication traffic may include sensitive row data, DDL statements, and metadata. Always encrypt it.</p>
<p>At minimum:</p>
<ul>
<li>Enable require_secure_transport=ON</li>
<li>Configure TLS certificates on source and replica</li>
<li>Set replication channel to use SSL:</li>
</ul>
<div class="code-block">
<div class="code-block__header"><span class="code-block__lang">sql</span><button class="code-block__copy" type="button" data-copy-target="codeblock-17" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-17">
<div class="highlight">
<pre class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="n">CHANGE</span><span class="w"> </span><span class="n">REPLICATION</span><span class="w"> </span><span class="k">SOURCE</span><span class="w"> </span><span class="k">TO</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">SOURCE_SSL</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">SOURCE_SSL_CA</span><span class="o">=</span><span class="s1">'/path/ca.pem'</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">SOURCE_SSL_CERT</span><span class="o">=</span><span class="s1">'/path/client-cert.pem'</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">SOURCE_SSL_KEY</span><span class="o">=</span><span class="s1">'/path/client-key.pem'</span><span class="p">;</span></span></span></code></pre>
</div>
</div>
</div>
<p>For MySQL Group Replication or InnoDB Cluster:</p>
<ul>
<li>Enable group communication SSL</li>
<li>Validate certificate identity</li>
<li>Use dedicated replication networks</li>
</ul>
<h3 id="binary-log-and-relay-log-protection">Binary Log and Relay Log Protection<a class="anchor-link" id="binary-log-and-relay-log-protection"></a></h3>
<p>Replication relies on binary logs. Protect them.</p>
<ul>
<li>Set binlog_encryption=ON</li>
<li>Set relay_log_info_repository=TABLE</li>
<li>Restrict filesystem access to log directories</li>
<li>Monitor log retention policies</li>
</ul>
<p>Compromised binary logs can reveal historical data changes.</p>
<h2 id="9-continuous-security-reviews">9. Continuous Security Reviews<a class="anchor-link" id="9-continuous-security-reviews"></a></h2>
<p>Security is not a one-time checklist. Regular audits help catch<br>
configuration drift and evolving threats.</p>
<h3 id="suggested-review-cadence">Suggested Review Cadence<a class="anchor-link" id="suggested-review-cadence"></a></h3>
<ul>
<li>Weekly: failed login review</li>
<li>Monthly: privilege audits</li>
<li>Quarterly: configuration review</li>
<li>Semiannually: full security assessment</li>
</ul>
<h2 id="security-checklist-summary">Security Checklist Summary<a class="anchor-link" id="security-checklist-summary"></a></h2>
<table>
<thead>
<tr>
<th>Area</th>
<th>Key Action</th>
</tr>
</thead>
<tbody>
<tr>
<td>Access Control</td>
<td>Least privilege grants</td>
</tr>
<tr>
<td>Authentication</td>
<td>Strong password policies</td>
</tr>
<tr>
<td>Encryption</td>
<td>TLS + encrypted storage</td>
</tr>
<tr>
<td>Updates</td>
<td>Regular patching</td>
</tr>
<tr>
<td>Monitoring</td>
<td>Audit logging enabled</td>
</tr>
<tr>
<td>Configuration</td>
<td>Harden defaults</td>
</tr>
<tr>
<td>Backups</td>
<td>Encrypt and protect</td>
</tr>
<tr>
<td>Replication</td>
<td>Secure replication users</td>
</tr>
</tbody>
</table>
<h2 id="final-thoughts">Final Thoughts<a class="anchor-link" id="final-thoughts"></a></h2>
<p>Strong MySQL security doesn&rsquo;t come from one feature or one tool. It comes from layers working together. Hardened configuration. Tight, intentional privilege design. Encryption everywhere it makes sense. And monitoring that actually gets reviewed instead of just written to disk.</p>
<p>In my experience, the strongest environments aren&rsquo;t the ones trying to be unbreakable. They&rsquo;re the ones built to detect, contain, and respond. Every layer should either reduce blast radius or increase visibility. If an attacker gets through one control, the next one slows them down. And while they&rsquo;re slowing down, your logging and monitoring should already be telling you something isn&rsquo;t right.</p>
<p>That&rsquo;s what a mature security posture looks like in practice.</p>

<p>The post <a rel="nofollow" href="https://percona.community/blog/2026/03/02/hardening-mysql-practical-security-strategies-for-dbas/">Hardening MySQL: Practical Security Strategies for DBAs</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>MariaDB Community Server 12.3 Will Include Galera Cluster</title>
      <link>https://mariadb.com/resources/blog/mariadb-community-server-12-3-will-include-galera-cluster/</link>
      <pubDate>Fri, 27 Feb 2026 15:21:28 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.com/">MariaDB</source>
      <description><![CDATA[<p>Community feedback is an important part of MariaDB, and recently, you made your voices heard regarding the inclusion of Galera […]</p>
<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/mariadb-community-server-12-3-will-include-galera-cluster/">MariaDB Community Server 12.3 Will Include Galera Cluster</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>Community feedback is an important part of MariaDB, and recently, you made your voices heard regarding the inclusion of Galera Cluster in the 12.3 series. We want to extend a sincere thank you to everyone who reached out &ndash; especially Fr&eacute;d&eacute;ric (lefred) Descamps and Ren&eacute; Bonvanie, whose impassioned perspectives were instrumental in our review. We&rsquo;ve thoroughly considered your feedback and&hellip;</p>
<p><a href="https://mariadb.com/resources/blog/mariadb-community-server-12-3-will-include-galera-cluster/" rel="nofollow">Source</a></p>

<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/mariadb-community-server-12-3-will-include-galera-cluster/">MariaDB Community Server 12.3 Will Include Galera Cluster</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>SQLSTATE [HY000] [2006] Galera has gone away</title>
      <link>https://medium.com/@arbaudie.it/sqlstate-hy000-2006-galera-has-gone-away-656f3b40ef8c?source=rss-c779d007e7fe------2</link>
      <pubDate>Fri, 27 Feb 2026 13:28:55 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://medium.com/@arbaudie.it?source=rss-c779d007e7fe------2">Stories by ArBauDie.IT on Medium</source>
      <description><![CDATA[<p>TL:DR : i find this move detrimental to MariaDB as a wholeMariaDB plc’s decision to pull Galera, its synchronous multi-master replication solution, out of the open-source ecosystem in MariaDB 12.3 LTS has been quietly shaking the extended MariaDB community lately.Galera has been a critical component for high-availability MariaDB deployments, enabling robust scaling and fault tolerance. But with MariaDB’s acquisition of Codership and the sudden shift of Galera to a commercial license, the message to the community is clear: the rules have changed. For organizations that migrated from MySQL to MariaDB in search of stability, openness, and innovation, this move raises serious questions.MariaDB plc frames this decision as a necessary step to accelerate innovation and deliver greater value to customers. By integrating Galera directly into its Enterprise Platform, the company aims to streamline development, reduce feature delays, and offer a more cohesive high-availability solution. This aligns with MariaDB’s broader push to strengthen its enterprise offerings, particularly following its transition to private ownership and the arrival of a new CEO focused on profitability.However, the acquisition also serves a more strategic purpose: preventing future forks of Galera by third parties. By owning the codebase, MariaDB now controls the technology’s evolution and distribution, effectively eliminating competition from community-driven alternatives. While this may secure revenue streams, it risks alienating the very community that has been MariaDB’s strongest advocate.The parallels with HashiCorp’s controversial shift to a Business Source License for Vault are striking. Both companies have taken widely used open-source tools and placed them behind commercial barriers. Yet while HashiCorp provided clear migration paths, MariaDB’s approach feels abrupt.Also the timing particularly damaging. MariaDB Foundation is making pushes to present MariaDB as the natural continuation of MySQL amidst growing concerns about Oracle’s neglect of MySQL. And in parallel we have MariaDB plc behaving just like Oracle with a very popular piece of tech. This comes in direct conflict with the Foundation claims giving MariaDB’s detractors just the perfect ammunition to kill the narrative even before it can spread out.Beside this already disastrous timing, it can only erode the trust in MariaDB’s future. To put it bluntly, stating that open source does not equate to “free for everyone in perpetuity” as the CEO of MariaDB plc did during a MariaDB Foudnation board meeting is mostly telling about an absence of commitment to openness. Furthermore this argument used to explain close-sourcing Galera and Maxscale could very well be also applied the server itself. C*O won’t like this potential for instability. I dont like it either. And i really think this will drive many potential and actual clients away from MariaDB. It’s litteraly a godsend for the likes of Percona, VillageSQL and mostly PostgreSQL. As if the latter needed any help in this very moment …One a personal level, it seems i underestimated the greed of the corporation when i expressed myself about the Codership acquisition. Maybe i was too naïve indeed despite having a usually rather cynical nature when it comes down to business.Now the question is : how will the Foundation handle this ? We have elements of answers in the minutes of their board meeting 1/2026. I totally concur with option 2 as well. Will it take the form of a formal community-driven fork of galera 4 ? Would it take a port of Galera to VillageSQL ? Will it be Percona driven ? I can’t tell yet, but actions will speak louder than words as always.</p>
<p>The post <a rel="nofollow" href="https://medium.com/@arbaudie.it/sqlstate-hy000-2006-galera-has-gone-away-656f3b40ef8c?source=rss-c779d007e7fe------2">SQLSTATE [HY000] [2006] Galera has gone away</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>TL:DR&nbsp;: i find this move detrimental to MariaDB as a&nbsp;whole</p>
<p>MariaDB plc&rsquo;s decision to pull Galera, its synchronous multi-master replication solution, <a href="https://jira.mariadb.org/browse/MDEV-38744">out of the open-source ecosystem in MariaDB 12.3 LTS</a> has been <a href="https://www.linkedin.com/posts/federicorazzoli_dear-mariadb-foundation-weve-been-friends-share-7432242107899310081-chm1?utm_source=share&amp;utm_medium=member_desktop&amp;rcm=ACoAAAGaVBsBb-oIeGyeZBxLG0008Lo2noBE7bk">quietly shaking the extended MariaDB community</a> lately.</p>
<p>Galera has been a critical component for high-availability MariaDB deployments, enabling robust scaling and fault tolerance. But with MariaDB&rsquo;s acquisition of Codership and the sudden shift of Galera to a commercial license, the message to the community is clear: the rules have changed. For organizations that migrated from MySQL to MariaDB in search of stability, openness, and innovation, this move raises serious questions.</p>
<p>MariaDB plc frames this decision as a necessary step to accelerate innovation and deliver greater value to customers. By integrating Galera directly into its Enterprise Platform, the company aims to streamline development, reduce feature delays, and offer a more cohesive high-availability solution. This aligns with MariaDB&rsquo;s broader push to strengthen its enterprise offerings, particularly following its transition to private ownership and the arrival of a new CEO focused on profitability.</p>
<p>However, the acquisition also serves a more strategic purpose: preventing future forks of Galera by third parties. By owning the codebase, MariaDB now controls the technology&rsquo;s evolution and distribution, effectively eliminating competition from community-driven alternatives. While this may secure revenue streams, it risks alienating the very community that has been MariaDB&rsquo;s strongest advocate.</p>
<p>The parallels with HashiCorp&rsquo;s controversial shift to a Business Source License for Vault are striking. Both companies have taken widely used open-source tools and placed them behind commercial barriers. Yet while HashiCorp provided clear migration paths, MariaDB&rsquo;s approach feels&nbsp;abrupt.</p>
<p>Also the timing particularly damaging. MariaDB Foundation is making pushes to present <a href="https://mariadb.org/is-mariadb-part-of-the-mysql-ecosystem/">MariaDB as the natural continuation of MySQL</a> amidst <a href="https://www.infoworld.com/article/4134394/community-push-intensifies-to-free-mysql-from-oracles-control-amid-stagnation-fears.html">growing concerns about Oracle&rsquo;s neglect of MySQL</a>. And in parallel we have MariaDB plc behaving just like Oracle with a very popular piece of tech. This comes in direct conflict with the Foundation claims giving MariaDB&rsquo;s detractors just the perfect ammunition to kill the narrative even before it can spread out.<br>Beside this already disastrous timing, it can only erode the trust in MariaDB&rsquo;s future. To put it bluntly, stating that open source does not equate to &ldquo;free for everyone in perpetuity&rdquo; as the CEO of MariaDB plc did during a MariaDB Foudnation board meeting is mostly telling about an absence of commitment to openness. Furthermore this argument used to explain close-sourcing Galera and Maxscale could very well be also applied the server itself. C*O won&rsquo;t like this potential for instability. I dont like it either. And i really think this will drive many potential and actual clients away from MariaDB. It&rsquo;s litteraly a godsend for the likes of Percona, VillageSQL and mostly PostgreSQL. As if the latter needed any help in this very moment&nbsp;&hellip;</p>
<p>One a personal level, it seems i underestimated the greed of the corporation when <a href="https://medium.com/@arbaudie.it/personal-opinion-the-future-of-galera-cluster-13827b522387">i expressed myself about the Codership acquisition</a>. Maybe i was too na&iuml;ve indeed despite having a usually rather cynical nature when it comes down to business.</p>
<p>Now the question is&nbsp;: how will the Foundation handle this&nbsp;? We have elements of answers in <a href="https://mariadb.org/bodminutes/2026-02-25/#4-decision-mariadb-foundations-stance-about-mariadb-plcs-galera-sunset-decision">the minutes of their board meeting 1/2026</a>. I totally concur with option 2 as well. Will it take the form of a formal community-driven fork of galera 4&nbsp;? Would it take a port of Galera to VillageSQL&nbsp;? Will it be Percona driven&nbsp;? I can&rsquo;t tell yet, but actions will speak louder than words as&nbsp;always.</p>
<p><img decoding="async" loading="lazy" src="https://medium.com/_/stat?event=post.clientViewed&amp;referrerSource=full_rss&amp;postId=656f3b40ef8c" width="1" height="1" alt=""></p>

<p>The post <a rel="nofollow" href="https://medium.com/@arbaudie.it/sqlstate-hy000-2006-galera-has-gone-away-656f3b40ef8c?source=rss-c779d007e7fe------2">SQLSTATE [HY000] [2006] Galera has gone away</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Operational guide to migrating to ClickHouse</title>
      <link>https://severalnines.com/blog/operational-guide-to-migrating-to-clickhouse/</link>
      <pubDate>Fri, 27 Feb 2026 08:00:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://severalnines.com/">Severalnines</source>
      <description><![CDATA[<p>Organizations frequently migrate to ClickHouse when they determine that their existing databases or analytics stacks are inadequate for accommodating escalating analytical requirements. This transition is predicated on the fact that ClickHouse is a purpose-built OLAP engine specifically engineered to meet such demands. In this post, I will outline the operational blueprint for migrating high-volume analytical […]<br />
The post Operational guide to migrating to ClickHouse appeared first on Severalnines.</p>
<p>The post <a rel="nofollow" href="https://severalnines.com/blog/operational-guide-to-migrating-to-clickhouse/">Operational guide to migrating to ClickHouse</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>Organizations frequently migrate to ClickHouse when they determine that their existing databases or analytics stacks are inadequate for accommodating escalating analytical requirements. This transition is predicated on the fact that ClickHouse is a purpose-built OLAP engine specifically engineered to meet such demands.</p>
<p>In this post, I will outline the operational blueprint for migrating high-volume analytical workloads from legacy database systems (like MySQL) to ClickHouse. The guide covers the critical steps from pre-migration assessment and schema redesign to execution, validation, and establishing post-migration operational excellence to achieve a stable and superior analytical environment capable of sub-second query execution on massive datasets.</p>
<h2 class="wp-block-heading" id="h-common-circumstances-teams-who-migrate-to-clickhouse-experience"><strong>Common circumstances teams who migrate to ClickHouse</strong> <strong>experience</strong><a class="anchor-link" id="common-circumstances-teams-who-migrate-to-clickhouse-experience"></a></h2>
<ul class="wp-block-list">
<li>When <strong>dashboards and queries become too slow</strong> (seconds to minutes on tens or hundreds of millions of rows), blocking product requirements or user experience.</li>
<li>When <strong>data volume and concurrency explode</strong> (billions to trillions of rows, petabyte&#8209;scale logs, high QPS) and existing systems like Postgres or Elasticsearch can&rsquo;t scale efficiently.&nbsp;</li>
<li>When <strong>architectures become too complex</strong>, with multiple stores (Postgres, Redis, DynamoDB, Elasticsearch, etc.) and heavy ETL just to serve analytics.&nbsp;</li>
</ul>
<h2 class="wp-block-heading" id="h-operational-reasons-why-teams-migrate-to-clickhouse"><strong>Operational reasons why teams migrate to ClickHouse</strong><a class="anchor-link" id="operational-reasons-why-teams-migrate-to-clickhouse"></a></h2>
<ul class="wp-block-list">
<li>To get <strong>much faster analytics at scale</strong>: sub&#8209;second or millisecond queries on billions of rows, often 5 &ndash;10&times; + faster than previous systems.</li>
<li>To <strong>reduce cost</strong>: better compression and hardware efficiency lead to significantly lower storage and infrastructure costs (e.g., 4 &ndash;10&times; less storage, ~30%+ hardware savings vs Elasticsearch).</li>
<li>To <strong>separate OLTP and OLAP cleanly</strong>, keeping Postgres or other transactional DBs for writes while using ClickHouse for heavy analytics via CDC or pipelines.</li>
<li>To <strong>simplify architecture and operations</strong>, consolidating multiple systems into ClickHouse and reducing ETL, maintenance, and operational risk.</li>
<li>To enable <strong>richer, real&#8209;time analytics experiences</strong> for customers &mdash; interactive dashboards, observability, security analytics, and AI/ML workloads on fresh data.</li>
</ul>
<p>Migrating to a high-performance system like ClickHouse is a strategic move. It&rsquo;s more than a technical swap; it&rsquo;s a fundamental data infrastructure shift that offers significant analytical benefits alongside necessary operational risk management.</p>
<p>To really understand why this shift is so important, we have to look at the practical value it brings. While the migration might seem complex, the benefits you&rsquo;ll unlock at the end make the initial effort and investment more than worth it.</p>
<h2 class="wp-block-heading" id="h-operational-and-business-benefits-clickhouse-delivers-post-migration"><strong>Operational and business benefits ClickHouse</strong> <strong>delivers</strong> <strong>post-migration</strong><a class="anchor-link" id="operational-and-business-benefits-clickhouse-delivers-post-migration"></a></h2>
<p>Successfully navigating the migration process unlocks a host of powerful benefits that justify the initial investment and risk.</p>
<ol class="wp-block-list">
<li><strong>Accelerated Analytical Query Performance:</strong> ClickHouse&rsquo;s architecture enables fast, low-latency analytical queries on large datasets for real-time reporting, quick dashboard updates, and interactive data exploration.</li>
<li><strong>Scalability and Cost Efficiency:</strong> The new system provides superior horizontal scalability, handling petabytes and trillions of rows at lower cost per query than legacy data warehouses. This supports future business growth without prohibitive infrastructure costs and allows retention of finer-grained historical data for deeper analysis.</li>
<li><strong>Enhanced Data Granularity and Depth:</strong> Better performance and lower storage costs encourage keeping detailed, raw event data instead of pre-aggregated tables. This gives data scientists and analysts access to the raw truth, enabling more precise modeling, root cause analysis, and the discovery of subtle trends.</li>
<li><strong>Simplified Data Architecture:</strong> Consolidating high-volume analytical workloads onto a single, purpose-built platform streamlines the data stack, reducing maintenance overhead and specialized support needs, while accelerating time-to-insight.</li>
</ol>
<h2 class="wp-block-heading" id="h-key-operational-risks-to-be-mitigated"><strong>Key operational risks to be mitigated</strong><a class="anchor-link" id="key-operational-risks-to-be-mitigated"></a></h2>
<p>While the new system offers superior performance, the transition phase and the initial operational period present several key challenges that must be proactively mitigated:</p>
<ol class="wp-block-list">
<li><strong>Data Integrity and Consistency Risk:</strong> The primary concern during migration is ensuring that all data is transferred accurately and that the new system maintains consistency with the old source.</li>
<li><strong>Downtime and Service Interruption:</strong> The cutover from the legacy system to ClickHouse must be handled with minimal disruption to ongoing business intelligence and production services.</li>
<li><strong>Learning Curve and Skill Gap:</strong> Operating and optimizing a specialized system like ClickHouse requires a different set of skills from the existing team, particularly concerning its column-oriented architecture and SQL dialect.</li>
<li><strong>Integration Complexity:</strong> Ensuring seamless integration with existing upstream data sources (e.g., Kafka, object storage) and downstream analytical tools (e.g., BI platforms) is critical.</li>
</ol>
<p>This case study will outline a direct data migration procedure, specifically moving data from a legacy database system, MySQL, to ClickHouse.</p>
<p>Before we do migration from legacy database systems to Clickhouse, it is required to begin with the <strong>Pre-migration assessment</strong>. This foundational phase starts with defining the scope, objectives, success criteria, and high-level timeline.&nbsp;</p>
<p>Following this, a comprehensive <strong>inventory</strong> is undertaken to gain a clear understanding of the existing system through analysis of the following:</p>
<ul class="wp-block-list">
<li><strong>Workloads:</strong> Different kinds of analytical tasks, such as ad-hoc analysis, reporting, or real-time dashboards.</li>
<li><strong>Data Size:</strong> The current amount of data stored and how much is expected in the future.</li>
<li><strong>Ingest Rates:</strong> How fast and how often new data is written to the system.</li>
<li><strong>Query Patterns:</strong> The most common, demanding, and important queries run against the data, crucial for meeting performance guarantees.</li>
</ul>
<p>The core purpose of the inventory is <strong>identifying candidate data sets</strong>. This process includes:</p>
<ul class="wp-block-list">
<li>Evaluating schemas for ClickHouse compatibility and optimization.</li>
<li>Prioritizing high-volume, analytical data for maximum performance gain.</li>
<li>Defining exclusion criteria for unsuitable data (e.g., legacy, low-value, write-heavy transactional data).</li>
</ul>
<p>Finally, the assessment addresses <strong>on-prem environment implications</strong> for non-cloud migrations, including:</p>
<ul class="wp-block-list">
<li><strong>Hardware Sizing and Procurement</strong> (compute, memory, fast storage).</li>
<li><strong>Network Topology</strong> optimization for high-speed traffic.</li>
<li><strong>Security and Compliance</strong> integration (auth, encryption).</li>
<li><strong>Operational Readiness</strong> planning (monitoring, backup, DR, IT automation).</li>
</ul>
<h2 class="wp-block-heading" id="h-mysql-to-clickhouse-migration-planning-and-execution"><strong>MySQL to ClickHouse</strong> <strong>migration planning and execution</strong><a class="anchor-link" id="mysql-to-clickhouse-migration-planning-and-execution"></a></h2>
<p>A successful ClickHouse migration, exemplified by moving from MySQL, requires a critical, five-stage plan to minimize risk, downtime, and ensure performance: initial assessment, detailed planning, execution, rigorous testing, and final post-migration validation and optimization.</p>
<p>First, we must plan the data migration from MySQL to ClickHouse. There are several methods for this migration, which we will outline below.</p>
<p>A robust data migration pipeline is the backbone of any successful ClickHouse migration. The design should accommodate both historical data migration and continuous data synchronization.&nbsp;</p>
<h3 class="wp-block-heading" id="h-crucial-tasks-to-remember-for-successful-migration"><strong>Crucial tasks to remember for successful migration</strong><a class="anchor-link" id="crucial-tasks-to-remember-for-successful-migration"></a></h3>
<ul class="wp-block-list">
<li>Identify data sources (MySQL primary, replicas, read-only nodes)</li>
<li>Define data freshness requirements (near-real-time vs batch)</li>
<li>Separate historical backfill from live ingestion paths</li>
<li>To prevent disruption during data migration, no Data Definition Language (DDL) activities should be executed on the source database.</li>
</ul>
<h3 class="wp-block-heading" id="h-common-migration-approaches"><strong>Common migration approaches</strong><a class="anchor-link" id="common-migration-approaches"></a></h3>
<ul class="wp-block-list">
<li><strong>Initial backfill</strong>
<ul class="wp-block-list">
<li>Bulk export from MySQL (mysqldump, SELECT INTO OUTFILE)</li>
<li>Batch ingestion using <code>clickhouse-client</code>, <code>clickhouse-</code>local, or object storage (CSV/Parquet)</li>
</ul>
</li>
<li><strong>Ongoing ingestion / CDC</strong>
<ul class="wp-block-list">
<li>Binlog-based CDC tools (Debezium, Maxwell, custom CDC)</li>
<li>Stream-based pipelines (Kafka &rarr; ClickHouse)</li>
</ul>
</li>
<li><strong>Cut-over strategy</strong>
<ul class="wp-block-list">
<li>Dual-write or CDC-based sync during transition</li>
<li>Gradual read migration from MySQL to ClickHouse</li>
<li>Final cut-over once data parity and query validation are confirmed</li>
</ul>
</li>
<li><strong>Using third-party tools</strong>
<ul class="wp-block-list">
<li>Third party tools like <a href="https://github.com/bakwc/mysql_ch_replicator">mysql_ch_replicator</a>, Blade, and etc.</li>
</ul>
</li>
</ul>
<p>Before data migration, harmonize MySQL and ClickHouse schema design and modeling. ClickHouse&rsquo;s columnar, OLAP nature necessitates a schema optimized for analytical performance and data compression; a direct &ldquo;lift-and-shift&rdquo; from relational MySQL will lead to suboptimal results.</p>
<h3 class="wp-block-heading" id="h-key-mysql-to-clickhouse-concept-conversions"><strong>Key MySQL to ClickHouse concept conversions</strong><a class="anchor-link" id="key-mysql-to-clickhouse-concept-conversions"></a></h3>
<ul class="wp-block-list">
<li><strong>Columnar Orientation vs. Row-Based:</strong>
<ul class="wp-block-list">
<li><strong>MySQL:</strong> Optimized for transactional, row-level operations (OLTP).</li>
<li><strong>ClickHouse:</strong> Optimized for scanning large volumes of data across columns (OLAP). The goal is to minimize the number of columns read per query.</li>
</ul>
</li>
<li><strong>Denormalization:</strong>
<ul class="wp-block-list">
<li><strong>MySQL:</strong> Highly normalized structure, relying heavily on JOINs for query execution.</li>
<li><strong>ClickHouse:</strong> Strongly favors denormalization (flattening data, embedding dimension fields directly into the fact table). While ClickHouse supports JOINs, they are often less performant than in row-based systems, especially on huge datasets. Denormalization optimizes read performance by reducing I/O operations.</li>
</ul>
</li>
<li><strong>Engine Selection:</strong>
<ul class="wp-block-list">
<li><strong>MySQL:</strong> Primarily uses InnoDB.</li>
<li><strong>ClickHouse:</strong> Requires careful selection of the table engine (e.g., <code>MergeTree</code> family) based on the workload (e.g., <code>ReplacingMergeTree</code> for deduplication, <code>CollapsingMergeTree</code> for state management, <code>SummingMergeTree</code> for pre-aggregation). The engine dictates data storage, replication, and query behavior.</li>
</ul>
</li>
<li><strong>Primary Keys and Partitioning:</strong>
<ul class="wp-block-list">
<li><strong>MySQL:</strong> Primary key is for row identification and index lookup.</li>
<li><strong>ClickHouse:</strong> The <strong>Primary Key (<code>ORDER BY</code>)</strong> determines how data is physically sorted on disk, crucial for range queries and skip-index performance. The <strong>Partition Key (<code>PARTITION BY</code>)</strong> should align with common data retention or query filtering (e.g., by month or day).</li>
</ul>
</li>
<li><strong>Data Types:</strong>
<ul class="wp-block-list">
<li><strong>MySQL:</strong> Generic types (e.g., <code>DATETIME</code>, <code>VARCHAR</code>).</li>
<li><strong>ClickHouse:</strong> Utilize specialized, compact types (e.g., <code>Date</code>, <code>DateTime64</code>, <code>LowCardinality</code> for strings, <code>Decimal</code> for precise monetary values, <code>Array</code> for nested data) to maximize compression and query speed. For example, replacing a high-cardinality <code>VARCHAR</code> with <code>String</code> is common, but replacing low-cardinality strings with <code>LowCardinality</code>(<code>String</code>) is a massive performance win.</li>
</ul>
</li>
</ul>
<h3 class="wp-block-heading" id="h-actionable-clickhouse-schema-modeling-steps"><strong>Actionable ClickHouse schema modeling</strong> <strong>steps</strong><a class="anchor-link" id="actionable-clickhouse-schema-modeling-steps"></a></h3>
<ul class="wp-block-list">
<li><strong>Identify Analytical Queries:</strong> Use the inventory from the assessment phase to determine which MySQL queries are most critical and resource-intensive.</li>
<li><strong>Redesign for Columnar:</strong> For each key analytical workload, design a <em>denormalized</em> target schema in ClickHouse that includes all necessary dimension fields, avoiding heavy cross-table joins.</li>
<li><strong>Define <code>ORDER BY</code> and <code>PARTITION BY</code>:</strong> Select keys that will drastically reduce the amount of data ClickHouse needs to scan (e.g., order by <code>timestamp</code>, <code>partition by year_month</code>).</li>
<li><strong>Prototype and Test:</strong> Create the new ClickHouse table structure and load a representative data sample. Run the target queries and benchmark performance against the legacy MySQL system before full migration.</li>
</ul>
<h2 class="wp-block-heading" id="h-testing-performance-benchmarks-and-query-validation"><strong>Testing, performance benchmarks, and query validation</strong><a class="anchor-link" id="testing-performance-benchmarks-and-query-validation"></a></h2>
<p>Comprehensive testing and validation are essential for a successful ClickHouse migration, ensuring functional correctness, data integrity, and, most importantly, the expected performance gains.</p>
<h3 class="wp-block-heading" id="h-key-validation-strategy-points"><strong>Key validation strategy points</strong><a class="anchor-link" id="key-validation-strategy-points"></a></h3>
<ul class="wp-block-list">
<li><strong>Functional testing and data integrity verification:</strong>
<ul class="wp-block-list">
<li><strong>Row count and schema comparison:</strong> First, compare ClickHouse and MySQL row counts and concurrently verify table schemas (data types, columns, indexing) to quickly check for major data loss or incorrect transformation.</li>
<li><strong>Key metric verification:</strong> Validate core business metrics (e.g., revenue, DAU, inventory) by comparing complex aggregation query results between MySQL and ClickHouse. Discrepancies signal data migration or query translation issues.</li>
<li><strong>Comprehensive data spot checks: </strong>For granular verification, randomly sample data, focusing on edge cases. Verify nulls, timestamp accuracy, primary key uniqueness (if relevant), and special characters to ensure data fidelity and encoding.</li>
</ul>
</li>
<li><strong>Performance benchmarking:</strong>
<ul class="wp-block-list">
<li><strong>Query translation and optimization:</strong> MySQL queries must be translated to the ClickHouse SQL dialect, utilizing its column-oriented architecture and specialized functions for optimization. Each translated query requires a performance review.</li>
<li><strong>A/B performance testing:</strong> To validate, a suite of representative queries &mdash; simple lookups to complex joins and aggregations, will run on both MySQL and the new ClickHouse cluster. The objective is to rigorously measure the performance difference.
<ul class="wp-block-list">
<li><strong>Metrics to measure:</strong> Focus on latency (query execution time), Query Latency (p50, p95, p99 percentiles), throughput (queries per second), and resource utilization (CPU, memory, disk I/O).</li>
<li><strong>Expected results:</strong> ClickHouse migration is only successful if its analytical query execution times are significantly (often orders of magnitude) faster than the MySQL baseline.</li>
</ul>
</li>
<li><strong>Load and concurrency testing:</strong> ClickHouse testing must verify cluster stability, resource management, and sustained low latency under simulated production loads, concurrent users, and complex simultaneous queries; isolated query performance is insufficient.</li>
</ul>
</li>
<li><strong>End-to-end application validation:</strong></li>
</ul>
<p>The final step is <strong>Application Integration</strong>, connecting ClickHouse with downstream applications, reporting tools, and dashboards formerly using MySQL. This requires end-to-end testing to verify application logic, followed by <strong>User Acceptance Testing (UAT)</strong> with stakeholders to confirm the new system meets requirements and provides consistent, accurate data for all reporting</p>
<h2 class="wp-block-heading" id="h-executing-the-clickhouse-migration"><strong>Executing the ClickHouse migration</strong><a class="anchor-link" id="executing-the-clickhouse-migration"></a></h2>
<p>With the requirements for migrating data from MySQL to ClickHouse now established, this section focuses on the migration process itself. We will utilize the third-party tool<a href="https://github.com/bakwc/mysql_ch_replicator"> </a><code>mysql_ch_replicator</code> for this task. All necessary prerequisites must be met before proceeding. These prerequisites are also required for alternative approaches, particularly when using this specific tool.</p>
<figure class="wp-block-table">
<table class="has-fixed-layout">
<tbody>
<tr>
<td>Server</td>
<td>Linux / MacOS with python3.10 or higher</td>
</tr>
<tr>
<td>MySQL</td>
<td>binlog_format &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = ROWbinlog_expire_logs_seconds = 86400</td>
</tr>
<tr>
<td>Clickhouse</td>
<td><code>&lt;clickhouse&gt;&nbsp;&nbsp;&nbsp;&nbsp;</code><br>  <code>    &lt;profiles&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><br>                     <code>&lt;default&gt;</code><br><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;final&gt;1&lt;/final&gt;</code><br><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;max_query_size&gt;300000000&lt;/max_query_size&gt;</code><br><code>&nbsp;&nbsp;</code>                       <code>&lt; max_ast_elements&gt;</code><br><code>1000000&lt;/max_ast_elements&gt; &lt;max_expanded_ast_elements&gt;1000000&lt;/max_expanded_ast_elements&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><br>                   <code>&lt;/default&gt;&nbsp;&nbsp;&nbsp;&nbsp;</code><br><code>      &lt;/profiles&gt;</code><br><code>&lt;/clickhouse&gt;</code></td>
</tr>
</tbody>
</table>
</figure>
<p>Next to install <code>mysql_ch_replicator</code>, use the following command:</p>
<pre class="wp-block-code"><code>pip install --upgrade mysql_ch_replicator</code></pre>
<p>After that we can try to migrate the data from MySQL legacy database to ClickHouse.</p>
<ol class="wp-block-list">
<li>Prepare yaml config file. For an example.</li>
</ol>
<pre class="wp-block-code"><code># MySQL and ClickHouse credentials can be overridden using environment variables:
# MySQL: MYSQL_HOST, MYSQL_PORT, MYSQL_USER, MYSQL_PASSWORD, MYSQL_CHARSET
# ClickHouse: CLICKHOUSE_HOST, CLICKHOUSE_PORT, CLICKHOUSE_USER, CLICKHOUSE_PASSWORD

mysql:
  host: 'localhost'
  port: 3306
  user: 'admin'
  password: ''
  charset: 'utf8mb4'  # Optional: charset for MySQL connection (default: utf8mb4). Use utf8mb4 for full Unicode support including emoji and 4-byte characters

clickhouse:
  host: 'localhost'
  port: 8323
  user: 'default'
  password: ''

binlog_replicator:
  data_dir: '/root/mysql_ch_replicator/binlog/'
  records_per_file: 100000

databases: 'sakila'
tables: '*'</code></pre>
<ol start="2" class="wp-block-list">
<li>To begin replication, you can use <code>nohup</code> to run the process in the background and enable logging. Since the data is being migrated continuously, we will initiate the process using the <code>run_all</code> option.</li>
</ol>
<pre class="wp-block-code"><code>nohup mysql_ch_replicator --config config.yaml run_all</code></pre>
<ol start="3" class="wp-block-list">
<li>Then you can just monitor the replication process.</li>
</ol>
<p><strong>N.B.</strong></p>
<ul class="wp-block-list">
<li>The <code>mysql_ch_replicator</code> tool, a community-developed utility, is currently unable to replicate data directly from a MySQL source to a ClickHouse cluster.</li>
</ul>
<ul class="wp-block-list">
<li>If your migration involves a ClickHouse cluster, you will need to manually create the destination schema using the <code>ON CLUSTER</code> option first, or you could explore alternative methods such as <strong>Ongoing ingestion / CDC</strong> (Change Data Capture).</li>
</ul>
<h2 class="wp-block-heading" id="h-post-clickhouse-migration-operational-go-live-readiness"><strong>Post-ClickHouse migration operational go-live readiness</strong><a class="anchor-link" id="post-clickhouse-migration-operational-go-live-readiness"></a></h2>
<p>Successful ClickHouse deployment needs robust operational readiness, beyond migration, to handle production traffic reliably. This foundation requires a solid backup strategy, effective resource governance, comprehensive observability, a prepared team, and defined rollback paths. A strong operational footing minimizes cut-over risks and builds confidence in ClickHouse as a critical production system.</p>
<h3 class="wp-block-heading" id="h-monitoring-and-observability"><strong>Monitoring and observability</strong><a class="anchor-link" id="monitoring-and-observability"></a></h3>
<p>Comprehensive observability is essential for a properly functioning ClickHouse cluster, providing deep insights across three critical dimensions: <strong>query, storage, and cluster levels</strong>. This holistic view is necessary for diagnosing bottlenecks, capacity planning, and ensuring high availability.</p>
<ol class="wp-block-list">
<li><strong>Query level</strong></li>
</ol>
<p>Focuses on workload for performance tuning and capacity planning:</p>
<ul class="wp-block-list">
<li><strong>Query performance:</strong> Track execution time, rows processed, and memory usage to identify heavy or inefficient queries.</li>
<li><strong>Query load/concurrency:</strong> Monitor active/pending queries and concurrency (QPS) to signal scaling needs.</li>
<li><strong>Failures/errors</strong>: Log and alert on failed queries for quick issue identification (e.g., malformed SQL, resource exhaustion).</li>
<li><strong>User/source tracking:</strong> Identify users/applications submitting demanding queries for context on resource allocation.</li>
</ul>
<ol start="2" class="wp-block-list">
<li><strong>Storage level</strong></li>
</ol>
<p>Ensures data integrity and optimal read/write performance:</p>
<ul class="wp-block-list">
<li><strong>Disk I/O:</strong> Monitor latency, throughput, and I/O wait times across all volumes.</li>
<li><strong>Disk space:</strong> Track utilization and growth rate for data and temporary directories; alert proactively on low space.</li>
<li><strong>Merge operations:</strong> Observe the rate and efficiency of background data consolidation to prevent query degradation.</li>
<li><strong>Replication/consistency:</strong> Monitor replica lag to ensure synchronized data across nodes for durability and load balancing.</li>
</ul>
<ol start="3" class="wp-block-list">
<li><strong>Cluster level</strong></li>
</ol>
<p>Covers overall health and resource management of the distributed system:</p>
<ul class="wp-block-list">
<li><strong>System resources:</strong> Track CPU utilization, memory (RAM/swap), and network traffic for all nodes to guide scaling or load redistribution.</li>
<li><strong>ClickHouse status:</strong> Monitor server health, uptime, thread count, and internal metrics (memory pools, cache ratios).</li>
<li><strong>Distributed table health:</strong> Ensure inter-node communication is healthy and all sharded components are available.</li>
<li><strong>Configuration/version:</strong> Track running config and software versions for consistency and simplified debugging.</li>
</ul>
<p>Robust monitoring across these three dimensions transforms the approach from reactive checks to proactive performance and stability assurance for ClickHouse.</p>
<p><strong>Key areas to monitor:</strong></p>
<ul class="wp-block-list">
<li>Query latency (p95 / p99)</li>
<li>Query concurrency and queue depth</li>
<li>CPU usage per shard and replica</li>
<li>Disk IO wait and merge activity</li>
<li>Replication lag and ZooKeeper / Keeper health</li>
<li>Disk free space per volume and partition</li>
</ul>
<p><strong>Key metrics sources:</strong></p>
<ul class="wp-block-list">
<li><code>system.metrics, system.asynchronous_metrics</code></li>
<li><code>system.query_log, system.part_log</code></li>
<li><code>system.replication_queue</code></li>
<li>Keeper metrics (Raft state, leader election)</li>
</ul>
<h3 class="wp-block-heading" id="h-backup-amp-recovery-readiness"><strong>Backup &amp; recovery readiness</strong><a class="anchor-link" id="backup-recovery-readiness"></a></h3>
<p>Setting up a backup solution is not enough; rigorous testing is crucial. Regularly and thoroughly testing backups builds confidence that data can be successfully restored after a disaster, ensuring business continuity and data integrity.</p>
<p><strong>Backup considerations:</strong></p>
<ul class="wp-block-list">
<li>Full backups of MergeTree data (filesystem or object storage)</li>
<li>Metadata backups (DDL, users, quotas, settings)</li>
<li>Keeper metadata consistency</li>
<li>Backup verification and restore drills</li>
</ul>
<p><strong>Operational checklist:</strong></p>
<ul class="wp-block-list">
<li>Can you restore a single table?</li>
<li>Can you restore a shard independently?</li>
<li>Can you recover from a lost replica?</li>
<li>Is backup performance acceptable under load?</li>
</ul>
<p>Regularly integrating and monitoring backup procedures is essential to promptly detect and address failures, ensuring the integrity and recoverability of your ClickHouse data.</p>
<h3 class="wp-block-heading" id="h-resource-management-amp-capacity-planning"><strong>Resource management &amp; capacity planning</strong><a class="anchor-link" id="resource-management-capacity-planning"></a></h3>
<p>ClickHouse is excellent for analytical tasks because it is efficient and fast. Nevertheless, its performance ceiling is determined by the physical hardware such as the CPU, memory, and storage I/O speed. Consequently, optimizing and scaling the infrastructure is essential to fully exploit ClickHouse&rsquo;s capabilities.</p>
<p><strong>Key areas to validate before go-live:</strong></p>
<ul class="wp-block-list">
<li>CPU headroom during peak query windows</li>
<li>Disk write amplification from merges</li>
<li>Memory usage under concurrent workloads</li>
<li>Network throughput for distributed queries</li>
</ul>
<p><strong>Controls to validate:</strong></p>
<ul class="wp-block-list">
<li>Query limits (<code>max_memory_usage, max_threads</code>)</li>
<li>User and workload isolation</li>
<li>Merge throttling and background pool sizing</li>
<li>Shard balance and data distribution</li>
</ul>
<p>When performing capacity planning, it is crucial to anticipate and account for future <strong>growth</strong>. Simply basing the plan on the existing or current load will likely lead to resource shortages down the line.</p>
<h3 class="wp-block-heading" id="h-staffing-training-and-operational-ownership"><strong>Staffing, training and operational ownership</strong><a class="anchor-link" id="staffing-training-and-operational-ownership"></a></h3>
<p>Operational readiness requires more than stable systems; it critically involves the managing personnel. A comprehensive approach must incorporate team training, process documentation, and clear communication to ensure staff are skilled, informed, and prepared for any situation.</p>
<p>Ensure that:</p>
<ul class="wp-block-list">
<li>On-call engineers understand ClickHouse internals</li>
<li>Runbooks exist for common incidents (replication lag, disk pressure, slow queries)</li>
<li>Ownership is clear between DB, infra, and application teams</li>
<li>Escalation paths are defined</li>
</ul>
<p>Training should focus on:</p>
<ul class="wp-block-list">
<li>Reading system tables</li>
<li>Understanding MergeTree behavior</li>
<li>Debugging distributed queries</li>
<li>Safe operational actions (detaches, restarts, resyncs)</li>
</ul>
<h3 class="wp-block-heading" id="h-failback-rollback-plan"><strong>Failback / rollback plan</strong><a class="anchor-link" id="failback-rollback-plan"></a></h3>
<p>A comprehensive, well-prepared rollback plan is mandatory before any system or feature launch. This vital pre-go-live step ensures preparedness, minimizes downtime and data loss, and serves as crucial due diligence for risk mitigation.</p>
<p><strong>Typical rollback models:</strong></p>
<ul class="wp-block-list">
<li>Dual-write period (MySQL + ClickHouse) if a dual-write strategy is chosen during data migration.</li>
<li>Read-only fallback to legacy system</li>
<li>Snapshot-based recovery</li>
<li>Feature-flag based traffic routing&nbsp;</li>
<li>Batch ingestion using <code>clickhouse-client</code>, <code>clickhouse-local</code>, or object storage (CSV/Parquet)</li>
</ul>
<p><strong>Critical questions:</strong></p>
<ul class="wp-block-list">
<li>How long can rollback remain viable?</li>
<li>What data is lost if rollback occurs?</li>
<li>How do you reconcile post-rollback data?</li>
</ul>
<p>Rollback plans must be constrained by a set timeframe for their completion. It is also crucial that these plans are practiced regularly to ensure effectiveness.</p>
<h3 class="wp-block-heading" id="h-hybrid-deployment-readiness"><strong>Hybrid deployment readiness</strong><a class="anchor-link" id="hybrid-deployment-readiness"></a></h3>
<p>Deployments that span hybrid or multiple sites necessitate more rigorous verification steps. These additional checks are crucial to ensure successful operation across the distributed environment.</p>
<p><strong>Connectivity readiness:</strong></p>
<ul class="wp-block-list">
<li>Latency and packet loss between sites</li>
<li>Stable routing (VPN / VPC peering)</li>
<li>MTU alignment to avoid fragmentation</li>
</ul>
<p><strong>Storage tiering considerations:</strong></p>
<ul class="wp-block-list">
<li>Hot vs cold data placement</li>
<li>Object storage latency impact on queries</li>
<li>Local SSD usage for active partitions</li>
<li>Clear TTL and data movement policies</li>
</ul>
<p>Hybrid deployments necessitate more robust observability capabilities. Furthermore, they require enhanced failure isolation mechanisms to ensure system stability.</p>
<h2 class="wp-block-heading" id="h-post-migration-clickhouse-operations"><strong>Post-Migration ClickHouse operations</strong><a class="anchor-link" id="post-migration-clickhouse-operations"></a></h2>
<p>Once ClickHouse is live in production, operational focus shifts from migration execution to long-term stability, performance, and cost efficiency. Post-migration operations ensure that data remains well-organized, queries continue to perform as expected under evolving workloads, and operational risks are detected early. These activities are continuous and should be treated as part of standard database operations rather than one-time tasks.</p>
<h3 class="wp-block-heading" id="h-maintenance-compaction-pruning-data-retention"><strong>Maintenance: Compaction, pruning, data retention</strong><a class="anchor-link" id="maintenance-compaction-pruning-data-retention"></a></h3>
<p>ClickHouse relies on background processes to maintain data layout and performance.</p>
<p><strong>Key maintenance areas:</strong></p>
<ul class="wp-block-list">
<li><strong>MergeTree compaction (merges):</strong>
<ul class="wp-block-list">
<li>Monitor merge queue length and merge throughput</li>
<li>Avoid sustained merge backlogs that increase query latency</li>
</ul>
</li>
<li><strong>Partition pruning:</strong>
<ul class="wp-block-list">
<li>Ensure partitioning keys align with query patterns (e.g. time-based)</li>
<li>Regularly review unused or oversized partitions</li>
</ul>
</li>
<li><strong>Data retention &amp; TTLs:</strong>
<ul class="wp-block-list">
<li>Apply TTL policies for automatic data deletion or movement to colder storage</li>
<li>Validate TTL execution does not overload background threads</li>
</ul>
</li>
</ul>
<p>The goal is to keep data sets lean, query-friendly, and aligned with business retention requirements without manual intervention.</p>
<h3 class="wp-block-heading" id="h-performance-tuning-and-query-optimisation"><strong>Performance tuning and query optimisation</strong><a class="anchor-link" id="performance-tuning-and-query-optimisation"></a></h3>
<p>Post-migration workloads often differ from initial benchmarks.</p>
<p><strong>Ongoing tuning areas:</strong></p>
<ul class="wp-block-list">
<li>Review slow queries using <code>system.query_log</code></li>
<li>Validate use of primary keys, skipping indexes, and projections</li>
<li>Identify inefficient distributed queries (excessive shard fan-out)</li>
<li>Adjust:
<ul class="wp-block-list">
<li><code>max_threads</code>, <code>max_memory_usage</code></li>
<li>Join algorithms and aggregation strategies</li>
</ul>
</li>
<li>Periodically re-benchmark after schema or workload changes</li>
</ul>
<p>Performance tuning in ClickHouse is iterative and driven by real usage patterns, not static configuration.</p>
<h3 class="wp-block-heading" id="h-monitoring-and-cost-management-especially-cloud"><strong>Monitoring and cost management (especially cloud)</strong><a class="anchor-link" id="monitoring-and-cost-management-especially-cloud"></a></h3>
<p>Increased dataset sizes and query frequency necessitate a corresponding increase in system resources, directly impacting operational costs. Therefore, assessing the current system capacity and performance is essential for planning necessary upgrades and managing the associated budgetary requirements.</p>
<p><strong>Monitoring focus areas:</strong></p>
<ul class="wp-block-list">
<li>CPU and memory utilization per node</li>
<li>Disk usage growth rate and merge IO</li>
<li>Query concurrency and peak load windows</li>
<li>Replication lag and background task saturation</li>
</ul>
<p><strong>Cost management considerations (cloud or hybrid):</strong></p>
<ul class="wp-block-list">
<li>Detect over-provisioned shards or replicas</li>
<li>Use TTLs and tiered storage to reduce hot data footprint</li>
<li>Monitor egress and cross-region query costs</li>
<li>Align scaling decisions with actual query demand</li>
</ul>
<p>Keeping an eye on your operations is really important if you want to stop costs from slowly getting out of hand over time.</p>
<h2 class="wp-block-heading" id="h-integration-with-existing-db-ops-stack-after-clickhouse-migration"><strong>Integration with existing DB ops stack after ClickHouse</strong> <strong>migration</strong><a class="anchor-link" id="integration-with-existing-db-ops-stack-after-clickhouse-migration"></a></h2>
<p>After migration, ClickHouse should not operate in isolation. To maintain operational consistency and reduce cognitive overhead for support and operations teams, ClickHouse must be integrated into the existing database operations stack. This ensures that monitoring, alerting, incident response, and capacity planning continue to follow familiar workflows while extending visibility to the new analytics platform.</p>
<p>First we need to Identify Existing Operational Touchpoints. Start by mapping how databases are currently operated. The goal is to integrate ClickHouse into these existing paths rather than creating parallel processes. There are typical touchpoints that we need to aware <strong>:</strong></p>
<ul class="wp-block-list">
<li>Monitoring and alerting systems</li>
<li>Log aggregation and query auditing</li>
<li>Backup and recovery tooling</li>
<li>Configuration management</li>
<li>Incident response and on-call workflows</li>
</ul>
<p>After we know the touchpoints, you can start integrating the Clickhouse Metrics and Logs. Basically ClickHouse exposes rich internal telemetry via system tables so you can feed into the same observability platforms already used existing dashboard</p>
<ul class="wp-block-list">
<li>Export metrics from <code>system.metrics</code> and <code>system.asynchronous_metrics</code></li>
<li>Capture query behavior from <code>system.query_log</code></li>
<li>Track replication health via <code>system.replication_queue</code></li>
<li>Monitor disk and merge activity from <code>system.parts</code> and <code>system.part_log</code></li>
</ul>
<p>Apart from that, we also need to standardize the alerting system. ClickHouse alerts should align with existing alert semantics to avoid ClickHouse-only alert silos and Clickhouse&rsquo;s critical metrics itself. Don&rsquo;t forget to ensure that all alerts should be actionable, mapped to clear runbooks and routed through existing escalations paths.</p>
<p><strong>Examples of critical ClickHouse metrics:</strong></p>
<ul class="wp-block-list">
<li>Replication lag exceeds SLA</li>
<li>Query latency p95 above baseline</li>
<li>Merge backlog growing unexpectedly.</li>
</ul>
<p>You also need to unify backup and recovery operations. Backup operations must follow consistent patterns across databases. You need to be understanding and aware about this because this is really important and this reduces operational risk during incidents..</p>
<p><strong>Integration checklist:</strong></p>
<ul class="wp-block-list">
<li>Schedule ClickHouse backups alongside other DB backups</li>
<li>Centralize backup status reporting</li>
<li>Perform restore tests using the same operational playbooks</li>
<li>Document ClickHouse-specific restore nuances (replicas, shards, Keeper)</li>
</ul>
<p>A monitoring dashboard for ClickHouse must be enabled and configured to leverage the exposed metrics. Ideally, you should integrate ClickHouse into your existing monitoring system to ensure it is viewed as part of the wider data platform, not an isolated analytics component. Implementing cross-database dashboards will facilitate the identification of systemic issues, enabling better resource planning and expediting root-cause analysis.</p>
<p><strong>Useful dashboard include:</strong></p>
<ul class="wp-block-list">
<li>Ingestion rate vs query rate across systems</li>
<li>Disk growth trends per platform</li>
<li>Peak usage windows and contention</li>
<li>Cost and resource efficiency comparisons</li>
</ul>
<p>The integration process should be concluded by updating all relevant operational documentation. This includes revising and disseminating the necessary runbooks to reflect the final configuration.</p>
<ul class="wp-block-list">
<li>Define ownership for ClickHouse incidents</li>
<li>Add ClickHouse scenarios to on-call runbooks</li>
<li>Train teams on common failure patterns</li>
<li>Ensure consistent terminology across databases</li>
</ul>
<p>Establishing a comprehensive checklist and a clear timeline is a crucial final step after the migration process to ensure smooth, anticipated operations. Post-data migration to ClickHouse, a brief stabilization period is necessary before officially declaring the migration complete. This stabilization phase is vital for validating data accuracy, optimizing performance, and ensuring the support team is fully prepared operationally. The following week-by-week table provides a recommended operational timeline.</p>
<figure class="wp-block-table">
<table class="has-fixed-layout">
<tbody>
<tr>
<td>Phase</td>
<td>Focus area</td>
<td>Key activities</td>
<td>Exit criteria</td>
</tr>
<tr>
<td>1&nbsp;</td>
<td>Data Validation &amp; Baseline</td>
<td><strong>Validate</strong> row counts and aggregates<br><strong>Check</strong> replication health and merge backlog<br><strong>Verify</strong> partitions, TTLs, and monitoring dashboards<br><strong>Establish</strong> baseline query latency</td>
<td>Data is consistent and cluster is stable</td>
</tr>
<tr>
<td>2</td>
<td>Performance Tuning</td>
<td><strong>Review</strong> slow queries (<code>system.query_log</code>)<br><strong>Tune</strong> memory, threads, and concurrency limits<br><strong>Validate</strong> shard balance and peak load behavior</td>
<td>Queries meet performance expectations</td>
</tr>
<tr>
<td>3</td>
<td>Resilience &amp; Recovery</td>
<td><strong>Test</strong> replica and shard failure scenarios<br><strong>Validate</strong> Keeper quorum behavior<br><strong>Perform</strong> backup and restore tests<br><strong>Verify</strong> alerts and runbooks</td>
<td>Failures handled predictably</td>
</tr>
<tr>
<td>4</td>
<td>Operational Handover</td>
<td><strong>Review</strong> resource usage and storage growth<br><strong>Tune</strong> retention and tiered storage policies<br><strong>Finalize</strong> documentation and ownership<br><strong>Sign-off</strong> from support teams</td>
<td>ClickHouse declared production-ready</td>
</tr>
</tbody>
</table>
</figure>
<p>Each phase is planned to take approximately one week. This checklist is crucial for ensuring the migration results in a stable and well-operated ClickHouse environment, effectively mitigating the risks associated with a &lsquo;big bang&rsquo; go-live approach.</p>
<h2 class="wp-block-heading" id="h-conclusion"><strong>Conclusion</strong><a class="anchor-link" id="conclusion"></a></h2>
<p>Migration to ClickHouse is a strategic move, driven by the inability of existing OLTP or general analytics systems (like MySQL) to handle massive data volumes (billions/trillions of rows) and high-concurrency analytical queries efficiently. The core goals are achieving significantly faster, sub-second analytics at scale, reducing infrastructure costs through better compression, and simplifying the architecture with a specialized OLAP engine. However, this transition requires a structured approach to manage risks concerning data integrity, downtime, and team training. Successful migration requires a pre-migration assessment (inventory, data size, query patterns) and careful planning and execution. </p>
<p>Essential steps include implementing a robust data pipeline for historical backfill and ongoing CDC, and critically redesigning the schema for ClickHouse&rsquo;s columnar structure. Schema redesign involves denormalization, selecting MergeTree engines, and defining optimal <code>ORDER BY</code> and <code>PARTITION BY</code> keys for performance. The execution phase requires rigorous testing and validation, including data consistency checks, A/B performance benchmarking, and load testing before final cut-over. Lastly, don&rsquo;t forget that operational go-live readiness and subsequent post-migration operations are critical for sustained success. </p>
<p>Interested in streamlined ClickHouse operations? We will be adding ClickHouse support to ClusterControl soon.&nbsp;In the meantime, <a href="https://severalnines.com/clustercontrol/databases/clickhouse">visit our ClickHouse page</a>&nbsp;to see what we&rsquo;ll support in its initial release and help influence its roadmap by submitting info about your use case.</p>
<p>The post <a href="https://severalnines.com/blog/operational-guide-to-migrating-to-clickhouse/">Operational guide to migrating to ClickHouse</a> appeared first on <a href="https://severalnines.com">Severalnines</a>.</p>

<p>The post <a rel="nofollow" href="https://severalnines.com/blog/operational-guide-to-migrating-to-clickhouse/">Operational guide to migrating to ClickHouse</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Introducing MariaDB Enterprise Cluster on MariaDB Cloud</title>
      <link>https://mariadb.com/resources/blog/introducing-mariadb-enterprise-cluster-on-mariadb-cloud/</link>
      <pubDate>Thu, 26 Feb 2026 20:27:28 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.com/">MariaDB</source>
      <description><![CDATA[<p>The modern enterprise doesn’t just run on data, it survives on the absolute certainty of that data. As applications become […]</p>
<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/introducing-mariadb-enterprise-cluster-on-mariadb-cloud/">Introducing MariaDB Enterprise Cluster on MariaDB Cloud</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>The modern enterprise doesn&rsquo;t just run on data, it survives on the absolute certainty of that data. As applications become more global, more distributed and increasingly AI-driven, the expectations placed on cloud databases (DBaaS) have fundamentally changed. Enterprises need more than backups and replicas. They need continuous availability, consistent performance and intelligent traffic&hellip;</p>
<p><a href="https://mariadb.com/resources/blog/introducing-mariadb-enterprise-cluster-on-mariadb-cloud/" rel="nofollow">Source</a></p>

<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/introducing-mariadb-enterprise-cluster-on-mariadb-cloud/">Introducing MariaDB Enterprise Cluster on MariaDB Cloud</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>More than Flushing (also Caching) for innodb_flush_method, and Missing Release Candidate</title>
      <link>https://jfg-mysql.blogspot.com/2026/02/more-than-flushing-also-caching-for-innodb-flush-method-and-missing-release-candidate.html</link>
      <pubDate>Wed, 25 Feb 2026 20:57:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://jfg-mysql.blogspot.com/">J-F Gagné's MySQL Blog</source>
      <description><![CDATA[<p>Something changed in MySQL 8.4 related to caching, and it is easy to miss, so it deserves a post.&#160; And a subject adjacent to this is the missing Release Candidate for MySQL 8.4 LTS, with my hope that the next LTS will have a Release Candidate, so I also cover this topic below.</p>
<p>(if you are not interested in Caching and Flushing, you can jump directly to the section about Release Candidate)</p>
<p>The post <a rel="nofollow" href="https://jfg-mysql.blogspot.com/2026/02/more-than-flushing-also-caching-for-innodb-flush-method-and-missing-release-candidate.html">More than Flushing (also Caching) for innodb_flush_method, and Missing Release Candidate</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>Something changed in MySQL 8.4 related to caching, and it is easy to miss, so it deserves a post.&amp;nbsp; And a subject adjacent to this is the missing Release Candidate for MySQL 8.4 LTS, with my hope that the next LTS will have a Release Candidate, so I also cover this topic below.</p>
<p>(if you are not interested in Caching and Flushing, you can jump directly to the section about Release Candidate)</p>

<p>The post <a rel="nofollow" href="https://jfg-mysql.blogspot.com/2026/02/more-than-flushing-also-caching-for-innodb-flush-method-and-missing-release-candidate.html">More than Flushing (also Caching) for innodb_flush_method, and Missing Release Candidate</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>The Rising Tide of Community Contributions to MariaDB Server</title>
      <link>https://mariadb.org/the-rising-tide-of-community-contributions-to-mariadb-server/</link>
      <pubDate>Wed, 25 Feb 2026 13:45:33 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>I wanted to share with you all some statistics on the incoming community contributions for the MariaDB server in the last year or so. And some of my thoughts looking at the data. …<br />
Continue reading \"The Rising Tide of Community Contributions to MariaDB Server\"<br />
The post The Rising Tide of Community Contributions to MariaDB Server appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/the-rising-tide-of-community-contributions-to-mariadb-server/">The Rising Tide of Community Contributions to MariaDB Server</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>I wanted to share with you all some statistics on the incoming community contributions for the MariaDB server in the last year or so. And some of my thoughts looking at the data. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/the-rising-tide-of-community-contributions-to-mariadb-server/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;The Rising Tide of Community Contributions to MariaDB Server&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/the-rising-tide-of-community-contributions-to-mariadb-server/">The Rising Tide of Community Contributions to MariaDB Server</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/the-rising-tide-of-community-contributions-to-mariadb-server/">The Rising Tide of Community Contributions to MariaDB Server</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Meet Percona at KubeCon + CloudNativeCon Europe 2026</title>
      <link>https://percona.community/blog/2026/02/25/meet-percona-at-kubecon--cloudnativecon-europe-2026/</link>
      <pubDate>Wed, 25 Feb 2026 12:00:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://percona.community/blog/">Percona Community Blog - learn about MySQL, MariaDB, PostgreSQL, and MongoDB</source>
      <description><![CDATA[<p>The Percona team is heading to KubeCon + CloudNativeCon Europe in Amsterdam, and we’d love to meet you in person!</p>
<p>The post <a rel="nofollow" href="https://percona.community/blog/2026/02/25/meet-percona-at-kubecon--cloudnativecon-europe-2026/">Meet Percona at KubeCon + CloudNativeCon Europe 2026</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>The Percona team is heading to KubeCon + CloudNativeCon Europe in Amsterdam, and we&rsquo;d love to meet you in person!</p>
<p>You can find us at <strong>Booth 790</strong>. This is a great chance to talk with engineers working on Percona Operators.</p>
<p>We will be there to discuss:</p>
<ul>
<li>Running MySQL, PostgreSQL, and MongoDB on Kubernetes</li>
<li>Production-ready HA setups</li>
<li>Backup and PITR strategies</li>
<li>Multi-cluster and multi-region deployments</li>
<li>Operators roadmap and upcoming features</li>
<li>Real-world troubleshooting stories</li>
</ul>
<p>If you&rsquo;re running Percona Operators in production (or just getting started), we&rsquo;d love to hear your feedback and learn about your challenges.</p>
<p>If you&rsquo;re just curious (or even suspicious) about running databases on Kubernetes, we&rsquo;d love to talk and answer your questions.</p>
<h3 id="admission-tickets---20-off-for-our-community">Admission Tickets &ndash; 20% Off for Our Community<a class="anchor-link" id="admission-tickets-20-off-for-our-community"></a></h3>
<p>We have a 20% discount code available for Percona community members.<br>
If you&rsquo;re planning to attend and don&rsquo;t have a ticket yet, drop a comment or message us and we&rsquo;ll share the details.</p>
<h3 id="schedule-a-meeting">Schedule a Meeting<a class="anchor-link" id="schedule-a-meeting"></a></h3>
<p>Want dedicated time with our engineers?<br>
Drop a comment here or reach out directly. We&rsquo;re happy to schedule a meeting during the event.</p>
<p>See you in Amsterdam!</p>

<p>The post <a rel="nofollow" href="https://percona.community/blog/2026/02/25/meet-percona-at-kubecon--cloudnativecon-europe-2026/">Meet Percona at KubeCon + CloudNativeCon Europe 2026</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>PostgreSQL coffee break: version upgrade related reindexing &#8211; reasons</title>
      <link>https://percona.community/blog/2026/02/25/postgresql-coffee-break-version-upgrade-related-reindexing-reasons/</link>
      <pubDate>Wed, 25 Feb 2026 11:00:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://percona.community/blog/">Percona Community Blog - learn about MySQL, MariaDB, PostgreSQL, and MongoDB</source>
      <description><![CDATA[<p>During FOSDEM I had a chance to join a presentation of the backup procedure that the engineers from GitLab followed to decrease the downtime during major upgrades. I highly recommend the talk, definitely worth watching!</p>
<p>The post <a rel="nofollow" href="https://percona.community/blog/2026/02/25/postgresql-coffee-break-version-upgrade-related-reindexing-reasons/">PostgreSQL coffee break: version upgrade related reindexing &#8211; reasons</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>During <a href="https://fosdem.org/2026/" target="_blank" rel="noopener noreferrer">FOSDEM</a> I had a chance to join a <a href="https://fosdem.org/2026/schedule/event/ZF8ZLX-zero-downtime-postgresql-upgrades/" target="_blank" rel="noopener noreferrer">presentation</a> of the backup procedure that the engineers from GitLab followed to decrease the downtime during major upgrades. I highly recommend the talk, definitely worth watching!</p>
<p>I loved the presentation, especially since a similar procedure is what we recommend our users as well. Unfortunately it&rsquo;s not for everyone: it&rsquo;s applicable ONLY when you can stop DDL operations on your database cluster for some time. As you can imagine that&rsquo;s not a case for every deployment.</p>
<p>This limitation got me into some very engaging discussions during my fav part of any conference, the so called &ldquo;hallway track&rdquo;. Networking and discussions after the talks and on the conference corridors are why I like going to such events. I&rsquo;ve heard some stories of nasty surprises coming out of the unexpected re-indexing after an upgrade.</p>
<p>It made me wonder, how many professionals have moved to PostgreSQL from other areas of expertise and are lacking information about the index rebuilds after upgrades may be necessary. How often are DevOps engineers or SREs, neither fluent in PostgreSQL nor experienced with databases, tasked with maintaining database infrastructure?</p>
<p>In the meantime I figured out that a short coffee time read is what I want to aim at. No super deep dives, rather food for thought and inspiring more of those engaging discussions I like so much about the conferences.</p>
<p>So let&rsquo;s get to it. This week I want to go through some basic facts before diving into more challenging topics in the coming weeks.</p>
<h3 id="what-are-collations">What are collations<a class="anchor-link" id="what-are-collations"></a></h3>
<p>In general, a collation defines how values are ordered and compared. In databases, it most commonly applies to text. It&rsquo;s often not a trivial thing to determine how strings are sorted, whether two values are considered equal, and how things like upper vs lower case or special characters are treated. Just like alphabetical order helps us organize words in everyday life, collations define the rules the database uses when comparing and sorting character data. This allows us to sort structures like strings. Numbers are even simpler.</p>
<p>A good visualization of how significant collations are is when you try to imagine determining whether one item is before another if they come from different alphabets. Where do you position country specific characters in such a sorting order?</p>
<p>Lets look at a set of example data:</p>
<div class="code-block">
<div class="code-block__header"><button class="code-block__copy" type="button" data-copy-target="codeblock-0" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-0">
<div class="highlight">
<pre class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Bob
</span></span><span class="line"><span class="cl">Anna
</span></span><span class="line"><span class="cl">Zo&euml;
</span></span><span class="line"><span class="cl">&Aacute;lvaro</span></span></code></pre>
</div>
</div>
</div>
<p>When using an English-like collation the sorting would look like this as <code>&Aacute;</code> is treated like <code>A</code> . This is visible in Collation 1:</p>
<div class="code-block">
<div class="code-block__header"><button class="code-block__copy" type="button" data-copy-target="codeblock-1" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-1">
<div class="highlight">
<pre class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Anna
</span></span><span class="line"><span class="cl">&Aacute;lvaro
</span></span><span class="line"><span class="cl">Bob
</span></span><span class="line"><span class="cl">Zo&euml;</span></span></code></pre>
</div>
</div>
</div>
<p>Though with a collation changed so that <code>&Aacute;</code> is sorted separately before <code>A</code> . This is visible in Collation 2:</p>
<div class="code-block">
<div class="code-block__header"><button class="code-block__copy" type="button" data-copy-target="codeblock-2" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-2">
<div class="highlight">
<pre class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">&Aacute;lvaro
</span></span><span class="line"><span class="cl">Anna
</span></span><span class="line"><span class="cl">Bob
</span></span><span class="line"><span class="cl">Zo&euml;</span></span></code></pre>
</div>
</div>
</div>
<p>It&rsquo;s clearly visible that collation defines how text is sorted and compared.</p>
<h3 id="what-are-indexes">What are indexes<a class="anchor-link" id="what-are-indexes"></a></h3>
<p>If you&rsquo;re not a database professional, you may not be familiar with what an index is. Think of it as of a structure that speeds up the search. In an old school library you had to check the index of authors to find the book you&rsquo;ve been looking for. Similarly in databases when knowing what the data is going to be searched for, we introduce indexes.</p>
<p>A common use of indexes is to enforce uniqueness of values. Primary keys and unique constraints automatically create unique indexes to ensure that no duplicate values are inserted. In addition, users can create their own indexes to support specific query patterns. Indexes can be single or multi column, depending on the particular use cases and to help speed up specific queries.<br>
Let&rsquo;s look at an example of physically unsorted data stored in heap</p>
<div class="code-block">
<div class="code-block__header"><button class="code-block__copy" type="button" data-copy-target="codeblock-3" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-3">
<div class="highlight">
<pre class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Users (unsorted data)
</span></span><span class="line"><span class="cl">--------------------
</span></span><span class="line"><span class="cl">[1] Zo&euml;
</span></span><span class="line"><span class="cl">[2] &Aacute;lvaro
</span></span><span class="line"><span class="cl">[3] Anna
</span></span><span class="line"><span class="cl">[4] Bob</span></span></code></pre>
</div>
</div>
</div>
<p>with an Index on name</p>
<div class="code-block">
<div class="code-block__header"><button class="code-block__copy" type="button" data-copy-target="codeblock-4" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-4">
<div class="highlight">
<pre class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Index (B-tree on name)
</span></span><span class="line"><span class="cl">----------------------
</span></span><span class="line"><span class="cl">Anna -&gt; [3]
</span></span><span class="line"><span class="cl">Bob -&gt; [4]
</span></span><span class="line"><span class="cl">Zo&euml; -&gt; [1]
</span></span><span class="line"><span class="cl">&Aacute;lvaro -&gt; [2]</span></span></code></pre>
</div>
</div>
</div>
<p>The index stores values in sorted order and points to their location in the table. Scanning through the list in order is fine for small number of items, as indexes get larger, there are opportunities to optimize this process. The most commonly used optimisation, very simplified, is to split the list and create a pointer which stores the maximum value in the left half of the list and the minimum value in the right half. As the lists get too large again, they are split again, creating a tree of these pointers.</p>
<h3 id="why-re-indexing-is-needed">Why re-indexing is needed<a class="anchor-link" id="why-re-indexing-is-needed"></a></h3>
<p>If the rules for comparing the strings are different at query time from what they were when the index was created, the search may fail in various ways. If the scan encounters a value which, by the rules at query time, is higher in the sort order than the value being searched for, it will conclude that the value being searched for is not in the list. Similarly, the search may follow a pointer to the wrong list of values. To make this more concrete, let&rsquo;s look at a <a href="https://lists.debian.org/debian-glibc/2019/03/msg00030.html" target="_blank" rel="noopener noreferrer">real world example</a> from a <code>glibc</code> change in 2019. Before the change, a list of our values was sorted as follows</p>
<div class="code-block">
<div class="code-block__header"><button class="code-block__copy" type="button" data-copy-target="codeblock-5" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-5">
<div class="highlight">
<pre class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">aa
</span></span><span class="line"><span class="cl">a a
</span></span><span class="line"><span class="cl">a-a
</span></span><span class="line"><span class="cl">a+a</span></span></code></pre>
</div>
</div>
</div>
<p>after the change, the list was sorted as follows:</p>
<div class="code-block">
<div class="code-block__header"><button class="code-block__copy" type="button" data-copy-target="codeblock-6" aria-label="Copy code to clipboard"><br>
<span class="code-block__copy-default">Copy</span><br>
<span class="code-block__copy-success" aria-hidden="true">Copied!</span><br>
</button>
</div>
<div class="code-block__content" id="codeblock-6">
<div class="highlight">
<pre class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">a a
</span></span><span class="line"><span class="cl">a+a
</span></span><span class="line"><span class="cl">a-a
</span></span><span class="line"><span class="cl">aa</span></span></code></pre>
</div>
</div>
</div>
<p>If an index was built under the first set of rules and searched under the second, a search for the value &lsquo;a a&rsquo; will fail, because the first item in the index (&lsquo;aa&rsquo;) is higher in the sort order than the value being searched for. Failing to find a value which is in the list can cause issues with application behavior &ndash; like missing records or records which appear in some queries (when the index is not used) but not in others (where the index is used). It may allow the insertion of values that should now be considered equal under the new collation rules, effectively violating uniqueness expectations.</p>
<p>Since PostgreSQL 10, the server tracks the collation version used when an index was built and can detect when it no longer matches the system&rsquo;s current collation version, which is why reindexing may suddenly become mandatory after an upgrade.</p>
<p>However, PostgreSQL server does not analyze your actual data to determine whether the collation change affects your stored values. It only detects that the collation provider version has changed. In some cases, this means reindexing is required even though the effective sort order of your specific data has not changed. Because PostgreSQL cannot reliably determine whether your specific dataset is affected, it treats the situation as potentially unsafe.</p>
<h3 id="when-do-collations-change">When do collations change?<a class="anchor-link" id="when-do-collations-change"></a></h3>
<p><figure>
<img decoding="async" src="https://percona.community/blog/2026/02/Jan-glibc-confusion.png" alt="&nbsp;"></figure>
</p>
<p>There&rsquo;s a number of scenarios when collations change:</p>
<ul>
<li>OS / <code>glibc</code> upgrade (Linux) &ndash; PostgreSQL relies on the system <code>glibc</code> provided collations so if a version changes, the collation rules may change as well. The example above is <a href="https://lists.debian.org/debian-glibc/2019/03/msg00030.html" target="_blank" rel="noopener noreferrer">taken from the upgrade to glibc 2.28</a> which is one <a href="https://wiki.postgresql.org/wiki/Locale_data_changes" target="_blank" rel="noopener noreferrer">PostgreSQL Community remembers</a> due to the ripple effect it caused.</li>
<li>ICU library upgrade (for ICU collations) &ndash; if PostgreSQL deployment uses ICU collations, upgrading ICU library will affect these. While they are not tied to <code>glibc</code> these changes also happen.</li>
<li>Major PostgreSQL upgrade (in some cases) &ndash; if the new version uses a different collation provider behavior or updated ICU integration</li>
<li>Database restored on a system with different collation versions &ndash; logical dump/restore onto a host with different <code>glibc</code> / ICU versions can invalidate indexes.</li>
</ul>
<h3 id="when-re-indexing-is-necessary">When re-indexing is necessary<a class="anchor-link" id="when-re-indexing-is-necessary"></a></h3>
<p>Looking at the above provided list an observation is quite immediate, that not every collation type is affected:</p>
<ul>
<li>if the collation is not dependent on <code>glibc</code> / ICU then it won&rsquo;t be affected. As such C/POSIX collations are immune to such issues.</li>
<li>Same truth sticks to the data types. The collation change problem affects only those indexes which are the text or character based. All other datatypes like all types of integers and floating points, date, timestamp,&nbsp; geometric data types or even vector data remain unaffected.</li>
</ul>
<p>What&rsquo;s also important is that even if a collation changed it&rsquo;s not necessary it will affect a given index. Think of it this way, if your database does not use any language specific characters, chances are that collation change will not require re-index. Unfortunately you don&rsquo;t know that until you look inside your data.<br>
In controlled environments, teams may assess the risk before scheduling reindexing, but from PostgreSQL&rsquo;s perspective the index must be considered potentially inconsistent until rebuilt.</p>
<h3 id="whats-next">What&rsquo;s next?<a class="anchor-link" id="whats-next"></a></h3>
<p>Next week we&rsquo;ll look at what is the reality of the DBA team regarding upgrades</p>

<p>The post <a rel="nofollow" href="https://percona.community/blog/2026/02/25/postgresql-coffee-break-version-upgrade-related-reindexing-reasons/">PostgreSQL coffee break: version upgrade related reindexing &#8211; reasons</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>MariaDB innovation: vector index performance</title>
      <link>https://smalldatum.blogspot.com/2026/02/mariadb-innovation-vector-index.html</link>
      <pubDate>Mon, 23 Feb 2026 16:54:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://smalldatum.blogspot.com/">Small Datum</source>
      <description><![CDATA[<p>Last year I shared many posts documenting MariaDB performance for vector search using ann-benchmarks. Performance was great in MariaDB 11 and this blog post explains that it is even better in MariaDB 12. This work was done by Small Datum LLC and sponsored by the MariaDB Foundation. My previous posts were published in January and February 2025.tl;drVector search recall vs precision in MariaDB 12.3 is better than in MariaDB 11.8Vector search recall vs precision in Maria 11.8 is better than in Postgres 18.2 with pgvector 0.8.1The improvements in MariaDB 12.3 are more significant for larger datasetsMariaDB 12.3 has the best results because it use less CPU per queryBenchmarkThis post has much more detail about my approach. I ran the benchmark for 1 session. I use ann-benchmarks via my fork of a fork of a fork at this commit.  The ann-benchmarks config files are here for MariaDB and for Postgres.This time I used the dbpedia-openai-X-angular tests for X in 100k, 500k and 1000k.For hardware I used a larger server (Hetzner ax162-s) with 48 cores, 128G of RAM, Ubuntu 22.04 and HW RAID 10 using 2 NVMe devices. For databases I used:MariaDB versions 11.8.5 and 12.3.0 with this config file. Both were compiled from source. Postgres 18.2 with pgvector 0.8.1 with this config file. These were compiled from source. For Postgres tests were run with and without halfvec (float16).I had ps and vmstat running during the benchmark and confirmed there weren\'t storage reads as the table and index were cached by MariaDB and Postgres.The command lines to run the benchmark using my helper scripts are:    bash rall.batch.sh v1 dbpedia-openai-100k-angular c32r128    bash rall.batch.sh v1 dbpedia-openai-500k-angular c32r128    bash rall.batch.sh v1 dbpedia-openai-1000k-angular c32r128Results: dbpedia-openai-100k-angularSummaryMariaDB 12.3 has the best resultsthe difference between MariaDB 12.3 and 11.8 is smaller here than it is below for 500k and 1000kResults: dbpedia-openai-500k-angularSummaryMariaDB 12.3 has the best resultsthe difference between MariaDB 12.3 and 11.8 is larger here than above for 100kResults: dbpedia-openai-1000k-angularSummaryMariaDB 12.3 has the best resultsthe difference between MariaDB 12.3 and 11.8 is larger here than it is above for 100k and 500k</p>
<p>The post <a rel="nofollow" href="https://smalldatum.blogspot.com/2026/02/mariadb-innovation-vector-index.html">MariaDB innovation: vector index performance</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>Last year I shared many posts documenting MariaDB performance for vector search using <a href="https://github.com/erikbern/ann-benchmarks">ann-benchmarks</a>. Performance was great in MariaDB 11 and this blog post explains that it is even better in MariaDB 12. This work was done by&nbsp;<a href="https://smalldatum.github.io/">Small Datum LLC</a>&nbsp;and sponsored by the MariaDB Foundation. My previous posts were published in <a href="https://smalldatum.blogspot.com/2025/01/">January</a> and <a href="https://smalldatum.blogspot.com/2025/02/">February</a> 2025.</p>
<p>tl;dr</p>

<ul style="text-align: left">
<li>Vector search recall vs precision in MariaDB 12.3 is better than in MariaDB 11.8</li>
<li>Vector search recall vs precision in Maria 11.8 is better than in Postgres 18.2 with pgvector 0.8.1</li>
<li>The improvements in MariaDB 12.3 are more significant for larger datasets</li>
<li>MariaDB 12.3 has the best results because it use less CPU per query</li>
</ul>
<p><b>Benchmark</b></p>
<div>
<div><a href="https://smalldatum.blogspot.com/2025/01/evaluating-vector-indexes-in-mariadb.html">This post</a>&nbsp;has much more detail about my approach. I ran the benchmark for 1 session. I use&nbsp;<a href="https://github.com/erikbern/ann-benchmarks/">ann-benchmarks</a>&nbsp;via my&nbsp;<a href="https://github.com/mdcallag/ann-benchmarks-from-vuvova">fork of a fork of a fork</a>&nbsp;at&nbsp;<a href="https://github.com/mdcallag/ann-benchmarks-from-vuvova/commit/f0c0d0ccbbe765c6d758239eb95406b5dd07845d">this commit</a>.&nbsp; The ann-benchmarks config files are here&nbsp;<a href="https://github.com/mdcallag/mytools/blob/master/bench/arc/jan25.ann.gist-960-euclidean.v1/dop_1/pg172/config.yml.mariadb">for MariaDB</a>&nbsp;and&nbsp;<a href="https://github.com/mdcallag/mytools/blob/master/bench/arc/jan25.ann.gist-960-euclidean.v1/dop_1/pg172/config.yml.pgvector">for Postgres</a>.</div>
<div></div>
<div>This time I used the dbpedia-openai-X-angular tests for X in 100k, 500k and 1000k.</div>
<div></div>
<div>For hardware I used a larger server (Hetzner ax162-s) with 48 cores, 128G of RAM, Ubuntu 22.04 and HW RAID 10 using 2 NVMe devices.&nbsp;</div>
<div></div>
<div>For databases I used:</div>
<div>
<ul style="text-align: left">
<li>MariaDB versions 11.8.5 and 12.3.0 with&nbsp;<a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c32r128/ma1203/etc/my.cnf.cz12b_vector_c32r128">this config file</a>. Both were compiled from source.&nbsp;</li>
<li>Postgres 18.2 with pgvector 0.8.1 with&nbsp;<a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c32r128/pg18beta3_o2nofp/conf.diff.cx10a_vector_c32r128">this config file</a>. These were compiled from source. For Postgres tests were run with and without halfvec (float16).</li>
</ul>
</div>
<div>I had ps and vmstat running during the benchmark and confirmed there weren&rsquo;t storage reads as the table and index were cached by MariaDB and Postgres.</div>
<div>The command lines to run the benchmark using my&nbsp;<a href="https://github.com/mdcallag/mytools/tree/master/bench/arc/jan25.ann.gist-960-euclidean.v1/helper_scripts">helper scripts</a>&nbsp;are:<br><span style="font-family: courier">&nbsp; &nbsp; bash rall.batch.sh v1&nbsp;</span><span style="font-family: courier">dbpedia-openai-100k-angular c32r128</span></div>
<div><span style="font-family: courier">&nbsp; &nbsp; bash rall.batch.sh v1&nbsp;</span><span style="font-family: courier">dbpedia-openai-500k-angular c32r128</span></div>
<div>
<div><span style="font-family: courier">&nbsp; &nbsp; bash rall.batch.sh v1&nbsp;</span><span style="font-family: courier">dbpedia-openai-1000k-angular c32r128</span></div>
</div>
</div>
<div><span style="font-family: courier"><br></span></div>
<div><span style="font-family: courier"><b style="font-family: Times">Results: dbpedia-openai-100k-angular</b></span></div>
<div></div>
<div>
<div>Summary</div>
<div>
<ul>
<li>MariaDB 12.3 has the best results</li>
<li>the difference between MariaDB 12.3 and 11.8 is smaller here than it is below for 500k and 1000k</li>
</ul>
</div>
</div>
<div><span style="font-family: courier">
<div class="separator" style="clear: both;text-align: center"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghoAH4mzg4DbO9v-w27cVrEMvi6urszwdzCMxe4oeR8uCtd_oQLIBeM4-cqGIhNXPWKTZRpuSBQ5kXHb0uQvydPor3iSnFNvQgyEqwm6wf158-LYDb7Aaq6ggXdbOuUeg5Rvjgq5sYKlQLQvyI6Hl1Q8-QQrFTC8x2OgRSpAaK6O75IoktJRlGwjJ-baeF/s1173/dbpedia-openai-100k-angular.png" style="margin-left: 1em;margin-right: 1em"><img decoding="async" loading="lazy" border="0" data-original-height="778" data-original-width="1173" height="424" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghoAH4mzg4DbO9v-w27cVrEMvi6urszwdzCMxe4oeR8uCtd_oQLIBeM4-cqGIhNXPWKTZRpuSBQ5kXHb0uQvydPor3iSnFNvQgyEqwm6wf158-LYDb7Aaq6ggXdbOuUeg5Rvjgq5sYKlQLQvyI6Hl1Q8-QQrFTC8x2OgRSpAaK6O75IoktJRlGwjJ-baeF/w640-h424/dbpedia-openai-100k-angular.png" width="640"></a></div>
<p><b>Results: dbpedia-openai-500k-angular</b></p></span></div>
<div><span style="font-family: courier">
<div style="font-family: Times"></div>
<div style="font-family: Times">Summary</div>
<div style="font-family: Times">
<ul style="text-align: left">
<li>MariaDB 12.3 has the best results</li>
<li>the difference between MariaDB 12.3 and 11.8 is larger here than above for 100k</li>
</ul>
</div>
<div style="font-family: Times"><span style="font-family: courier">
<div class="separator" style="clear: both;text-align: center"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-NyNnnIHAY68jdbydeP6fPt5UIBj605fT1U_9QsE752XlGmjrr_GoxrJcF13iSsb7vXMtdo_R7t74qEoEy5Ton9-5g6J3ZsU1dsXc1OJOKeyODmpJcyn1pvta7NTr-RB0HAQfT6ok-DZF0Y_l5PaQsYFqJ0PVQo8rN34hK1BAmO1daC77rhEipWC7hxDL/s1173/dbpedia-openai-500k-angular.png" style="margin-left: 1em;margin-right: 1em"><img decoding="async" loading="lazy" border="0" data-original-height="778" data-original-width="1173" height="424" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-NyNnnIHAY68jdbydeP6fPt5UIBj605fT1U_9QsE752XlGmjrr_GoxrJcF13iSsb7vXMtdo_R7t74qEoEy5Ton9-5g6J3ZsU1dsXc1OJOKeyODmpJcyn1pvta7NTr-RB0HAQfT6ok-DZF0Y_l5PaQsYFqJ0PVQo8rN34hK1BAmO1daC77rhEipWC7hxDL/w640-h424/dbpedia-openai-500k-angular.png" width="640"></a></div>
<p><b style="font-family: Times">Results: dbpedia-openai-1000k-angular</b></p></span></div>
<div style="font-family: Times"><span style="font-family: courier">
<div style="font-family: Times"></div>
<div style="font-family: Times">Summary</div>
<div style="font-family: Times">
<ul style="text-align: left">
<li>MariaDB 12.3 has the best results</li>
<li>the difference between MariaDB 12.3 and 11.8 is larger here than it is above for 100k and 500k</li>
</ul>
</div>
<div style="font-family: Times"><span style="font-family: courier">
<div class="separator" style="clear: both;text-align: center"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNnnpzCeUzX3yDzd_WybVB8o826nBL5XLt4OQTHz2Hrt2zR2UdQf-7uC_k1dIzZMYph15ZKzKWsF4N_rIaYzhyphenhyphenYgFGMcGXJufkVUNfAyacKDLqpqbDwy84KtxQpScLax6tulPCWfC2NQ_t7VRZ46g8ugCEpyX93h36WJZq46YZB7G2aYG2krQregdIf7wZ/s1173/dbpedia-openai-1000k-angular.png" style="margin-left: 1em;margin-right: 1em"><img decoding="async" loading="lazy" border="0" data-original-height="778" data-original-width="1173" height="424" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNnnpzCeUzX3yDzd_WybVB8o826nBL5XLt4OQTHz2Hrt2zR2UdQf-7uC_k1dIzZMYph15ZKzKWsF4N_rIaYzhyphenhyphenYgFGMcGXJufkVUNfAyacKDLqpqbDwy84KtxQpScLax6tulPCWfC2NQ_t7VRZ46g8ugCEpyX93h36WJZq46YZB7G2aYG2krQregdIf7wZ/w640-h424/dbpedia-openai-1000k-angular.png" width="640"></a></div>
<p><span style="font-family: Times"><br></span></p></span></div>
<p></p></span></div>
<p></p></span></div>

<p>The post <a rel="nofollow" href="https://smalldatum.blogspot.com/2026/02/mariadb-innovation-vector-index.html">MariaDB innovation: vector index performance</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>MySQL + Neo4j for AI Workloads: Why Relational Databases Still Matter</title>
      <link>https://anothermysqldba.blogspot.com/2026/02/mysql-neo4j-for-ai-workloads-why.html</link>
      <pubDate>Sun, 22 Feb 2026 00:01:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://anothermysqldba.blogspot.com/">Another MySQL DBA</source>
      <description><![CDATA[<p>So I figured it was about time I documented how to build persistent memory for AI agents using the databases you already know. Not vector databases - MySQL and Neo4j.</p>
<p>This isn\'t theoretical. I use this architecture daily, handling AI agent memory across multiple projects. Here\'s the schema and query patterns that actually work.</p>
<p>The Architecture</p>
<p>AI agents need two types of memory:</p>
<p> Structured memory - What happened, when, why (MySQL)<br />
 Pattern memory - What connects to what (Neo4j)</p>
<p>Vector databases are for similarity search. They\'re not for tracking workflow state or decision history. For that, you need ACID transactions and proper relationships.</p>
<p>The MySQL Schema</p>
<p>Here\'s the actual schema for AI agent persistent memory:</p>
<p>-- Architecture decisions the AI made<br />
CREATE TABLE architecture_decisions (<br />
  id INT AUTO_INCREMENT PRIMARY KEY,<br />
  project_id INT NOT NULL,<br />
  title VARCHAR(255) NOT NULL,<br />
  decision TEXT NOT NULL,<br />
  rationale TEXT,<br />
  alternatives_considered TEXT,<br />
  status ENUM(\'accepted\', \'rejected\', \'pending\') DEFAULT \'accepted\',<br />
  decided_at DATETIME DEFAULT CURRENT_TIMESTAMP,<br />
  tags JSON,<br />
  INDEX idx_project_date (project_id, decided_at),<br />
  INDEX idx_status (status)<br />
) ENGINE=InnoDB;</p>
<p>-- Code patterns the AI learned<br />
CREATE TABLE code_patterns (<br />
  id INT AUTO_INCREMENT PRIMARY KEY,<br />
  project_id INT NOT NULL,<br />
  category VARCHAR(50) NOT NULL,<br />
  name VARCHAR(255) NOT NULL,<br />
  description TEXT,<br />
  code_example TEXT,<br />
  language VARCHAR(50),<br />
  confidence_score FLOAT DEFAULT 0.5,<br />
  usage_count INT DEFAULT 0,<br />
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,<br />
  updated_at DATETIME ON UPDATE CURRENT_TIMESTAMP,<br />
  INDEX idx_project_category (project_id, category),<br />
  INDEX idx_confidence (confidence_score)<br />
) ENGINE=InnoDB;</p>
<p>-- Work session tracking<br />
CREATE TABLE work_sessions (<br />
  id INT AUTO_INCREMENT PRIMARY KEY,<br />
  session_id VARCHAR(255) UNIQUE NOT NULL,<br />
  project_id INT NOT NULL,<br />
  started_at DATETIME DEFAULT CURRENT_TIMESTAMP,<br />
  ended_at DATETIME,<br />
  summary TEXT,<br />
  context JSON,<br />
  INDEX idx_project_session (project_id, started_at)<br />
) ENGINE=InnoDB;</p>
<p>-- Pitfalls to avoid (learned from mistakes)<br />
CREATE TABLE pitfalls (<br />
  id INT AUTO_INCREMENT PRIMARY KEY,<br />
  project_id INT NOT NULL,<br />
  category VARCHAR(50),<br />
  title VARCHAR(255) NOT NULL,<br />
  description TEXT,<br />
  how_to_avoid TEXT,<br />
  severity ENUM(\'critical\', \'high\', \'medium\', \'low\'),<br />
  encountered_count INT DEFAULT 1,<br />
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,<br />
  INDEX idx_project_severity (project_id, severity)<br />
) ENGINE=InnoDB;</p>
<p>Foreign keys. Check constraints. Proper indexing. This is what relational databases are good at.</p>
<p>Query Patterns</p>
<p>Here\'s how you actually query this for AI agent memory:</p>
<p>-- Get recent decisions for context<br />
SELECT title, decision, rationale, decided_at<br />
FROM architecture_decisions<br />
WHERE project_id = ?<br />
 AND decided_at > DATE_SUB(NOW(), INTERVAL 30 DAY)<br />
ORDER BY decided_at DESC<br />
LIMIT 10;</p>
<p>-- Find high-confidence patterns<br />
SELECT category, name, description, code_example<br />
FROM code_patterns<br />
WHERE project_id = ?<br />
 AND confidence_score >= 0.80<br />
ORDER BY usage_count DESC, confidence_score DESC<br />
LIMIT 20;</p>
<p>-- Check for known pitfalls before implementing<br />
SELECT title, description, how_to_avoid<br />
FROM pitfalls<br />
WHERE project_id = ?<br />
 AND category = ?<br />
 AND severity IN (\'critical\', \'high\')<br />
ORDER BY encountered_count DESC;</p>
<p>-- Track session context across interactions<br />
SELECT context<br />
FROM work_sessions<br />
WHERE session_id = ?<br />
ORDER BY started_at DESC<br />
LIMIT 1;</p>
<p>These are straightforward SQL queries. EXPLAIN shows index usage exactly where expected. No surprises.</p>
<p>The Neo4j Layer</p>
<p>MySQL handles the structured data. Neo4j handles the relationships:</p>
<p>// Create nodes for decisions<br />
CREATE (d:Decision {<br />
 id: \'dec_123\',<br />
 title: \'Use FastAPI\',<br />
 project_id: 1,<br />
 embedding: [0.23, -0.45, ...] // Vector for similarity<br />
})</p>
<p>// Create relationships<br />
CREATE (d1:Decision {id: \'dec_123\', title: \'Use FastAPI\'})<br />
CREATE (d2:Decision {id: \'dec_45\', title: \'Used Flask before\'})<br />
CREATE (d1)-[:SIMILAR_TO {score: 0.85}]- >(d2)<br />
CREATE (d1)-[:CONTRADICTS]- >(d3:Decision {title: \'Avoid frameworks\'})</p>
<p>// Query: Find similar past decisions<br />
MATCH (current:Decision {id: $decision_id})<br />
MATCH (current)-[r:SIMILAR_TO]-(similar:Decision)<br />
WHERE r.score > 0.80<br />
RETURN similar.title, r.score<br />
ORDER BY r.score DESC</p>
<p>// Query: What outcomes followed this pattern?<br />
MATCH (d:Decision)-[:LEADS_TO]- >(o:Outcome)<br />
WHERE d.title CONTAINS \'Redis\'<br />
RETURN d.title, o.type, o.success_rate</p>
<p>How They Work Together</p>
<p>The flow looks like this:</p>
<p> AI agent generates content or makes a decision<br />
 Store structured data in MySQL (what, when, why, full context)<br />
 Generate embedding, store in Neo4j with relationships to similar items<br />
 Next session: Neo4j finds relevant similar decisions<br />
 MySQL provides the full details of those decisions</p>
<p>MySQL is the source of truth. Neo4j is the pattern finder.</p>
<p>Why Not Just Vector Databases?</p>
<p>I\'ve seen teams try to build AI agent memory with just Pinecone or Weaviate. It doesn\'t work well because:</p>
<p>Vector DBs are good for:</p>
<p> Finding documents similar to a query<br />
 Semantic search (RAG)<br />
 \"Things like this\"</p>
<p>Vector DBs are bad for:</p>
<p> \"What did we decide on March 15th?\"<br />
 \"Show me decisions that led to outages\"<br />
 \"What\'s the current status of this workflow?\"<br />
 \"Which patterns have confidence > 0.8 AND usage_count > 10?\"</p>
<p>Those queries need structured filtering, joins, and transactions. That\'s relational database territory.</p>
<p>MCP and the Future</p>
<p>The Model Context Protocol (MCP) is standardizing how AI systems handle context. Early MCP implementations are discovering what we already knew: you need both structured storage and graph relationships.</p>
<p>MySQL handles the MCP \"resources\" and \"tools\" catalog. Neo4j handles the \"relationships\" between context items. Vector embeddings are just one piece of the puzzle.</p>
<p>Production Notes</p>
<p>Current system running this architecture:</p>
<p> MySQL 8.0, 48 tables, ~2GB data<br />
 Neo4j Community, ~50k nodes, ~200k relationships<br />
 Query latency: MySQL</p>
<p>The post <a rel="nofollow" href="https://anothermysqldba.blogspot.com/2026/02/mysql-neo4j-for-ai-workloads-why.html">MySQL + Neo4j for AI Workloads: Why Relational Databases Still Matter</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>So I figured it was about time I documented how to build persistent memory for AI agents using the databases you already know. Not vector databases &ndash; MySQL and Neo4j.</p>
<p>This isn&rsquo;t theoretical. I use this architecture daily, handling AI agent memory across multiple projects. Here&rsquo;s the schema and query patterns that actually work.</p>
<h3>The Architecture<a class="anchor-link" id="the-architecture"></a></h3>
<p>AI agents need two types of memory:</p>
<ul>
<li><strong>Structured memory</strong> &ndash; What happened, when, why (MySQL)</li>
<li><strong>Pattern memory</strong> &ndash; What connects to what (Neo4j)</li>
</ul>
<p>Vector databases are for similarity search. They&rsquo;re not for tracking workflow state or decision history. For that, you need ACID transactions and proper relationships.</p>
<h3>The MySQL Schema<a class="anchor-link" id="the-mysql-schema"></a></h3>
<p>Here&rsquo;s the actual schema for AI agent persistent memory:</p>
<pre><code>-- Architecture decisions the AI made
CREATE TABLE architecture_decisions (
    id INT AUTO_INCREMENT PRIMARY KEY,
    project_id INT NOT NULL,
    title VARCHAR(255) NOT NULL,
    decision TEXT NOT NULL,
    rationale TEXT,
    alternatives_considered TEXT,
    status ENUM('accepted', 'rejected', 'pending') DEFAULT 'accepted',
    decided_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    tags JSON,
    INDEX idx_project_date (project_id, decided_at),
    INDEX idx_status (status)
) ENGINE=InnoDB;

-- Code patterns the AI learned
CREATE TABLE code_patterns (
    id INT AUTO_INCREMENT PRIMARY KEY,
    project_id INT NOT NULL,
    category VARCHAR(50) NOT NULL,
    name VARCHAR(255) NOT NULL,
    description TEXT,
    code_example TEXT,
    language VARCHAR(50),
    confidence_score FLOAT DEFAULT 0.5,
    usage_count INT DEFAULT 0,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_project_category (project_id, category),
    INDEX idx_confidence (confidence_score)
) ENGINE=InnoDB;

-- Work session tracking
CREATE TABLE work_sessions (
    id INT AUTO_INCREMENT PRIMARY KEY,
    session_id VARCHAR(255) UNIQUE NOT NULL,
    project_id INT NOT NULL,
    started_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    ended_at DATETIME,
    summary TEXT,
    context JSON,
    INDEX idx_project_session (project_id, started_at)
) ENGINE=InnoDB;

-- Pitfalls to avoid (learned from mistakes)
CREATE TABLE pitfalls (
    id INT AUTO_INCREMENT PRIMARY KEY,
    project_id INT NOT NULL,
    category VARCHAR(50),
    title VARCHAR(255) NOT NULL,
    description TEXT,
    how_to_avoid TEXT,
    severity ENUM('critical', 'high', 'medium', 'low'),
    encountered_count INT DEFAULT 1,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_project_severity (project_id, severity)
) ENGINE=InnoDB;</code></pre>
<p>Foreign keys. Check constraints. Proper indexing. This is what relational databases are good at.</p>
<h3>Query Patterns<a class="anchor-link" id="query-patterns"></a></h3>
<p>Here&rsquo;s how you actually query this for AI agent memory:</p>
<pre><code>-- Get recent decisions for context
SELECT title, decision, rationale, decided_at
FROM architecture_decisions
WHERE project_id = ?
  AND decided_at &gt; DATE_SUB(NOW(), INTERVAL 30 DAY)
ORDER BY decided_at DESC
LIMIT 10;

-- Find high-confidence patterns
SELECT category, name, description, code_example
FROM code_patterns
WHERE project_id = ?
  AND confidence_score &gt;= 0.80
ORDER BY usage_count DESC, confidence_score DESC
LIMIT 20;

-- Check for known pitfalls before implementing
SELECT title, description, how_to_avoid
FROM pitfalls
WHERE project_id = ?
  AND category = ?
  AND severity IN ('critical', 'high')
ORDER BY encountered_count DESC;

-- Track session context across interactions
SELECT context
FROM work_sessions
WHERE session_id = ?
ORDER BY started_at DESC
LIMIT 1;</code></pre>
<p>These are straightforward SQL queries. EXPLAIN shows index usage exactly where expected. No surprises.</p>
<h3>The Neo4j Layer<a class="anchor-link" id="the-neo4j-layer"></a></h3>
<p>MySQL handles the structured data. Neo4j handles the relationships:</p>
<pre><code>// Create nodes for decisions
CREATE (d:Decision {
  id: 'dec_123',
  title: 'Use FastAPI',
  project_id: 1,
  embedding: [0.23, -0.45, ...]  // Vector for similarity
})

// Create relationships
CREATE (d1:Decision {id: 'dec_123', title: 'Use FastAPI'})
CREATE (d2:Decision {id: 'dec_45', title: 'Used Flask before'})
CREATE (d1)-[:SIMILAR_TO {score: 0.85}]-&gt;(d2)
CREATE (d1)-[:CONTRADICTS]-&gt;(d3:Decision {title: 'Avoid frameworks'})

// Query: Find similar past decisions
MATCH (current:Decision {id: $decision_id})
MATCH (current)-[r:SIMILAR_TO]-(similar:Decision)
WHERE r.score &gt; 0.80
RETURN similar.title, r.score
ORDER BY r.score DESC

// Query: What outcomes followed this pattern?
MATCH (d:Decision)-[:LEADS_TO]-&gt;(o:Outcome)
WHERE d.title CONTAINS 'Redis'
RETURN d.title, o.type, o.success_rate</code></pre>
<h3>How They Work Together<a class="anchor-link" id="how-they-work-together"></a></h3>
<p>The flow looks like this:</p>
<ol>
<li>AI agent generates content or makes a decision</li>
<li>Store structured data in MySQL (what, when, why, full context)</li>
<li>Generate embedding, store in Neo4j with relationships to similar items</li>
<li>Next session: Neo4j finds relevant similar decisions</li>
<li>MySQL provides the full details of those decisions</li>
</ol>
<p>MySQL is the source of truth. Neo4j is the pattern finder.</p>
<h3>Why Not Just Vector Databases?<a class="anchor-link" id="why-not-just-vector-databases"></a></h3>
<p>I&rsquo;ve seen teams try to build AI agent memory with just Pinecone or Weaviate. It doesn&rsquo;t work well because:</p>
<p><strong>Vector DBs are good for:</strong></p>
<ul>
<li>Finding documents similar to a query</li>
<li>Semantic search (RAG)</li>
<li>&ldquo;Things like this&rdquo;</li>
</ul>
<p><strong>Vector DBs are bad for:</strong></p>
<ul>
<li>&ldquo;What did we decide on March 15th?&rdquo;</li>
<li>&ldquo;Show me decisions that led to outages&rdquo;</li>
<li>&ldquo;What&rsquo;s the current status of this workflow?&rdquo;</li>
<li>&ldquo;Which patterns have confidence &gt; 0.8 AND usage_count &gt; 10?&rdquo;</li>
</ul>
<p>Those queries need structured filtering, joins, and transactions. That&rsquo;s relational database territory.</p>
<h3>MCP and the Future<a class="anchor-link" id="mcp-and-the-future"></a></h3>
<p>The Model Context Protocol (MCP) is standardizing how AI systems handle context. Early MCP implementations are discovering what we already knew: you need both structured storage and graph relationships.</p>
<p>MySQL handles the MCP &ldquo;resources&rdquo; and &ldquo;tools&rdquo; catalog. Neo4j handles the &ldquo;relationships&rdquo; between context items. Vector embeddings are just one piece of the puzzle.</p>
<h3>Production Notes<a class="anchor-link" id="production-notes"></a></h3>
<p>Current system running this architecture:</p>
<ul>
<li>MySQL 8.0, 48 tables, ~2GB data</li>
<li>Neo4j Community, ~50k nodes, ~200k relationships</li>
<li>Query latency: MySQL &lt;10ms, Neo4j &lt;50ms</li>
<li>Backup: Standard mysqldump + neo4j-admin dump</li>
<li>Monitoring: Same Percona tools I&rsquo;ve used for years</li>
</ul>
<p>The operational complexity is low because these are mature databases with well-understood operational patterns.</p>
<h3>Too Much Work? Let AI Build It For You<a class="anchor-link" id="too-much-work-let-ai-build-it-for-you"></a></h3>
<p>Look, I get it. This is a lot of schema to set up, a lot of queries to write, a lot of moving parts.</p>
<p>Here&rsquo;s the thing: you don&rsquo;t have to type it all yourself. Copy the schema above, paste it into Claude Code or Kimi CLI, and tell it what you want to build. The AI will generate the Python code, the connection handling, the query patterns &ndash; all of it.</p>
<p>If you want to understand what&rsquo;s happening under the hood, start here:</p>
<p><a href="https://machinelearningmastery.com/building-a-simple-mcp-server-in-python/">Building a Simple MCP Server in Python</a></p>
<p>Then let your AI tool do the heavy lifting. That&rsquo;s literally what I did. The schema is mine, the architecture decisions are mine, but the implementation?<br>
  Claude wrote most of it while I watched and corrected.</p>
<p>Use the tools. That&rsquo;s what they&rsquo;re for.</p>
<h3>When to Use What<a class="anchor-link" id="when-to-use-what"></a></h3>
<table>
<tr>
<th>Use Case</th>
<th>Database</th>
</tr>
<tr>
<td>Workflow state, decisions, audit trail</td>
<td>MySQL/PostgreSQL</td>
</tr>
<tr>
<td>Pattern detection, similarity, relationships</td>
<td>Neo4j</td>
</tr>
<tr>
<td>Semantic document search (RAG)</td>
<td>Vector DB (optional)</td>
</tr>
</table>
<p>Start with MySQL for state. Add Neo4j when you need pattern recognition. Only add vector DBs if you&rsquo;re actually doing semantic document retrieval.</p>
<h3>Summary<a class="anchor-link" id="summary"></a></h3>
<p>AI agents need persistent memory. Not just embeddings in a vector database &ndash; structured, relational, temporal memory with pattern recognition.</p>
<p>MySQL handles the structured state. Neo4j handles the graph relationships. Together they provide what vector databases alone cannot.</p>
<p>Don&rsquo;t abandon relational databases for AI workloads. Use the right tool for each job, which is using both together.</p>
<p><strong>For more on the AI agent perspective on this architecture, see the companion post on <a href="https://3k1o.blogspot.com/2026/02/beyond-vector-databases-how-ai-actually.html">3k1o</a>.</strong></p>

<p>The post <a rel="nofollow" href="https://anothermysqldba.blogspot.com/2026/02/mysql-neo4j-for-ai-workloads-why.html">MySQL + Neo4j for AI Workloads: Why Relational Databases Still Matter</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Do AI models still keep getting better, or have they plateaued?</title>
      <link>https://optimizedbyotto.com/post/ai-models-plateaued-or-not/</link>
      <pubDate>Sun, 22 Feb 2026 00:00:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://optimizedbyotto.com/">Optimized by Otto</source>
      <description><![CDATA[<p>The AI hype is based on the assumption that the frontier AI labs are producing better and better foundational models at an accelerating pace. Is that really true, or are people just in sort of a mass psychosis because AI models have become so good at mimicking human behavior that we unconsciously attribute increasing intelligence to them? I decided to conduct a mini-benchmark of my own to find out if the latest and greatest AI models are actually really good or not.<br />
The problem with benchmarks<br />
Every time any team releases a new LLM, they boast how well it performs on various industry benchmarks such as Humanity’s Last Exam, SWE-Bench and Ai2 ARC or ARC-AGI. An overall leaderboard can be viewed at LLM-stats. This incentivizes teams to optimize for specific benchmarks, which might make them excel on specific tasks while general abilities degrade. Also, the older a benchmark dataset is, the more online material there is discussing the questions and best answers, which in turn increases the chances of newer models trained on more recent web content scoring better.<br />
Thus I prefer looking at real-time leaderboards such as the LM Arena leaderboard (or OpenCompass for Chinese models that might be missing from LM Arena). However, even though the LM Arena Elo score is rated by humans in real-time, the benchmark can still be played. For example, Meta reportedly used a special chat-optimized model instead of the actual Llama 4 model when getting scored on the LM Arena.<br />
Therefore I trust my own first-hand experience more than the benchmarks for gaining intuition. Intuition however is not a compelling argument in discussions on whether or not new flagship AI models have plateaued. Thus, I decided to devise my own mini-benchmark so that no model could have possibly seen it in its training data or be specifically optimized for it in any way.<br />
My mini-benchmark<br />
I crafted 6 questions based on my own experience using various LLMs for several years and having developed some intuition about what kinds of questions LLMs typically struggle with.<br />
I conducted the benchmark using the OpenRouter.ai chat playroom with the following state-of-the-art models:</p>
<p>Claude Opus 4.6 (Anthropic)<br />
GPT-5.2 (OpenAI)<br />
Grok 4.1 (xAI)<br />
Gemini 3.1 Pro Preview (Google)<br />
GLM 5 (Z.ai)<br />
MinMax M2.5 (MinMax)<br />
Qwen3.5 Plus 2026-02-15 (Alibaba)<br />
Kimi K2.5 (Moonshot.ai)</p>
<p>OpenRouter.ai is great as it very easy to get responses from multiple models in parallel to a single question. Also it allows to turn off web search to force the models to answer purely based on their embedded knowledge.</p>
<p>Common for all the test questions is that they are fairly straightforward and have a clear answer, yet the answer isn’t common knowledge or statistically the most obvious one, and instead requires a bit of reasoning to get correct.<br />
Some of these questions are also based on myself witnessing a flagship model failing miserably to answer it.<br />
1. Which cities have hosted the Olympics more than just once?<br />
This question requires accounting for both summer and winter Olympics, and for Olympics hosted across multiple cities.<br />
The variance in responses comes from if the model understands that Beijing should be counted as it has hosted both summer and winter Olympics. Interestingly GPT was the only model to not mention Beijing at all. Some variance also comes from how models account for co-hosted Olympics. For example Cortina should be counted as having hosted the Olympics twice, in 1956 and 2026, but only Claude, Gemini and Kimi pointed this out. Stockholm’s 1956 hosting of the equestrian games during the Melbourne Olympics is a special case, which GPT, Gemini and Kimi pointed out in a side note. Some models seem to have old training material, and for example Grok assumes the current year is 2024. All models that accounted for awarded future Olympics (e.g. Los Angeles 2028) marked them clearly as upcoming.<br />
Overall I would judge that only GPT and MinMax gave incomplete answers, while all other models replied as the best humans could reasonably have.<br />
2. If EUR/USD continues to slide to 1.5 by mid-2026, what is the likely effect on BMW’s stock price by end of 2026?<br />
This question requires mapping the currency exchange rate to historic value, dodging the misleading word “slide”, and reasoning on where the revenue of a company comes from and how a weaker US dollar affects it in multiple ways. I’ve frequently witnessed flagship models get it wrong how interest rates and exchange rates work. Apparently the binary choice between “up” or “down” is somehow challenging to the internal statistical model in the LLMs on a topic where there are a lot of training material that talk about both things being likely to happen, and choosing between them requires specifically reasoning about the scenario at hand and disregarding general knowledge of the situation.<br />
However, this time all the models concluded correctly that a weak dollar would have a negative overall effect on the BMW stock price. Gemini, GLM, Qwen and Kimi also mention the potential hedging effect of BMW’s X-series production in South Carolina for worldwide export.<br />
3. What is the Unicode code point for the traffic cone emoji?<br />
This was the first question where the the flagship models clearly still struggle in 2026. The trap here is that there is no traffic cone emoji, so an advanced model should simply refuse to give any Unicode numbers at all. Most LLMs however have an urge to give some answer, leading to hallucinations. Also, as the answer has a graphical element to it, the LLM might not understand how the emoji “looks” in ways that would be obvious to a human, and thus many models claim the construction sign emoji is a traffic cone, which it is not.<br />
By far the worst response was from GPT, that simply hallucinates and stops there:</p>
<p>While Gemini and Grok were among the three models not falling into this trap, the response from Claude was exemplary good:</p>
<p>4. Which languages are the 10 most commonly spoken in the world? For each language, count from 1 to 5.<br />
This seemingly simple question can become quite complex when you account for the fact that other languages might also have their own character system, both for words and the numerals (e.g. Arabic واحد/one vs ١/1), and that numbers might have variations depending on what the thing being counted is (e.g in French object gender un/une), or whether plain numbers and occurrences have their own words (e.g. Russian один/odin vs раз/raz). I’ve also witnessed older models frequently mix up on what parts of the reply should be using what language.<br />
For a human this question is trivial, assuming they know the language. For LLMs it is surprisingly challenging. Grok and MinMax only gave a list of the most popular languages and stopped there without showing any words in the languages. Qwen got a bit further by trying to list numbers in each language, but the lists were only the numbers 1 to 5:</p>
<p>I have seen Grok do something similar before, which in fact inspired me to include this question in my mini-benchmark.<br />
GPT got a bit further, but for Hindi, Arabic and Bengali it listed the numerals in local script, not the number words. Gemini, GLM and Kimi gave a complete and correct answer as a list, while the absolute best answer and presentation was by Claude, that gave the table below:</p>
<p>Claude Opus 4.6<br />
10 Most Commonly Spoken Languages (by Total Speakers)</p>
<p>#<br />
Language<br />
1<br />
2<br />
3<br />
4<br />
5</p>
<p>1<br />
English<br />
One<br />
Two<br />
Three<br />
Four<br />
Five</p>
<p>2<br />
Mandarin Chinese<br />
一 (yī)<br />
二 (èr)<br />
三 (sān)<br />
四 (sì)<br />
五 (wǔ)</p>
<p>3<br />
Hindi<br />
एक (ek)<br />
दो (do)<br />
तीन (tīn)<br />
चार (chār)<br />
पाँच (pā̃ch)</p>
<p>4<br />
Spanish<br />
Uno<br />
Dos<br />
Tres<br />
Cuatro<br />
Cinco</p>
<p>5<br />
French<br />
Un<br />
Deux<br />
Trois<br />
Quatre<br />
Cinq</p>
<p>6<br />
Arabic<br />
واحد (wāḥid)<br />
اثنان (ithnān)<br />
ثلاثة (thalātha)<br />
أربعة (arbaʿa)<br />
خمسة (khamsa)</p>
<p>7<br />
Bengali<br />
এক (ek)<br />
দুই (dui)<br />
তিন (tin)<br />
চার (chār)<br />
পাঁচ (pā̃ch)</p>
<p>8<br />
Portuguese<br />
Um<br />
Dois<br />
Três<br />
Quatro<br />
Cinco</p>
<p>9<br />
Russian<br />
Один (odin)<br />
Два (dva)<br />
Три (tri)<br />
Четыре (chetyre)<br />
Пять (pyat\')</p>
<p>10<br />
Japanese<br />
一 (ichi)<br />
二 (ni)<br />
三 (san)<br />
四 (shi)<br />
五 (go)</p>
<p>Note: Rankings are based on estimated total speakers (native + non-native). Rankings can vary depending on the source and methodology used.</p>
<p>5. Count the number of drone launch pads in the picture.<br />
Together with the question, I uploaded this picture:</p>
<p>A human can easily count that there are 10 rows and 30+ columns in the grid, but because the picture resolution isn’t good enough, the exact number of columns can’t be counted, and the answer should be that there are at least 300 launch pads in the picture.<br />
GPT and Grok both guessed the count is zero. Instead of hallucinating some number they say zero, but it would have been better to not give any number at all, and just state that they are unable to perform the task. Gemini gave as its answer “101”, which is quite odd, but reading the reasoning section, it seems to have tried counting items in the image without reasoning much about what it is actually counting and that there is clearly a grid that can make the counting much easier. Both Qwen and Kimi state they can see four parallel structures, but are unable to count drone launch pads.<br />
The absolutely best answer was given by Claude, which counted 10-12 rows and 30-40+ columns, and concluded that there must be 300-500 drone launch pads. Very close to best human level - impressive!<br />
This question applied only to multi-modal models that can see images, so GLM and MinMax could not give any response.<br />
6. Explain why I am getting the error below, and what is the best way to fix it?<br />
Together with the question above, I gave this code block:</p>
<p>Copy</p>
<p>$ SH_SCRIPTS=\"$(mktemp; grep -Irnw debian/ -e \'^#!.*/sh\' &#124; sort -u &#124; cut -d \':\' -f 1 &#124;&#124; true)\"<br />
$ shellcheck -x --enable=all --shell=sh \"$SH_SCRIPTS\"<br />
/tmp/tmp.xQOpI5Nljx<br />
debian/tests/integration-tests: /tmp/tmp.xQOpI5Nljx<br />
debian/tests/integration-tests: openBinaryFile: does not exist (No such file or directory)$ SH_SCRIPTS=\"$(mktemp; grep -Irnw debian/ -e \'^#!.*/sh\' &#124; sort -u &#124; cut -d \':\' -f 1 &#124;&#124; true)\"<br />
$ shellcheck -x --enable=all --shell=sh \"$SH_SCRIPTS\"<br />
/tmp/tmp.xQOpI5Nljx<br />
debian/tests/integration-tests: /tmp/tmp.xQOpI5Nljx<br />
debian/tests/integration-tests: openBinaryFile: does not exist (No such file or directory)<br />
Older models would easily be misled by the last error message thinking that a file went missing, and focus on suggesting changes to the complex-looking first line. In reality the error is simply caused by having the quotes around the $SH_SCRIPTS, resulting in the entire multi-line string being passed as a single argument to shellcheck. So instead of receiving two separate file paths, shellcheck tries to open one file literally named /tmp/tmp.xQOpI5Nljxndebian/tests/integration-tests.<br />
Incorrect argument expansion is fairly easy for an experienced human programmer to notice, but tricky for an LLM. Indeed, Grok, MinMax, and Qwen fell for this trap and focused on the mktemp, assuming it somehow fails to create a file. Interestingly GLM fails to produce an answer at all, as the reasoning step seems to be looping, thinking too much about the missing file, but not understanding why it would be missing when there is nothing wrong with how mktemp is executed.<br />
Claude, Gemini, and Kimi immediately spot the real root cause of passing the variable quoted and suggested correct fixes that involve either removing the quotes, or using Bash arrays or xargs in a way that makes the whole command also handle correctly filenames with spaces in them.<br />
Conclusion</p>
<p>Model<br />
Sports<br />
Economics<br />
Emoji<br />
Languages<br />
Visual<br />
Shell<br />
Score</p>
<p>Claude Opus 4.6<br />
✓<br />
✓<br />
✓<br />
✓<br />
✓<br />
✓<br />
6/6</p>
<p>GPT-5.2<br />
✗<br />
✓<br />
✗<br />
~<br />
✗<br />
✓<br />
2.5/6</p>
<p>Grok 4.1<br />
✓<br />
✓<br />
✓<br />
✗<br />
✗<br />
✗<br />
3/6</p>
<p>Gemini 3.1 Pro<br />
✓<br />
✓<br />
✓<br />
✓<br />
✗<br />
✓<br />
5/6</p>
<p>GLM 5<br />
✓<br />
✓<br />
?<br />
✓<br />
N/A<br />
✗<br />
3/5</p>
<p>MinMax M2.5<br />
✗<br />
✓<br />
✗<br />
✗<br />
N/A<br />
✗<br />
1/5</p>
<p>Qwen3.5 Plus<br />
✓<br />
✓<br />
✗<br />
~<br />
✗<br />
✗<br />
2.5/6</p>
<p>Kimi K2.5<br />
✓<br />
✓<br />
✗<br />
✓<br />
✗<br />
✓<br />
4/6</p>
<p>Obviously, my mini-benchmark only had 6 questions, and I ran it only once. This was obviously not scientifically rigorous. However it was systematic enough to trump just a mere feeling.<br />
The main finding for me personally is that Claude Opus 4.6, the flagship model by Anthropic, seems to give great answers consistently. The answers are not only correct, but also well scoped giving enough information to cover everything that seems relevant, without blurping unnecessary filler.<br />
I used Claude extensively in 2023-2024 when it was the main model available at my day work, but for the past year I had been using other models that I felt were better at the time. Now Claude seems to be the best-of-the-best again, with Gemini and Kimi as close follow-ups. Comparing their pricing at OpenRouter.ai the Kimi K2.5 price of $0.6 / million tokens is almost 90% cheaper than the Claude Opus 4.6’s $5.0 / million tokens suggests that Kimi K2.5 offers the best price-per-performance ratio. Claude might be cheaper with a monthly subscription directly from Anthropic, potentially narrowing the price gap.<br />
Overall I do feel that Anthropic, Google and Moonshot.ai have been pushing the envelope with their latest models in a way that one can’t really claim that AI models have plateaued. In fact, one could claim that at least Claude has now climbed over the hill of “AI slop” and consistently produces valuable results. If and when AI usage expands from here, we might actually not drown in AI slop as chances of accidentally crappy results decrease. This makes me positive about the future.<br />
I am also really happy to see that there wasn’t just one model crushing everybody else, but that there are at least three models doing very well. As an open source enthusiast I am particularly glad to see that Moonshot.ai’s Kimi K2.5 is published with an open license. Given the hardware, anyone can run it on their own. OpenRouter.ai currently lists 9 independent providers alongside Moonshot.ai itself, showcasing the potential of open-weight models in practice.<br />
If the pattern holds and flagship models continue improving at this pace we might look back at 2026 as the year AI stopped feeling like a call center associate and started to resemble a scientific researcher. While new models become available we need to keep testing, keep questioning, and keep our expectations grounded in actual performance rather than press releases.<br />
Thanks to OpenRouter.ai for providing a great service that makes testing various models incredibly easy!</p>
<p>The post <a rel="nofollow" href="https://optimizedbyotto.com/post/ai-models-plateaued-or-not/">Do AI models still keep getting better, or have they plateaued?</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p><img decoding="async" src="https://optimizedbyotto.com/post/ai-models-plateaued-or-not/flagship-ai-mini-benchmark.png" alt="Featured image of post Do AI models still keep getting better, or have they plateaued?"></p>
<p>The AI hype is based on the assumption that the frontier AI labs are producing better and better foundational models <em>at an accelerating pace</em>. Is that really true, or are people just in sort of a mass psychosis because AI models have become so good at mimicking human behavior that we unconsciously attribute increasing intelligence to them? I decided to conduct a mini-benchmark of my own to find out if the latest and greatest AI models are actually really good or not.</p>
<h2 id="the-problem-with-benchmarks"><a href="#the-problem-with-benchmarks" class="header-anchor"></a>The problem with benchmarks<br>
<a class="anchor-link" id="the-problem-with-benchmarks"></a></h2>
<p>Every time any team releases a new LLM, they boast how well it performs on various industry benchmarks such as <a class="link" href="https://agi.safe.ai/" target="_blank" rel="noopener">Humanity&rsquo;s Last Exam</a>, <a class="link" href="https://www.swebench.com/" target="_blank" rel="noopener">SWE-Bench</a> and <a class="link" href="https://allenai.org/data/arc" target="_blank" rel="noopener">Ai2 ARC</a> or <a class="link" href="https://arcprize.org/leaderboard" target="_blank" rel="noopener">ARC-AGI</a>. An overall leaderboard can be viewed at <a class="link" href="https://llm-stats.com/" target="_blank" rel="noopener">LLM-stats</a>. This incentivizes teams to optimize for specific benchmarks, which might make them excel on specific tasks while general abilities degrade. <strong>Also, the older a benchmark dataset is, the more online material there is discussing the questions and best answers,</strong> which in turn increases the chances of newer models trained on more recent web content scoring better.</p>
<p>Thus I prefer looking at real-time leaderboards such as the <a class="link" href="https://arena.ai/leaderboard" target="_blank" rel="noopener">LM Arena leaderboard</a> (or <a class="link" href="https://rank.opencompass.org.cn/leaderboard-llm" target="_blank" rel="noopener">OpenCompass</a> for Chinese models that might be missing from LM Arena). However, even though the LM Arena Elo score is rated by humans in real-time, the benchmark can still be played. For example, <a class="link" href="https://www.heise.de/en/news/Meta-cheats-on-Llama-4-benchmark-10344087.html" target="_blank" rel="noopener">Meta reportedly</a> used a special chat-optimized model instead of the actual Llama 4 model when getting scored on the LM Arena.</p>
<p>Therefore I trust my own first-hand experience more than the benchmarks for gaining intuition. Intuition however is not a compelling argument in discussions on whether or not new flagship AI models have plateaued. Thus, I decided to devise my own mini-benchmark so that no model could have possibly seen it in its training data or be specifically optimized for it in any way.</p>
<h2 id="my-mini-benchmark"><a href="#my-mini-benchmark" class="header-anchor"></a>My mini-benchmark<br>
<a class="anchor-link" id="my-mini-benchmark"></a></h2>
<p>I crafted 6 questions based on my own experience using various LLMs for several years and having developed some intuition about what kinds of questions LLMs typically struggle with.</p>
<p>I conducted the benchmark using the <a class="link" href="https://openrouter.ai/chat?models=anthropic%2Fclaude-opus-4.6%2Copenai%2Fgpt-5.2%2Cx-ai%2Fgrok-4.1-fast%2Cgoogle%2Fgemini-3.1-pro-preview%2Cz-ai%2Fglm-5%2Cminimax%2Fminimax-m2.5%2Cqwen%2Fqwen3.5-plus-02-15%2Cmoonshotai%2Fkimi-k2.5" target="_blank" rel="noopener">OpenRouter.ai chat playroom</a> with the following state-of-the-art models:</p>
<ul>
<li><a class="link" href="https://openrouter.ai/anthropic/claude-opus-4.6" target="_blank" rel="noopener">Claude Opus 4.6 (Anthropic)</a></li>
<li><a class="link" href="https://openrouter.ai/openai/gpt-5.2" target="_blank" rel="noopener">GPT-5.2 (OpenAI)</a></li>
<li><a class="link" href="https://openrouter.ai/x-ai/grok-4.1-fast" target="_blank" rel="noopener">Grok 4.1 (xAI)</a></li>
<li><a class="link" href="https://openrouter.ai/google/gemini-3.1-pro-preview" target="_blank" rel="noopener">Gemini 3.1 Pro Preview (Google)</a></li>
<li><a class="link" href="https://openrouter.ai/z-ai/glm-5" target="_blank" rel="noopener">GLM 5 (Z.ai)</a></li>
<li><a class="link" href="https://openrouter.ai/minimax/minimax-m2.5" target="_blank" rel="noopener">MinMax M2.5 (MinMax)</a></li>
<li><a class="link" href="https://openrouter.ai/qwen/qwen3.5-plus-02-15" target="_blank" rel="noopener">Qwen3.5 Plus 2026-02-15 (Alibaba)</a></li>
<li><a class="link" href="https://openrouter.ai/moonshotai/kimi-k2.5" target="_blank" rel="noopener">Kimi K2.5 (Moonshot.ai)</a></li>
</ul>
<p>OpenRouter.ai is great as it very easy to get responses from multiple models in parallel to a single question. Also it allows to turn off web search to force the models to answer purely based on their embedded knowledge.</p>
<p><img decoding="async" src="https://optimizedbyotto.com/post/ai-models-plateaued-or-not/flagship-ai-mini-benchmark.gif" width="800" height="679" loading="lazy" alt="OpenRouter.ai Chat playroom" class="gallery-image" data-flex-grow="117" data-flex-basis="282px">
</p>
<p>Common for all the test questions is that they are fairly straightforward and have a clear answer, yet the answer isn&rsquo;t common knowledge or statistically the most obvious one, and instead requires a bit of reasoning to get correct.</p>
<p>Some of these questions are also based on myself witnessing a flagship model failing miserably to answer it.</p>
<h3 id="1-which-cities-have-hosted-the-olympics-more-than-just-once"><a href="#1-which-cities-have-hosted-the-olympics-more-than-just-once" class="header-anchor"></a>1. Which cities have hosted the Olympics more than just once?<br>
<a class="anchor-link" id="1-which-cities-have-hosted-the-olympics-more-than-just-once"></a></h3>
<p>This question requires accounting for both summer and winter Olympics, and for Olympics hosted across multiple cities.</p>
<p>The variance in responses comes from if the model understands that Beijing should be counted as it has hosted both summer and winter Olympics. Interestingly GPT was the only model to not mention Beijing at all. Some variance also comes from how models account for co-hosted Olympics. For example Cortina should be counted as having hosted the Olympics twice, in 1956 and 2026, but only Claude, Gemini and Kimi pointed this out. Stockholm&rsquo;s 1956 hosting of the equestrian games during the Melbourne Olympics is a special case, which GPT, Gemini and Kimi pointed out in a side note. Some models seem to have old training material, and for example Grok assumes the current year is 2024. All models that accounted for awarded future Olympics (e.g. Los Angeles 2028) marked them clearly as upcoming.</p>
<p>Overall I would judge that only GPT and MinMax gave incomplete answers, while all other models replied as the best humans could reasonably have.</p>
<h3 id="2-if-eurusd-continues-to-slide-to-15-by-mid-2026-what-is-the-likely-effect-on-bmws-stock-price-by-end-of-2026"><a href="#2-if-eurusd-continues-to-slide-to-15-by-mid-2026-what-is-the-likely-effect-on-bmws-stock-price-by-end-of-2026" class="header-anchor"></a>2. If EUR/USD continues to slide to 1.5 by mid-2026, what is the likely effect on BMW&rsquo;s stock price by end of 2026?<br>
<a class="anchor-link" id="2-if-eur-usd-continues-to-slide-to-1-5-by-mid-2026-what-is-the-likely-effect-on-bmws-stock-price-by-end-of-2026"></a></h3>
<p>This question requires mapping the currency exchange rate to historic value, dodging the misleading word &ldquo;slide&rdquo;, and reasoning on where the revenue of a company comes from and how a weaker US dollar affects it in multiple ways. I&rsquo;ve frequently witnessed flagship models get it wrong how interest rates and exchange rates work. Apparently the binary choice between &ldquo;up&rdquo; or &ldquo;down&rdquo; is somehow challenging to the internal statistical model in the LLMs on a topic where there are a lot of training material that talk about both things being likely to happen, and choosing between them requires specifically reasoning about the scenario at hand and disregarding general knowledge of the situation.</p>
<p>However, this time all the models concluded correctly that a weak dollar would have a negative overall effect on the BMW stock price. Gemini, GLM, Qwen and Kimi also mention the potential hedging effect of BMW&rsquo;s X-series production in South Carolina for worldwide export.</p>
<h3 id="3-what-is-the-unicode-code-point-for-the-traffic-cone-emoji"><a href="#3-what-is-the-unicode-code-point-for-the-traffic-cone-emoji" class="header-anchor"></a>3. What is the Unicode code point for the traffic cone emoji?<br>
<a class="anchor-link" id="3-what-is-the-unicode-code-point-for-the-traffic-cone-emoji"></a></h3>
<p>This was the first question where the the flagship models clearly still struggle in 2026. The trap here is that there is no traffic cone emoji, so an advanced model should simply refuse to give any Unicode numbers at all. Most LLMs however have an urge to give some answer, leading to hallucinations. Also, as the answer has a graphical element to it, the LLM might not understand how the emoji &ldquo;looks&rdquo; in ways that would be obvious to a human, and thus many models claim the construction sign emoji is a traffic cone, which it is not.</p>
<p>By far the worst response was from GPT, that simply hallucinates and stops there:</p>
<p><img decoding="async" src="https://optimizedbyotto.com/post/ai-models-plateaued-or-not/gpt-5.2-traffic-cone-emoji.png" width="899" height="117" loading="lazy" alt="OpenAIs GPT-5.2 completely wrong answer to traffic cone emoji question" class="gallery-image" data-flex-grow="768" data-flex-basis="1844px">
</p>
<p>While Gemini and Grok were among the three models not falling into this trap, the response from Claude was exemplary good:</p>
<p><img decoding="async" src="https://optimizedbyotto.com/post/ai-models-plateaued-or-not/claude-opus-4.6-traffic-cone-emoji.png" width="899" height="387" loading="lazy" alt="Claude Opus 4.6 exemplary good answer to traffic cone emoji question" class="gallery-image" data-flex-grow="232" data-flex-basis="557px">
</p>
<h3 id="4-which-languages-are-the-10-most-commonly-spoken-in-the-world-for-each-language-count-from-1-to-5"><a href="#4-which-languages-are-the-10-most-commonly-spoken-in-the-world-for-each-language-count-from-1-to-5" class="header-anchor"></a>4. Which languages are the 10 most commonly spoken in the world? For each language, count from 1 to 5.<br>
<a class="anchor-link" id="4-which-languages-are-the-10-most-commonly-spoken-in-the-world-for-each-language-count-from-1-to-5"></a></h3>
<p>This seemingly simple question can become quite complex when you account for the fact that other languages might also have their own character system, both for words and the numerals (e.g. Arabic &#1608;&#1575;&#1581;&#1583;/one vs &#1633;/1), and that numbers might have variations depending on what the thing being counted is (e.g in French object gender un/une), or whether plain numbers and occurrences have their own words (e.g. Russian &#1086;&#1076;&#1080;&#1085;/odin vs &#1088;&#1072;&#1079;/raz). I&rsquo;ve also witnessed older models frequently mix up on what parts of the reply should be using what language.</p>
<p>For a human this question is trivial, assuming they know the language. For LLMs it is surprisingly challenging. Grok and MinMax only gave a list of the most popular languages and stopped there without showing any words in the languages. Qwen got a bit further by trying to list numbers in each language, but the lists were only the numbers 1 to 5:</p>
<p><img decoding="async" src="https://optimizedbyotto.com/post/ai-models-plateaued-or-not/qwen3.5-plus-count-to-five.png" width="899" height="562" loading="lazy" alt="Qwen3.5 Plus count to five in top 10 languages" class="gallery-image" data-flex-grow="159" data-flex-basis="383px">
</p>
<p>I have seen Grok do something similar before, which in fact inspired me to include this question in my mini-benchmark.</p>
<p>GPT got a bit further, but for Hindi, Arabic and Bengali it listed the numerals in local script, not the number words. Gemini, GLM and Kimi gave a complete and correct answer as a list, while the absolute best answer and presentation was by Claude, that gave the table below:</p>
<blockquote>
<p><strong>Claude Opus 4.6</strong></p>
<p>10 Most Commonly Spoken Languages (by Total Speakers)</p>
<table>
<thead>
<tr>
<th>#</th>
<th>Language</th>
<th>1</th>
<th>2</th>
<th>3</th>
<th>4</th>
<th>5</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td><strong>English</strong></td>
<td>One</td>
<td>Two</td>
<td>Three</td>
<td>Four</td>
<td>Five</td>
</tr>
<tr>
<td>2</td>
<td><strong>Mandarin Chinese</strong></td>
<td>&#19968; (y&#299;)</td>
<td>&#20108; (&egrave;r)</td>
<td>&#19977; (s&#257;n)</td>
<td>&#22235; (s&igrave;)</td>
<td>&#20116; (w&#468;)</td>
</tr>
<tr>
<td>3</td>
<td><strong>Hindi</strong></td>
<td>&#2319;&#2325; (ek)</td>
<td>&#2342;&#2379; (do)</td>
<td>&#2340;&#2368;&#2344; (t&#299;n)</td>
<td>&#2330;&#2366;&#2352; (ch&#257;r)</td>
<td>&#2346;&#2366;&#2305;&#2330; (p&#257;&#771;ch)</td>
</tr>
<tr>
<td>4</td>
<td><strong>Spanish</strong></td>
<td>Uno</td>
<td>Dos</td>
<td>Tres</td>
<td>Cuatro</td>
<td>Cinco</td>
</tr>
<tr>
<td>5</td>
<td><strong>French</strong></td>
<td>Un</td>
<td>Deux</td>
<td>Trois</td>
<td>Quatre</td>
<td>Cinq</td>
</tr>
<tr>
<td>6</td>
<td><strong>Arabic</strong></td>
<td>&#1608;&#1575;&#1581;&#1583; (w&#257;&#7717;id)</td>
<td>&#1575;&#1579;&#1606;&#1575;&#1606; (ithn&#257;n)</td>
<td>&#1579;&#1604;&#1575;&#1579;&#1577; (thal&#257;tha)</td>
<td>&#1571;&#1585;&#1576;&#1593;&#1577; (arba&#703;a)</td>
<td>&#1582;&#1605;&#1587;&#1577; (khamsa)</td>
</tr>
<tr>
<td>7</td>
<td><strong>Bengali</strong></td>
<td>&#2447;&#2453; (ek)</td>
<td>&#2470;&#2497;&#2439; (dui)</td>
<td>&#2468;&#2495;&#2472; (tin)</td>
<td>&#2458;&#2494;&#2480; (ch&#257;r)</td>
<td>&#2474;&#2494;&#2433;&#2458; (p&#257;&#771;ch)</td>
</tr>
<tr>
<td>8</td>
<td><strong>Portuguese</strong></td>
<td>Um</td>
<td>Dois</td>
<td>Tr&ecirc;s</td>
<td>Quatro</td>
<td>Cinco</td>
</tr>
<tr>
<td>9</td>
<td><strong>Russian</strong></td>
<td>&#1054;&#1076;&#1080;&#1085; (odin)</td>
<td>&#1044;&#1074;&#1072; (dva)</td>
<td>&#1058;&#1088;&#1080; (tri)</td>
<td>&#1063;&#1077;&#1090;&#1099;&#1088;&#1077; (chetyre)</td>
<td>&#1055;&#1103;&#1090;&#1100; (pyat&rsquo;)</td>
</tr>
<tr>
<td>10</td>
<td><strong>Japanese</strong></td>
<td>&#19968; (ichi)</td>
<td>&#20108; (ni)</td>
<td>&#19977; (san)</td>
<td>&#22235; (shi)</td>
<td>&#20116; (go)</td>
</tr>
</tbody>
</table>
<p><em><strong>Note:</strong> Rankings are based on estimated <em>total</em> speakers (native + non-native). Rankings can vary depending on the source and methodology used.</em></p>
</blockquote>
<h3 id="5-count-the-number-of-drone-launch-pads-in-the-picture"><a href="#5-count-the-number-of-drone-launch-pads-in-the-picture" class="header-anchor"></a>5. Count the number of drone launch pads in the picture.<br>
<a class="anchor-link" id="5-count-the-number-of-drone-launch-pads-in-the-picture"></a></h3>
<p>Together with the question, I uploaded this picture:</p>
<p><img decoding="async" src="https://optimizedbyotto.com/post/ai-models-plateaued-or-not/drone-pad-counting-task.jpg" width="2196" height="1584" loading="lazy" alt="Drone pad picture attached to question above" class="gallery-image" data-flex-grow="138" data-flex-basis="332px">
</p>
<p>A human can easily count that there are 10 rows and 30+ columns in the grid, but because the picture resolution isn&rsquo;t good enough, the exact number of columns can&rsquo;t be counted, and the answer should be that there are at least 300 launch pads in the picture.</p>
<p>GPT and Grok both guessed the count is zero. Instead of hallucinating some number they say zero, but it would have been better to not give any number at all, and just state that they are unable to perform the task. Gemini gave as its answer &ldquo;101&rdquo;, which is quite odd, but reading the reasoning section, it seems to have tried counting items in the image without reasoning much about what it is actually counting and that there is clearly a grid that can make the counting much easier. Both Qwen and Kimi state they can see four parallel structures, but are unable to count drone launch pads.</p>
<p>The absolutely best answer was given by Claude, which counted 10-12 rows and 30-40+ columns, and concluded that there must be 300-500 drone launch pads. Very close to best human level &ndash; impressive!</p>
<p>This question applied only to multi-modal models that can see images, so GLM and MinMax could not give any response.</p>
<h3 id="6-explain-why-i-am-getting-the-error-below-and-what-is-the-best-way-to-fix-it"><a href="#6-explain-why-i-am-getting-the-error-below-and-what-is-the-best-way-to-fix-it" class="header-anchor"></a>6. Explain why I am getting the error below, and what is the best way to fix it?<br>
<a class="anchor-link" id="6-explain-why-i-am-getting-the-error-below-and-what-is-the-best-way-to-fix-it"></a></h3>
<p>Together with the question above, I gave this code block:</p>
<div class="codeblock ">
<header>
<span class="codeblock-lang"></span><br>
<button class="codeblock-copy" data-id="codeblock-id-0" data-copied-text="Copied!"><br>
Copy<br>
</button><br>
</header>
<p><code id="codeblock-id-0">$ SH_SCRIPTS="$(mktemp; grep -Irnw debian/ -e '^#!.*/sh' | sort -u | cut -d ':' -f 1 || true)"<br>
$ shellcheck -x --enable=all --shell=sh "$SH_SCRIPTS"<br>
/tmp/tmp.xQOpI5Nljx<br>
debian/tests/integration-tests: /tmp/tmp.xQOpI5Nljx<br>
debian/tests/integration-tests: openBinaryFile: does not exist (No such file or directory)</code></p>
<pre><code>$ SH_SCRIPTS="$(mktemp; grep -Irnw debian/ -e '^#!.*/sh' | sort -u | cut -d ':' -f 1 || true)"
$ shellcheck -x --enable=all --shell=sh "$SH_SCRIPTS"
/tmp/tmp.xQOpI5Nljx
debian/tests/integration-tests: /tmp/tmp.xQOpI5Nljx
debian/tests/integration-tests: openBinaryFile: does not exist (No such file or directory)</code></pre>
</div>
<p>Older models would easily be misled by the last error message thinking that a file went missing, and focus on suggesting changes to the complex-looking first line. In reality the error is simply caused by having the quotes around the <code>$SH_SCRIPTS</code>, resulting in the entire multi-line string being passed as a single argument to <code>shellcheck</code>. So instead of receiving two separate file paths, <code>shellcheck</code> tries to open one file literally named <code>/tmp/tmp.xQOpI5Nljxndebian/tests/integration-tests</code>.</p>
<p>Incorrect argument expansion is fairly easy for an experienced human programmer to notice, but tricky for an LLM. Indeed, Grok, MinMax, and Qwen fell for this trap and focused on the <code>mktemp</code>, assuming it somehow fails to create a file. Interestingly GLM fails to produce an answer at all, as the reasoning step seems to be looping, thinking too much about the missing file, but not understanding why it would be missing when there is nothing wrong with how <code>mktemp</code> is executed.</p>
<p>Claude, Gemini, and Kimi immediately spot the real root cause of passing the variable quoted and suggested correct fixes that involve either removing the quotes, or using Bash arrays or <code>xargs</code> in a way that makes the whole command also handle correctly filenames with spaces in them.</p>
<h2 id="conclusion"><a href="#conclusion" class="header-anchor"></a>Conclusion<br>
<a class="anchor-link" id="conclusion"></a></h2>
<table>
<thead>
<tr>
<th>Model</th>
<th>Sports</th>
<th>Economics</th>
<th>Emoji</th>
<th>Languages</th>
<th>Visual</th>
<th>Shell</th>
<th>Score</th>
</tr>
</thead>
<tbody>
<tr>
<td>Claude Opus 4.6</td>
<td>&#10003;</td>
<td>&#10003;</td>
<td>&#10003;</td>
<td>&#10003;</td>
<td>&#10003;</td>
<td>&#10003;</td>
<td>6/6</td>
</tr>
<tr>
<td>GPT-5.2</td>
<td>&#10007;</td>
<td>&#10003;</td>
<td>&#10007;</td>
<td>~</td>
<td>&#10007;</td>
<td>&#10003;</td>
<td>2.5/6</td>
</tr>
<tr>
<td>Grok 4.1</td>
<td>&#10003;</td>
<td>&#10003;</td>
<td>&#10003;</td>
<td>&#10007;</td>
<td>&#10007;</td>
<td>&#10007;</td>
<td>3/6</td>
</tr>
<tr>
<td>Gemini 3.1 Pro</td>
<td>&#10003;</td>
<td>&#10003;</td>
<td>&#10003;</td>
<td>&#10003;</td>
<td>&#10007;</td>
<td>&#10003;</td>
<td>5/6</td>
</tr>
<tr>
<td>GLM 5</td>
<td>&#10003;</td>
<td>&#10003;</td>
<td>?</td>
<td>&#10003;</td>
<td>N/A</td>
<td>&#10007;</td>
<td>3/5</td>
</tr>
<tr>
<td>MinMax M2.5</td>
<td>&#10007;</td>
<td>&#10003;</td>
<td>&#10007;</td>
<td>&#10007;</td>
<td>N/A</td>
<td>&#10007;</td>
<td>1/5</td>
</tr>
<tr>
<td>Qwen3.5 Plus</td>
<td>&#10003;</td>
<td>&#10003;</td>
<td>&#10007;</td>
<td>~</td>
<td>&#10007;</td>
<td>&#10007;</td>
<td>2.5/6</td>
</tr>
<tr>
<td>Kimi K2.5</td>
<td>&#10003;</td>
<td>&#10003;</td>
<td>&#10007;</td>
<td>&#10003;</td>
<td>&#10007;</td>
<td>&#10003;</td>
<td>4/6</td>
</tr>
</tbody>
</table>
<p>Obviously, my mini-benchmark only had 6 questions, and I ran it only once. This was obviously not scientifically rigorous. However it was systematic enough to trump just a <em>mere feeling</em>.</p>
<p>The main finding for me personally is that Claude Opus 4.6, the flagship model by <a class="link" href="https://www.anthropic.com/" target="_blank" rel="noopener">Anthropic</a>, seems to give great answers consistently. The answers are not only correct, but also well scoped giving enough information to cover everything that seems relevant, without blurping unnecessary filler.</p>
<p>I used Claude extensively in 2023-2024 when it was the main model available at my day work, but for the past year I had been using other models that I felt were better at the time. Now Claude seems to be the best-of-the-best again, with Gemini and Kimi as close follow-ups. <a class="link" href="https://openrouter.ai/compare/anthropic/claude-opus-4.6/google/gemini-3.1-pro-preview/moonshotai/kimi-k2.5" target="_blank" rel="noopener">Comparing their pricing at OpenRouter.ai</a> the Kimi K2.5 price of $0.6 / million tokens is almost 90% cheaper than the Claude Opus 4.6&rsquo;s $5.0 / million tokens suggests that Kimi K2.5 offers the best <strong>price-per-performance ratio</strong>. Claude might be cheaper with a monthly subscription directly from Anthropic, potentially narrowing the price gap.</p>
<p>Overall I do feel that Anthropic, Google and Moonshot.ai have been pushing the envelope with their latest models in a way that <strong>one can&rsquo;t really claim that AI models have plateaued</strong>. In fact, one could claim that at least Claude has now climbed over the hill of <a class="link" href="https://en.wikipedia.org/wiki/AI_slop" target="_blank" rel="noopener">&ldquo;AI slop&rdquo;</a> and consistently produces valuable results. If and when AI usage expands from here, <strong>we might actually not drown in AI slop</strong> as chances of accidentally crappy results decrease. This makes me positive about the future.</p>
<p>I am also really happy to see that there wasn&rsquo;t just one model crushing everybody else, but that there are <strong>at least three models doing very well</strong>. As an open source enthusiast I am particularly glad to see that <a class="link" href="https://www.moonshot.ai/" target="_blank" rel="noopener">Moonshot.ai&rsquo;s</a> Kimi K2.5 is published with an open license. Given the hardware, anyone can run it on their own. OpenRouter.ai currently lists <a class="link" href="https://openrouter.ai/moonshotai/kimi-k2.5/providers" target="_blank" rel="noopener">9 independent providers</a> alongside Moonshot.ai itself, showcasing the potential of open-weight models in practice.</p>
<p>If the pattern holds and flagship models continue improving at this pace we might look back at 2026 as the year AI stopped feeling like a call center associate and started to resemble a scientific researcher. While new models become available we need to keep testing, keep questioning, and keep our expectations grounded in actual performance rather than press releases.</p>
<p>Thanks to OpenRouter.ai for providing a great service that makes testing various models incredibly easy!</p>

<p>The post <a rel="nofollow" href="https://optimizedbyotto.com/post/ai-models-plateaued-or-not/">Do AI models still keep getting better, or have they plateaued?</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>MySQL 8.0 JSON Functions: Practical Examples and Indexing</title>
      <link>https://anothermysqldba.blogspot.com/2026/02/mysql-80-json-functions-practical.html</link>
      <pubDate>Sat, 21 Feb 2026 22:39:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://anothermysqldba.blogspot.com/">Another MySQL DBA</source>
      <description><![CDATA[<p>This post covers a hands-on walkthrough of MySQL 8.0\'s JSON functions. JSON support has been in MySQL since 5.7, but 8.0 added a meaningful set of improvements — better indexing strategies, new functions, and multi-valued indexes — that make working with JSON data considerably more practical. The following documents several of the most commonly needed patterns, including EXPLAIN output and performance observations worth knowing about.</p>
<p>This isn\'t a \"JSON vs. relational\" debate post. If you\'re storing JSON in MySQL, you probably already have your reasons. The goal here is to make sure you\'re using the available tooling effectively.</p>
<p>Environment</p>
<p>mysql > SELECT @@version, @@version_commentG<br />
*************************** 1. row ***************************<br />
    @@version: 8.0.36<br />
@@version_comment: MySQL Community Server - GPL</p>
<p>Testing was done on a VM with 8GB RAM and innodb_buffer_pool_size set to 4G. One housekeeping note worth mentioning: query_cache_type is irrelevant in 8.0 since the query cache was removed entirely. If you migrated a 5.7 instance and still have that variable in your my.cnf, remove it — MySQL 8.0 will throw a startup error.</p>
<p>Setting Up a Test Table</p>
<p>The test table simulates a fairly common pattern — an application storing user profile data and event metadata as JSON blobs:</p>
<p>CREATE TABLE user_events (<br />
 id     INT UNSIGNED NOT NULL AUTO_INCREMENT,<br />
 user_id   INT UNSIGNED NOT NULL,<br />
 event_data JSON NOT NULL,<br />
 created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,<br />
 PRIMARY KEY (id),<br />
 INDEX idx_user (user_id)<br />
) ENGINE=InnoDB;</p>
<p>INSERT INTO user_events (user_id, event_data) VALUES<br />
(1, \'{\"action\":\"login\",\"ip\":\"192.168.1.10\",\"tags\":[\"mobile\",\"vpn\"],\"score\":88}\'),<br />
(1, \'{\"action\":\"purchase\",\"ip\":\"192.168.1.10\",\"tags\":[\"desktop\"],\"score\":72,\"amount\":49.99}\'),<br />
(2, \'{\"action\":\"login\",\"ip\":\"10.0.0.5\",\"tags\":[\"mobile\"],\"score\":91}\'),<br />
(3, \'{\"action\":\"logout\",\"ip\":\"10.0.0.9\",\"tags\":[\"desktop\",\"vpn\"],\"score\":65}\'),<br />
(2, \'{\"action\":\"purchase\",\"ip\":\"10.0.0.5\",\"tags\":[\"mobile\"],\"score\":84,\"amount\":129.00}\');</p>
<p>Basic Extraction: JSON_VALUE vs. JSON_EXTRACT</p>
<p>JSON_VALUE() was introduced in MySQL 8.0.21 and is the cleaner way to extract scalar values with built-in type casting. Before that, you were using JSON_EXTRACT() (or the - > shorthand) and casting manually, which works but adds noise to your queries.</p>
<p>-- Pre-8.0.21 approach<br />
SELECT user_id,<br />
    JSON_EXTRACT(event_data, \'$.action\') AS action,<br />
    CAST(JSON_EXTRACT(event_data, \'$.score\') AS UNSIGNED) AS score<br />
FROM user_events;</p>
<p>-- Cleaner 8.0.21+ approach<br />
SELECT user_id,<br />
    JSON_VALUE(event_data, \'$.action\') AS action,<br />
    JSON_VALUE(event_data, \'$.score\' RETURNING UNSIGNED) AS score<br />
FROM user_events;</p>
<p>Output from the second query:</p>
<p>+---------+----------+-------+<br />
&#124; user_id &#124; action  &#124; score &#124;<br />
+---------+----------+-------+<br />
&#124;    1 &#124; login  &#124;  88 &#124;<br />
&#124;    1 &#124; purchase &#124;  72 &#124;<br />
&#124;    2 &#124; login  &#124;  91 &#124;<br />
&#124;    3 &#124; logout  &#124;  65 &#124;<br />
&#124;    2 &#124; purchase &#124;  84 &#124;<br />
+---------+----------+-------+<br />
5 rows in set (0.00 sec)</p>
<p>The RETURNING clause is genuinely useful. It eliminates the awkward double-cast pattern and makes intent clearer when reading query code later.</p>
<p>Multi-Valued Indexes: The Real Game Changer</p>
<p>This is where 8.0 actually moved the needle for JSON workloads. Multi-valued indexes, available since MySQL 8.0.17, let you index array elements inside a JSON column directly. Here\'s what that looks like in practice:</p>
<p>ALTER TABLE user_events<br />
 ADD INDEX idx_tags ((CAST(event_data- >\'$.tags\' AS CHAR(64) ARRAY)));</p>
<p>Here is what EXPLAIN shows before and after on a query filtering by tag value:</p>
<p>-- Without the multi-valued index:<br />
EXPLAIN SELECT * FROM user_events<br />
WHERE JSON_CONTAINS(event_data- >\'$.tags\', \'\"vpn\"\')G</p>
<p>*************************** 1. row ***************************<br />
      id: 1<br />
 select_type: SIMPLE<br />
    table: user_events<br />
  partitions: NULL<br />
     type: ALL<br />
possible_keys: NULL<br />
     key: NULL<br />
   key_len: NULL<br />
     ref: NULL<br />
     rows: 5<br />
   filtered: 100.00<br />
    Extra: Using where</p>
<p>-- After adding the multi-valued index:<br />
EXPLAIN SELECT * FROM user_events<br />
WHERE JSON_CONTAINS(event_data- >\'$.tags\', \'\"vpn\"\')G</p>
<p>*************************** 1. row ***************************<br />
      id: 1<br />
 select_type: SIMPLE<br />
    table: user_events<br />
  partitions: NULL<br />
     type: range<br />
possible_keys: idx_tags<br />
     key: idx_tags<br />
   key_len: 67<br />
     ref: NULL<br />
     rows: 2<br />
   filtered: 100.00<br />
    Extra: Using where</p>
<p>Full table scan down to a range scan. On 5 rows this is trivial, but on a table with millions of rows and frequent tag-based filtering, that difference is significant. The improvement scales directly with table size and query frequency.</p>
<p>One important gotcha: MEMBER OF() and JSON_OVERLAPS() also benefit from multi-valued indexes, but JSON_SEARCH() does not. This matters when choosing your query pattern at design time:</p>
<p>-- This WILL use the multi-valued index:<br />
SELECT * FROM user_events<br />
WHERE \'vpn\' MEMBER OF (event_data- >\'$.tags\');</p>
<p>-- This will NOT use it:<br />
SELECT * FROM user_events<br />
WHERE JSON_SEARCH(event_data- >\'$.tags\', \'one\', \'vpn\') IS NOT NULL;</p>
<p>Aggregating and Transforming JSON</p>
<p>A few aggregation functions worth knowing well:</p>
<p>-- Build a JSON array of actions per user<br />
SELECT user_id,<br />
    JSON_ARRAYAGG(JSON_VALUE(event_data, \'$.action\')) AS actions<br />
FROM user_events<br />
GROUP BY user_id;</p>
<p>+---------+----------------------+<br />
&#124; user_id &#124; actions       &#124;<br />
+---------+----------------------+<br />
&#124;    1 &#124; [\"login\",\"purchase\"] &#124;<br />
&#124;    2 &#124; [\"login\",\"purchase\"] &#124;<br />
&#124;    3 &#124; [\"logout\"]      &#124;<br />
+---------+----------------------+<br />
3 rows in set (0.01 sec)</p>
<p>-- Summarize into a JSON object keyed by action<br />
SELECT user_id,<br />
    JSON_OBJECTAGG(<br />
     JSON_VALUE(event_data, \'$.action\'),<br />
     JSON_VALUE(event_data, \'$.score\' RETURNING UNSIGNED)<br />
    ) AS score_by_action<br />
FROM user_events<br />
GROUP BY user_id;</p>
<p>+---------+--------------------------------+<br />
&#124; user_id &#124; score_by_action        &#124;<br />
+---------+--------------------------------+<br />
&#124;    1 &#124; {\"login\": 88, \"purchase\": 72} &#124;<br />
&#124;    2 &#124; {\"login\": 91, \"purchase\": 84} &#124;<br />
&#124;    3 &#124; {\"logout\": 65}         &#124;<br />
+---------+--------------------------------+<br />
3 rows in set (0.00 sec)</p>
<p>JSON_OBJECTAGG() will throw an error if there are duplicate keys within a group. This is worth knowing before you encounter it in a production ETL pipeline. In that case, you\'ll need to deduplicate upstream or handle it in application logic before the data reaches this aggregation step.</p>
<p>Checking SHOW STATUS After JSON-Heavy Queries</p>
<p>When evaluating query patterns, checking handler metrics is a useful habit:</p>
<p>FLUSH STATUS;</p>
<p>SELECT * FROM user_events<br />
WHERE JSON_VALUE(event_data, \'$.score\' RETURNING UNSIGNED) > 80;</p>
<p>SHOW STATUS LIKE \'Handler_read%\';</p>
<p>+----------------------------+-------+<br />
&#124; Variable_name       &#124; Value &#124;<br />
+----------------------------+-------+<br />
&#124; Handler_read_first     &#124; 1   &#124;<br />
&#124; Handler_read_key      &#124; 0   &#124;<br />
&#124; Handler_read_last     &#124; 0   &#124;<br />
&#124; Handler_read_next     &#124; 4   &#124;<br />
&#124; Handler_read_prev     &#124; 0   &#124;<br />
&#124; Handler_read_rnd      &#124; 0   &#124;<br />
&#124; Handler_read_rnd_next   &#124; 6   &#124;<br />
+----------------------------+-------+<br />
7 rows in set (0.00 sec)</p>
<p>The Handler_read_rnd_next value confirms a full scan — no surprise since there\'s no functional index on the score value. For score-based filtering at scale, a generated column with an index is the right answer:</p>
<p>ALTER TABLE user_events<br />
 ADD COLUMN score_val TINYINT UNSIGNED<br />
  GENERATED ALWAYS AS (JSON_VALUE(event_data, \'$.score\' RETURNING UNSIGNED)) VIRTUAL,<br />
 ADD INDEX idx_score (score_val);</p>
<p>After adding that, the same query drops to a proper index range scan. Generated columns on JSON fields are available in both MySQL 8.0 and Percona Server 8.0, and they remain the most reliable path for scalar JSON field filtering at any meaningful scale.</p>
<p>If you\'re running Percona Server, pt-query-digest from the Percona Toolkit is still the most practical way to identify which JSON-heavy queries are actually causing pain in production before you start adding indexes speculatively.</p>
<p>Practical Observations</p>
<p> Multi-valued indexes (8.0.17+) are a long overdue improvement and work well when your query patterns align with JSON_CONTAINS() or MEMBER OF()<br />
 JSON_VALUE() with RETURNING (8.0.21+) is cleaner than the old cast-after-extract pattern and worth adopting consistently<br />
 Generated columns plus indexes remain the most reliable path for scalar JSON field filtering at scale<br />
 Watch for JSON_OBJECTAGG() duplicate key errors in grouped data — it surfaces as a hard error in ETL pipelines and can be easy to miss in testing if your sample data happens to be clean<br />
 Always verify index usage with EXPLAIN — the optimizer doesn\'t always pick up multi-valued indexes in complex WHERE clauses, and it\'s worth confirming rather than assuming</p>
<p>Summary</p>
<p>MySQL 8.0\'s JSON improvements are genuinely useful, particularly multi-valued indexes and JSON_VALUE() with type casting. They don\'t replace good schema design, but for cases where JSON storage is appropriate or inherited, you now have real tools to work with rather than just hoping the optimizer figures it out. The generated column pattern in particular is worth evaluating early if you know certain JSON fields will be used in WHERE clauses regularly.</p>
<p>Useful references:</p>
<p> MySQL 8.0 JSON Function Reference<br />
 Multi-Valued Indexes Documentation<br />
 JSON_VALUE() Function Reference<br />
 Percona Toolkit</p>
<p>The post <a rel="nofollow" href="https://anothermysqldba.blogspot.com/2026/02/mysql-80-json-functions-practical.html">MySQL 8.0 JSON Functions: Practical Examples and Indexing</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[
<p>This post covers a hands-on walkthrough of MySQL 8.0&rsquo;s JSON functions. JSON support has been in MySQL since 5.7, but 8.0 added a meaningful set of improvements &mdash; better indexing strategies, new functions, and multi-valued indexes &mdash; that make working with JSON data considerably more practical. The following documents several of the most commonly needed patterns, including EXPLAIN output and performance observations worth knowing about.</p>
<p>This isn&rsquo;t a &ldquo;JSON vs. relational&rdquo; debate post. If you&rsquo;re storing JSON in MySQL, you probably already have your reasons. The goal here is to make sure you&rsquo;re using the available tooling effectively.</p>
<h3>Environment<a class="anchor-link" id="environment"></a></h3>
<pre><code>mysql&gt; SELECT @@version, @@version_commentG
*************************** 1. row ***************************
        @@version: 8.0.36
@@version_comment: MySQL Community Server - GPL
</code></pre>
<p>Testing was done on a VM with 8GB RAM and <strong>innodb_buffer_pool_size</strong> set to 4G. One housekeeping note worth mentioning: <strong>query_cache_type</strong> is irrelevant in 8.0 since the query cache was removed entirely. If you migrated a 5.7 instance and still have that variable in your my.cnf, remove it &mdash; MySQL 8.0 will throw a startup error.</p>
<h3>Setting Up a Test Table<a class="anchor-link" id="setting-up-a-test-table"></a></h3>
<p>The test table simulates a fairly common pattern &mdash; an application storing user profile data and event metadata as JSON blobs:</p>
<pre><code>CREATE TABLE user_events (
  id          INT UNSIGNED NOT NULL AUTO_INCREMENT,
  user_id     INT UNSIGNED NOT NULL,
  event_data  JSON NOT NULL,
  created_at  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id),
  INDEX idx_user (user_id)
) ENGINE=InnoDB;

INSERT INTO user_events (user_id, event_data) VALUES
(1, '{"action":"login","ip":"192.168.1.10","tags":["mobile","vpn"],"score":88}'),
(1, '{"action":"purchase","ip":"192.168.1.10","tags":["desktop"],"score":72,"amount":49.99}'),
(2, '{"action":"login","ip":"10.0.0.5","tags":["mobile"],"score":91}'),
(3, '{"action":"logout","ip":"10.0.0.9","tags":["desktop","vpn"],"score":65}'),
(2, '{"action":"purchase","ip":"10.0.0.5","tags":["mobile"],"score":84,"amount":129.00}');
</code></pre>
<h3>Basic Extraction: JSON_VALUE vs. JSON_EXTRACT<a class="anchor-link" id="basic-extraction-json_value-vs-json_extract"></a></h3>
<p><strong>JSON_VALUE()</strong> was introduced in MySQL 8.0.21 and is the cleaner way to extract scalar values with built-in type casting. Before that, you were using <strong>JSON_EXTRACT()</strong> (or the <strong>-&gt;</strong> shorthand) and casting manually, which works but adds noise to your queries.</p>
<pre><code>-- Pre-8.0.21 approach
SELECT user_id,
       JSON_EXTRACT(event_data, '$.action') AS action,
       CAST(JSON_EXTRACT(event_data, '$.score') AS UNSIGNED) AS score
FROM user_events;

-- Cleaner 8.0.21+ approach
SELECT user_id,
       JSON_VALUE(event_data, '$.action') AS action,
       JSON_VALUE(event_data, '$.score' RETURNING UNSIGNED) AS score
FROM user_events;
</code></pre>
<p>Output from the second query:</p>
<pre><code>+---------+----------+-------+
| user_id | action   | score |
+---------+----------+-------+
|       1 | login    |    88 |
|       1 | purchase |    72 |
|       2 | login    |    91 |
|       3 | logout   |    65 |
|       2 | purchase |    84 |
+---------+----------+-------+
5 rows in set (0.00 sec)
</code></pre>
<p>The <strong>RETURNING</strong> clause is genuinely useful. It eliminates the awkward double-cast pattern and makes intent clearer when reading query code later.</p>
<h3>Multi-Valued Indexes: The Real Game Changer<a class="anchor-link" id="multi-valued-indexes-the-real-game-changer"></a></h3>
<p>This is where 8.0 actually moved the needle for JSON workloads. Multi-valued indexes, available since MySQL 8.0.17, let you index array elements inside a JSON column directly. Here&rsquo;s what that looks like in practice:</p>
<pre><code>ALTER TABLE user_events
  ADD INDEX idx_tags ((CAST(event_data-&gt;'$.tags' AS CHAR(64) ARRAY)));
</code></pre>
<p>Here is what EXPLAIN shows before and after on a query filtering by tag value:</p>
<pre><code>-- Without the multi-valued index:
EXPLAIN SELECT * FROM user_events
WHERE JSON_CONTAINS(event_data-&gt;'$.tags', '"vpn"')G

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: user_events
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 5
     filtered: 100.00
        Extra: Using where

-- After adding the multi-valued index:
EXPLAIN SELECT * FROM user_events
WHERE JSON_CONTAINS(event_data-&gt;'$.tags', '"vpn"')G

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: user_events
   partitions: NULL
         type: range
possible_keys: idx_tags
          key: idx_tags
      key_len: 67
          ref: NULL
         rows: 2
     filtered: 100.00
        Extra: Using where
</code></pre>
<p>Full table scan down to a range scan. On 5 rows this is trivial, but on a table with millions of rows and frequent tag-based filtering, that difference is significant. The improvement scales directly with table size and query frequency.</p>
<p>One important gotcha: <strong>MEMBER OF()</strong> and <strong>JSON_OVERLAPS()</strong> also benefit from multi-valued indexes, but <strong>JSON_SEARCH()</strong> does not. This matters when choosing your query pattern at design time:</p>
<pre><code>-- This WILL use the multi-valued index:
SELECT * FROM user_events
WHERE 'vpn' MEMBER OF (event_data-&gt;'$.tags');

-- This will NOT use it:
SELECT * FROM user_events
WHERE JSON_SEARCH(event_data-&gt;'$.tags', 'one', 'vpn') IS NOT NULL;
</code></pre>
<h3>Aggregating and Transforming JSON<a class="anchor-link" id="aggregating-and-transforming-json"></a></h3>
<p>A few aggregation functions worth knowing well:</p>
<pre><code>-- Build a JSON array of actions per user
SELECT user_id,
       JSON_ARRAYAGG(JSON_VALUE(event_data, '$.action')) AS actions
FROM user_events
GROUP BY user_id;

+---------+----------------------+
| user_id | actions              |
+---------+----------------------+
|       1 | ["login","purchase"] |
|       2 | ["login","purchase"] |
|       3 | ["logout"]           |
+---------+----------------------+
3 rows in set (0.01 sec)

-- Summarize into a JSON object keyed by action
SELECT user_id,
       JSON_OBJECTAGG(
         JSON_VALUE(event_data, '$.action'),
         JSON_VALUE(event_data, '$.score' RETURNING UNSIGNED)
       ) AS score_by_action
FROM user_events
GROUP BY user_id;

+---------+--------------------------------+
| user_id | score_by_action                |
+---------+--------------------------------+
|       1 | {"login": 88, "purchase": 72}  |
|       2 | {"login": 91, "purchase": 84}  |
|       3 | {"logout": 65}                 |
+---------+--------------------------------+
3 rows in set (0.00 sec)
</code></pre>
<p><strong>JSON_OBJECTAGG()</strong> will throw an error if there are duplicate keys within a group. This is worth knowing before you encounter it in a production ETL pipeline. In that case, you&rsquo;ll need to deduplicate upstream or handle it in application logic before the data reaches this aggregation step.</p>
<h3>Checking SHOW STATUS After JSON-Heavy Queries<a class="anchor-link" id="checking-show-status-after-json-heavy-queries"></a></h3>
<p>When evaluating query patterns, checking handler metrics is a useful habit:</p>
<pre><code>FLUSH STATUS;

SELECT * FROM user_events
WHERE JSON_VALUE(event_data, '$.score' RETURNING UNSIGNED) &gt; 80;

SHOW STATUS LIKE 'Handler_read%';

+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| Handler_read_first         | 1     |
| Handler_read_key           | 0     |
| Handler_read_last          | 0     |
| Handler_read_next          | 4     |
| Handler_read_prev          | 0     |
| Handler_read_rnd           | 0     |
| Handler_read_rnd_next      | 6     |
+----------------------------+-------+
7 rows in set (0.00 sec)
</code></pre>
<p>The <strong>Handler_read_rnd_next</strong> value confirms a full scan &mdash; no surprise since there&rsquo;s no functional index on the score value. For score-based filtering at scale, a generated column with an index is the right answer:</p>
<pre><code>ALTER TABLE user_events
  ADD COLUMN score_val TINYINT UNSIGNED
    GENERATED ALWAYS AS (JSON_VALUE(event_data, '$.score' RETURNING UNSIGNED)) VIRTUAL,
  ADD INDEX idx_score (score_val);
</code></pre>
<p>After adding that, the same query drops to a proper index range scan. Generated columns on JSON fields are available in both MySQL 8.0 and Percona Server 8.0, and they remain the most reliable path for scalar JSON field filtering at any meaningful scale.</p>
<p>If you&rsquo;re running Percona Server, <strong>pt-query-digest</strong> from the <a href="https://www.percona.com/software/database-tools/percona-toolkit">Percona Toolkit</a> is still the most practical way to identify which JSON-heavy queries are actually causing pain in production before you start adding indexes speculatively.</p>
<h3>Practical Observations<a class="anchor-link" id="practical-observations"></a></h3>
<ul>
<li>Multi-valued indexes (8.0.17+) are a long overdue improvement and work well when your query patterns align with <strong>JSON_CONTAINS()</strong> or <strong>MEMBER OF()</strong></li>
<li><strong>JSON_VALUE() with RETURNING</strong> (8.0.21+) is cleaner than the old cast-after-extract pattern and worth adopting consistently</li>
<li>Generated columns plus indexes remain the most reliable path for scalar JSON field filtering at scale</li>
<li>Watch for <strong>JSON_OBJECTAGG()</strong> duplicate key errors in grouped data &mdash; it surfaces as a hard error in ETL pipelines and can be easy to miss in testing if your sample data happens to be clean</li>
<li>Always verify index usage with EXPLAIN &mdash; the optimizer doesn&rsquo;t always pick up multi-valued indexes in complex WHERE clauses, and it&rsquo;s worth confirming rather than assuming</li>
</ul>
<h3>Summary<a class="anchor-link" id="summary"></a></h3>
<p>MySQL 8.0&rsquo;s JSON improvements are genuinely useful, particularly multi-valued indexes and <strong>JSON_VALUE()</strong> with type casting. They don&rsquo;t replace good schema design, but for cases where JSON storage is appropriate or inherited, you now have real tools to work with rather than just hoping the optimizer figures it out. The generated column pattern in particular is worth evaluating early if you know certain JSON fields will be used in WHERE clauses regularly.</p>
<p>Useful references:</p>
<ul>
<li><a href="https://dev.mysql.com/doc/refman/8.0/en/json-function-reference.html">MySQL 8.0 JSON Function Reference</a></li>
<li><a href="https://dev.mysql.com/doc/refman/8.0/en/create-index.html#create-index-multi-valued">Multi-Valued Indexes Documentation</a></li>
<li><a href="https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#function_json-value">JSON_VALUE() Function Reference</a></li>
<li><a href="https://www.percona.com/software/database-tools/percona-toolkit">Percona Toolkit</a></li>
</ul>

<p>The post <a rel="nofollow" href="https://anothermysqldba.blogspot.com/2026/02/mysql-80-json-functions-practical.html">MySQL 8.0 JSON Functions: Practical Examples and Indexing</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Is MariaDB part of the MySQL ecosystem?</title>
      <link>https://mariadb.org/is-mariadb-part-of-the-mysql-ecosystem/</link>
      <pubDate>Fri, 20 Feb 2026 20:29:33 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>Why MariaDB is both its own database — and the natural continuation of MySQL<br />
Because MariaDB is at the same time a completely independent database and a fundamentally compatible extension of MySQL, the MySQL user base is not dependent upon Oracle nor upon new forks of MySQL for their future. For pragmatical reasons – …<br />
Continue reading \"Is MariaDB part of the MySQL ecosystem?\"<br />
The post Is MariaDB part of the MySQL ecosystem? appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/is-mariadb-part-of-the-mysql-ecosystem/">Is MariaDB part of the MySQL ecosystem?</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>Why MariaDB is both its own database &mdash; and the natural continuation of MySQL<br>
Because MariaDB is at the same time a completely independent database and a fundamentally compatible extension of MySQL, the MySQL user base is not dependent upon Oracle nor upon new forks of MySQL for their future.&nbsp;For pragmatical reasons &ndash; &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/is-mariadb-part-of-the-mysql-ecosystem/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;Is MariaDB part of the MySQL ecosystem?&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/is-mariadb-part-of-the-mysql-ecosystem/">Is MariaDB part of the MySQL ecosystem?</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/is-mariadb-part-of-the-mysql-ecosystem/">Is MariaDB part of the MySQL ecosystem?</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>How to Migrate Cloud PostgreSQL to On-Prem with Minimal Downtime</title>
      <link>https://severalnines.com/blog/how-to-migrate-cloud-postgresql-to-on-prem-with-minimal-downtime/</link>
      <pubDate>Fri, 20 Feb 2026 08:00:16 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://severalnines.com/">Severalnines</source>
      <description><![CDATA[<p>In the last decade, cloud adoption has accelerated as organizations looked to scale quickly, reduce upfront capital investment, and leverage managed services. However, in recent years, a growing number of companies are choosing to migrate certain workloads back from the cloud to on-premises environments.  The reasons vary; cost reduction, compliance with data sovereignty laws, improved […]<br />
The post How to Migrate Cloud PostgreSQL to On-Prem with Minimal Downtime appeared first on Severalnines.</p>
<p>The post <a rel="nofollow" href="https://severalnines.com/blog/how-to-migrate-cloud-postgresql-to-on-prem-with-minimal-downtime/">How to Migrate Cloud PostgreSQL to On-Prem with Minimal Downtime</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>In the last decade, cloud adoption has accelerated as organizations looked to scale quickly, reduce upfront capital investment, and leverage managed services. However, in recent years, a growing number of companies are choosing to migrate certain workloads back from the cloud to on-premises environments.&nbsp;</p>
<p>The reasons vary; cost reduction, compliance with data sovereignty laws, improved performance, and more. Once decided, one of the most critical migration challenges is minimizing downtime. Today&rsquo;s businesses cannot afford long service interruptions &mdash; customers expect 24/7 availability.</p>
<p>This blog will explore best practices for cloud-to-on-premises database migration, featuring a step-by-step use example that highlights migrating from AWS RDS <a href="https://severalnines.com/clustercontrol/databases/postgresql">PostgreSQL</a> to a self-hosted environment with ClusterControl.</p>
<h2 class="wp-block-heading" id="h-pre-migration-planning">Pre-Migration Planning<a class="anchor-link" id="pre-migration-planning"></a></h2>
<h3 class="wp-block-heading" id="h-1-workload-assessment">1. Workload Assessment<a class="anchor-link" id="1-workload-assessment"></a></h3>
<p>Before embarking on any database migration, a comprehensive, meticulous assessment of the existing database infrastructure is paramount. This initial phase is critical for identifying potential challenges, optimizing the migration strategy, and ensuring a smooth transition with minimal disruption. The assessment should begin with a thorough identification of the following key aspects:</p>
<ul class="wp-block-list">
<li><strong>Database Type:</strong> OLTP (transaction-heavy) databases such as MySQL or PostgreSQL require different approaches compared to OLAP data warehouses.</li>
<li><strong>Criticality of Data:</strong> Which databases are mission-critical, and which can tolerate longer downtime?</li>
<li><strong>Size of Data:</strong> Multi-terabyte datasets require more sophisticated strategies than smaller workloads.</li>
</ul>
<h3 class="wp-block-heading" id="h-2-sizing-amp-capacity-planning">2. Sizing &amp; Capacity Planning<a class="anchor-link" id="2-sizing-capacity-planning"></a></h3>
<p>For optimal performance and scalability, an on-premises infrastructure must be robust enough to manage existing workloads efficiently while simultaneously offering ample headroom to accommodate future growth. The key considerations:</p>
<ul class="wp-block-list">
<li><strong>CPU and Memory:</strong> Match or exceed cloud specs.</li>
<li><strong>Storage:</strong> Low-latency SSDs for OLTP workloads.</li>
<li><strong>Network Bandwidth:</strong> High throughput between cloud and on-prem ensures faster sync.</li>
</ul>
<h3 class="wp-block-heading" id="h-3-security-amp-compliance">3. Security &amp; Compliance<a class="anchor-link" id="3-security-compliance"></a></h3>
<p>Security and compliance are paramount considerations throughout the migration process, encompassing both the transfer of data and its subsequent state at rest. During data transfer, robust encryption protocols, such as TLS/SSL, are essential to prevent interception and unauthorized access. This ensures data integrity and confidentiality as it moves from the source to the destination environment. Furthermore, secure network configurations, including firewalls and intrusion detection systems, play a crucial role in safeguarding data in transit by mitigating potential cyber threats.</p>
<h3 class="wp-block-heading" id="h-4-network-connectivity">4. Network Connectivity<a class="anchor-link" id="4-network-connectivity"></a></h3>
<p>Data transfer is a critical component of any database migration, especially with large datasets. The initial snapshot of the database must be completed quickly to minimize downtime, and the ongoing replication during the migration process needs to be efficient to prevent any lag or data inconsistencies.</p>
<p>To facilitate large-scale data transfers between cloud environments and on-premises infrastructure, it&rsquo;s crucial to establish secure, high-bandwidth connections. One common solution is to utilize VPN tunnels, which provide a secure pathway over the public internet.&nbsp;</p>
<p>However, for large volume transfers or those requiring guaranteed performance, dedicated private lines are highly recommended. These include services like AWS Direct Connect, Azure ExpressRoute, and GCP Interconnect. These dedicated connections bypass the public internet, offering lower latency, higher throughput, and greater reliability, which are essential for maintaining data integrity and minimizing timelines.</p>
<h2 class="wp-block-heading" id="h-migration-strategies">Migration Strategies<a class="anchor-link" id="migration-strategies"></a></h2>
<p>Migrating a database from a cloud environment to an on-premises infrastructure presents a unique set of challenges and considerations, demanding a carefully tailored approach. There is no single, universally applicable migration strategy; the optimal path depends heavily on various factors, including the database type, its size and complexity, the specific cloud provider and its services used, the target environment&rsquo;s capabilities, downtime tolerance, data sensitivity, and regulatory compliance requirements.&nbsp;</p>
<p>Below are common approaches for online migration for minimum downtime:</p>
<h3 class="wp-block-heading" id="h-1-physical-replication">1. Physical Replication<a class="anchor-link" id="1-physical-replication"></a></h3>
<p>Physical replication typically utilizes WAL/binary logs to replicate data from a source to a target database. This method is well-suited for homogeneous migrations; e.g., MySQL &rarr; MySQL. Essentially, it establishes an on-premises replica node that is synchronized with the cloud environment.</p>
<h3 class="wp-block-heading" id="h-2-logical-replication-change-data-capture-cdc">2. Logical Replication / Change Data Capture (CDC)<a class="anchor-link" id="2-logical-replication-change-data-capture-cdc"></a></h3>
<p>Logical replication operates at a higher level of abstraction compared to physical replication. Instead of replicating block-level changes or entire data files, it focuses on replicating the actual data modifications, such as individual row insertions, updates, and deletions, as well as the transactional order in which these changes occur. This method provides greater flexibility, as it is independent of the underlying storage engine or physical data layout.</p>
<p>A key advantage of logical replication is its ability to replicate data between different versions of a database or even different database systems, provided they support a compatible logical replication protocol. This makes it an ideal solution for heterogeneous environments, upgrades, and migrations.&nbsp;</p>
<p>Furthermore, it allows for selective replication, where only specific tables or subsets of data are replicated, reducing network bandwidth usage and storage requirements on the replica. You can utilize tools such as AWS DMS, GCP Data Migration Services, Debezium + Kafka, or pg_logical in PostgreSQL.</p>
<h2 class="wp-block-heading" id="h-techniques-to-minimize-downtime">Techniques to Minimize Downtime<a class="anchor-link" id="techniques-to-minimize-downtime"></a></h2>
<h3 class="wp-block-heading" id="h-1-change-data-capture-cdc">1. Change Data Capture (CDC)<a class="anchor-link" id="1-change-data-capture-cdc"></a></h3>
<p>Change Data Capture (CDC) can play a crucial role in ensuring data consistency and minimal downtime during database migrations, particularly when moving from a cloud-based database to an on-premises solution. By continuously capturing and streaming write operations from the source cloud DB to the target on-prem DB in near real-time, CDC significantly reduces the data gap between the two systems. This real-time synchronization is essential for a smooth and efficient cutover process.</p>
<p>The cutover itself is designed to be as seamless as possible. Once the on-premises database has caught up with the cloud database, the critical step involves re-routing all query traffic. This means directing applications and services that previously accessed the cloud database to now point to the newly synchronized on-premises database. <strong>The downtime experienced during this switchover is limited exclusively to the brief period required for these database endpoint changes to propagate and take effect.</strong> This approach minimizes the impact on ongoing operations, ensuring that business continuity is maintained throughout the migration.</p>
<h3 class="wp-block-heading" id="h-2-read-replica-promotion">2. Read Replica Promotion<a class="anchor-link" id="2-read-replica-promotion"></a></h3>
<p>In a hybrid cloud environment, a read replica promotion strategy essentially creates a unified, single cluster. This setup involves an active, primary database residing in the cloud, with a replica maintained on-premises. The core principle is to ensure continuous replication between the cloud and on-premises nodes until a planned cutover event.&nbsp;</p>
<p>It involves promoting the on-premises replica to become the new primary database. Once this promotion is complete, all query traffic is re-routed from the previous cloud primary to the newly promoted on-premises primary. This allows for a seamless transition, ensuring data consistency and continuous service availability during the cutover.&nbsp;</p>
<p>The on-premises replica&rsquo;s ability to take over as the primary ensures business continuity, even in non-migration scenarios where the cloud primary might experience issues. This architecture provides flexibility, disaster recovery capabilities, and potentially reduced latency for on-premises applications accessing the local primary.</p>
<h2 class="wp-block-heading" id="h-testing-amp-validation"><strong>Testing &amp; Validation</strong><a class="anchor-link" id="testing-validation"></a></h2>
<p>Testing and validation are important topics during the migration process. We need to ensure the data is replicated exactly the same within the source database and involves a multi-faceted approach to verify data integrity, consistency, and completeness.</p>
<h3 class="wp-block-heading" id="h-key-aspects-of-testing-and-validation">Key Aspects of Testing and Validation:<a class="anchor-link" id="key-aspects-of-testing-and-validation"></a></h3>
<ul class="wp-block-list">
<li><strong>Data Integrity Verification:</strong> This involves checking for any corruption or alteration of data during transit. Techniques include:
<ul class="wp-block-list">
<li><strong>Checksums and Hashing:</strong> Comparing checksums or hash values of data blocks between source and target.</li>
<li><strong>Record Counts:</strong> Verifying that the total number of records in each table matches between the source and target.</li>
<li><strong>Row-by-Row Comparison:</strong> For critical tables, a detailed comparison of individual rows and their values is essential to catch subtle discrepancies. This can be achieved through custom scripts or data comparison tools.</li>
</ul>
</li>
<li><strong>Data Consistency Checks:</strong> Ensuring that relationships between data elements are maintained and that data types are accurately mapped.
<ul class="wp-block-list">
<li><strong>Foreign Key Constraints:</strong> Validating that all foreign key relationships are correctly established and enforced in the target database.</li>
<li><strong>Data Type Mapping:</strong> Confirming that data types from the source are appropriately converted to the target database&rsquo;s data types, avoiding truncation or loss of precision.</li>
<li><strong>Uniqueness Constraints:</strong> Verifying that unique keys and primary keys are correctly enforced.</li>
</ul>
</li>
<li><strong>Data Completeness Validation:</strong> Guaranteeing that all expected data has been successfully migrated.
<ul class="wp-block-list">
<li><strong>Schema Comparison:</strong> Comparing the schema of the source and target databases to ensure all tables, columns, indexes, and views are present.</li>
<li><strong>Data Volume Comparison:</strong> Checking the total volume of data (e.g., in gigabytes) to ensure a similar size, accounting for potential differences in storage efficiency.</li>
<li><strong>Specific Data Set Spot Checks:</strong> Selecting a representative sample of data and performing manual checks to confirm its presence and accuracy in the target.</li>
</ul>
</li>
<li><strong>Application-Level Testing:</strong> Beyond just the database, it&rsquo;s vital to test how applications interact with the new migrated database.
<ul class="wp-block-list">
<li><strong>Functional Testing:</strong> Ensuring that all application functionalities work as expected with the new database.</li>
<li><strong>Performance Testing:</strong> Benchmarking the performance of the applications with the migrated database to ensure it meets or exceeds previous performance levels. This includes query response times, transaction throughput, and overall system responsiveness.</li>
</ul>
</li>
</ul>
<h2 class="wp-block-heading" id="h-cutover-planning">Cutover Planning<a class="anchor-link" id="cutover-planning"></a></h2>
<p>The database cutover from cloud to on-premises typically occurs during low-traffic periods, e.g. nights / weekends. A rollback plan should be prepared to revert to the cloud database if validation issues arise. Additionally, don&rsquo;t forget to inform users about the cutover beforehand.</p>
<h2 class="wp-block-heading" id="h-post-migration-best-practices">Post-Migration Best Practices<a class="anchor-link" id="post-migration-best-practices"></a></h2>
<p>After moving to your on-prem environment, we should take a look in the following:</p>
<ol class="wp-block-list">
<li><strong>Monitoring &amp; Alerting,</strong> it is important to monitor the workloads of the database after the migration is completed. Keep an eye on some OS metrics such as CPU, memory, disk usage, also database metrics &mdash; buffer pool hit ratio, connections, row operations, locks, checkpoint. We can utilize some tools for monitoring such as&nbsp; Prometheus + Grafana, Percona Monitoring, pg_stat_statements.</li>
<li><strong>Performance Tuning,</strong> performance tuning is repeatable activities which need to be regularly monitored and corrected. It can be database parameter tuning, query tuning, indexing strategies.</li>
<li><strong>Security Hardening, </strong>ensure the firewall rules meet requirements, especially for connections from the application to the database. Implement least-privilege access and regularly rotate credentials.</li>
<li><strong>Cost Validation, </strong>after the migration, compare the actual on-prem TCO with the cloud environment, and don&rsquo;t forget to factor in operational overhead.</li>
</ol>
<h2 class="wp-block-heading" id="h-use-case-postgresql-cloud-to-on-prem-migration-with-cluster">Use Case: PostgreSQL Cloud to On-Prem Migration with Cluster<a class="anchor-link" id="use-case-postgresql-cloud-to-on-prem-migration-with-cluster"></a></h2>
<p>In a Cloud PostgreSQL to on-prem migration, we will utilize <code>logical replication</code> to replicate data between the cloud and on-prem environments. Before setting up logical replication from Cloud RDS PostgreSQL to self-hosted PostgreSQL, we need to configure the following parameters in the RDS PostgreSQL(restart required):</p>
<pre class="wp-block-code"><code>rds.logical_replication = 1
wal_level = logical
max_replication_slots = 10&nbsp;
max_wal_sender = 10</code></pre>
<h3 class="wp-block-heading" id="h-step-1-prepare-on-prem-postgresql-cluster">Step 1: Prepare On-Prem PostgreSQL Cluster<a class="anchor-link" id="step-1-prepare-on-prem-postgresql-cluster"></a></h3>
<p>Set up a PostgreSQL streaming replication cluster on-prem (at least 2 nodes for HA), we can use ClusterControl to spin up a PostgreSQL cluster with 2 nodes (Primary &amp; Replica) and configure replication slots for resilience.</p>
<p>After the PostgreSQL cluster is up and running in the on-prem environment, we need to ensure the following parameters are set:</p>
<pre class="wp-block-code"><code>wal_level = logical
max_replication_slots = 10
max_wal_senders = 10</code></pre>
<h3 class="wp-block-heading" id="h-step-2-schema-backup-and-restore">Step 2: Schema Backup and Restore<a class="anchor-link" id="step-2-schema-backup-and-restore"></a></h3>
<p>When using logical replication, only the schema (DDL) must be backed up and restored in order to mirror the existing database structure while the data itself is replicated separately &mdash; confirm there are no DDL changes when setting up logical replication.</p>
<ul class="wp-block-list">
<li>Run the database&rsquo;s schema backup using <code>pg_dump</code><em> </em>in the Cloud PostgreSQL node:</li>
</ul>
<p><code>pg_dump -h mydb.xxxxxx.ap-southeast-1.rds.amazonaws.com -p 5432 -U dbadmin -d mydb &mdash;-schema-only -f db_schema_dump.sql</code></p>
<ul class="wp-block-list">
<li>Create your on-prem database:</li>
</ul>
<p><code>psql# create database mydb</code></p>
<ul class="wp-block-list">
<li>Restore the schema backup into your on-prem PostgreSQL database.</li>
</ul>
<p><code>psql -h &lt;onprem-host&gt; -p 5432 -U &lt;username&gt; -f db_schema_dump.sql mydb</code></p>
<h3 class="wp-block-heading" id="h-step-3-logical-replication-publication-amp-subscription">Step 3: Logical Replication (Publication &amp; Subscription)<a class="anchor-link" id="step-3-logical-replication-publication-subscription"></a></h3>
<p>PostgreSQL logical replication relies on tables having a primary key because&nbsp; every replicated table must have a <strong>REPLICA IDENTITY</strong>, which is the <strong>primary key </strong>by default. If the table does not have a primary key, we must configure the table as <strong>REPLICA IDENTITY FULL</strong>.</p>
<ul class="wp-block-list">
<li>If the tables does not have primary keys, we need to configure the following in Cloud RDS PostgreSQL:</li>
</ul>
<p><code>ALTER TABLE my_table REPLICA IDENTITY FULL;</code></p>
<ul class="wp-block-list">
<li>On Cloud RDS PostgreSQL (source), create a publication:</li>
</ul>
<p><code>CREATE PUBLICATION mypub FOR ALL TABLES;</code></p>
<ul class="wp-block-list">
<li>On your on-prem PostgreSQL (target), create a subscription:</li>
</ul>
<pre class="wp-block-code"><code>CREATE SUBSCRIPTION mysub
CONNECTION 'host=mydb.xxxxxx.ap-southeast-1.rds.amazonaws.com dbname=mydb user=replica password=pass'
PUBLICATION mypub
WITH (
&nbsp;&nbsp;copy_data = true,
&nbsp;&nbsp;create_slot = true,
&nbsp;&nbsp;enabled = true
);</code></pre>
<p>The option copy_data will sync the raw data from Cloud RDS to on-prem PostgreSQL.</p>
<ul class="wp-block-list">
<li>Monitor data replication in the source (Cloud RDS PostgreSQL) and self-hosted on-prem PostgreSQL by executing the following query:</li>
</ul>
<p><strong>Cloud RDS PostgreSQL (Publisher):</strong></p>
<p><code>psql# SELECT * FROM pg_replication_slots;</code></p>
<p><strong>Self-hosted on-prem PostgreSQL (Subscription):</strong></p>
<p><code>psql# SELECT * FROM pg_stat_subscription;</code></p>
<h3 class="wp-block-heading" id="h-step-4-cutover">Step 4: Cutover<a class="anchor-link" id="step-4-cutover"></a></h3>
<p>The cutover process involves transferring the application&rsquo;s endpoint from the AWS RDS instance to your on-prem PG cluster and confirming the on-prem cluster is in sync (<code>pg_stat_replication</code>).</p>
<p><strong>Downtime:</strong> Minimal, limited to DNS or connection string changes.</p>
<h2 class="wp-block-heading" id="h-conclusion">Conclusion<a class="anchor-link" id="conclusion"></a></h2>
<p>Migrating databases from cloud to on-prem with minimal downtime is possible with careful planning, replication strategies, and thorough testing. Regardless of your database, the key is to minimize the cutover window, validate data consistency, and maintain business continuity.</p>
<p>Organizations should treat migration not as a one-time event, but as an iterative process: plan, test, migrate, validate, and optimize. By following these best practices, businesses can achieve a smooth transition back to on-prem while meeting performance, compliance, and cost objectives.</p>
<p>Ready to migrate your PostgreSQL database and maintain your orchestration on-prem?</p>
<h2 class="wp-block-heading" id="h-install-clustercontrol-in-10-minutes-nbsp-free-30-day-nbsp-enterprise-trial-included">Install ClusterControl in 10-minutes.&nbsp;<strong>Free 30-day&nbsp;</strong>Enterprise trial included!<a class="anchor-link" id="install-clustercontrol-in-10-minutes-free-30-day-enterprise-trial-included"></a></h2>
<h3 class="wp-block-heading" id="h-script-installation-instructions">Script Installation Instructions<a class="anchor-link" id="script-installation-instructions"></a></h3>
<p>The installer script is the simplest way to get ClusterControl up and running. Run it on your chosen host, and it will take care of installing all required packages and dependencies.</p>
<p>Offline environments are supported as well. See the&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/offline-installation/">Offline Installation</a>&nbsp;guide for more details.</p>
<p>On the ClusterControl server, run the following commands:</p>
<pre class="wp-block-code"><code>wget https://severalnines.com/downloads/cmon/install-cc
chmod +x install-cc</code></pre>
<p>With your install script ready, run the command below. Replace&nbsp;<code>S9S_CMON_PASSWORD</code>&nbsp;and&nbsp;<code>S9S_ROOT_PASSWORD</code>&nbsp;placeholders with your choice password, or remove the environment variables from the command to interactively set the passwords. If you have multiple network interface cards, assign one IP address for the&nbsp;<code>HOST</code>&nbsp;variable in the command using&nbsp;<code>HOST=&lt;ip_address&gt;</code>.</p>
<pre class="wp-block-code"><code>S9S_CMON_PASSWORD=&lt;your_password&gt; S9S_ROOT_PASSWORD=&lt;your_password&gt; HOST=&lt;ip_address&gt; ./install-cc # as root or sudo user</code></pre>
<p>After the installation is complete, open a web browser, navigate to&nbsp;<code>https://&lt;ClusterControl_host&gt;/</code>, and create the first admin user by entering a username (note that &ldquo;admin&rdquo; is reserved) and a password on the&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/quickstart/#step-2-create-the-first-admin-user">welcome page</a>. Once you&rsquo;re in, you can&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/user-guide/deployment/create-database-cluster/">deploy</a>&nbsp;a new database cluster or&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/user-guide/deployment/import-database-cluster/">import</a>&nbsp;an existing one.</p>
<p>The installer script supports a range of environment variables for advanced setup. You can define them using export or by prefixing the install command.</p>
<p>See the&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/online-installation/#environment-variables">list of supported variables</a>&nbsp;and&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/online-installation/#example-use-cases">example use cases</a>&nbsp;to tailor your installation.</p>
<h4 class="wp-block-heading" id="h-other-installation-options">Other Installation Options</h4>
<p><strong>Helm Chart</strong></p>
<p>Deploy ClusterControl on Kubernetes using our&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/online-installation/#helm-chart">official Helm chart</a>.</p>
<p><strong>Ansible Role</strong></p>
<p>Automate installation and configuration using our&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/online-installation/#ansible-role">Ansible playbooks</a>.</p>
<p><strong>Puppet Module</strong></p>
<p>Manage your ClusterControl deployment with the&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/online-installation/#puppet-module">Puppet module</a>.</p>
<h4 class="wp-block-heading" id="h-clustercontrol-on-marketplaces">ClusterControl on Marketplaces</h4>
<p>Prefer to launch ClusterControl directly from the cloud? It&rsquo;s available on these platforms:</p>
<ul class="wp-block-list">
<li><a href="https://marketplace.digitalocean.com/apps/clustercontrol">DigitalOcean Marketplace</a></li>
<li><a href="https://gridscale.io/en/marketplace">gridscale.io Marketplace</a></li>
<li><a href="https://www.vultr.com/marketplace/apps/clustercontrol/">Vultr Marketplace</a></li>
<li><a href="https://www.linode.com/marketplace/apps/severalnines/clustercontrol/">Linode Marketplace</a></li>
<li><a href="https://console.cloud.google.com/marketplace/product/severalnines-public/clustercontrol">Google Cloud Platform</a></li>
</ul>
<p>The post <a href="https://severalnines.com/blog/how-to-migrate-cloud-postgresql-to-on-prem-with-minimal-downtime/">How to Migrate Cloud PostgreSQL to On-Prem with Minimal Downtime</a> appeared first on <a href="https://severalnines.com">Severalnines</a>.</p>

<p>The post <a rel="nofollow" href="https://severalnines.com/blog/how-to-migrate-cloud-postgresql-to-on-prem-with-minimal-downtime/">How to Migrate Cloud PostgreSQL to On-Prem with Minimal Downtime</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>TidesDB becomes Silver Sponsor of the MariaDB Foundation</title>
      <link>https://mariadb.org/tidesdb-becomes-silver-sponsor-of-the-mariadb-foundation/</link>
      <pubDate>Fri, 20 Feb 2026 04:05:15 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>We’re excited to welcome TidesDB as a Silver Sponsor of the MariaDB Foundation.<br />
TidesDB’s sponsorship directly supports our mission to strengthen the MariaDB ecosystem and accelerate innovation through open, community-driven collaboration. …<br />
Continue reading \"TidesDB becomes Silver Sponsor of the MariaDB Foundation\"<br />
The post TidesDB becomes Silver Sponsor of the MariaDB Foundation appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/tidesdb-becomes-silver-sponsor-of-the-mariadb-foundation/">TidesDB becomes Silver Sponsor of the MariaDB Foundation</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>We&rsquo;re excited to welcome TidesDB as a Silver Sponsor of the MariaDB Foundation.<br>
TidesDB&rsquo;s sponsorship directly supports our mission to strengthen the MariaDB ecosystem and accelerate innovation through open, community-driven collaboration. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/tidesdb-becomes-silver-sponsor-of-the-mariadb-foundation/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;TidesDB becomes Silver Sponsor of the MariaDB Foundation&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/tidesdb-becomes-silver-sponsor-of-the-mariadb-foundation/">TidesDB becomes Silver Sponsor of the MariaDB Foundation</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/tidesdb-becomes-silver-sponsor-of-the-mariadb-foundation/">TidesDB becomes Silver Sponsor of the MariaDB Foundation</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>FromDual Performance Monitor 2.2.1 has been released</title>
      <link>https://www.fromdual.com/blog/fromdual-performance-monitor-2.2.1-has-been-released/</link>
      <pubDate>Thu, 19 Feb 2026 17:18:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://www.fromdual.com/aggregator/categories/9/">FromDual GmbH</source>
      <description><![CDATA[<p>FromDual has the pleasure to announce the release of the new version 2.2.1 of its popular Database Performance Monitor for MariaDB, Galera Cluster, MySQL and PostgreSQL fpmmm.<br />
The FromDual Performance Monitor enables Database and System Administrators to monitor and understand what is going on inside their databases and on the machines where the databases reside.<br />
More information you can find here: FromDual Performance Monitor.<br />
Download<br />
The new FromDual Performance Monitor can be downloaded from our Sofware Download page or you can use our repositories. How to install and use the FromDual Performance Monitor is documented in the Documentation.<br />
In the inconceivable case that you find a bug in the FromDual Performance Monitor please report it to us by sending an email.<br />
Any feedback, statements and testimonials are welcome as well! Please send them to us.<br />
Monitoring as a Service (MaaS)<br />
You do not want to set-up your database monitoring yourself? No problem: Choose our Monitoring as a Service (MaaS) to safe time and costs!<br />
Installation of Performance Monitor 2.2.1<br />
How to install the FromDual Performance Monitor you can find in the Installation Guide.<br />
Upgrade of fpmmm tar ball from 1.x to 2.2.1<br />
There are some changes in the configuration file (fpmmm.conf):</p>
<p>The access rights should be change as follows: chmod 600 /etc/fpmmm.conf<br />
The key Methode was spelled wrong in the configuration file. It was renamed to Method.<br />
The key PidFile is ambiguous which could lead to problems and bugs. Thus it was changed to either MyPidFile for fpmmm and DbPidFile for the database.</p>
<p>Upgrade with DEB/RPM packages should happen automatically. For tar balls follow this instruction:<br />
$ cd /opt<br />
$ tar xf /download/fpmmm-2.2.1.tar.gz<br />
$ rm -f fpmmm<br />
$ ln -s fpmmm-2.2.1 fpmmm</p>
<p>Changes in FromDual Performance Monitor 2.2.1<br />
These release notes include both the changes that came with version 2.2.0 and version 2.2.1.<br />
This release contains new features and various bug fixes.<br />
You can verify your current FromDual Performance Monitor version with the following command:<br />
$ /opt/fpmmm/bin/fpmmm --version</p>
<p>General</p>
<p>Updated to latest myEnv library.<br />
PHP 8.5 incompatibilities fixed.<br />
Typos fixed.<br />
Error messages improved.<br />
Function real_connect warnings send to console are suppressed now.<br />
Connection problems timeout reduced so in case of troubles we should see more and earlier…<br />
Other cosmetic errors and debugging information fixed.<br />
Data are gathered and set to zero even thought database is not reachable.<br />
Indention of logged messages fixed.<br />
Function exit is logged now as well.<br />
SSL connection handling added.<br />
Fix of error: array_sum(): Addition is not supported on type string in warning after upgrade to Ubuntu 24.04/PHP 8.3.<br />
Error log parsing had problems with huge error logs. Now we have added a size barrier.<br />
Function getDistributions updated/cleaned-up.<br />
Command lsb_release removed.<br />
Documentation added.<br />
Nagios: Tests fixed for MariaDB 11.8.</p>
<p>Templates</p>
<p>Server: Available I/O system information added to each I/O system on top, pages named.<br />
InnoDB: Pages named, row write operations graph added.<br />
MySQL: Some graphs and query dashboard made nicer.</p>
<p>Agent</p>
<p>none</p>
<p>Server</p>
<p>Items FromDual.MySQL.server.disk.avg_io_read_wait and FromDual.MySQL.server.disk.avg_io_write_wait removed because they are showing completely wrong values. Use FromDual.MySQL.server.disk.r_await and FromDual.MySQL.server.disk.w_await instead.<br />
Workaround for missing cpuinfo old cachefile implemented.</p>
<p>Galera</p>
<p>Old style variable fixed which causes problems with newer version.<br />
Default values on database stop added.<br />
Workaround for cut wsrep_provider_options bug in MySQL Galera Cluster added.</p>
<p>InnoDB</p>
<p>Variable innodb_log_file_size made consistent for MariaDB and MySQL.<br />
Deprecated and removed variable innodb_log_files_in_group removed.<br />
Fix for innodb_log_file_size in MySQL 9.4.<br />
Log occupancy graph added and graph added to dashboard.<br />
Variable tx_isolation replaced by transaction isolation which is deprecated in MariaDB 11.2 and MySQL 5.7.</p>
<p>MySQL</p>
<p>Variable vendor_versions_behind special case caught.<br />
Connection charset changed from utf8 to utf8mb4 due to errors in MariaDB 11.8.<br />
Template pages named.</p>
<p>Process</p>
<p>none</p>
<p>Security</p>
<p>Module improved for new behaviour in MariaDB 11.8.</p>
<p>Master</p>
<p>Wrong version check for master fixed.</p>
<p>Slave</p>
<p>Slave lagging problem fixed.<br />
Wrong version check for slave fixed.<br />
MySQL 8.4 commands added for replication monitoring.</p>
<p>Backup</p>
<p>none</p>
<p>PostgreSQL</p>
<p>Rudimentary PostgreSQL monitoring added.</p>
<p>Packaging</p>
<p>RHEL 8 added again.<br />
RPM spec adapted for RHEL 10.<br />
SNMP library updated.<br />
Debian 10 and RHEL 7 removed.<br />
DEB sign stuff added.</p>
<p>For subscriptions of commercial use of fpmmm please get in contact with us.</p>
<p>The post <a rel="nofollow" href="https://www.fromdual.com/blog/fromdual-performance-monitor-2.2.1-has-been-released/">FromDual Performance Monitor 2.2.1 has been released</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>FromDual has the pleasure to announce the release of the new version 2.2.1 of its popular Database Performance Monitor for MariaDB, Galera Cluster, MySQL and PostgreSQL <a href="https://www.fromdual.com/software/fromdual-performance-monitor/"><code>fpmmm</code></a>.</p>
<p>The FromDual Performance Monitor enables Database and System Administrators to monitor and understand what is going on inside their databases and on the machines where the databases reside.</p>
<p>More information you can find here: <a href="https://www.fromdual.com/software/fromdual-performance-monitor/">FromDual Performance Monitor</a>.</p>
<h2 id="download">Download<a class="anchor-link" id="download"></a></h2>
<p>The new FromDual Performance Monitor can be downloaded from our <a href="https://support.fromdual.com/admin/public/download.php" target="_blank" rel="noopener">Sofware Download</a> page or you can use our <a href="https://www.fromdual.com/repositories/">repositories</a>. How to install and use the FromDual Performance Monitor is documented in the <a href="https://support.fromdual.com/documentation/fpmmm/fpmmm.html" target="_blank" rel="noopener">Documentation</a>.</p>
<p>In the inconceivable case that you find a bug in the FromDual Performance Monitor please report it to us by sending an <a href="mailto:contact@fromdual.com?Subject=Bug%20report%20for%20fpmmm">email</a>.</p>
<p>Any feedback, statements and testimonials are welcome as well! Please send them <a>to us</a>.</p>
<h2 id="monitoring-as-a-service-maas">Monitoring as a Service (MaaS)<a class="anchor-link" id="monitoring-as-a-service-maas"></a></h2>
<p>You do not want to set-up your database monitoring yourself? No problem: Choose our <a href="https://www.fromdual.com/services/monitoring-as-a-service-maas/">Monitoring as a Service</a> (MaaS) to safe time and costs!</p>
<h2 id="installation-of-performance-monitor-221">Installation of Performance Monitor 2.2.1<a class="anchor-link" id="installation-of-performance-monitor-2-2-1"></a></h2>
<p>How to install the FromDual Performance Monitor you can find in the <a href="https://support.fromdual.com/documentation/fpmmm/fpmmm.html#installation-guide" target="_blank" rel="noopener">Installation Guide</a>.</p>
<h2 id="upgrade-of-fpmmm-tar-ball-from-1x-to-221">Upgrade of fpmmm tar ball from 1.x to 2.2.1<a class="anchor-link" id="upgrade-of-fpmmm-tar-ball-from-1-x-to-2-2-1"></a></h2>
<p>There are some changes in the configuration file (<code>fpmmm.conf</code>):</p>
<ul>
<li>The access rights should be change as follows: <code>chmod 600 /etc/fpmmm.conf</code></li>
<li>The key <code>Methode</code> was spelled wrong in the configuration file. It was renamed to <code>Method</code>.</li>
<li>The key <code>PidFile</code> is ambiguous which could lead to problems and bugs. Thus it was changed to either <code>MyPidFile</code> for fpmmm and <code>DbPidFile</code> for the database.</li>
</ul>
<p>Upgrade with DEB/RPM packages should happen automatically. For tar balls follow this instruction:</p>
<pre><code>$ cd /opt
$ tar xf /download/fpmmm-2.2.1.tar.gz
$ rm -f fpmmm
$ ln -s fpmmm-2.2.1 fpmmm
</code></pre>
<h2 id="changes-in-fromdual-performance-monitor-221">Changes in FromDual Performance Monitor 2.2.1<a class="anchor-link" id="changes-in-fromdual-performance-monitor-2-2-1"></a></h2>
<p>These release notes include both the changes that came with version 2.2.0 and version 2.2.1.</p>
<p>This release contains new features and various bug fixes.</p>
<p>You can verify your current FromDual Performance Monitor version with the following command:</p>
<pre><code>$ /opt/fpmmm/bin/fpmmm --version
</code></pre>
<h3 id="general">General<a class="anchor-link" id="general"></a></h3>
<ul>
<li>Updated to latest myEnv library.</li>
<li>PHP 8.5 incompatibilities fixed.</li>
<li>Typos fixed.</li>
<li>Error messages improved.</li>
<li>Function <code>real_connect</code> warnings send to console are suppressed now.</li>
<li>Connection problems timeout reduced so in case of troubles we should see more and earlier&hellip;</li>
<li>Other cosmetic errors and debugging information fixed.</li>
<li>Data are gathered and set to zero even thought database is not reachable.</li>
<li>Indention of logged messages fixed.</li>
<li>Function exit is logged now as well.</li>
<li>SSL connection handling added.</li>
<li>Fix of error: array_sum(): Addition is not supported on type string in warning after upgrade to Ubuntu 24.04/PHP 8.3.</li>
<li>Error log parsing had problems with huge error logs. Now we have added a size barrier.</li>
<li>Function <code>getDistributions</code> updated/cleaned-up.</li>
<li>Command lsb_release removed.</li>
<li>Documentation added.</li>
<li>Nagios: Tests fixed for MariaDB 11.8.</li>
</ul>
<h3 id="templates">Templates<a class="anchor-link" id="templates"></a></h3>
<ul>
<li>Server: Available I/O system information added to each I/O system on top, pages named.</li>
<li>InnoDB: Pages named, row write operations graph added.</li>
<li>MySQL: Some graphs and query dashboard made nicer.</li>
</ul>
<h3 id="agent">Agent<a class="anchor-link" id="agent"></a></h3>
<ul>
<li>none</li>
</ul>
<h3 id="server">Server<a class="anchor-link" id="server"></a></h3>
<ul>
<li>Items <code>FromDual.MySQL.server.disk.avg_io_read_wait</code> and <code>FromDual.MySQL.server.disk.avg_io_write_wait</code> removed because they are showing completely wrong values. Use <code>FromDual.MySQL.server.disk.r_await</code> and <code>FromDual.MySQL.server.disk.w_await</code> instead.</li>
<li>Workaround for missing cpuinfo old cachefile implemented.</li>
</ul>
<h3 id="galera">Galera<a class="anchor-link" id="galera"></a></h3>
<ul>
<li>Old style variable fixed which causes problems with newer version.</li>
<li>Default values on database stop added.</li>
<li>Workaround for cut <code>wsrep_provider_options</code> bug in MySQL Galera Cluster added.</li>
</ul>
<h3 id="innodb">InnoDB<a class="anchor-link" id="innodb"></a></h3>
<ul>
<li>Variable <code>innodb_log_file_size</code> made consistent for MariaDB and MySQL.</li>
<li>Deprecated and removed variable <code>innodb_log_files_in_group</code> removed.</li>
<li>Fix for <code>innodb_log_file_size</code> in MySQL 9.4.</li>
<li>Log occupancy graph added and graph added to dashboard.</li>
<li>Variable <code>tx_isolation</code> replaced by transaction isolation which is deprecated in MariaDB 11.2 and MySQL 5.7.</li>
</ul>
<h3 id="mysql">MySQL<a class="anchor-link" id="mysql"></a></h3>
<ul>
<li>Variable <code>vendor_versions_behind</code> special case caught.</li>
<li>Connection charset changed from <code>utf8</code> to <code>utf8mb4</code> due to errors in MariaDB 11.8.</li>
<li>Template pages named.</li>
</ul>
<h3 id="process">Process<a class="anchor-link" id="process"></a></h3>
<ul>
<li>none</li>
</ul>
<h3 id="security">Security<a class="anchor-link" id="security"></a></h3>
<ul>
<li>Module improved for new behaviour in MariaDB 11.8.</li>
</ul>
<h3 id="master">Master<a class="anchor-link" id="master"></a></h3>
<ul>
<li>Wrong version check for master fixed.</li>
</ul>
<h3 id="slave">Slave<a class="anchor-link" id="slave"></a></h3>
<ul>
<li>Slave lagging problem fixed.</li>
<li>Wrong version check for slave fixed.</li>
<li>MySQL 8.4 commands added for replication monitoring.</li>
</ul>
<h3 id="backup">Backup<a class="anchor-link" id="backup"></a></h3>
<ul>
<li>none</li>
</ul>
<h3 id="postgresql">PostgreSQL<a class="anchor-link" id="postgresql"></a></h3>
<ul>
<li>Rudimentary PostgreSQL monitoring added.</li>
</ul>
<h3 id="packaging">Packaging<a class="anchor-link" id="packaging"></a></h3>
<ul>
<li>RHEL 8 added again.</li>
<li>RPM spec adapted for RHEL 10.</li>
<li>SNMP library updated.</li>
<li>Debian 10 and RHEL 7 removed.</li>
<li>DEB sign stuff added.</li>
</ul>
<p>For subscriptions of commercial use of <code>fpmmm</code> please <a href="mailto:contact@fromdual.com?Subject=Commercial%20use%20of%20fpmmm">get in contact</a> with us.</p>

<p>The post <a rel="nofollow" href="https://www.fromdual.com/blog/fromdual-performance-monitor-2.2.1-has-been-released/">FromDual Performance Monitor 2.2.1 has been released</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>FromDual Performance Monitor 2.2.1 has been released</title>
      <link>https://www.fromdual.com/blog/fpmmm-release-notes/fromdual-performance-monitor-2.2.1-has-been-released/</link>
      <pubDate>Thu, 19 Feb 2026 17:18:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://www.fromdual.com/aggregator/categories/9/">FromDual GmbH</source>
      <description><![CDATA[<p>FromDual has the pleasure to announce the release of the new version 2.2.1 of its popular Database Performance Monitor for MariaDB, Galera Cluster, MySQL and PostgreSQL fpmmm.<br />
The FromDual Performance Monitor enables Database and System Administrators to monitor and understand what is going on inside their databases and on the machines where the databases reside.<br />
More information you can find here: FromDual Performance Monitor.<br />
Download<br />
The new FromDual Performance Monitor can be downloaded from our Sofware Download page or you can use our repositories. How to install and use the FromDual Performance Monitor is documented in the Documentation.<br />
In the inconceivable case that you find a bug in the FromDual Performance Monitor please report it to us by sending an email.<br />
Any feedback, statements and testimonials are welcome as well! Please send them to us.<br />
Monitoring as a Service (MaaS)<br />
You do not want to set-up your database monitoring yourself? No problem: Choose our Monitoring as a Service (MaaS) to safe time and costs!<br />
Installation of Performance Monitor 2.2.1<br />
How to install the FromDual Performance Monitor you can find in the Installation Guide.<br />
Upgrade of fpmmm tar ball from 1.x to 2.2.1<br />
There are some changes in the configuration file (fpmmm.conf):</p>
<p>The access rights should be change as follows: chmod 600 /etc/fpmmm.conf<br />
The key Methode was spelled wrong in the configuration file. It was renamed to Method.<br />
The key PidFile is ambiguous which could lead to problems and bugs. Thus it was changed to either MyPidFile for fpmmm and DbPidFile for the database.</p>
<p>Upgrade with DEB/RPM packages should happen automatically. For tar balls follow this instruction:<br />
$ cd /opt<br />
$ tar xf /download/fpmmm-2.2.1.tar.gz<br />
$ rm -f fpmmm<br />
$ ln -s fpmmm-2.2.1 fpmmm</p>
<p>Changes in FromDual Performance Monitor 2.2.1<br />
These release notes include both the changes that came with version 2.2.0 and version 2.2.1.<br />
This release contains new features and various bug fixes.<br />
You can verify your current FromDual Performance Monitor version with the following command:<br />
$ /opt/fpmmm/bin/fpmmm --version</p>
<p>General</p>
<p>Updated to latest myEnv library.<br />
PHP 8.5 incompatibilities fixed.<br />
Typos fixed.<br />
Error messages improved.<br />
Function real_connect warnings send to console are suppressed now.<br />
Connection problems timeout reduced so in case of troubles we should see more and earlier…<br />
Other cosmetic errors and debugging information fixed.<br />
Data are gathered and set to zero even thought database is not reachable.<br />
Indention of logged messages fixed.<br />
Function exit is logged now as well.<br />
SSL connection handling added.<br />
Fix of error: array_sum(): Addition is not supported on type string in warning after upgrade to Ubuntu 24.04/PHP 8.3.<br />
Error log parsing had problems with huge error logs. Now we have added a size barrier.<br />
Function getDistributions updated/cleaned-up.<br />
Command lsb_release removed.<br />
Documentation added.<br />
Nagios: Tests fixed for MariaDB 11.8.</p>
<p>Templates</p>
<p>Server: Available I/O system information added to each I/O system on top, pages named.<br />
InnoDB: Pages named, row write operations graph added.<br />
MySQL: Some graphs and query dashboard made nicer.</p>
<p>Agent</p>
<p>none</p>
<p>Server</p>
<p>Items FromDual.MySQL.server.disk.avg_io_read_wait and FromDual.MySQL.server.disk.avg_io_write_wait removed because they are showing completely wrong values. Use FromDual.MySQL.server.disk.r_await and FromDual.MySQL.server.disk.w_await instead.<br />
Workaround for missing cpuinfo old cachefile implemented.</p>
<p>Galera</p>
<p>Old style variable fixed which causes problems with newer version.<br />
Default values on database stop added.<br />
Workaround for cut wsrep_provider_options bug in MySQL Galera Cluster added.</p>
<p>InnoDB</p>
<p>Variable innodb_log_file_size made consistent for MariaDB and MySQL.<br />
Deprecated and removed variable innodb_log_files_in_group removed.<br />
Fix for innodb_log_file_size in MySQL 9.4.<br />
Log occupancy graph added and graph added to dashboard.<br />
Variable tx_isolation replaced by transaction isolation which is deprecated in MariaDB 11.2 and MySQL 5.7.</p>
<p>MySQL</p>
<p>Variable vendor_versions_behind special case caught.<br />
Connection charset changed from utf8 to utf8mb4 due to errors in MariaDB 11.8.<br />
Template pages named.</p>
<p>Process</p>
<p>none</p>
<p>Security</p>
<p>Module improved for new behaviour in MariaDB 11.8.</p>
<p>Master</p>
<p>Wrong version check for master fixed.</p>
<p>Slave</p>
<p>Slave lagging problem fixed.<br />
Wrong version check for slave fixed.<br />
MySQL 8.4 commands added for replication monitoring.</p>
<p>Backup</p>
<p>none</p>
<p>PostgreSQL</p>
<p>Rudimentary PostgreSQL monitoring added.</p>
<p>Packaging</p>
<p>RHEL 8 added again.<br />
RPM spec adapted for RHEL 10.<br />
SNMP library updated.<br />
Debian 10 and RHEL 7 removed.<br />
DEB sign stuff added.</p>
<p>For subscriptions of commercial use of fpmmm please get in contact with us.</p>
<p>The post <a rel="nofollow" href="https://www.fromdual.com/blog/fpmmm-release-notes/fromdual-performance-monitor-2.2.1-has-been-released/">FromDual Performance Monitor 2.2.1 has been released</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>FromDual has the pleasure to announce the release of the new version 2.2.1 of its popular Database Performance Monitor for MariaDB, Galera Cluster, MySQL and PostgreSQL <a href="https://www.fromdual.com/software/fromdual-performance-monitor/"><code>fpmmm</code></a>.</p>
<p>The FromDual Performance Monitor enables Database and System Administrators to monitor and understand what is going on inside their databases and on the machines where the databases reside.</p>
<p>More information you can find here: <a href="https://www.fromdual.com/software/fromdual-performance-monitor/">FromDual Performance Monitor</a>.</p>
<h2 id="download">Download<a class="anchor-link" id="download"></a></h2>
<p>The new FromDual Performance Monitor can be downloaded from our <a href="https://support.fromdual.com/admin/public/download.php" target="_blank" rel="noopener">Sofware Download</a> page or you can use our <a href="https://www.fromdual.com/repositories/">repositories</a>. How to install and use the FromDual Performance Monitor is documented in the <a href="https://support.fromdual.com/documentation/fpmmm/fpmmm.html" target="_blank" rel="noopener">Documentation</a>.</p>
<p>In the inconceivable case that you find a bug in the FromDual Performance Monitor please report it to us by sending an <a href="mailto:contact@fromdual.com?Subject=Bug%20report%20for%20fpmmm">email</a>.</p>
<p>Any feedback, statements and testimonials are welcome as well! Please send them <a href="mailto:feedback@fromdual.com?Subject=Feedback%20for%20fpmmm">to us</a>.</p>
<h2 id="monitoring-as-a-service-maas">Monitoring as a Service (MaaS)<a class="anchor-link" id="monitoring-as-a-service-maas"></a></h2>
<p>You do not want to set-up your database monitoring yourself? No problem: Choose our <a href="https://www.fromdual.com/services/monitoring-as-a-service-maas/">Monitoring as a Service</a> (MaaS) to safe time and costs!</p>
<h2 id="installation-of-performance-monitor-221">Installation of Performance Monitor 2.2.1<a class="anchor-link" id="installation-of-performance-monitor-2-2-1"></a></h2>
<p>How to install the FromDual Performance Monitor you can find in the <a href="https://support.fromdual.com/documentation/fpmmm/fpmmm.html#installation-guide" target="_blank" rel="noopener">Installation Guide</a>.</p>
<h2 id="upgrade-of-fpmmm-tar-ball-from-1x-to-221">Upgrade of fpmmm tar ball from 1.x to 2.2.1<a class="anchor-link" id="upgrade-of-fpmmm-tar-ball-from-1-x-to-2-2-1"></a></h2>
<p>There are some changes in the configuration file (<code>fpmmm.conf</code>):</p>
<ul>
<li>The access rights should be change as follows: <code>chmod 600 /etc/fpmmm.conf</code></li>
<li>The key <code>Methode</code> was spelled wrong in the configuration file. It was renamed to <code>Method</code>.</li>
<li>The key <code>PidFile</code> is ambiguous which could lead to problems and bugs. Thus it was changed to either <code>MyPidFile</code> for fpmmm and <code>DbPidFile</code> for the database.</li>
</ul>
<p>Upgrade with DEB/RPM packages should happen automatically. For tar balls follow this instruction:</p>
<pre><code>$ cd /opt
$ tar xf /download/fpmmm-2.2.1.tar.gz
$ rm -f fpmmm
$ ln -s fpmmm-2.2.1 fpmmm
</code></pre>
<h2 id="changes-in-fromdual-performance-monitor-221">Changes in FromDual Performance Monitor 2.2.1<a class="anchor-link" id="changes-in-fromdual-performance-monitor-2-2-1"></a></h2>
<p>These release notes include both the changes that came with version 2.2.0 and version 2.2.1.</p>
<p>This release contains new features and various bug fixes.</p>
<p>You can verify your current FromDual Performance Monitor version with the following command:</p>
<pre><code>$ /opt/fpmmm/bin/fpmmm --version
</code></pre>
<h3 id="general">General<a class="anchor-link" id="general"></a></h3>
<ul>
<li>Updated to latest myEnv library.</li>
<li>PHP 8.5 incompatibilities fixed.</li>
<li>Typos fixed.</li>
<li>Error messages improved.</li>
<li>Function <code>real_connect</code> warnings send to console are suppressed now.</li>
<li>Connection problems timeout reduced so in case of troubles we should see more and earlier&hellip;</li>
<li>Other cosmetic errors and debugging information fixed.</li>
<li>Data are gathered and set to zero even thought database is not reachable.</li>
<li>Indention of logged messages fixed.</li>
<li>Function exit is logged now as well.</li>
<li>SSL connection handling added.</li>
<li>Fix of error: array_sum(): Addition is not supported on type string in warning after upgrade to Ubuntu 24.04/PHP 8.3.</li>
<li>Error log parsing had problems with huge error logs. Now we have added a size barrier.</li>
<li>Function <code>getDistributions</code> updated/cleaned-up.</li>
<li>Command lsb_release removed.</li>
<li>Documentation added.</li>
<li>Nagios: Tests fixed for MariaDB 11.8.</li>
</ul>
<h3 id="templates">Templates<a class="anchor-link" id="templates"></a></h3>
<ul>
<li>Server: Available I/O system information added to each I/O system on top, pages named.</li>
<li>InnoDB: Pages named, row write operations graph added.</li>
<li>MySQL: Some graphs and query dashboard made nicer.</li>
</ul>
<h3 id="agent">Agent<a class="anchor-link" id="agent"></a></h3>
<ul>
<li>none</li>
</ul>
<h3 id="server">Server<a class="anchor-link" id="server"></a></h3>
<ul>
<li>Items <code>FromDual.MySQL.server.disk.avg_io_read_wait</code> and <code>FromDual.MySQL.server.disk.avg_io_write_wait</code> removed because they are showing completely wrong values. Use <code>FromDual.MySQL.server.disk.r_await</code> and <code>FromDual.MySQL.server.disk.w_await</code> instead.</li>
<li>Workaround for missing cpuinfo old cachefile implemented.</li>
</ul>
<h3 id="galera">Galera<a class="anchor-link" id="galera"></a></h3>
<ul>
<li>Old style variable fixed which causes problems with newer version.</li>
<li>Default values on database stop added.</li>
<li>Workaround for cut <code>wsrep_provider_options</code> bug in MySQL Galera Cluster added.</li>
</ul>
<h3 id="innodb">InnoDB<a class="anchor-link" id="innodb"></a></h3>
<ul>
<li>Variable <code>innodb_log_file_size</code> made consistent for MariaDB and MySQL.</li>
<li>Deprecated and removed variable <code>innodb_log_files_in_group</code> removed.</li>
<li>Fix for <code>innodb_log_file_size</code> in MySQL 9.4.</li>
<li>Log occupancy graph added and graph added to dashboard.</li>
<li>Variable <code>tx_isolation</code> replaced by transaction isolation which is deprecated in MariaDB 11.2 and MySQL 5.7.</li>
</ul>
<h3 id="mysql">MySQL<a class="anchor-link" id="mysql"></a></h3>
<ul>
<li>Variable <code>vendor_versions_behind</code> special case caught.</li>
<li>Connection charset changed from <code>utf8</code> to <code>utf8mb4</code> due to errors in MariaDB 11.8.</li>
<li>Template pages named.</li>
</ul>
<h3 id="process">Process<a class="anchor-link" id="process"></a></h3>
<ul>
<li>none</li>
</ul>
<h3 id="security">Security<a class="anchor-link" id="security"></a></h3>
<ul>
<li>Module improved for new behaviour in MariaDB 11.8.</li>
</ul>
<h3 id="master">Master<a class="anchor-link" id="master"></a></h3>
<ul>
<li>Wrong version check for master fixed.</li>
</ul>
<h3 id="slave">Slave<a class="anchor-link" id="slave"></a></h3>
<ul>
<li>Slave lagging problem fixed.</li>
<li>Wrong version check for slave fixed.</li>
<li>MySQL 8.4 commands added for replication monitoring.</li>
</ul>
<h3 id="backup">Backup<a class="anchor-link" id="backup"></a></h3>
<ul>
<li>none</li>
</ul>
<h3 id="postgresql">PostgreSQL<a class="anchor-link" id="postgresql"></a></h3>
<ul>
<li>Rudimentary PostgreSQL monitoring added.</li>
</ul>
<h3 id="packaging">Packaging<a class="anchor-link" id="packaging"></a></h3>
<ul>
<li>RHEL 8 added again.</li>
<li>RPM spec adapted for RHEL 10.</li>
<li>SNMP library updated.</li>
<li>Debian 10 and RHEL 7 removed.</li>
<li>DEB sign stuff added.</li>
</ul>
<p>For subscriptions of commercial use of <code>fpmmm</code> please <a href="mailto:contact@fromdual.com?Subject=Commercial%20use%20of%20fpmmm">get in contact</a> with us.</p>

<p>The post <a rel="nofollow" href="https://www.fromdual.com/blog/fpmmm-release-notes/fromdual-performance-monitor-2.2.1-has-been-released/">FromDual Performance Monitor 2.2.1 has been released</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>What is MariaDB AI RAG and ​​what can I do with it?</title>
      <link>https://mariadb.com/resources/blog/what-is-mariadb-ai-rag-and-what-can-i-do-with-it/</link>
      <pubDate>Thu, 19 Feb 2026 16:56:34 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.com/">MariaDB</source>
      <description><![CDATA[<p>I recently had the pleasure of speaking at MariaDB Day Brussels 2026, an event where MariaDB enthusiasts like myself gather, […]</p>
<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/what-is-mariadb-ai-rag-and-what-can-i-do-with-it/">What is MariaDB AI RAG and ​​what can I do with it?</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>I recently had the pleasure of speaking at MariaDB Day Brussels 2026, an event where MariaDB enthusiasts like myself gather, learn, and share experiences with peers in the ecosystem. My talk was about a new component in the MariaDB Enterprise Platform called MariaDB AI RAG. This component allows teams not only to standardize RAG pipelines but also to accelerate the development of GenAI&hellip;</p>
<p><a href="https://mariadb.com/resources/blog/what-is-mariadb-ai-rag-and-what-can-i-do-with-it/" rel="nofollow">Source</a></p>

<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/what-is-mariadb-ai-rag-and-what-can-i-do-with-it/">What is MariaDB AI RAG and ​​what can I do with it?</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>A Guide to Accelerating Your Application with Valkey: Caching Database Queries and Sessions</title>
      <link>https://www.percona.com/blog/a-guide-to-accelerating-your-application-with-valkey-caching-database-queries-and-sessions/</link>
      <pubDate>Thu, 19 Feb 2026 15:33:30 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://www.percona.com/blog/">Percona Database Performance Blog</source>
      <description><![CDATA[<p>Modern applications often rely on multiple services to provide fast, reliable, and scalable responses. A common and highly effective architecture involves an application, a persistent database (like MySQL), and a high-speed cache service (like Valkey). In this guide, we’ll explore how to integrate these components effectively using Python to dramatically improve your application’s performance. Understanding […]</p>
<p>The post <a rel="nofollow" href="https://www.percona.com/blog/a-guide-to-accelerating-your-application-with-valkey-caching-database-queries-and-sessions/">A Guide to Accelerating Your Application with Valkey: Caching Database Queries and Sessions</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" width="200" height="112" src="https://www.percona.com/blog/wp-content/uploads/2025/09/Redis-License-has-Changed-200x112.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="" style="margin-bottom: 5px;clear:both;max-width: 100%">Modern applications often rely on multiple services to provide fast, reliable, and scalable responses. A common and highly effective architecture involves an application, a persistent database (like MySQL), and a high-speed cache service (like Valkey). In this guide, we&rsquo;ll explore how to integrate these components effectively using Python to dramatically improve your application&rsquo;s performance. Understanding [&hellip;]</p>

<p>The post <a rel="nofollow" href="https://www.percona.com/blog/a-guide-to-accelerating-your-application-with-valkey-caching-database-queries-and-sessions/">A Guide to Accelerating Your Application with Valkey: Caching Database Queries and Sessions</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>MariaDB Foundation Releases Alpha of the Test Automation Framework (TAF)</title>
      <link>https://mariadb.org/mariadb-foundation-releases-alpha-of-the-test-automation-framework-taf/</link>
      <pubDate>Thu, 19 Feb 2026 12:38:44 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>The MariaDB Foundation is releasing the alpha version of the Test Automation Framework (TAF), an open-source benchmarking framework designed for clarity, repeatability, and vendor-neutral testing. TAF provides a structured way to run database benchmarks using consistent workloads, configuration, and reporting pipelines, making results easier to reproduce and discuss. …<br />
Continue reading \"MariaDB Foundation Releases Alpha of the Test Automation Framework (TAF)\"<br />
The post MariaDB Foundation Releases Alpha of the Test Automation Framework (TAF) appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-foundation-releases-alpha-of-the-test-automation-framework-taf/">MariaDB Foundation Releases Alpha of the Test Automation Framework (TAF)</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>The MariaDB Foundation is releasing the alpha version of the Test Automation Framework (TAF), an open-source benchmarking framework designed for clarity, repeatability, and vendor-neutral testing. TAF provides a structured way to run database benchmarks using consistent workloads, configuration, and reporting pipelines, making results easier to reproduce and discuss. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/mariadb-foundation-releases-alpha-of-the-test-automation-framework-taf/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;MariaDB Foundation Releases Alpha of the Test Automation Framework (TAF)&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-foundation-releases-alpha-of-the-test-automation-framework-taf/">MariaDB Foundation Releases Alpha of the Test Automation Framework (TAF)</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-foundation-releases-alpha-of-the-test-automation-framework-taf/">MariaDB Foundation Releases Alpha of the Test Automation Framework (TAF)</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Explaining why throughput varies for Postgres with a CPU-bound Insert Benchmark</title>
      <link>https://smalldatum.blogspot.com/2026/02/explaining-why-throughput-varies-for.html</link>
      <pubDate>Wed, 18 Feb 2026 20:38:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://smalldatum.blogspot.com/">Small Datum</source>
      <description><![CDATA[<p>Throughput for the write-heavy steps of the Insert Benchmark look like a distorted sine wave with Postgres on CPU-bound workloads but not on IO-bound workloads. For the CPU-bound workloads the chart for max response time at N-second intervals for inserts is flat but for deletes it looks like the distorted sine wave. To see the chart for deletes, scroll down from here. So this looks like a problem for deletes and this post starts to explain that.tl;drOnce again, blame vacuumHistory of the Insert BenchmarkLong ago (prior to 2010) the Insert Benchmark was published by Tokutek to highlight things that the TokuDB storage engine was great at. I was working on MySQL at Google at the time and the benchmark was useful to me, however it was written in C++. While the Insert Benchmark is great at showing the benefits of an LSM storage engine, this was years before MyRocks and I was only doing InnoDB at the time, on spinning disks. So I rewrote it in Python to make it easier to modify, and then the Tokutek team improved a few things about my rewrite, and I have been enhancing it slowly since then.Until a few years ago the steps of the benchmark were:load - insert in PK ordercreate 3 secondary indexesdo more inserts as fast as possibledo rate-limited inserts concurrent with range and point queriesThe problem with this approach is that the database size grows forever and that limited for how long I could run the benchmark before running out of storage. So I changed it and the new approach keeps the database at a fixed size after the load. The new workflow is:load - insert in PK ordercreate 3 secondary indexesdo inserts+deletes at the same rate, as fast as possibledo rate-limited inserts+deletes at the same rate concurrent with range and point queriesThe benchmark treats the table like a queue, and when ordered by PK (transactionid) there are inserts at the high end and deletes at the low end. The delete statement currently looks like:    delete from %s where transactionid in        (select transactionid from %s where transactionid >= %d order by transactionid asc limit %d)The delete statement is written like that because it must delete the oldest rows -- the ones that have the smallest value for transactionid. While the process that does deletes has some idea of what that smallest value is, it doesn\'t know it for sure, thus the query. To improve performance it maintains a guess for the value that will be</p>
<p>The post <a rel="nofollow" href="https://smalldatum.blogspot.com/2026/02/explaining-why-throughput-varies-for.html">Explaining why throughput varies for Postgres with a CPU-bound Insert Benchmark</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>Throughput for the write-heavy steps of the Insert Benchmark look like a distorted sine wave with Postgres <a href="https://mdcallag.github.io/reports/dec25.ib.pn53.pg.latest.mem.30m.50m.1800s/tput.l.i1.html#pg181_o2nofp.cx10b_c8r32.ips">on CPU-bound</a>&nbsp;workloads but not <a href="https://mdcallag.github.io/reports/dec25.ib.pn53.pg.latest.io.800m.5m.1800s/tput.l.i1.html#pg181_o2nofp.cx10b_c8r32.ips">on IO-bound</a> workloads. For the CPU-bound workloads the chart for <a href="https://mdcallag.github.io/reports/dec25.ib.pn53.pg.latest.mem.30m.50m.1800s/tput.l.i1.html#pg181_o2nofp.cx10b_c8r32.imax">max response time at N-second intervals</a> for inserts is flat but for deletes it looks like the distorted sine wave. To see the chart for deletes, scroll down <a href="https://mdcallag.github.io/reports/dec25.ib.pn53.pg.latest.mem.30m.50m.1800s/tput.l.i1.html#pg181_o2nofp.cx10b_c8r32.imax">from here</a>. So this looks like a problem for deletes and this post starts to explain that.</p>
<p>tl;dr</p>

<ul style="text-align: left">
<li>Once again, blame vacuum</li>
</ul>
<p><b>History of the Insert Benchmark</b></p>
<p>Long ago (prior to 2010) the <a href="https://smalldatum.blogspot.com/2017/06/the-insert-benchmark.html">Insert Benchmark</a> was published by Tokutek to highlight things that the TokuDB storage engine was great at. I was working on MySQL at Google at the time and the benchmark was useful to me, however it was written in C++. While the Insert Benchmark is great at showing the benefits of an LSM storage engine, this was years before MyRocks and I was only doing InnoDB at the time, on spinning disks. So I rewrote it in Python to make it easier to modify, and then the Tokutek team improved a few things about my rewrite, and I have been enhancing it slowly since then.</p>
<p>Until a few years ago the steps of the benchmark were:</p>

<ul style="text-align: left">
<li>load &ndash; insert in PK order</li>
<li>create 3 secondary indexes</li>
<li>do more inserts as fast as possible</li>
<li>do rate-limited inserts concurrent with range and point queries</li>
</ul>
<div>The problem with this approach is that the database size grows forever and that limited for how long I could run the benchmark before running out of storage. So I changed it and the new approach keeps the database at a fixed size after the load. The new workflow is:</div>
<div>
<ul style="text-align: left">
<li>load &ndash; insert in PK order</li>
<li>create 3 secondary indexes</li>
<li>do inserts+deletes at the same rate, as fast as possible</li>
<li>do rate-limited inserts+deletes at the same rate concurrent with range and point queries</li>
</ul>
<div>The benchmark treats the table like a queue, and when ordered by PK (transactionid) there are inserts at the high end and deletes at the low end. The delete statement currently looks like:<br><i>&nbsp; &nbsp; delete from %s where transactionid in</i></div>
<div><i>&nbsp; &nbsp; &nbsp; &nbsp; (select transactionid from %s where transactionid &gt;= %d order by transactionid asc limit %d)</i></div>
</div>
<div></div>
<div>The delete statement is written like that because it must delete the oldest rows &mdash; the ones that have the smallest value for transactionid. While the process that does deletes has some idea of what that smallest value is, it doesn&rsquo;t know it for sure, thus the query. To improve performance it maintains a guess for the value that will be &lt;= the real minimum and it updates that guess over time.</div>
<div></div>
<div>I encountered other performance problems with Postgres while figuring out how to maintain that guess and <a href="https://www.google.com/search?q=site%3Asmalldatum.blogspot.com+get_actual_variable_range">get_actual_variable_range() in Postgres</a> was the problem. Maintaining that guess requires a resync query every N seconds where the resync query is:&nbsp;<i>select min(transactionid) from %s</i>. The problem for this query in general is that is scans the low end of the PK index on transactionid and when vacuum hasn&rsquo;t been done recently, then it will scan and skip many entries that aren&rsquo;t visible (wasting much CPU and some IO) before finding visible rows. Unfortunately, there will be some time between consecutive vacuums to the same table and this problem can&rsquo;t be avoided. The result is that the response time for the query increases a lot in between vacuums. For more on how get_actual_variable_range() contributes to this problem, see <a href="https://smalldatum.blogspot.com/2024/01/explaining-performance-regression-in.html">this post</a>.
<p>I assume the sine wave for delete response time is caused by one or both of:</p>
<ul style="text-align: left">
<li>get_actual_varable_range() CPU overhead while planning the delete statement</li>
<li>CPU overhead from scanning and skipping tombstones while executing the select subquery</li>
</ul>
</div>
<div>The structure of the delete statement above reduces the number of tombstones that the select subquery might encounter by specifying where <i>transactionid &gt;= %d</i>. Perhaps that isn&rsquo;t sufficient. Perhaps the Postgres query planner still has too much CPU overhead from get_actual_variable_range() while planning that delete statement. I have yet to figure that out. But I have figured out that vacuum is a frequent source of problems.</div>

<div></div>

<div>
<ul style="text-align: left"></ul>
</div>

<p>The post <a rel="nofollow" href="https://smalldatum.blogspot.com/2026/02/explaining-why-throughput-varies-for.html">Explaining why throughput varies for Postgres with a CPU-bound Insert Benchmark</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Updated MariaDB R2DBC and Node.js Connectors now available</title>
      <link>https://mariadb.com/resources/blog/updated-mariadb-r2dbc-and-node-js-connectors-now-available-2/</link>
      <pubDate>Wed, 18 Feb 2026 17:43:17 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.com/">MariaDB</source>
      <description><![CDATA[<p>MariaDB is pleased to announce the immediate availability of MariaDB Connector/R2DBC 1.4.0, and Connector/Node.js 3.5.1. Both are Stable (GA) releases. […]</p>
<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/updated-mariadb-r2dbc-and-node-js-connectors-now-available-2/">Updated MariaDB R2DBC and Node.js Connectors now available</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>MariaDB is pleased to announce the immediate availability of MariaDB Connector/R2DBC 1.4.0, and Connector/Node.js 3.5.1. Both are Stable (GA) releases. Download Now See the release notes and changelogs for more details and visit mariadb.com/downloads/connectors to download.</p>
<p><a href="https://mariadb.com/resources/blog/updated-mariadb-r2dbc-and-node-js-connectors-now-available-2/" rel="nofollow">Source</a></p>

<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/updated-mariadb-r2dbc-and-node-js-connectors-now-available-2/">Updated MariaDB R2DBC and Node.js Connectors now available</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>PostgreSQL minor release postponed in Q1’ 2026</title>
      <link>https://percona.community/blog/2026/02/18/postgresql-minor-release-postponed-in-q1-2026/</link>
      <pubDate>Wed, 18 Feb 2026 11:00:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://percona.community/blog/">Percona Community Blog - learn about MySQL, MariaDB, PostgreSQL, and MongoDB</source>
      <description><![CDATA[<p>In case you are awaiting the February PostgreSQL Community minor update released on plan on February 12 we want to make sure that our users and customers are up to date and aware of what to expect.</p>
<p>The post <a rel="nofollow" href="https://percona.community/blog/2026/02/18/postgresql-minor-release-postponed-in-q1-2026/">PostgreSQL minor release postponed in Q1’ 2026</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>In case you are awaiting the February PostgreSQL Community minor update <a href="https://www.postgresql.org/about/news/postgresql-182-178-1612-1516-and-1421-released-3235/" target="_blank" rel="noopener noreferrer">released on plan on February 12</a> we want to make sure that our users and customers are up to date and aware of what to expect.</p>
<p>This scheduled PostgreSQL release was delivered by the PostgreSQL Community on time and came carrying 5 CVE fixes and over 65 bugs bug fixes.</p>
<p>Unfortunately shortly after, the <a href="https://www.postgresql.org/about/news/out-of-cycle-release-scheduled-for-february-26-2026-3241/" target="_blank" rel="noopener noreferrer">release team announced that an additional out of cycle release</a> is planned for February 26. This follow up release addresses two regressions identified in the February 12 update.</p>
<p>Because of this, we have decided not to ship a <a href="https://docs.percona.com/postgresql/18/" target="_blank" rel="noopener noreferrer">Percona Distribution for PostgreSQL</a> build based on the February 12 release. Instead, we will wait for the February 26 Community update and base our release on that version once it becomes available from PGDG. This means also a delay in the release of Percona Operator for PostgreSQL that uses images based on our PostgreSQL releases.</p>
<h3 id="always-look-on-the-bright-side">Always look on the bright side<a class="anchor-link" id="always-look-on-the-bright-side"></a></h3>
<p><figure>
<img decoding="async" src="https://percona.community/blog/2026/02/Jan-always-Feb17.png" alt="&nbsp;"></figure>
</p>
<p>While this is a delay in release it comes with some benefits. For our users and customers, this means a cleaner upgrade path. Rather than releasing February 12 now and asking you to update again shortly after, we prefer to wait and deliver a single update that includes the fixes. Our goal is to make updates predictable and smooth for users of Percona Distribution for PostgreSQL, as well as extensions such as <a href="https://github.com/percona/pg_tde" target="_blank" rel="noopener noreferrer">pg_tde</a> and <a href="https://github.com/percona/pg_stat_monitor" target="_blank" rel="noopener noreferrer">pg_stat_monitor</a>. It should also allow you to carry less operational burden with the added maintenance that an extra update would require.</p>
<p>We appreciate how quickly the PostgreSQL Community identified and addressed the regressions. Open collaboration across the ecosystem, including reports and testing from many contributors, helps ensure PostgreSQL continues to improve for everyone.</p>
<h3 id="path-forward">Path forward<a class="anchor-link" id="path-forward"></a></h3>
<p>This is the third out of cycle release in the past year, following similar updates in <a href="https://www.postgresql.org/about/news/out-of-cycle-release-scheduled-for-november-21-2024-2958/" target="_blank" rel="noopener noreferrer">November 2024</a> and <a href="https://www.postgresql.org/about/news/out-of-cycle-release-scheduled-for-february-20-2025-3016/" target="_blank" rel="noopener noreferrer">February 2025</a>. It highlights how responsive and diligent the PostgreSQL Community is when issues are identified. At the same time, it reminds us all how important continuous testing and collaboration are as PostgreSQL adoption continues to grow. Contributing to PostgreSQL, whether through testing, reporting, or development, is one of the best ways to help strengthen quality across the ecosystem.</p>
<h3 id="elephants-keep-ears-open">Elephants keep ears open<a class="anchor-link" id="elephants-keep-ears-open"></a></h3>
<p>As soon as the February 26 release is available and our builds are ready, we will share the update.</p>
<p>If you need to move forward with the February 12 version in the meantime, please reach out. We are happy to talk through your situation and help you assess what makes the most sense for your environment.</p>
<p>Our customers can contact us through Percona Support Services to receive the high quality assistance we are known for. We also encourage community users to reach out via the <a href="https://forums.percona.com/" target="_blank" rel="noopener noreferrer">Percona Community Forums</a>, where we will do our best to provide guidance based on the information you share.</p>
<p>Thank you for your trust and for being part of the Percona community.</p>

<p>The post <a rel="nofollow" href="https://percona.community/blog/2026/02/18/postgresql-minor-release-postponed-in-q1-2026/">PostgreSQL minor release postponed in Q1’ 2026</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Let’s go physical(ly separated)</title>
      <link>https://medium.com/@arbaudie.it/lets-go-physical-ly-separated-4053ae5be14c?source=rss-c779d007e7fe------2</link>
      <pubDate>Wed, 18 Feb 2026 09:44:18 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://medium.com/@arbaudie.it?source=rss-c779d007e7fe------2">Stories by ArBauDie.IT on Medium</source>
      <description><![CDATA[<p>In my previous article , we discussed best practices for securing and monitoring admin access, such as role-based access controls (RBAC). It’s a good time to have a quick reminder about AAA aka “ Authentication, Authorization and Accounting” , which is a framework designed to manage access to networked resources and ensure secure, controlled interactions :Authentication : Verifies the identity of a user or system attempting to access a resource. This step involves credentials to confirm legitimacy before granting access.Authorization : Determines what a user or system is allowed to do after authentication. It sets permissions, defining the actions or resources that can be accessed based on the user’s role, ensuring they only interact with the data or systems for which they have clearance.Accounting : Tracks and logs user activities within a system, providing a record of what actions were taken, when, and by whom. This audit trail helps in monitoring, troubleshooting, and detecting unauthorized or malicious behavior.Altogether, AAA enhances security by ensuring that only authenticated users can access specific resources, with their actions monitored and recorded for accountability. And that’s exactly what we did with use of personal logins, role based access control and using the audit log to track admin activity.We know that admins aren’t the only one being able to temper with or gaining unwanted access to datas : any user is a potential risk for our database. Obviously, the previous advices still applies but role based access can prove to be inefficient in case of fine grained access control.Let’s imagine we are in a high security line of business and users should have very strict and tight access control. One way to prevent malicous users to circumvent the filtering si to physically isolate the user from the data. But can we dot it with MariaDB ?First step, we create a user as mentionned in my previous article.CREATE USER \'IT-O\'@\'Coruscant\' IDENTIFIED via ed25519 REQUIRE SSL WITH MAX_USER_CONNECTIONS 1 PASSWORD EXPIRE INTERVAL xx DAYS;Second step, we create a dedicated schema using his user token.Third step, we are gonna use to match the tables. We are now leveraging 3 parameters of said views : , DEFINER and SQL SECURITY :ALGORITHM allows us to tell how to execute the view. Here we will be choosing to make use of a temporary table to store the result of the view. While not the fastest, it has the good taste to make the view unwritable, on top of not having granted INSERT,UPDATE/DELETE privileges to the user,DEFINER allows us to give “ownership” of the view to a specific account,SQL SECURITY allows us to have the view executed with the DEFINER set of privileges instead of the INVOKER (aka the user) one.CREATE VIEW `IT-O`.BoobyTable ALGORITHM=temptable DEFINER=`locked.admin.account`@`localhost` SQL SECURITY=DEFINER AS SELECT necessary,columns,only FROM RealSchema.RealTable WHERE RestrictionClauses=values;Fourth thing, said user needs to manipulate the underlying table datas. We could theoritically allow the user to write through the view . But the control over its action is then limited, hence i prefer creating ad hoc that will emulate the desired actions. With those objects we can leverage the same last 2 parameters( stored routines ) as with views to ensure a good isolation. DEFINER &#038; SQL SECURITYDELIMITER // CREATE OR REPLACE DEFINER=`locked.admin.account`@`localhost` PROCEDURE `IT-O`.Action_BoobyTable (IN param_name type , OUT param_name type) SQL SECURITY=DEFINER BEGIN ACTIONS in SQL/PSM or PL/SQL END// DELIMITER ;Fifth step, we give the permissions to said user over the objects it needs.CREATE ROLE `IT-O`; GRANT SELECT on `IT-O`.* to `IT-O`; GRANT EXECUTE on `IT-O`.necesary_procs to `IT-O`; GRANT `IT-O` to `IT-O`@`Coruscant`; SET DEFAULT ROLE `IT-O` for `IT-O`@`Coruscant`;And now we have a user which acces is tightly controled and monitored even tho he has direct access to the database. Of course this could also be upgraded making him flow through on a dedicated service with , general login , query throttling , and possibly also resultset size limitation . inserting connexion IP in the statement data maskingOf course, deployment of such an internal architecture can be partially automated since it is mostly linked to parametrizing.Special thanks to Federico Razzoli for mentionning the use of a locked admin account as DEFINER.Originally published at https://www.linkedin.com.</p>
<p>The post <a rel="nofollow" href="https://medium.com/@arbaudie.it/lets-go-physical-ly-separated-4053ae5be14c?source=rss-c779d007e7fe------2">Let’s go physical(ly separated)</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<figure><img decoding="async" alt="" src="https://cdn-images-1.medium.com/max/1024/0*Ox7hBbPR7lTe6cyq"></figure>
<p>In my <a href="https://medium.com/@arbaudie.it/cave-adminem-7c97503f289c">previous article</a>&nbsp;, we discussed best practices for securing and monitoring admin access, such as role-based access controls (RBAC). It&rsquo;s a good time to have a quick reminder about AAA aka &ldquo; Authentication, Authorization and Accounting&rdquo;&nbsp;, which is a framework designed to manage access to networked resources and ensure secure, controlled interactions&nbsp;:</p>
<ol>
<li>Authentication&nbsp;: Verifies the identity of a user or system attempting to access a resource. This step involves credentials to confirm legitimacy before granting&nbsp;access.</li>
<li>Authorization&nbsp;: Determines what a user or system is allowed to do after authentication. It sets permissions, defining the actions or resources that can be accessed based on the user&rsquo;s role, ensuring they only interact with the data or systems for which they have clearance.</li>
<li>Accounting&nbsp;: Tracks and logs user activities within a system, providing a record of what actions were taken, when, and by whom. This audit trail helps in monitoring, troubleshooting, and detecting unauthorized or malicious behavior.</li>
</ol>
<p>Altogether, AAA enhances security by ensuring that only authenticated users can access specific resources, with their actions monitored and recorded for accountability. And that&rsquo;s exactly what we did with use of personal logins, role based access control and using the audit log to track admin activity.</p>
<p>We know that admins aren&rsquo;t the only one being able to temper with or gaining unwanted access to datas&nbsp;: any user is a potential risk for our database. Obviously, the previous advices still applies but role based access can prove to be inefficient in case of fine grained access&nbsp;control.</p>
<p>Let&rsquo;s imagine we are in a high security line of business and users should have very strict and tight access control. One way to prevent malicous users to circumvent the filtering si to physically isolate the user from the data. But can we dot it with MariaDB&nbsp;?</p>
<p>First step, we create a user as mentionned in my previous&nbsp;article.</p>
<pre>CREATE USER 'IT-O'@'Coruscant' <br>IDENTIFIED via ed25519 REQUIRE SSL <br>WITH MAX_USER_CONNECTIONS 1 <br>PASSWORD EXPIRE INTERVAL xx DAYS;</pre>
<p>Second step, we create a dedicated schema using his user&nbsp;token.</p>
<p>Third step, we are gonna use to match the tables. We are now leveraging 3 parameters of said views&nbsp;:&nbsp;, DEFINER and SQL SECURITY&nbsp;:</p>
<ul>
<li>ALGORITHM allows us to tell how to execute the view. Here we will be choosing to make use of a temporary table to store the result of the view. While not the fastest, it has the good taste to make the view unwritable, on top of not having granted INSERT,UPDATE/DELETE privileges to the&nbsp;user,</li>
<li>DEFINER allows us to give &ldquo;ownership&rdquo; of the view to a specific&nbsp;account,</li>
<li>SQL SECURITY allows us to have the view executed with the DEFINER set of privileges instead of the INVOKER (aka the user)&nbsp;one.</li>
</ul>
<pre>CREATE VIEW `IT-O`.BoobyTable ALGORITHM=temptable <br>DEFINER=`locked.admin.account`@`localhost` <br>SQL SECURITY=DEFINER AS <br>SELECT necessary,columns,only <br>FROM RealSchema.RealTable <br>WHERE RestrictionClauses=values;</pre>
<p>Fourth thing, said user needs to manipulate the underlying table datas. We could theoritically allow the user to <a href="https://www.linkedin.com/redir/redirect?url=https%3A%2F%2Fmariadb%2Ecom%2Fkb%2Fen%2Finserting-and-updating-with-views%2F&amp;urlhash=228t&amp;trk=article-ssr-frontend-pulse_little-text-block">write through the view</a>&nbsp;. But the control over its action is then limited, hence i prefer creating ad hoc that will emulate the desired actions. With those objects we can leverage the same last 2 parameters( <a href="https://www.linkedin.com/redir/redirect?url=https%3A%2F%2Fmariadb%2Ecom%2Fkb%2Fen%2Fstored-procedures%2F&amp;urlhash=zaDr&amp;trk=article-ssr-frontend-pulse_little-text-block">stored routines</a> ) as with views to ensure a good isolation. <a href="https://www.linkedin.com/redir/redirect?url=https%3A%2F%2Fmariadb%2Ecom%2Fkb%2Fen%2Fstored-routine-privileges%2F%23definer-clause&amp;urlhash=nYug&amp;trk=article-ssr-frontend-pulse_little-text-block">DEFINER &amp; SQL&nbsp;SECURITY</a></p>
<pre>DELIMITER // CREATE OR REPLACE DEFINER=`locked.admin.account`@`localhost` <br>PROCEDURE `IT-O`.Action_BoobyTable (IN param_name type , OUT param_name type) <br>SQL SECURITY=DEFINER <br>BEGIN ACTIONS in SQL/PSM or PL/SQL <br>END// <br>DELIMITER ;</pre>
<p>Fifth step, we give the permissions to said user over the objects it&nbsp;needs.</p>
<pre>CREATE ROLE `IT-O`; <br>GRANT SELECT on `IT-O`.* to `IT-O`; <br>GRANT EXECUTE on `IT-O`.necesary_procs to `IT-O`; <br>GRANT `IT-O` to `IT-O`@`Coruscant`; <br>SET DEFAULT ROLE `IT-O` for `IT-O`@`Coruscant`;</pre>
<p>And now we have a user which acces is tightly controled and monitored even tho he has direct access to the database. Of course this could also be upgraded making him flow through on a dedicated service with&nbsp;, <a href="https://www.linkedin.com/redir/redirect?url=https%3A%2F%2Fmariadb%2Ecom%2Fkb%2Fen%2Fmariadb-maxscale-2402-maxscale-2402-query-log-all-filter%2F&amp;urlhash=WLkQ&amp;trk=article-ssr-frontend-pulse_little-text-block">general login</a>&nbsp;, <a href="https://www.linkedin.com/redir/redirect?url=https%3A%2F%2Fmariadb%2Ecom%2Fkb%2Fen%2Fmariadb-maxscale-2402-maxscale-2402-throttle%2F&amp;urlhash=l87J&amp;trk=article-ssr-frontend-pulse_little-text-block">query throttling</a>&nbsp;, and possibly also <a href="https://www.linkedin.com/redir/redirect?url=https%3A%2F%2Fmariadb%2Ecom%2Fkb%2Fen%2Fmariadb-maxscale-2402-maxscale-2402-maxrows%2F&amp;urlhash=wlll&amp;trk=article-ssr-frontend-pulse_little-text-block">resultset size limitation</a>&nbsp;. <a href="https://www.linkedin.com/redir/redirect?url=https%3A%2F%2Fmariadb%2Ecom%2Fkb%2Fen%2Fmariadb-maxscale-2402-maxscale-2402-comment-filter%2F%23example-1-inject-ip-address-of-the-connected-client-into-statements&amp;urlhash=YSCO&amp;trk=article-ssr-frontend-pulse_little-text-block">inserting connexion IP in the statement</a> <a href="https://www.linkedin.com/redir/redirect?url=https%3A%2F%2Fmariadb%2Ecom%2Fkb%2Fen%2Fmariadb-maxscale-2402-maxscale-2402-masking%2F&amp;urlhash=Nr7I&amp;trk=article-ssr-frontend-pulse_little-text-block">data&nbsp;masking</a></p>
<p>Of course, deployment of such an internal architecture can be partially automated since it is mostly linked to parametrizing.</p>
<p>Special thanks to <a href="https://uk.linkedin.com/in/federicorazzoli?trk=article-ssr-frontend-pulse_little-mention">Federico Razzoli </a>for mentionning the use of a locked admin account as&nbsp;DEFINER.</p>
<p><em>Originally published at </em><a href="https://www.linkedin.com/pulse/lets-go-physically-separated-sylvain-arbaudie-yzu3f/"><em>https://www.linkedin.com</em></a><em>.</em></p>
<p><img decoding="async" loading="lazy" src="https://medium.com/_/stat?event=post.clientViewed&amp;referrerSource=full_rss&amp;postId=4053ae5be14c" width="1" height="1" alt=""></p>

<p>The post <a rel="nofollow" href="https://medium.com/@arbaudie.it/lets-go-physical-ly-separated-4053ae5be14c?source=rss-c779d007e7fe------2">Let’s go physical(ly separated)</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>MariaDB Foundation Welcomes Epsio as a Silver Sponsor</title>
      <link>https://mariadb.org/mariadb-foundation-welcomes-epsio-as-a-silver-sponsor/</link>
      <pubDate>Wed, 18 Feb 2026 04:31:12 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>We are delighted to announce that Epsio (https://www.epsio.io/) has joined the MariaDB Foundation as an official silver sponsor.<br />
Epsio’s commitment to innovation and open technology aligns closely with the mission of the MariaDB Foundation: to support and advance the MariaDB ecosystem and strengthen the long-term viability of MariaDB as a reliable, open source database platform. …<br />
Continue reading \"MariaDB Foundation Welcomes Epsio as a Silver Sponsor\"<br />
The post MariaDB Foundation Welcomes Epsio as a Silver Sponsor appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-foundation-welcomes-epsio-as-a-silver-sponsor/">MariaDB Foundation Welcomes Epsio as a Silver Sponsor</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>We are delighted to announce that Epsio (<a href="https://www.epsio.io/">https://www.epsio.io/</a>) has joined the MariaDB Foundation as an official silver sponsor.<br>
Epsio&rsquo;s commitment to innovation and open technology aligns closely with the mission of the MariaDB Foundation: to support and advance the MariaDB ecosystem and strengthen the long-term viability of MariaDB as a reliable, open source database platform. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/mariadb-foundation-welcomes-epsio-as-a-silver-sponsor/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;MariaDB Foundation Welcomes Epsio as a Silver Sponsor&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-foundation-welcomes-epsio-as-a-silver-sponsor/">MariaDB Foundation Welcomes Epsio as a Silver Sponsor</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-foundation-welcomes-epsio-as-a-silver-sponsor/">MariaDB Foundation Welcomes Epsio as a Silver Sponsor</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>MariaDB innovation: binlog_storage_engine, small server, Insert Benchmark</title>
      <link>https://smalldatum.blogspot.com/2026/02/mariadb-innovation-binlogstorageengine_17.html</link>
      <pubDate>Wed, 18 Feb 2026 04:20:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://smalldatum.blogspot.com/">Small Datum</source>
      <description><![CDATA[<p> MariaDB 12.3 has a new feature enabled by the option binlog_storage_engine. When enabled it uses InnoDB instead of raw files to store the binlog. A big benefit from this is reducing the number of fsync calls per commit from 2 to 1 because it reduces the number of resource managers from 2 (binlog, InnoDB) to 1 (InnoDB).My previous post had results for sysbench with a small server. This post has results for the Insert Benchmark with a similar small server. Both servers use an SSD that has has high fsync latency. This is probably a best-case comparison for the feature. If you really care, then get enterprise SSDs with power loss protection. But you might encounter high fsync latency on public cloud servers.tl;dr for a CPU-bound workloadEnabling sync on commit for InnoDB and the binlog has a large impact on throughput for the write-heavy steps -- l.i0, l.i1 and l.i2.When sync on commit is enabled, then also enabling the binlog_storage_engine is great for performance as throughput on the write-heavy steps is 1.75X larger for l.i0 (load) and 4X or more larger on the random write steps (l.i1, l.i2)tl;dr for an IO-bound workloadEnabling sync on commit for InnoDB and the binlog has a large impact on throughput for the write-heavy steps -- l.i0, l.i1 and l.i2. It also has a large impact on qp1000, which is the most write-heavy of the query+write steps.When sync on commit is enabled, then also enabling the binlog_storage_engine is great for performance as throughput on the write-heavy steps is 4.74X larger for l.i0 (load), 1.50X larger for l.i1 (random writes) and 2.99X larger for l.i2 (random writes)Builds, configuration and hardwareI compiled MariaDB 12.3.0 from source.The server is an ASUS ExpertCenter PN53 with an AMD Ryzen 7 7735HS CPU, 8 cores, SMT disabled, and 32G of RAM. Storage is one NVMe device for the database using ext-4 with discard enabled. The OS is Ubuntu 24.04. More details on it are here. The storage device has high fsync latency.I used 4 my.cnf files:z12bmy.cnf.cz12b_c8r32 is my default configuration. Sync-on-commit is disabled for both the binlog and InnoDB so that write-heavy benchmarks create more stress.z12cmy.cnf.cz12c_c8r32 is like z12b except it enables binlog_storage_enginez12b_syncmy.cnf.cz12b_sync_c8r32 is like z12b except it enables sync-on-commit for the binlog and InnoDBz12c_syncmy.cnf.cz12c_sync_c8r32 is like cz12c except it enables sync-on-commit for InnoDB. Note that InnoDB is used to store the binlog so there is nothing else to sync on commit.The BenchmarkThe benchmark is explained here. It was run with 1 client for two workloads:CPU-bound - the database is cached by InnoDB, but there is still much write IOIO-bound - most, but not all, benchmark steps are IO-boundThe benchmark steps are:l.i0insert XM rows per table in PK order. The table has a PK index but no secondary indexes. There is one connection per client. X is 30M for CPU-bound and 800M for IO-bound.l.xcreate 3 secondary indexes per table. There is one connection per client.l.i1use 2 connections/client. One inserts XM rows per table and the other does deletes at the same rate as the inserts. Each transaction modifies 50 rows (big transactions). This step is run for a fixed number of inserts, so the run time varies depending on the insert rate. X is 40M for CPU-bound and 4M for IO-bound.l.i2like l.i1 but each transaction modifies 5 rows (small transactions) and YM rows are inserted and deleted per table. Y is 10M for CPU-bound and 1M for IO-bound.Wait for S seconds after the step finishes to reduce MVCC GC debt and perf variance during the read-write benchmark steps that follow. The value of S is a function of the table size.qr100use 3 connections/client. One does range queries and performance is reported for this. The second does does 100 inserts/s and the third does 100 deletes/s. The second and third are less busy than the first. The range queries use covering secondary indexes. If the target insert rate is not sustained then that is considered to be an SLA failure. If the target insert rate is sustained then the step does the same number of inserts for all systems tested. This step is frequently not IO-bound for the IO-bound workload. This step runs for 1800 seconds.qp100like qr100 except uses point queries on the PK indexqr500like qr100 but the insert and delete rates are increased from 100/s to 500/sqp500like qp100 but the insert and delete rates are increased from 100/s to 500/sqr1000like qr100 but the insert and delete rates are increased from 100/s to 1000/sqp1000like qp100 but the insert and delete rates are increased from 100/s to 1000/sResults: summaryResults: summaryThe performance reports are here for:CPU-boundall-versions - results for z12b, z12c, z12b_sync and z12c_syncsync-only - results for z12b_sync vs 12c_syncIO-boundall-versions - results for z12b, z12c, z12b_sync and z12c_syncsync-only - results for z12b_sync vs 12c_syncThe summary sections from the performance reports have 3 tables. The first shows absolute throughput by DBMS tested X benchmark step. The second has throughput relative to the version from the first row of the table. The third shows the background insert rate for benchmark steps with background inserts. The second table makes it easy to see how performance changes over time. The third table makes it easy to see which DBMS+configs failed to meet the SLA.I use relative QPS to explain how performance changes. It is: (QPS for $me / QPS for $base) where $me is the result for some version $base is the result from the base version. The base version is Postgres 12.22.When relative QPS is > 1.0 then performance improved over time. When it is < 1.0 then there are regressions. The Q in relative QPS measures: insert/s for l.i0, l.i1, l.i2indexed rows/s for l.xrange queries/s for qr100, qr500, qr1000point queries/s for qp100, qp500, qp1000Below I use colors to highlight the relative QPS values with yellow for regressions and blue for improvements.I often use context switch rates as a proxy for mutex contention.Results: CPU-boundThe summaries are here for all-versions and sync-only.Enabling sync on commit for InnoDB and the binlog has a large impact on throughput for the write-heavy steps -- l.i0, l.i1 and l.i2.When sync on commit is enabled, then also enabling the binlog_storage_engine is great for performance as throughput on the write-heavy steps is 1.75X larger for l.i0 (load) and 4X or more larger on the random write steps (l.i1, l.i2)The second table from the summary section has been inlined below. That table shows relative throughput which is:all-versions: (QPS for my config / QPS for z12b)sync-only: (QPS for my z12c / QPS for z12b)For all-versionsdbmsl.i0l.xl.i1l.i2qr100qp100qr500qp500qr1000qp1000ma120300_rel_withdbg.cz12b_c8r321.001.001.001.001.001.001.001.001.001.00ma120300_rel_withdbg.cz12c_c8r321.031.011.001.031.000.991.001.001.011.00ma120300_rel_withdbg.cz12b_sync_c8r320.041.020.070.011.011.011.001.011.001.00ma120300_rel_withdbg.cz12c_sync_c8r320.081.030.280.061.021.011.011.021.021.01For sync-onlydbmsl.i0l.xl.i1l.i2qr100qp100qr500qp500qr1000qp1000ma120300_rel_withdbg.cz12b_sync_c8r321.001.001.001.001.001.001.001.001.001.00ma120300_rel_withdbg.cz12c_sync_c8r321.751.013.996.831.011.011.011.011.031.01Results: IO-boundThe summaries are here for all-versions and sync-only.Enabling sync on commit for InnoDB and the binlog has a large impact on throughput for the write-heavy steps -- l.i0, l.i1 and l.i2. It also has a large impact on qp1000, which is the most write-heavy of the query+write steps.When sync on commit is enabled, then also enabling the binlog_storage_engine is great for performance as throughput on the write-heavy steps is 4.74X larger for l.i0 (load), 1.50X larger for l.i1 (random writes) and 2.99X larger for l.i2 (random writes)The second table from the summary section has been inlined below. That table shows relative throughput which is:all-versions: (QPS for my config / QPS for z12b)sync-only: (QPS for my z12c / QPS for z12b)For all-versionsdbmsl.i0l.xl.i1l.i2qr100qp100qr500qp500qr1000qp1000ma120300_rel_withdbg.cz12b_c8r321.001.001.001.001.001.001.001.001.001.00ma120300_rel_withdbg.cz12c_c8r321.010.990.991.011.011.011.011.071.011.04ma120300_rel_withdbg.cz12b_sync_c8r320.041.000.550.101.020.971.000.800.950.55ma120300_rel_withdbg.cz12c_sync_c8r320.181.000.830.311.021.011.020.961.020.86For sync-onlydbmsl.i0l.xl.i1l.i2qr100qp100qr500qp500qr1000qp1000ma120300_rel_withdbg.cz12b_sync_c8r321.001.001.001.001.001.001.001.001.001.00ma120300_rel_withdbg.cz12c_sync_c8r324.741.001.502.991.001.041.021.201.081.57
</p>
<p>The post <a rel="nofollow" href="https://smalldatum.blogspot.com/2026/02/mariadb-innovation-binlogstorageengine_17.html">MariaDB innovation: binlog_storage_engine, small server, Insert Benchmark</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>&nbsp;MariaDB 12.3 has a new feature enabled by the option&nbsp;<a href="https://mariadb.org/new-binlog-implementation-in-mariadb-12-3/">binlog_storage_engine</a>. When enabled it uses InnoDB instead of raw files to store the binlog. A big benefit from this is reducing the number of fsync calls per commit from 2 to 1 because it reduces the number of resource managers from 2 (binlog, InnoDB) to 1 (InnoDB).</p>
<p>My <a href="https://smalldatum.blogspot.com/2026/02/mariadb-innovation-binlogstorageengine.html">previous post</a> had results for sysbench with a small server. This post has results for the Insert Benchmark with a similar small server. Both servers use an SSD that has has <a href="https://smalldatum.blogspot.com/2026/01/ssds-power-loss-protection-and-fsync.html">high fsync latency</a>. This is probably a best-case comparison for the feature. If you really care, then get enterprise SSDs with power loss protection. But you might encounter high fsync latency on public cloud servers.</p>
<p>tl;dr for a CPU-bound workload</p>

<ul style="text-align: left">
<li>Enabling sync on commit for InnoDB and the binlog has a large impact on throughput for the write-heavy steps &mdash; l.i0, l.i1 and l.i2.</li>
<li>When sync on commit is enabled, then also enabling the binlog_storage_engine is great for performance as throughput on the write-heavy steps is 1.75X larger for l.i0 (load) and 4X or more larger on the random write steps (l.i1, l.i2)</li>
</ul>
<div>tl;dr for an IO-bound workload</div>
<div>
<ul style="text-align: left">
<li>Enabling sync on commit for InnoDB and the binlog has a large impact on throughput for the write-heavy steps &mdash; l.i0, l.i1 and l.i2. It also has a large impact on qp1000, which is the most write-heavy of the query+write steps.</li>
<li>When sync on commit is enabled, then also enabling the binlog_storage_engine is great for performance as throughput on the write-heavy steps is 4.74X larger for l.i0 (load), 1.50X larger for l.i1 (random writes) and 2.99X larger for l.i2 (random writes)</li>
</ul>
</div>
<div><b>Builds, configuration and hardware</b></div>
<div>
<div>
<div></div>
<div>I compiled MariaDB 12.3.0 from source.</div>
<div>The server is an ASUS ExpertCenter PN53 with an AMD Ryzen 7 7735HS CPU, 8 cores, SMT disabled, and 32G of RAM. Storage is one NVMe device for the database using ext-4 with discard enabled. The OS is Ubuntu 24.04. More details on it&nbsp;<a href="ASUS%20ExpertCenter%20PN53%20with%20AMD%20Ryzen%207%207735HS,%2032G%20RAM%20and%202%20m.2%20slots%20(one%20for%20OS%20install,%20one%20for%20DB%20perf%20tests)">are here</a>. The storage device has&nbsp;<a href="https://smalldatum.blogspot.com/2026/01/ssds-power-loss-protection-and-fsync.h">high fsync latency</a>.</div>
</div>
<div></div>
<div>I used 4 my.cnf files:</div>
<div>
<ul>
<li>z12b</li>
<ul>
<li><a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c8r32/ma1203/etc/my.cnf.cz12b_c8r32">my.cnf.cz12b_c8r32</a>&nbsp;is my default configuration. Sync-on-commit is disabled for both the binlog and InnoDB so that write-heavy benchmarks create more stress.</li>
</ul>
<li>z12c</li>
<ul>
<li><a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c8r32/ma1203/etc/my.cnf.cz12c_c8r32">my.cnf.cz12c_c8r32</a>&nbsp;is like z12b except it enables binlog_storage_engine</li>
</ul>
<li>z12b_sync</li>
<ul>
<li><a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c8r32/ma1203/etc/my.cnf.cz12b_sync_c8r32">my.cnf.cz12b_sync_c8r32</a>&nbsp;is like z12b except it enables sync-on-commit for the binlog and InnoDB</li>
</ul>
<li>z12c_sync</li>
<ul>
<li><a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c8r32/ma1203/etc/my.cnf.cz12c_sync_c8r32">my.cnf.cz12c_sync_c8r32</a>&nbsp;is like cz12c except it enables sync-on-commit for InnoDB. Note that InnoDB is used to store the binlog so there is nothing else to sync on commit.</li>
</ul>
</ul>
<div>
<div><b>The Benchmark</b></div>
<div>
<div></div>
<div>The benchmark is&nbsp;<a href="https://smalldatum.blogspot.com/2023/12/updates-for-insert-benchmark-december.html">explained here</a>. It was run with 1 client for two workloads:</div>
<div>
<ul style="text-align: left">
<li>CPU-bound &ndash; the database is cached by InnoDB, but there is still much write IO</li>
<li>IO-bound &ndash; most, but not all, benchmark steps are IO-bound</li>
</ul>
</div>
<div>The benchmark steps are:</div>
<div>
<div>
<ul>
<li>l.i0</li>
<ul>
<li>insert XM rows per table in PK order. The table has a PK index but no secondary indexes. There is one connection per client. X is 30M for CPU-bound and 800M for IO-bound.</li>
</ul>
<li>l.x</li>
<ul>
<li>create 3 secondary indexes per table. There is one connection per client.</li>
</ul>
<li>l.i1</li>
<ul>
<li>use 2 connections/client. One inserts XM rows per table and the other does deletes at the same rate as the inserts. Each transaction modifies 50 rows (big transactions). This step is run for a fixed number of inserts, so the run time varies depending on the insert rate. X is 40M for CPU-bound and 4M for IO-bound.</li>
</ul>
<li>l.i2</li>
<ul>
<li>like l.i1 but each transaction modifies 5 rows (small transactions) and YM rows are inserted and deleted per table. Y is 10M for CPU-bound and 1M for IO-bound.</li>
<li>Wait for S seconds after the step finishes to reduce MVCC GC debt and perf variance during the read-write benchmark steps that follow. The value of S is a function of the table size.</li>
</ul>
<li>qr100</li>
<ul>
<li>use 3 connections/client. One does range queries and performance is reported for this. The second does does 100 inserts/s and the third does 100 deletes/s. The second and third are less busy than the first. The range queries use covering secondary indexes. If the target insert rate is not sustained then that is considered to be an SLA failure. If the target insert rate is sustained then the step does the same number of inserts for all systems tested. This step is frequently not IO-bound for the IO-bound workload. This step runs for 1800 seconds.</li>
</ul>
<li>qp100</li>
<ul>
<li>like qr100 except uses point queries on the PK index</li>
</ul>
<li>qr500</li>
<ul>
<li>like qr100 but the insert and delete rates are increased from 100/s to 500/s</li>
</ul>
<li>qp500</li>
<ul>
<li>like qp100 but the insert and delete rates are increased from 100/s to 500/s</li>
</ul>
<li>qr1000</li>
<ul>
<li>like qr100 but the insert and delete rates are increased from 100/s to 1000/s</li>
</ul>
<li>qp1000</li>
<ul>
<li>like qp100 but the insert and delete rates are increased from 100/s to 1000/s</li>
</ul>
</ul>
<div><b>Results: summary</b></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div></div>
<div>
<div>
<div><b>Results: summary</b></div>
<div></div>
<div>The performance reports are here for:</div>
</div>
<div>
<ul style="text-align: left">
<li>CPU-bound</li>
<ul>
<li><a href="https://mdcallag.github.io/reports/feb26.ib.mem.pn53.ma1203.syncall.30m.50m.1800s/all.html">all-versions</a> &ndash; results for z12b, z12c, z12b_sync and z12c_sync</li>
<li><a href="https://mdcallag.github.io/reports/feb26.ib.mem.pn53.ma1203.synconly.30m.50m.1800s/all.html">sync-only</a> &ndash; results for z12b_sync vs 12c_sync</li>
</ul>
<li>IO-bound</li>
<ul>
<li><a href="https://mdcallag.github.io/reports/feb26.ib.mem.pn53.ma1203.syncall.800m.5m.1800s/all.html">all-versions</a> &ndash; results for z12b, z12c, z12b_sync and z12c_sync</li>
<li><a href="https://mdcallag.github.io/reports/feb26.ib.mem.pn53.ma1203.synconly.800m.5m.1800s/all.html">sync-only</a> &ndash; results for z12b_sync vs 12c_sync</li>
</ul>
</ul>
</div>
<div>The summary sections from&nbsp;the performance reports have 3 tables. The first shows absolute throughput by DBMS tested X benchmark step. The second has throughput relative to the version from the first row of the table. The third shows the background insert rate for benchmark steps with background inserts. The second table makes it easy to see how performance changes over time. The third table makes it easy to see which DBMS+configs failed to meet the SLA.</div>
<div>
<div></div>
<div>I use relative QPS to explain how performance changes. It is: (QPS for $me / QPS for $base) where $me is the result for some version $base is the result from the base version. The base version is Postgres 12.22.
<p>When relative QPS is &gt; 1.0 then performance improved over time. When it is &lt; 1.0 then there are regressions. The Q in relative QPS measures:&nbsp;</p></div>
<div>
<ul>
<li>insert/s for l.i0, l.i1, l.i2</li>
<li>indexed rows/s for l.x</li>
<li>range queries/s for qr100, qr500, qr1000</li>
<li>point queries/s for qp100, qp500, qp1000</li>
</ul>
<div>Below I use colors to highlight the relative QPS values with yellow for regressions and blue for improvements.</div>
</div>
</div>
<div></div>
<div>I often use context switch rates as a proxy for mutex contention.</div>
</div>
<div></div>
<div><b>Results: CPU-bound</b></div>
<div></div>
<div>The summaries are here for <a href="https://mdcallag.github.io/reports/feb26.ib.mem.pn53.ma1203.syncall.30m.50m.1800s/all.html#summary">all-versions</a> and <a href="https://mdcallag.github.io/reports/feb26.ib.mem.pn53.ma1203.synconly.30m.50m.1800s/all.html#summary">sync-only</a>.</div>
<div>
<ul style="text-align: left">
<li>Enabling sync on commit for InnoDB and the binlog has a large impact on throughput for the write-heavy steps &mdash; l.i0, l.i1 and l.i2.</li>
<li>When sync on commit is enabled, then also enabling the binlog_storage_engine is great for performance as throughput on the write-heavy steps is 1.75X larger for l.i0 (load) and 4X or more larger on the random write steps (l.i1, l.i2)</li>
</ul>
</div>
<div>The second table from the summary section has been inlined below. That table shows relative throughput which is:</div>
<div>
<ul style="text-align: left">
<li>all-versions: (QPS for my config / QPS for z12b)</li>
<li>sync-only: (QPS for my z12c / QPS for z12b)</li>
</ul>
<div>For all-versions</div>
<div>
<table border="1" cellpadding="8" style="color: black">
<tbody>
<tr>
<th><span style="font-size: xx-small">dbms</span></th>
<th><span style="font-size: xx-small">l.i0</span></th>
<th><span style="font-size: xx-small">l.x</span></th>
<th><span style="font-size: xx-small">l.i1</span></th>
<th><span style="font-size: xx-small">l.i2</span></th>
<th><span style="font-size: xx-small">qr100</span></th>
<th><span style="font-size: xx-small">qp100</span></th>
<th><span style="font-size: xx-small">qr500</span></th>
<th><span style="font-size: xx-small">qp500</span></th>
<th><span style="font-size: xx-small">qr1000</span></th>
<th><span style="font-size: xx-small">qp1000</span></th>
</tr>
<tr>
<td style="text-align: right"><span style="font-size: xx-small">ma120300_rel_withdbg.cz12b_c8r32</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
</tr>
<tr>
<td style="text-align: right"><span style="font-size: xx-small">ma120300_rel_withdbg.cz12c_c8r32</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.03</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.01</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.03</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">0.99</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.01</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
</tr>
<tr>
<td style="text-align: right"><span style="font-size: xx-small">ma120300_rel_withdbg.cz12b_sync_c8r32</span></td>
<td id="clo" style="background-color: #ffdd81;text-align: right"><span style="font-size: xx-small">0.04</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.02</span></td>
<td id="clo" style="background-color: #ffdd81;text-align: right"><span style="font-size: xx-small">0.07</span></td>
<td id="clo" style="background-color: #ffdd81;text-align: right"><span style="font-size: xx-small">0.01</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.01</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.01</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.01</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
</tr>
<tr>
<td style="text-align: right"><span style="font-size: xx-small">ma120300_rel_withdbg.cz12c_sync_c8r32</span></td>
<td id="clo" style="background-color: #ffdd81;text-align: right"><span style="font-size: xx-small">0.08</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.03</span></td>
<td id="clo" style="background-color: #ffdd81;text-align: right"><span style="font-size: xx-small">0.28</span></td>
<td id="clo" style="background-color: #ffdd81;text-align: right"><span style="font-size: xx-small">0.06</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.02</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.01</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.01</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.02</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.02</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.01</span></td>
</tr>
</tbody>
</table>
</div>
</div>
<div></div>
<div>For sync-only</div>
<div>
<table border="1" cellpadding="8" style="color: black">
<tbody>
<tr>
<th><span style="font-size: xx-small">dbms</span></th>
<th><span style="font-size: xx-small">l.i0</span></th>
<th><span style="font-size: xx-small">l.x</span></th>
<th><span style="font-size: xx-small">l.i1</span></th>
<th><span style="font-size: xx-small">l.i2</span></th>
<th><span style="font-size: xx-small">qr100</span></th>
<th><span style="font-size: xx-small">qp100</span></th>
<th><span style="font-size: xx-small">qr500</span></th>
<th><span style="font-size: xx-small">qp500</span></th>
<th><span style="font-size: xx-small">qr1000</span></th>
<th><span style="font-size: xx-small">qp1000</span></th>
</tr>
<tr>
<td style="text-align: right"><span style="font-size: xx-small">ma120300_rel_withdbg.cz12b_sync_c8r32</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
</tr>
<tr>
<td style="text-align: right"><span style="font-size: xx-small">ma120300_rel_withdbg.cz12c_sync_c8r32</span></td>
<td id="chi" style="background-color: #81fff9;text-align: right"><span style="font-size: xx-small">1.75</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.01</span></td>
<td id="chi" style="background-color: #81fff9;text-align: right"><span style="font-size: xx-small">3.99</span></td>
<td id="chi" style="background-color: #81fff9;text-align: right"><span style="font-size: xx-small">6.83</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.01</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.01</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.01</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.01</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.03</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.01</span></td>
</tr>
</tbody>
</table>
</div>
<div></div>
<div><b>Results: IO-bound</b></div>
<div></div>
<div>The summaries are here for <a href="https://mdcallag.github.io/reports/feb26.ib.mem.pn53.ma1203.syncall.800m.5m.1800s/all.html">all-versions</a> and <a href="https://mdcallag.github.io/reports/feb26.ib.mem.pn53.ma1203.synconly.800m.5m.1800s/all.html">sync-only</a>.</div>
<div>
<ul>
<li>Enabling sync on commit for InnoDB and the binlog has a large impact on throughput for the write-heavy steps &mdash; l.i0, l.i1 and l.i2. It also has a large impact on qp1000, which is the most write-heavy of the query+write steps.</li>
<li>When sync on commit is enabled, then also enabling the binlog_storage_engine is great for performance as throughput on the write-heavy steps is 4.74X larger for l.i0 (load), 1.50X larger for l.i1 (random writes) and 2.99X larger for l.i2 (random writes)</li>
</ul>
</div>
<div>The second table from the summary section has been inlined below. That table shows relative throughput which is:</div>
<div>
<div>
<ul>
<li>all-versions: (QPS for my config / QPS for z12b)</li>
<li>sync-only: (QPS for my z12c / QPS for z12b)</li>
</ul>
<div>For all-versions</div>
</div>
</div>
<div>
<table border="1" cellpadding="8" style="color: black">
<tbody>
<tr>
<th><span style="font-size: xx-small">dbms</span></th>
<th><span style="font-size: xx-small">l.i0</span></th>
<th><span style="font-size: xx-small">l.x</span></th>
<th><span style="font-size: xx-small">l.i1</span></th>
<th><span style="font-size: xx-small">l.i2</span></th>
<th><span style="font-size: xx-small">qr100</span></th>
<th><span style="font-size: xx-small">qp100</span></th>
<th><span style="font-size: xx-small">qr500</span></th>
<th><span style="font-size: xx-small">qp500</span></th>
<th><span style="font-size: xx-small">qr1000</span></th>
<th><span style="font-size: xx-small">qp1000</span></th>
</tr>
<tr>
<td style="text-align: right"><span style="font-size: xx-small">ma120300_rel_withdbg.cz12b_c8r32</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
</tr>
<tr>
<td style="text-align: right"><span style="font-size: xx-small">ma120300_rel_withdbg.cz12c_c8r32</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.01</span></td>
<td style="text-align: right"><span style="font-size: xx-small">0.99</span></td>
<td style="text-align: right"><span style="font-size: xx-small">0.99</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.01</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.01</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.01</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.01</span></td>
<td id="chi" style="background-color: #81fff9;text-align: right"><span style="font-size: xx-small">1.07</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.01</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.04</span></td>
</tr>
<tr>
<td style="text-align: right"><span style="font-size: xx-small">ma120300_rel_withdbg.cz12b_sync_c8r32</span></td>
<td id="clo" style="background-color: #ffdd81;text-align: right"><span style="font-size: xx-small">0.04</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td id="clo" style="background-color: #ffdd81;text-align: right"><span style="font-size: xx-small">0.55</span></td>
<td id="clo" style="background-color: #ffdd81;text-align: right"><span style="font-size: xx-small">0.10</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.02</span></td>
<td style="text-align: right"><span style="font-size: xx-small">0.97</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td id="clo" style="background-color: #ffdd81;text-align: right"><span style="font-size: xx-small">0.80</span></td>
<td style="text-align: right"><span style="font-size: xx-small">0.95</span></td>
<td id="clo" style="background-color: #ffdd81;text-align: right"><span style="font-size: xx-small">0.55</span></td>
</tr>
<tr>
<td style="text-align: right"><span style="font-size: xx-small">ma120300_rel_withdbg.cz12c_sync_c8r32</span></td>
<td id="clo" style="background-color: #ffdd81;text-align: right"><span style="font-size: xx-small">0.18</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td id="clo" style="background-color: #ffdd81;text-align: right"><span style="font-size: xx-small">0.83</span></td>
<td id="clo" style="background-color: #ffdd81;text-align: right"><span style="font-size: xx-small">0.31</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.02</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.01</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.02</span></td>
<td style="text-align: right"><span style="font-size: xx-small">0.96</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.02</span></td>
<td id="clo" style="background-color: #ffdd81;text-align: right"><span style="font-size: xx-small">0.86</span></td>
</tr>
</tbody>
</table>
</div>
<div></div>
<div>For sync-only</div>
<div>
<table border="1" cellpadding="8" style="color: black">
<tbody>
<tr>
<th><span style="font-size: xx-small">dbms</span></th>
<th><span style="font-size: xx-small">l.i0</span></th>
<th><span style="font-size: xx-small">l.x</span></th>
<th><span style="font-size: xx-small">l.i1</span></th>
<th><span style="font-size: xx-small">l.i2</span></th>
<th><span style="font-size: xx-small">qr100</span></th>
<th><span style="font-size: xx-small">qp100</span></th>
<th><span style="font-size: xx-small">qr500</span></th>
<th><span style="font-size: xx-small">qp500</span></th>
<th><span style="font-size: xx-small">qr1000</span></th>
<th><span style="font-size: xx-small">qp1000</span></th>
</tr>
<tr>
<td style="text-align: right"><span style="font-size: xx-small">ma120300_rel_withdbg.cz12b_sync_c8r32</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
</tr>
<tr>
<td style="text-align: right"><span style="font-size: xx-small">ma120300_rel_withdbg.cz12c_sync_c8r32</span></td>
<td id="chi" style="background-color: #81fff9;text-align: right"><span style="font-size: xx-small">4.74</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td id="chi" style="background-color: #81fff9;text-align: right"><span style="font-size: xx-small">1.50</span></td>
<td id="chi" style="background-color: #81fff9;text-align: right"><span style="font-size: xx-small">2.99</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.00</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.04</span></td>
<td style="text-align: right"><span style="font-size: xx-small">1.02</span></td>
<td id="chi" style="background-color: #81fff9;text-align: right"><span style="font-size: xx-small">1.20</span></td>
<td id="chi" style="background-color: #81fff9;text-align: right"><span style="font-size: xx-small">1.08</span></td>
<td id="chi" style="background-color: #81fff9;text-align: right"><span style="font-size: xx-small">1.57<br></span></td>
</tr>
</tbody>
</table>
</div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>

<p>The post <a rel="nofollow" href="https://smalldatum.blogspot.com/2026/02/mariadb-innovation-binlogstorageengine_17.html">MariaDB innovation: binlog_storage_engine, small server, Insert Benchmark</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>An Open Letter to Oracle: Let’s Talk About MySQL’s Future</title>
      <link>https://www.percona.com/blog/an-open-letter-to-oracle-lets-talk-about-mysqls-future/</link>
      <pubDate>Tue, 17 Feb 2026 15:07:32 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://www.percona.com/blog/">Percona Database Performance Blog</source>
      <description><![CDATA[<p>What Happened at the Summits We just wrapped up two MySQL Community Summits – one in San Francisco in January, and one in Brussels right before FOSDEM. The energy in the rooms: a lot of people who care deeply about MySQL got together, exchanged ideas, and left with a clear sense that we need to […]</p>
<p>The post <a rel="nofollow" href="https://www.percona.com/blog/an-open-letter-to-oracle-lets-talk-about-mysqls-future/">An Open Letter to Oracle: Let’s Talk About MySQL’s Future</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" width="200" height="86" src="https://www.percona.com/blog/wp-content/uploads/2025/07/Can-AI-Talk-to-My-Database-Part-Two-MySQL-and-Gemini-200x86.jpg" class="webfeedsFeaturedVisual wp-post-image" alt="An Open Letter to Oracle" style="margin-bottom: 5px;clear:both;max-width: 100%">What Happened at the Summits We just wrapped up two MySQL Community Summits &ndash; one in San Francisco in January, and one in Brussels right before FOSDEM. The energy in the rooms: a lot of people who care deeply about MySQL got together, exchanged ideas, and left with a clear sense that we need to [&hellip;]</p>

<p>The post <a rel="nofollow" href="https://www.percona.com/blog/an-open-letter-to-oracle-lets-talk-about-mysqls-future/">An Open Letter to Oracle: Let’s Talk About MySQL’s Future</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>MariaDB innovation: binlog_storage_engine</title>
      <link>https://smalldatum.blogspot.com/2026/02/mariadb-innovation-binlogstorageengine.html</link>
      <pubDate>Mon, 16 Feb 2026 19:06:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://smalldatum.blogspot.com/">Small Datum</source>
      <description><![CDATA[<p>MariaDB 12.3 has a new feature enabled by the option binlog_storage_engine. When enabled it uses InnoDB instead of raw files to store the binlog. A big benefit from this is reducing the number of fsync calls per commit from 2 to 1 because it reduces the number of resource managers from 2 (binlog, InnoDB) to 1 (InnoDB).In this post I have results for the performance benefit from this when using storage that has a high fsync latency. This is probably a best-case comparison for the feature. A future post will cover the benefit on servers that don\'t have high fsync latency.tl;drthe performance benefit from this is excellent when storage has a high fsync latencymy mental performance model needs to be improved. I gussed that throughput would increase by ~2X when using binlog_storage_engine relative to not using it but using sync_binlog=1 and innodb_flush_log_at_trx_commit=1. However the improvement is larger than 4X.Builds, configuration and hardwareI compiled MariaDB 12.3.0 from source.The server is an ASUS ExpertCenter PN53 with an AMD Ryzen 7 7735HS CPU, 8 cores, SMT disabled, and 32G of RAM. Storage is one NVMe device for the database using ext-4 with discard enabled. The OS is Ubuntu 24.04. More details on it are here. The storage device has high fsync latency.I used 4 my.cnf files:z12bmy.cnf.cz12b_c8r32 is my default configuration. Sync-on-commit is disabled for both the binlog and InnoDB so that write-heavy benchmarks create more stress.z12cmy.cnf.cz12c_c8r32 is like z12b except it enables binlog_storage_enginez12b_syncmy.cnf.cz12b_sync_c8r32 is like z12b except it enables sync-on-commit for the binlog and InnoDBz12c_syncmy.cnf.cz12c_sync_c8r32 is like cz12c except it enables sync-on-commit for InnoDB. Note that InnoDB is used to store the binlog so there is nothing else to sync on commit.BenchmarkI used sysbench and my usage is explained here. To save time I only run 32 of the 42 microbenchmarks and most test only 1 type of SQL statement. Benchmarks are run with the database cached by Postgres.The read-heavy microbenchmarks run for 600 seconds and the write-heavy for 900 seconds.The benchmark is run with 1 client, 1 table and 50M rows. ResultsThe microbenchmarks are split into 4 groups -- 1 for point queries, 2 for range queries, 1 for writes. For the range query microbenchmarks, part 1 has queries that don\'t do aggregation while part 2 has queries that do aggregation.  But here I only report results for the write-heavy tests.I provide charts below with relative QPS. The relative QPS is the following:(QPS for some version) / (QPS for base version)When the relative QPS is > 1 then some version is faster than base version.  When it is < 1 then there might be a regression. I present results for:z12b, z12c, z12b_sync and z12c_sync with z12b as the base version z12b_sync and z12c_sync with z12b_sync as the base versionResults: z12b, z12c, z12b_sync, z12c_syncSummary:z12b_sync has the worst performance thanks to 2 fsyncs per commitz12c_sync gets more than 4X the throughput vs z12b_sync. If fsync latency were the only thing that determined performance then I would expect the difference to be ~2X. There is more going on here and in the next section I mention that enabling binlog_storage_engine also reduces the CPU overhead.some per-test data from iostat and vmstat is herea representative sample of iostat collected at 1-second intervals during the update-inlist test is here. When comparing z12b_sync with z12c_syncthe fsync rate (f/s) is ~2.5X larger for z12c_sync vs z12b_sync (~690/s vs ~275/s) but fsync latency (f_await) is similar. So with binlog_storage_engine enabled MySQL is more efficient, and perhaps thanks to a lower CPU overhead, there is less work to do in between calls to fsyncRelative to: z12bcol-1 : z12ccol-2 : z12b_synccol-3 : z12c_synccol-1   col-2   col-31.06    0.01    0.05    delete1.05    0.01    0.05    insert1.01    0.12    0.47    read-write_range=1001.01    0.10    0.44    read-write_range=101.03    0.01    0.11    update-index1.02    0.02    0.12    update-inlist1.05    0.01    0.06    update-nonindex1.05    0.01    0.06    update-one1.05    0.01    0.06    update-zipf1.01    0.03    0.20    write-onlyResults: z12b_sync, z12c_syncSummary:z12c_sync gets more than 4X the throughput vs z12b_sync. If fsync latency were the only thing that determined performance then I would expect the difference to be ~2X. There is more going on here and below I mention that enabling binlog_storage_engine also reduces the CPU overhead.some per-test data from iostat and vmstat is here and the CPU overhead per operation is much smaller with binlog_storage_engine -- see here for the update-inlist test. In general, when sync-on-commit is enabled then the CPU overhead with binlog_storage_engine enabled is between 1/3 and 2/3 of the overhead without it enabled.Relative to: z12b_synccol-1 : z12c_synccol-16.40    delete5.64    insert4.06    read-write_range=1004.40    read-write_range=107.64    update-index7.17    update-inlist5.73    update-nonindex5.82    update-one5.80    update-zipf6.61    write-only
</p>
<p>The post <a rel="nofollow" href="https://smalldatum.blogspot.com/2026/02/mariadb-innovation-binlogstorageengine.html">MariaDB innovation: binlog_storage_engine</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>MariaDB 12.3 has a new feature enabled by the option <a href="https://mariadb.org/new-binlog-implementation-in-mariadb-12-3/">binlog_storage_engine</a>. When enabled it uses InnoDB instead of raw files to store the binlog. A big benefit from this is reducing the number of fsync calls per commit from 2 to 1 because it reduces the number of resource managers from 2 (binlog, InnoDB) to 1 (InnoDB).</p>
<p>In this post I have results for the performance benefit from this when using storage that has a <a href="https://smalldatum.blogspot.com/2026/01/ssds-power-loss-protection-and-fsync.html">high fsync latency</a>. This is probably a best-case comparison for the feature. A future post will cover the benefit on servers that don&rsquo;t have high fsync latency.</p>
<p>tl;dr</p>

<ul style="text-align: left">
<li>the performance benefit from this is excellent when storage has a high fsync latency</li>
<li>my mental performance model needs to be improved. I gussed that throughput would increase by ~2X when using binlog_storage_engine relative to not using it but using sync_binlog=1 and innodb_flush_log_at_trx_commit=1. However the improvement is larger than 4X.</li>
</ul>
<div><b>Builds, configuration and hardware</b></div>
<div>
<div></div>
<div>I compiled MariaDB 12.3.0 from source.</div>
<div>The server is an ASUS ExpertCenter PN53 with an AMD Ryzen 7 7735HS CPU, 8 cores, SMT disabled, and 32G of RAM. Storage is one NVMe device for the database using ext-4 with discard enabled. The OS is Ubuntu 24.04. More details on it&nbsp;<a href="ASUS%20ExpertCenter%20PN53%20with%20AMD%20Ryzen%207%207735HS,%2032G%20RAM%20and%202%20m.2%20slots%20(one%20for%20OS%20install,%20one%20for%20DB%20perf%20tests)">are here</a>. The storage device has <a href="https://smalldatum.blogspot.com/2026/01/ssds-power-loss-protection-and-fsync.h">high fsync latency</a>.</div>
</div>
<div></div>
<div>I used 4 my.cnf files:</div>
<div>
<ul style="text-align: left">
<li>z12b</li>
<ul>
<li><a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c8r32/ma1203/etc/my.cnf.cz12b_c8r32">my.cnf.cz12b_c8r32</a>&nbsp;is my default configuration. Sync-on-commit is disabled for both the binlog and InnoDB so that write-heavy benchmarks create more stress.</li>
</ul>
<li>z12c</li>
<ul>
<li><a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c8r32/ma1203/etc/my.cnf.cz12c_c8r32">my.cnf.cz12c_c8r32</a> is like z12b except it enables binlog_storage_engine</li>
</ul>
<li>z12b_sync</li>
<ul>
<li><a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c8r32/ma1203/etc/my.cnf.cz12b_sync_c8r32">my.cnf.cz12b_sync_c8r32</a> is like z12b except it enables sync-on-commit for the binlog and InnoDB</li>
</ul>
<li>z12c_sync</li>
<ul>
<li><a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c8r32/ma1203/etc/my.cnf.cz12c_sync_c8r32">my.cnf.cz12c_sync_c8r32</a> is like cz12c except it enables sync-on-commit for InnoDB. Note that InnoDB is used to store the binlog so there is nothing else to sync on commit.</li>
</ul>
</ul>
<div>
<div>
<p><b>Benchmark</b></p>
<div>
<div>I used sysbench and my usage is&nbsp;<a href="http://smalldatum.blogspot.com/2017/02/using-modern-sysbench-to-compare.html">explained here</a>. To save time I only run 32 of the 42 microbenchmarks&nbsp;</div>
<div>and most test only 1 type of SQL statement. Benchmarks are run with the database cached by Postgres.</div>
<div>The read-heavy microbenchmarks run for 600 seconds and the write-heavy for 900 seconds.
<p>The benchmark is run with 1 client, 1 table and 50M rows.&nbsp;</p></div>
</div>
</div>
</div>
<div></div>
<div>
<div style="font-family: inherit"><b>Results</b></div>
<div><span>
<div style="font-family: inherit"></div>
<div style="font-family: inherit"><span style="font-family: inherit">The microbenchmarks are split into 4 groups &mdash; 1 for point queries, 2 for range queries, 1 for writes. For the range query microbenchmarks, part 1 has queries that don&rsquo;t do aggregation while part 2 has queries that do aggregation.&nbsp;&nbsp;
<p>But here I only report results for the write-heavy tests.</p></span></div>
<div style="font-family: inherit">I provide charts below with relative QPS. The relative QPS is the following:</div>
<div style="font-family: inherit">
<div></div>
<blockquote><p>(QPS for some version) / (QPS for base version)</p></blockquote>
</div>
<div><span style="font-family: inherit">When the relative QPS is &gt; 1 then&nbsp;</span><i style="font-family: inherit">some version</i><span style="font-family: inherit">&nbsp;is faster than&nbsp;</span><i style="font-family: inherit">base version</i><span style="font-family: inherit">.&nbsp; When it is &lt; 1 then there might be a regression.&nbsp;</span><span>
<p><span style="font-family: inherit">I present results for:</span></p>
<ul style="font-family: inherit">
<li><span style="font-family: inherit">z12b, z12c, z12b_sync and z12c_sync with z12b as the base version</span></li>
<li>&nbsp;z12b_sync and z12c_sync with z12b_sync as the base version</li>
</ul>
<div><b>Results: z12b, z12c, z12b_sync, z12c_sync</b></div>
<div></div>
<div>Summary:</div>
<div>
<ul style="text-align: left">
<li>z12b_sync has the worst performance thanks to 2 fsyncs per commit</li>
<li>z12c_sync gets more than 4X the throughput vs z12b_sync. If fsync latency were the only thing that determined performance then I would expect the difference to be ~2X. There is more going on here and in the next section I mention that enabling binlog_storage_engine also reduces the CPU overhead.</li>
<li>some per-test data from iostat and vmstat <a href="https://gist.github.com/mdcallag/ec11b28478551d5fbe69ec52ef9faf2c#file-gistfile1-txt-L6-L7">is here</a></li>
<li>a representative sample of iostat collected at 1-second intervals during the update-inlist test <a href="https://gist.github.com/mdcallag/a8e8055f6f290982f1c6594657cacd21">is here</a>. When comparing <a href="https://gist.github.com/mdcallag/a8e8055f6f290982f1c6594657cacd21#file-gistfile1-txt-L27-L38">z12b_sync</a> with <a href="https://gist.github.com/mdcallag/a8e8055f6f290982f1c6594657cacd21#file-gistfile1-txt-L40-L51">z12c_sync</a></li>
<ul>
<li>the fsync rate (f/s) is ~2.5X larger for z12c_sync vs z12b_sync (~690/s vs ~275/s) but fsync latency (f_await) is similar. So with binlog_storage_engine enabled MySQL is more efficient, and perhaps thanks to a lower CPU overhead, there is less work to do in between calls to fsync</li>
</ul>
</ul>
</div>
<div><span style="font-family: courier">Relative to: z12b</span></div>
<div>
<div><span style="font-family: courier">col-1 : z12c</span></div>
<div><span style="font-family: courier">col-2 : z12b_sync</span></div>
<div><span style="font-family: courier">col-3 : z12c_sync</span></div>
<div><span style="font-family: courier"><br></span></div>
<div><span style="font-family: courier">col-1&nbsp; &nbsp;col-2&nbsp; &nbsp;col-3</span></div>
<div><span style="font-family: courier">1.06&nbsp; &nbsp; <span style="background-color: #f4cccc">0.01</span>&nbsp; &nbsp; <span style="background-color: #fff2cc">0.05</span>&nbsp; &nbsp; delete</span></div>
<div><span style="font-family: courier">1.05&nbsp; &nbsp; <span style="background-color: #f4cccc">0.01</span>&nbsp; &nbsp; <span style="background-color: #fff2cc">0.05</span>&nbsp; &nbsp; insert</span></div>
<div><span style="font-family: courier">1.01&nbsp; &nbsp; <span style="background-color: #f4cccc">0.12</span>&nbsp; &nbsp; <span style="background-color: #fff2cc">0.47</span>&nbsp; &nbsp; read-write_range=100</span></div>
<div><span style="font-family: courier">1.01&nbsp; &nbsp; <span style="background-color: #f4cccc">0.10</span>&nbsp; &nbsp; <span style="background-color: #fff2cc">0.44</span>&nbsp; &nbsp; read-write_range=10</span></div>
<div><span style="font-family: courier">1.03&nbsp; &nbsp; <span style="background-color: #f4cccc">0.01</span><span style="background-color: white">&nbsp; &nbsp; </span><span style="background-color: #fff2cc">0.11</span>&nbsp; &nbsp; update-index</span></div>
<div><span style="font-family: courier">1.02&nbsp; &nbsp; <span style="background-color: #f4cccc">0.02</span><span style="background-color: white">&nbsp; &nbsp; </span><span style="background-color: #fff2cc">0.12</span>&nbsp; &nbsp; update-inlist</span></div>
<div><span style="font-family: courier">1.05&nbsp; &nbsp; <span style="background-color: #f4cccc">0.01</span><span style="background-color: white">&nbsp; &nbsp; </span><span style="background-color: #fff2cc">0.06</span>&nbsp; &nbsp; update-nonindex</span></div>
<div><span style="font-family: courier">1.05&nbsp; &nbsp; <span style="background-color: #f4cccc">0.01</span><span style="background-color: white">&nbsp; &nbsp; </span><span style="background-color: #fff2cc">0.06</span>&nbsp; &nbsp; update-one</span></div>
<div><span style="font-family: courier">1.05&nbsp; &nbsp; <span style="background-color: #f4cccc">0.01</span><span style="background-color: white">&nbsp; &nbsp; </span><span style="background-color: #fff2cc">0.06</span>&nbsp; &nbsp; update-zipf</span></div>
<div><span style="font-family: courier">1.01&nbsp; &nbsp; <span style="background-color: #f4cccc">0.03</span><span style="background-color: white">&nbsp; &nbsp; </span><span style="background-color: #fff2cc">0.20</span>&nbsp; &nbsp; write-only</span></div>
</div>
<p></p></span></div>
<p></p></span></div>
</div>
<div></div>
</div>
<div>
<div><b>Results: z12b_sync, z12c_sync</b></div>
<div></div>
</div>
<div>
<div>Summary:</div>
<div>
<ul>
<li>z12c_sync gets more than 4X the throughput vs z12b_sync. If fsync latency were the only thing that determined performance then I would expect the difference to be ~2X. There is more going on here and below I mention that enabling binlog_storage_engine also reduces the CPU overhead.</li>
<li>some per-test data from iostat and vmstat&nbsp;<a href="https://gist.github.com/mdcallag/8a02ec11fa4c8a04d9430cbb463c20d3">is here</a>&nbsp;and the CPU overhead per operation is much smaller with binlog_storage_engine &mdash; <a href="https://gist.github.com/mdcallag/8a02ec11fa4c8a04d9430cbb463c20d3#file-gistfile1-txt-L4-L5">see here</a> for the update-inlist test. In general, when sync-on-commit is enabled then the CPU overhead with binlog_storage_engine enabled is between 1/3 and 2/3 of the overhead without it enabled.</li>
</ul>
</div>
</div>
<div><span style="font-family: courier">Relative to: z12b_sync</span></div>
<div>
<div><span style="font-family: courier">col-1 : z12c_sync</span></div>
<div><span style="font-family: courier"><br></span></div>
<div><span style="font-family: courier">col-1</span></div>
<div><span style="font-family: courier"><span style="background-color: #d9ead3">6.40</span>&nbsp; &nbsp; delete</span></div>
<div><span style="font-family: courier"><span style="background-color: #d9ead3">5.64</span>&nbsp; &nbsp; insert</span></div>
<div><span style="font-family: courier"><span style="background-color: #d9ead3">4.06</span>&nbsp; &nbsp; read-write_range=100</span></div>
<div><span style="font-family: courier"><span style="background-color: #d9ead3">4.40</span>&nbsp; &nbsp; read-write_range=10</span></div>
<div><span style="font-family: courier"><span style="background-color: #d9ead3">7.64</span>&nbsp; &nbsp; update-index</span></div>
<div><span style="font-family: courier"><span style="background-color: #d9ead3">7.17</span>&nbsp; &nbsp; update-inlist</span></div>
<div><span style="font-family: courier"><span style="background-color: #d9ead3">5.73</span>&nbsp; &nbsp; update-nonindex</span></div>
<div><span style="font-family: courier"><span style="background-color: #d9ead3">5.82</span>&nbsp; &nbsp; update-one</span></div>
<div><span style="font-family: courier"><span style="background-color: #d9ead3">5.80</span>&nbsp; &nbsp; update-zipf</span></div>
<div><span style="font-family: courier"><span style="background-color: #d9ead3">6.61</span>&nbsp; &nbsp; write-only</span></div>
</div>
<div></div>

<p>The post <a rel="nofollow" href="https://smalldatum.blogspot.com/2026/02/mariadb-innovation-binlogstorageengine.html">MariaDB innovation: binlog_storage_engine</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>HammerDB tproc-c on a large server, Postgres and MySQL</title>
      <link>https://smalldatum.blogspot.com/2026/02/hammerdb-tproc-c-on-large-server.html</link>
      <pubDate>Sun, 15 Feb 2026 20:42:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://smalldatum.blogspot.com/">Small Datum</source>
      <description><![CDATA[<p>This has results for HammerDB tproc-c on a small server using MySQL and Postgres. I am new to HammerDB and still figuring out how to explain and present results so I will keep this simple and just share graphs without explaining the results.The comparison might favor Postgres for the IO-bound workloads because I used smaller buffer pools than normal to avoid OOM. I have to do this because RSS for the HammerDB client grows over time as it buffers more response time stats. And while I used buffered IO for Postgres, I use O_DIRECT for InnoDB. So Postgres might have avoided some read IO thanks to the OS page cache while InnoDB did not.tl;dr for MySQLWith vu=40 MySQL 8.4.8 uses about 2X more CPU per transaction and does more than 2X more context switches per transaction compared to Postgres 18.1. I will get CPU profiles soon.Modern MySQL brings us great improvements to concurrency and too many new CPU overheadsMySQL 5.6 and 8.4 have similar throughput at the lowest concurrency (vu=10)MySQl 8.4 is a lot faster than 5.6 at the highest concurrency (vu=40)tl;dr for PostgresModern Postgres has regressions relative to old PostgresThe regressions increase with the warehouse count, at wh=4000 the NOPM drops between 3% and 13% depending on the virtual user count (vu).tl;dr for Postgres vs MySQLPostgres and MySQL have similar throughput for the largest warehouse count (wh=4000)Otherwise Postgres gets between 1.4X and 2X more throughput (NOPM)Builds, configuration and hardwareI compiled Postgres versions from source: 12.22, 13.23, 14.20, 15.15, 16.11, 17.7 and 18.1.I compiled MySQL versions from source: 5.6.51, 5.7.44, 8.0.45, 8.4.8, 9.4.0 and 9.6.0.I used a 48-core server from Hetzneran ax162s with an AMD EPYC 9454P 48-Core Processor with SMT disabled2 Intel D7-P5520 NVMe storage devices with RAID 1 (3.8T each) using ext4128G RAMUbuntu 22.04 running the non-HWE kernel (5.5.0-118-generic)Postgres configuration files:prior to v18 the config file is named conf.diff.cx10a50g_c32r128 (x10a_c32r128) and is here for versions 12, 13, 14, 15, 16 and 17.for Postgres 18 I used conf.diff.cx10b_c32r128 (x10b_c32r128) with io_method=sync to be similar to the config used for versions 12 through 17.MySQL configuration filesprior to 9.6 the config file is named my.cnf.cz12a50g_c32r128 (z12a50g_c32r128 or z12a50g) and is here for versions 5.6, 5.7, 8.0 and 8.4for 9.6 it is named my.cnf.cz13a50g_c32r128 (z13a50g_c32r128 or z13a50g) and is hereFor both Postgres and MySQL fsync on commit is disabled to avoid turning this into an fsync benchmark. The server has 2 SSDs with SW RAID and low fsync latency.BenchmarkThe benchmark is tproc-c from HammerDB. The tproc-c benchmark is derived from TPC-C.The benchmark was run for several workloads:vu=10, wh=1000 - 10 virtual users, 1000 warehousesvu=20, wh=1000 - 20 virtual users, 1000 warehousesvu=40, wh=1000 - 40 virtual users, 1000 warehousesvu=10, wh=2000 - 10 virtual users, 2000 warehousesvu=20, wh=2000 - 20 virtual users, 2000 warehousesvu=40, wh=2000 - 40 virtual users, 2000 warehousesvu=10, wh=4000 - 10 virtual users, 4000 warehousesvu=20, wh=4000 - 20 virtual users, 4000 warehousesvu=40, wh=4000 - 40 virtual users, 4000 warehousesThe wh=1000 workloads are less heavy on IO. The wh=4000 workloads are more heavy on IO.The benchmark for Postgres is run by a variant of this script which depends on scripts here. The MySQL scripts are similar.stored procedures are enabledpartitioning is used because the warehouse count is >= 1000a 5 minute rampup is usedthen performance is measured for 60 minutesBasic metrics: iostatI am still improving my helper scripts to report various performance metrics. The table here has average values from iostat during the benchmark run phase for MySQL 8.4.8 and Postgres 18.1. For these configurations the NOPM values for Postgres and MySQL were similar so I won\'t present normalized values (average value / NOPM) and NOPM is throughput.average wMB/s increases with the warehouse count for Postgres but not for MySQLr/s increases with the warehouse count for Postgres and MySQLiostat metrics* r/s = average rate of reads/s from storage* wMB/s = average MB/s written to storagemy8408r/s     wMB/s22833.0 906.2   vu=40, wh=100063079.8 1428.5  vu=40, wh=200082282.3 1398.2  vu=40, wh=4000pg181r/s     wMB/s30394.9 1261.9  vu=40, wh=100059770.4 1267.8  vu=40, wh=200078052.3 1272.9  vu=40, wh=4000Basic metrics: vmstatI am still improving my helper scripts to report various performance metrics. The table here has average values from vmstat during the benchmark run phase for MySQL 8.4.8 and Postgres 18.1. For these configurations the NOPM values for Postgres and MySQL were similar so I won\'t present normalized values (average value / NOPM).CPU utilization is almost 2X larger for MySQLContext switch rates are more than 2X larger for MySQLIn the future I hope to learn why MySQL uses almost 2X more CPU per transaction and has more than 2X more context switches per transaction relative to Postgresvmstat metrics* cs - average value for cs (context switches/s)* us - average value for us (user CPU)* sy - average value for sy (system CPU)* id - average value for id (idle)* wa - average value for wa (waiting for IO)* us+sy - sum of us and symy8408cs      us      sy      id      wa      us+sy455648  61.9    8.2     24.2    5.7     70.1484955  50.4    9.2     19.5    21.0    59.6487410  39.5    8.4     19.4    32.6    48.0pg181cs      us      sy      id      wa      us+sy127486  23.5    10.1    63.3    3.0     33.6166257  17.2    11.1    62.5    9.1     28.3203578  13.9    11.3    59.2    15.6    25.2ResultsMy analysis at this point is simple -- I only consider average throughput. Eventually I will examine throughput over time and efficiency (CPU and IO).On the charts that follow y-axis does not start at 0 to improve readability at the risk of overstating the differences. The y-axis shows relative throughput. There might be a regression when the relative throughput is less than 1.0. There might be an improvement when it is > 1.0. The relative throughput is:(NOPM for some-version / NOPM for base-version)I provide three charts below:only MySQL - base-version is MySQL 5.6.51only Postgres - base-version is Postgres 12.22Postgres vs MySQL - base-version is Postgres 18.1, some-version is MySQL 8.4.8Results: MySQL 5.6 to 9.6Legend:my5651.z12a is MySQL 5.6.51 with the z12a50g configmy5744.z12a is MySQL 5.7.44 with the z12a50g configmy8045.z12a is MySQL 8.0.45 with the z12a50g configmy8408.z12a is MySQL 8.4.8 with the z12a50g configmy9500.z13a is MySQL 9.6.0 with the z13a50g configSummaryAt the lowest concurrency (vu=10) MySQL 8.4.8 has similar throughput as 5.6.51 because CPU regressions in modern MySQL offset the concurrency improvements.At the highest concurrency (vu=40) MySQL 8.4.8 is much faster than 5.6.51 and the regressions after 5.7 are small. This matches what I have seen elsewhere -- while modern MySQL suffers from CPU regressions it benefits from concurrency improvements. Imagine if we could get those concurrency improvements without the CPU regressions.And the absolute NOPM values are here:my5651my5744my8045my8408my9600vu=10, wh=1000163059183268156039155194151748vu=20, wh=1000210506321670283282281038279269vu=40, wh=1000216677454743439589435095433618vu=10, wh=2000107492130229111798110161108386vu=20, wh=2000155398225068193658190717189847vu=40, wh=2000178278302723297236307504293217vu=10, wh=400081242103406894148931688458vu=20, wh=4000131241179112155134152998152301vu=40, wh=4000146809228554234922229511230557Results: Postgres 12 to 18Legend:pg1222 is Postgres 12.22 with the x10a50g configpg1323 is Postgres 13.23 with the x10a50g configpg1420 is Postgres 14.20 with the x10a50g configpg1515 is Postgres 15.15 with the x10a50g configpg1611 is Postgres 16.11 with the x10a50g configpg177 is Postgres 17.7 with the x10a50g configpg181 is Postgres 18.1 with the x10b50g configSummaryModern Postgres has regressions relative to old PostgresThe regressions increase with the warehouse count, at wh=4000 the NOPM drops between 3% and 13% depending on the virtual user count (vu).The relative NOPM values are here:pg1222pg1323pg1420pg1515pg1611pg177pg181vu=10, wh=10001.0001.0001.0541.0421.0041.0100.968vu=20, wh=10001.0001.0351.0371.0281.0281.0010.997vu=40, wh=10001.0001.0400.9881.0001.0270.9980.970vu=10, wh=20001.0001.0261.0591.0751.0681.0811.029vu=20, wh=20001.0001.0221.0461.0430.9790.9720.934vu=40, wh=20001.0001.0141.0321.0360.9791.0100.947vu=10, wh=40001.0001.0271.0321.0350.9930.9980.974vu=20, wh=40001.0001.0051.0491.0480.9400.9270.876vu=40, wh=40001.0000.9911.0190.9831.0010.9790.937The absolute NOPM values are here:pg1222pg1323pg1420pg1515pg1611pg177pg181vu=10, wh=1000353077353048372015367933354513356469341688vu=20, wh=1000423565438456439398435454435288423986422397vu=40, wh=1000445114462851439728445144457110444364431648vu=10, wh=2000223048228914236231239868238117241185229549vu=20, wh=2000314380321380328688328044307728305452293627vu=40, wh=2000320347324769330444331896313553323454303403vu=10, wh=4000162054166461167320167761160962161716157872vu=20, wh=4000244598245804256593256231230037226844214309vu=40, wh=4000252931250634257820248584253059247610236986Results: MySQL vs PostgresLegend:pg181 is Postgres 18.1 with the x10b50g configmy8408 is MySQL 8.4.8 with the z12a50g configSummaryPostgres and MySQL have similar throughput for the largest warehouse count (wh=4000)Otherwise Postgres gets between 1.4X and 2X more throughput (NOPM)The absolute NOPM values are here:pg181my8408vu=10, wh=1000341688155194vu=20, wh=1000422397281038vu=40, wh=1000431648435095vu=10, wh=2000229549110161vu=20, wh=2000293627190717vu=40, wh=2000303403307504vu=10, wh=400015787289316vu=20, wh=4000214309152998vu=40, wh=4000236986229511</p>
<p>The post <a rel="nofollow" href="https://smalldatum.blogspot.com/2026/02/hammerdb-tproc-c-on-large-server.html">HammerDB tproc-c on a large server, Postgres and MySQL</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>This has results for&nbsp;<a href="https://www.hammerdb.com/">HammerDB</a>&nbsp;tproc-c on a small server using MySQL and Postgres. I am new to HammerDB and still figuring out how to explain and present results so I will keep this simple and just share graphs without explaining the results.</p>
<p>The comparison might favor Postgres for the IO-bound workloads because I used smaller buffer pools than normal to avoid OOM. I have to do this because RSS for the HammerDB client grows over time as it buffers more response time stats. And while I used buffered IO for Postgres, I use O_DIRECT for InnoDB. So Postgres might have avoided some read IO thanks to the OS page cache while InnoDB did not.</p>
<p>tl;dr for MySQL</p>

<ul style="text-align: left">
<li>With vu=40 MySQL 8.4.8 uses about 2X more CPU per transaction and does more than 2X more context switches per transaction compared to Postgres 18.1. I will get CPU profiles soon.</li>
<li>Modern MySQL brings us great improvements to concurrency and too many new CPU overheads</li>
<ul>
<li>MySQL 5.6 and 8.4 have similar throughput at the lowest concurrency (vu=10)</li>
<li>MySQl 8.4 is a lot faster than 5.6 at the highest concurrency (vu=40)</li>
</ul>
</ul>
<div>tl;dr for Postgres</div>
<div>
<ul style="text-align: left">
<li>Modern Postgres has regressions relative to old Postgres</li>
<li>The regressions increase with the warehouse count, at wh=4000 the NOPM drops between 3% and 13% depending on the virtual user count (vu).</li>
</ul>
<div>tl;dr for Postgres vs MySQL</div>
</div>
<div>
<ul style="text-align: left">
<li>Postgres and MySQL have similar throughput for the largest warehouse count (wh=4000)</li>
<li>Otherwise Postgres gets between 1.4X and 2X more throughput (NOPM)</li>
</ul>
</div>
<div><b>Builds, configuration and hardware</b></div>
<div>
<div></div>
<div>I compiled Postgres versions from source: 12.22, 13.23, 14.20, 15.15, 16.11, 17.7 and 18.1.</div>
<div></div>
<div>I compiled MySQL versions from source: 5.6.51, 5.7.44, 8.0.45, 8.4.8, 9.4.0 and 9.6.0.</div>
</div>
<div></div>
<div>
<div>I used a 48-core server from Hetzner</div>
<div>
<ul>
<li>an ax162s with an AMD EPYC 9454P 48-Core Processor with SMT disabled</li>
<li>2 Intel D7-P5520 NVMe storage devices with RAID 1 (3.8T each) using ext4</li>
<li>128G RAM</li>
<li>Ubuntu 22.04 running the non-HWE kernel (5.5.0-118-generic)</li>
</ul>
<div>
<div><span style="font-family: inherit">Postgres configuration files:</span></div>
<div>
<ul>
<li><span style="font-family: inherit">prior to v18 the config file is named conf.diff.cx10a50g_c32r128 (x10a_c32r128) and is here for versions&nbsp;</span><a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c32r128/pg1219_o2nofp/conf.diff.cx10a50g_c32r128">12</a>,&nbsp;<a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c32r128/pg1315_o2nofp/conf.diff.cx10a50g_c32r128">13</a>,&nbsp;<a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c32r128/pg1412_o2nofp/conf.diff.cx10a50g_c32r128">14</a>,&nbsp;<a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c32r128/pg157_o2nofp/conf.diff.cx10a50g_c32r128">15</a>,&nbsp;<a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c32r128/pg163_o2nofp/conf.diff.cx10a50g_c32r128">16</a>&nbsp;and&nbsp;<a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c32r128/pg17beta1_o2nofp/conf.diff.cx10a50g_c32r128">17</a>.</li>
<li>for Postgres 18 I used&nbsp;<a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c32r128/pg18beta3_o2nofp/conf.diff.cx10b50g_c32r128">conf.diff.cx10b_c32r128</a>&nbsp;(x10b_c32r128) with io_method=sync to be similar to the config used for versions 12 through 17.</li>
</ul>
<div>MySQL configuration files</div>
</div>
</div>
</div>
</div>
<div>
<ul style="text-align: left">
<li>prior to 9.6 the config file is named my.cnf.cz12a50g_c32r128 (z12a50g_c32r128 or z12a50g) and is here for versions <a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c32r128/my5651_rel_o2nofp/etc/my.cnf.cz12a50g_c32r128">5.6</a>, <a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c32r128/my5744_rel_o2nofp/etc/my.cnf.cz12a50g_c32r128">5.7</a>, <a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c32r128/my8043_rel_o2nofp/etc/my.cnf.cz12a50g_c32r128">8.0</a> and <a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c32r128/my8406_rel_o2nofp/etc/my.cnf.cz12a50g_c32r128">8.4</a></li>
<li>for 9.6 it is named my.cnf.cz13a50g_c32r128 (z13a50g_c32r128 or z13a50g) and <a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c32r128/my9500/etc/my.cnf.cz13a50g_c32r128">is here</a></li>
</ul>
<div>For both Postgres and MySQL fsync on commit is disabled to avoid turning this into an fsync benchmark. The server has 2 SSDs with SW RAID and <a href="https://smalldatum.blogspot.com/2026/01/ssds-power-loss-protection-and-fsync.html">low fsync latency</a>.</div>
<div></div>
<div>
<div><b>Benchmark</b>
<div></div>
</div>
<div></div>
<div>The benchmark is&nbsp;<a href="https://www.hammerdb.com/docs/ch03.html">tproc-c</a>&nbsp;from&nbsp;<a href="https://www.hammerdb.com/">HammerDB</a>. The tproc-c benchmark is derived from TPC-C.
<p>The benchmark was run for several workloads:</p></div>
<div>
<ul>
<li>vu=10, wh=1000 &ndash; 10 virtual users, 1000 warehouses</li>
<li>vu=20, wh=1000 &ndash; 20 virtual users, 1000 warehouses</li>
<li>vu=40, wh=1000 &ndash; 40 virtual users, 1000 warehouses</li>
<li>vu=10, wh=2000 &ndash; 10 virtual users, 2000 warehouses</li>
<li>vu=20, wh=2000 &ndash; 20 virtual users, 2000 warehouses</li>
<li>vu=40, wh=2000 &ndash; 40 virtual users, 2000 warehouses</li>
<li>vu=10, wh=4000 &ndash; 10 virtual users, 4000 warehouses</li>
<li>vu=20, wh=4000 &ndash; 20 virtual users, 4000 warehouses</li>
<li>vu=40, wh=4000 &ndash; 40 virtual users, 4000 warehouses</li>
</ul>
<div>The wh=1000 workloads are less heavy on IO. The wh=4000 workloads are more heavy on IO.</div>
<div></div>
<div>The benchmark for Postgres is run by a variant of&nbsp;<a href="https://github.com/mdcallag/mytools/blob/master/bench/arc/jan26.tprocc.pn53.pg/allpg.N.sh">this script</a>&nbsp;which depends on&nbsp;<a href="https://github.com/mdcallag/mytools/tree/master/bench/arc/jan26.tprocc.pn53.pg/testscripts">scripts here</a>. The MySQL scripts are similar.</div>
<div>
<ul>
<li>stored procedures are enabled</li>
<li>partitioning is used because the warehouse count is &gt;= 1000</li>
<li>a 5 minute rampup is used</li>
<li>then performance is measured for 60 minutes</li>
</ul>
<div><b>Basic metrics: iostat</b></div>
<div></div>
<div>I am still improving my helper scripts to report various performance metrics. The table here has average values from iostat during the benchmark run phase for MySQL 8.4.8 and Postgres 18.1. For these configurations the NOPM values for Postgres and MySQL were similar so I won&rsquo;t present normalized values (average value / NOPM) and NOPM is throughput.</div>
<div>
<ul style="text-align: left">
<li>average wMB/s increases with the warehouse count for Postgres but not for MySQL</li>
<li>r/s increases with the warehouse count for Postgres and MySQL</li>
</ul>
</div>
<div><span style="font-family: courier">iostat metrics</span></div>
<div>
<div><span style="font-family: courier">* r/s = average rate of reads/s from storage</span></div>
<div><span style="font-family: courier">* wMB/s = average MB/s written to storage</span></div>
<div><span style="font-family: courier"><br></span></div>
<div><span style="font-family: courier">my8408</span></div>
<div><span style="font-family: courier">r/s&nbsp; &nbsp; &nbsp;wMB/s</span></div>
<div><span style="font-family: courier">22833.0 906.2&nbsp; &nbsp;vu=40, wh=1000</span></div>
<div><span style="font-family: courier">63079.8 1428.5&nbsp; vu=40, wh=2000</span></div>
<div><span style="font-family: courier">82282.3 1398.2&nbsp; vu=40, wh=4000</span></div>
<div><span style="font-family: courier"><br></span></div>
<div><span style="font-family: courier">pg181</span></div>
<div><span style="font-family: courier">r/s&nbsp; &nbsp; &nbsp;wMB/s</span></div>
<div><span style="font-family: courier">30394.9 1261.9&nbsp; vu=40, wh=1000</span></div>
<div><span style="font-family: courier">59770.4 1267.8&nbsp; vu=40, wh=2000</span></div>
<div><span style="font-family: courier">78052.3 1272.9&nbsp; vu=40, wh=4000</span></div>
</div>
<div></div>
<div><b>Basic metrics: vmstat</b></div>
<div></div>
<div>I am still improving my helper scripts to report various performance metrics. The table here has average values from vmstat during the benchmark run phase for MySQL 8.4.8 and Postgres 18.1. For these configurations the NOPM values for Postgres and MySQL were similar so I won&rsquo;t present normalized values (average value / NOPM).</div>
<div>
<ul style="text-align: left">
<li>CPU utilization is almost 2X larger for MySQL</li>
<li>Context switch rates are more than 2X larger for MySQL</li>
<li>In the future I hope to learn why MySQL uses almost 2X more CPU per transaction and has more than 2X more context switches per transaction relative to Postgres</li>
</ul>
</div>
<div><span style="font-family: courier">vmstat metrics</span></div>
<div>
<div><span style="font-family: courier">* cs &ndash; average value for cs (context switches/s)</span></div>
<div><span style="font-family: courier">* us &ndash; average value for us (user CPU)</span></div>
<div><span style="font-family: courier">* sy &ndash; average value for sy (system CPU)</span></div>
<div><span style="font-family: courier">* id &ndash; average value for id (idle)</span></div>
<div><span style="font-family: courier">* wa &ndash; average value for wa (waiting for IO)</span></div>
<div><span style="font-family: courier">* us+sy &ndash; sum of us and sy</span></div>
<div><span style="font-family: courier"><br></span></div>
<div><span style="font-family: courier">my8408</span></div>
<div><span style="font-family: courier">cs&nbsp; &nbsp; &nbsp; us&nbsp; &nbsp; &nbsp; sy&nbsp; &nbsp; &nbsp; id&nbsp; &nbsp; &nbsp; wa&nbsp; &nbsp; &nbsp; us+sy</span></div>
<div><span style="font-family: courier">455648&nbsp; 61.9&nbsp; &nbsp; 8.2&nbsp; &nbsp; &nbsp;24.2&nbsp; &nbsp; 5.7&nbsp; &nbsp; &nbsp;70.1</span></div>
<div><span style="font-family: courier">484955&nbsp; 50.4&nbsp; &nbsp; 9.2&nbsp; &nbsp; &nbsp;19.5&nbsp; &nbsp; 21.0&nbsp; &nbsp; 59.6</span></div>
<div><span style="font-family: courier">487410&nbsp; 39.5&nbsp; &nbsp; 8.4&nbsp; &nbsp; &nbsp;19.4&nbsp; &nbsp; 32.6&nbsp; &nbsp; 48.0</span></div>
<div><span style="font-family: courier"><br></span></div>
<div><span style="font-family: courier">pg181</span></div>
<div><span style="font-family: courier">cs&nbsp; &nbsp; &nbsp; us&nbsp; &nbsp; &nbsp; sy&nbsp; &nbsp; &nbsp; id&nbsp; &nbsp; &nbsp; wa&nbsp; &nbsp; &nbsp; us+sy</span></div>
<div><span style="font-family: courier">127486&nbsp; 23.5&nbsp; &nbsp; 10.1&nbsp; &nbsp; 63.3&nbsp; &nbsp; 3.0&nbsp; &nbsp; &nbsp;33.6</span></div>
<div><span style="font-family: courier">166257&nbsp; 17.2&nbsp; &nbsp; 11.1&nbsp; &nbsp; 62.5&nbsp; &nbsp; 9.1&nbsp; &nbsp; &nbsp;28.3</span></div>
<div><span style="font-family: courier">203578&nbsp; 13.9&nbsp; &nbsp; 11.3&nbsp; &nbsp; 59.2&nbsp; &nbsp; 15.6&nbsp; &nbsp; 25.2</span></div>
</div>
<div></div>
<div>
<div>
<div><b>Results</b></div>
<div><b><br></b></div>
<div>My analysis at this point is simple &mdash; I only consider average throughput. Eventually I will examine throughput over time and efficiency (CPU and IO).</div>
<div></div>
<div>On the charts that follow y-axis does not start at 0 to improve readability <b>at the risk of overstating the differences</b>. The y-axis shows relative throughput. There might be a regression when the relative throughput is less than 1.0. There might be an improvement when it is &gt; 1.0. The relative throughput is:</div>
</div>
<blockquote style="border: none;margin: 0px 0px 0px 40px;padding: 0px"><p>(NOPM for&nbsp;<i>some-version</i>&nbsp;/ NOPM for&nbsp;<i>base-version</i>)</p></blockquote>
<p>I provide three charts below:</p>

<ul>
<li>only MySQL &ndash;&nbsp;<i>base-version</i>&nbsp;is MySQL 5.6.51</li>
<li>only Postgres &ndash;&nbsp;<i>base-version</i>&nbsp;is Postgres 12.22</li>
<li>Postgres vs MySQL &ndash;&nbsp;<i>base-version</i>&nbsp;is Postgres 18.1,&nbsp;<i>some-version</i>&nbsp;is MySQL 8.4.8</li>
</ul>
</div>
<div>
<div></div>
</div>
</div>
</div>
</div>
<div><b>Results: MySQL 5.6 to 9.6</b></div>
</div>
<div>
<p>Legend:</p>
<ul>
<li>my5651.z12a is MySQL 5.6.51 with the z12a50g config</li>
<li>my5744.z12a is MySQL 5.7.44 with the z12a50g config</li>
<li>my8045.z12a is MySQL 8.0.45 with the z12a50g config</li>
<li>my8408.z12a is MySQL 8.4.8 with the z12a50g config</li>
<li>my9500.z13a is MySQL 9.6.0 with the z13a50g config</li>
</ul>
<p>Summary</p>

<ul style="text-align: left">
<li>At the lowest concurrency (vu=10) MySQL 8.4.8 has similar throughput as 5.6.51 because CPU regressions in modern MySQL offset the concurrency improvements.</li>
<li>At the highest concurrency (vu=40) MySQL 8.4.8 is much faster than 5.6.51 and the regressions after 5.7 are small. This matches what I have seen elsewhere &mdash; while modern MySQL suffers from CPU regressions it benefits from concurrency improvements. Imagine if we could get those concurrency improvements without the CPU regressions.</li>
</ul>
<div class="separator" style="clear: both;text-align: center"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-juZIFwWtxtBi0SQ7Whhv6G0nLgtkFKSNGivr7_4Fh-Cmx8mq9zy8L_JTbvE9iZiYluDQmZWqd5p2J2CDLAXGI0T61rC_CQS1DVMiHoarSmH2BPh40mvxFyBskM-8IajNoHZfczNKPO_dDSuKxrFynR9H1n9mlsxhBsHVZw8Zeo3hrdYyziRc9gvxl5MG/s600/Relative%20NOPM%20on%20a%20large%20server_%20MySQL.png" style="margin-left: 1em;margin-right: 1em"><img decoding="async" loading="lazy" border="0" data-original-height="371" data-original-width="600" height="396" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-juZIFwWtxtBi0SQ7Whhv6G0nLgtkFKSNGivr7_4Fh-Cmx8mq9zy8L_JTbvE9iZiYluDQmZWqd5p2J2CDLAXGI0T61rC_CQS1DVMiHoarSmH2BPh40mvxFyBskM-8IajNoHZfczNKPO_dDSuKxrFynR9H1n9mlsxhBsHVZw8Zeo3hrdYyziRc9gvxl5MG/w640-h396/Relative%20NOPM%20on%20a%20large%20server_%20MySQL.png" width="640"></a></div>
<p>And the absolute NOPM values are here:</p>

<table border="1" cellpadding="0" cellspacing="0" data-sheets-baot="1" data-sheets-root="1" dir="ltr" style="border-collapse: collapse;border: none;font-family: Arial;font-size: 10pt;width: 0px">
<colgroup>
<col width="100">
<col width="100">
<col width="100">
<col width="100">
<col width="100">
<col width="100">
</colgroup>
<tbody>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"></td>
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">my5651</td>
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">my5744</td>
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">my8045</td>
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">my8408</td>
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">my9600</td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">vu=10, wh=1000</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">163059</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">183268</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">156039</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">155194</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">151748</td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">vu=20, wh=1000</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">210506</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">321670</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">283282</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">281038</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">279269</td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">vu=40, wh=1000</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">216677</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">454743</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">439589</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">435095</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">433618</td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">vu=10, wh=2000</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">107492</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">130229</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">111798</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">110161</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">108386</td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">vu=20, wh=2000</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">155398</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">225068</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">193658</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">190717</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">189847</td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">vu=40, wh=2000</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">178278</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">302723</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">297236</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">307504</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">293217</td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">vu=10, wh=4000</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">81242</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">103406</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">89414</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">89316</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">88458</td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">vu=20, wh=4000</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">131241</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">179112</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">155134</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">152998</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">152301</td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">vu=40, wh=4000</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">146809</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">228554</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">234922</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">229511</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">230557</td>
</tr>
</tbody>
</table>
<p><b>Results: Postgres 12 to 18</b></p>
<p>Legend:</p>
<ul>
<li>pg1222 is Postgres 12.22 with the x10a50g config</li>
<li>pg1323 is Postgres 13.23 with the x10a50g config</li>
<li>pg1420 is Postgres 14.20 with the x10a50g config</li>
<li>pg1515 is Postgres 15.15 with the x10a50g config</li>
<li>pg1611 is Postgres 16.11 with the x10a50g config</li>
<li>pg177 is Postgres 17.7 with the x10a50g config</li>
<li>pg181 is Postgres 18.1 with the x10b50g config</li>
</ul>
<p>Summary</p>

<ul>
<li>Modern Postgres has regressions relative to old Postgres</li>
<li>The regressions increase with the warehouse count, at wh=4000 the NOPM drops between 3% and 13% depending on the virtual user count (vu).</li>
</ul>
<div class="separator" style="clear: both;text-align: center"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9bKWABILkGwJHvK9zBvnoWwpw6vbI6_A8OTEglhbPt7BjBEVVVT1KpC6NUQVlvpK3JbiP7y6Rnei7kTCKyPxLsnwqTzAs0Aa0Q5kHylICO2IuKMiadIY_o8w_qWG-s8HavljpBT6aA6GBEkNnyVoHciGH70-IwYQ0aF3hjRyuW1HLodMGnm3hgPoV7bt0/s600/Relative%20NOPM%20on%20a%20large%20server_%20Postgres.png" style="margin-left: 1em;margin-right: 1em"><img decoding="async" loading="lazy" border="0" data-original-height="371" data-original-width="600" height="396" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9bKWABILkGwJHvK9zBvnoWwpw6vbI6_A8OTEglhbPt7BjBEVVVT1KpC6NUQVlvpK3JbiP7y6Rnei7kTCKyPxLsnwqTzAs0Aa0Q5kHylICO2IuKMiadIY_o8w_qWG-s8HavljpBT6aA6GBEkNnyVoHciGH70-IwYQ0aF3hjRyuW1HLodMGnm3hgPoV7bt0/w640-h396/Relative%20NOPM%20on%20a%20large%20server_%20Postgres.png" width="640"></a></div>
<p></p>
<div>The relative NOPM values are here:</div>
<div></div>

<table border="1" cellpadding="0" cellspacing="0" data-sheets-baot="1" data-sheets-root="1" dir="ltr" style="border-collapse: collapse;border: none;font-family: Arial;font-size: 10pt;width: 0px">
<colgroup>
<col width="100">
<col width="100">
<col width="100">
<col width="100">
<col width="100">
<col width="100">
<col width="100">
<col width="100">
</colgroup>
<tbody>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"></td>
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">pg1222</td>
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">pg1323</td>
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">pg1420</td>
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">pg1515</td>
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">pg1611</td>
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">pg177</td>
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">pg181</td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"><span style="font-size: xx-small">vu=10, wh=1000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.054</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.042</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.004</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.010</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">0.968</span></td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"><span style="font-size: xx-small">vu=20, wh=1000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.035</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.037</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.028</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.028</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.001</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">0.997</span></td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"><span style="font-size: xx-small">vu=40, wh=1000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.040</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">0.988</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.027</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">0.998</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">0.970</span></td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"><span style="font-size: xx-small">vu=10, wh=2000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.026</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.059</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.075</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.068</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.081</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.029</span></td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"><span style="font-size: xx-small">vu=20, wh=2000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.022</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.046</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.043</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">0.979</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">0.972</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">0.934</span></td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"><span style="font-size: xx-small">vu=40, wh=2000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.014</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.032</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.036</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">0.979</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.010</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">0.947</span></td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"><span style="font-size: xx-small">vu=10, wh=4000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.027</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.032</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.035</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">0.993</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">0.998</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">0.974</span></td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"><span style="font-size: xx-small">vu=20, wh=4000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.005</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.049</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.048</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">0.940</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">0.927</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">0.876</span></td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"><span style="font-size: xx-small">vu=40, wh=4000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">0.991</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.019</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">0.983</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">1.001</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">0.979</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">0.937</span></td>
</tr>
</tbody>
</table>
<div></div>

<div>The absolute NOPM values are here:</div>
<div></div>

<table border="1" cellpadding="0" cellspacing="0" data-sheets-baot="1" data-sheets-root="1" dir="ltr" style="border-collapse: collapse;border: none;font-family: Arial;font-size: 10pt;width: 0px">
<colgroup>
<col width="100">
<col width="100">
<col width="100">
<col width="100">
<col width="100">
<col width="100">
<col width="100">
<col width="100">
</colgroup>
<tbody>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"></td>
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"><span style="font-size: xx-small">pg1222</span></td>
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"><span style="font-size: xx-small">pg1323</span></td>
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"><span style="font-size: xx-small">pg1420</span></td>
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"><span style="font-size: xx-small">pg1515</span></td>
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"><span style="font-size: xx-small">pg1611</span></td>
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"><span style="font-size: xx-small">pg177</span></td>
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"><span style="font-size: xx-small">pg181</span></td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"><span style="font-size: xx-small">vu=10, wh=1000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">353077</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">353048</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">372015</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">367933</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">354513</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">356469</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">341688</span></td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"><span style="font-size: xx-small">vu=20, wh=1000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">423565</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">438456</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">439398</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">435454</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">435288</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">423986</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">422397</span></td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"><span style="font-size: xx-small">vu=40, wh=1000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">445114</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">462851</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">439728</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">445144</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">457110</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">444364</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">431648</span></td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"><span style="font-size: xx-small">vu=10, wh=2000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">223048</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">228914</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">236231</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">239868</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">238117</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">241185</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">229549</span></td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"><span style="font-size: xx-small">vu=20, wh=2000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">314380</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">321380</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">328688</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">328044</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">307728</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">305452</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">293627</span></td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"><span style="font-size: xx-small">vu=40, wh=2000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">320347</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">324769</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">330444</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">331896</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">313553</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">323454</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">303403</span></td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"><span style="font-size: xx-small">vu=10, wh=4000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">162054</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">166461</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">167320</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">167761</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">160962</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">161716</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">157872</span></td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"><span style="font-size: xx-small">vu=20, wh=4000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">244598</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">245804</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">256593</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">256231</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">230037</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">226844</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">214309</span></td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"><span style="font-size: xx-small">vu=40, wh=4000</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">252931</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">250634</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">257820</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">248584</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">253059</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">247610</span></td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom"><span style="font-size: xx-small">236986</span></td>
</tr>
</tbody>
</table>
<div></div>

<div><b>Results: MySQL vs Postgres</b></div>
<div>
<p>Legend:</p>
<ul>
<li>pg181 is Postgres 18.1 with the x10b50g config</li>
<li>my8408 is MySQL 8.4.8 with the z12a50g config</li>
</ul>
<p>Summary</p>

<ul style="text-align: left">
<li>Postgres and MySQL have similar throughput for the largest warehouse count (wh=4000)</li>
<li>Otherwise Postgres gets between 1.4X and 2X more throughput (NOPM)</li>
</ul>
<div class="separator" style="clear: both;text-align: center"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNjiZaCuVAOks1rZk-KPyNStyYZD2aDw-hIz1FTFy98BpwArrlQ-ngWnzEvwEnqAL44DgcmH1eNTcGePbxf2UctfP2rzpz8SGRv8Byd9d2da4mD1TXpqD2uqLHcOJN2B84iKq0sJuVD8kRUp_D69YlVq8tPPMJrlFXK2UEywx3MIDLJDxasmEgGwy8eNYz/s600/Relative%20NOPM%20on%20a%20large%20server_%20Postgres%2018.1%20vs%20MySQL%208.4.8.png" style="margin-left: 1em;margin-right: 1em"><img decoding="async" loading="lazy" border="0" data-original-height="371" data-original-width="600" height="396" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNjiZaCuVAOks1rZk-KPyNStyYZD2aDw-hIz1FTFy98BpwArrlQ-ngWnzEvwEnqAL44DgcmH1eNTcGePbxf2UctfP2rzpz8SGRv8Byd9d2da4mD1TXpqD2uqLHcOJN2B84iKq0sJuVD8kRUp_D69YlVq8tPPMJrlFXK2UEywx3MIDLJDxasmEgGwy8eNYz/w640-h396/Relative%20NOPM%20on%20a%20large%20server_%20Postgres%2018.1%20vs%20MySQL%208.4.8.png" width="640"></a></div>
<div>The absolute NOPM values are here:</div>
</div>
</div>
<div></div>
<div>
<table border="1" cellpadding="0" cellspacing="0" data-sheets-baot="1" data-sheets-root="1" dir="ltr" style="border-collapse: collapse;border: none;font-family: Arial;font-size: 10pt;width: 0px">
<colgroup>
<col width="100">
<col width="100">
<col width="100">
</colgroup>
<tbody>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom"></td>
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">pg181</td>
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">my8408</td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">vu=10, wh=1000</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">341688</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">155194</td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">vu=20, wh=1000</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">422397</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">281038</td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">vu=40, wh=1000</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">431648</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">435095</td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">vu=10, wh=2000</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">229549</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">110161</td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">vu=20, wh=2000</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">293627</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">190717</td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">vu=40, wh=2000</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">303403</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">307504</td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">vu=10, wh=4000</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">157872</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">89316</td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">vu=20, wh=4000</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">214309</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">152998</td>
</tr>
<tr style="height: 21px">
<td style="overflow: hidden;padding: 2px 3px;vertical-align: bottom">vu=40, wh=4000</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">236986</td>
<td style="overflow: hidden;padding: 2px 3px;text-align: right;vertical-align: bottom">229511</td>
</tr>
</tbody>
</table>
</div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>

<p>The post <a rel="nofollow" href="https://smalldatum.blogspot.com/2026/02/hammerdb-tproc-c-on-large-server.html">HammerDB tproc-c on a large server, Postgres and MySQL</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>HammerDB tproc-c on a small server, Postgres and MySQL</title>
      <link>https://smalldatum.blogspot.com/2026/02/hammerdb-tproc-c-on-small-server.html</link>
      <pubDate>Sat, 14 Feb 2026 19:03:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://smalldatum.blogspot.com/">Small Datum</source>
      <description><![CDATA[<p>This has results for HammerDB tproc-c on a small server using MySQL and Postgres. I am new to HammerDB and still figuring out how to explain and present results so I will keep this simple and just share graphs without explaining the results.tl;drModern Postgres is faster than old PostgresModern MySQL has large perf regressions relative to old MySQL, and they are worst at low concurrency for CPU-bound worklads. This is similar to what I see on other benchmarks.Modern Postgres is about 2X faster than MySQL at low concurrency (vu=1) and when the workload isn\'t IO-bound (w=100). But with some concurrency (vu=6) or with more IO per transaction (w=1000, w=2000) they have similar throughput. Note that partitioning is used at w=1000 and 2000 but not at w=100.Builds, configuration and hardwareI compiled Postgres versions from source: 12.22, 13.23, 14.20, 15.15, 16.11, 17.7 and 18.1.I compiled MySQL versions from source: 5.6.51, 5.7.44, 8.0.44, 8.4.7, 9.4.0 and 9.5.0.The server is an ASUS ExpertCenter PN53 with an AMD Ryzen 7 7735HS CPU, 8 cores, SMT disabled, and 32G of RAM. Storage is one NVMe device for the database using ext-4 with discard enabled. The OS is Ubuntu 24.04. More details on it are here.For versions prior to 18, the config file is named conf.diff.cx10a_c8r32 and they are as similar as possible and here for versions 12, 13, 14, 15, 16 and 17.For Postgres 18 the config file is named conf.diff.cx10b_c8r32 and adds io_mod=\'sync\' which matches behavior in earlier Postgres versions.For MySQL the config files are named my.cnf.cz12a_c8r32 and are here: 5.6.51, 5.7.44, 8.0.4x, 8.4.x, 9.x.0.For both Postgres and MySQL fsync on commit is disabled to avoid turning this into an fsync benchmark. The server has an SSD with high fsync latency.BenchmarkThe benchmark is tproc-c from HammerDB. The tproc-c benchmark is derived from TPC-C.The benchmark was run for several workloads:vu=1, w=100 - 1 virtual user, 100 warehousesvu=6, w=100 - 6 virtual users, 100 warehousesvu=1, w=1000 - 1 virtual user, 1000 warehousesvu=6, w=1000 - 6 virtual users, 1000 warehousesvu=1, w=2000 - 1 virtual user, 2000 warehousesvu=6, w=2000 - 6 virtual users, 2000 warehousesThe w=100 workloads are less heavy on IO. The w=1000 and w=2000 workloads are more heavy on IO.The benchmark for Postgres is run by this script which depends on scripts here. The MySQL scripts are similar.stored procedures are enabledpartitioning is used for when the warehouse count is >= 1000a 5 minute rampup is usedthen performance is measured for 120 minutesResultsMy analysis at this point is simple -- I only consider average throughput. Eventually I will examine throughput over time and efficiency (CPU and IO).On the charts that follow y-axis does not start at 0 to improve readability at the risk of overstating the differences. The y-axis shows relative throughput. There might be a regression when the relative throughput is less than 1.0. There might be an improvement when it is > 1.0. The relative throughput is:(NOPM for some-version / NOPM for base-version)I provide three charts below:only MySQL - base-version is MySQL 5.6.51only Postgres - base-version is Postgres 12.22Postgres vs MySQL - base-version is Postgres 18.1, some-version is MySQL 8.4.7Results: MySQL 5.6 to 8.4Legend:my5651.z12a is MySQL 5.6.51 with the z12a_c8r32 configmy5744.z12a is MySQL 5.7.44 with the z12a_c8r32 configmy8044.z12a is MySQL 8.0.44 with the z12a_c8r32 configmy847.z12a is MySQL 8.4.7 with the z12a_c8r32 configmy9400.z12a is MySQL 9.4.0 with the z12a_c8r32 configmy9500.z12a is MySQL 9.5.0 with the z12a_c8r32 configSummaryPerf regressions in MySQL 8.4 are smaller with vu=6 and wh >= 1000 -- the cases where there is more concurrency (vu=6) and the workload does more IO per transaction (wh=1000 &#038; 2000). Note that partitioning is used at w=1000 and 2000 but not at w=100.Perf regressions in MySQL 8.4 are larger with vu=1 and even more so with wh=100 (low concurrency, less IO per transaction).Performance has mostly been dropping from MySQL 5.6 to 8.4. From other benchmarks the problem is from new CPU overheads at low concurrency.While perf regressions in modern MySQL at high concurrency have been less of a problem on other benchmarks, this server is too small to support high concurrency.Results: Postgres 12 to 18Legend:pg1222.x10a is Postgres 12.22 with the x10a_c8r32 configpg1323.x10a is Postgres 13.23 with the x10a_c8r32 configpg1420.x10a is Postgres 14.20 with the x10a_c8r32 configpg1515.x10a is Postgres 15.15 with the x10a_c8r32 configpg1611.x10a is Postgres 16.11 with the x10a_c8r32 configpg177.x10a is Postgres 17.7 with the x10a_c8r32 configpg181.x10b is Postgres 18.1 with the x10b_c8r32 configSummaryModern Postgres is faster than old PostgresResults: MySQL vs PostgresLegend:pg181.x10b is Postgres 18.1 with the x10b_c8r32 configmy847.z12a is MySQL 8.4.7 with the z12a_c8r32 configSummaryMySQL and Postgres have similar throughput for vu=6 at w=1000 and 2000. Note that partitioning is used at w=1000 and 2000 but not at w=100.Otherwise Postgres is 2X faster than MySQL </p>
<p>The post <a rel="nofollow" href="https://smalldatum.blogspot.com/2026/02/hammerdb-tproc-c-on-small-server.html">HammerDB tproc-c on a small server, Postgres and MySQL</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>This has results for <a href="https://www.hammerdb.com/">HammerDB</a>&nbsp;tproc-c on a small server using MySQL and Postgres. I am new to HammerDB and still figuring out how to explain and present results so I will keep this simple and just share graphs without explaining the results.</p>
<p>tl;dr</p>

<ul style="text-align: left">
<li>Modern Postgres is faster than old Postgres</li>
<li>Modern MySQL has large perf regressions relative to old MySQL, and they are worst at low concurrency for CPU-bound worklads. This is similar to what I see on other benchmarks.</li>
<li>Modern Postgres is about 2X faster than MySQL at low concurrency (vu=1) and when the workload isn&rsquo;t IO-bound (w=100). But with some concurrency (vu=6) or with more IO per transaction (w=1000, w=2000) they have similar throughput. Note that partitioning is used at w=1000 and 2000 but not at w=100.</li>
</ul>
<div><b>Builds, configuration and hardware</b></div>
<div>
<div></div>
<div>I compiled Postgres versions from source: 12.22, 13.23, 14.20, 15.15, 16.11, 17.7 and 18.1.</div>
<div></div>
<div>I compiled MySQL versions from source: 5.6.51, 5.7.44, 8.0.44, 8.4.7, 9.4.0 and 9.5.0.</div>
<div>The server is an ASUS ExpertCenter PN53 with an AMD Ryzen 7 7735HS CPU, 8 cores, SMT disabled, and 32G of RAM. Storage is one NVMe device for the database using ext-4 with discard enabled. The OS is Ubuntu 24.04. More details on it&nbsp;<a href="ASUS%20ExpertCenter%20PN53%20with%20AMD%20Ryzen%207%207735HS,%2032G%20RAM%20and%202%20m.2%20slots%20(one%20for%20OS%20install,%20one%20for%20DB%20perf%20tests)">are here</a>.</div>
<div></div>
<div>
<div>For versions prior to 18, the config file is named conf.diff.cx10a_c8r32 and they are as similar as possible and here for versions&nbsp;<a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c8r32/pg1219_o2nofp/conf.diff.cx10a_c8r32">12</a>,&nbsp;<a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c8r32/pg1315_o2nofp/conf.diff.cx10a_c8r32">13</a>,&nbsp;<a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c8r32/pg1412_o2nofp/conf.diff.cx10a_c8r32">14</a>,&nbsp;<a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c8r32/pg157_o2nofp/conf.diff.cx10a_c8r32">15</a>,&nbsp;<a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c8r32/pg163_o2nofp/conf.diff.cx10a_c8r32">16</a>&nbsp;and&nbsp;<a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c8r32/pg172_o2nofp/conf.diff.cx10a_c8r32">17</a>.</div>

<div>For Postgres 18 the config file is named&nbsp;<a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/may25.pg18/pn53/conf.diff.cx10b_c8r32">conf.diff.cx10b_c8r32</a>&nbsp;and adds io_mod=&rsquo;sync&rsquo; which matches behavior in earlier Postgres versions.</div>
</div>
</div>
<div>For MySQL the config files are named my.cnf.cz12a_c8r32 and are here:&nbsp;<a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c8r32/my5651_rel_o2nofp/etc/my.cnf.cz12a_c8r32">5.6.51</a>,&nbsp;<a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c8r32/my5744_rel_o2nofp/etc/my.cnf.cz12a_c8r32">5.7.44</a>,&nbsp;<a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c8r32/my8040_rel_o2nofp/etc/my.cnf.cz12a_c8r32">8.0.4x</a>,&nbsp;<a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c8r32/my8406_rel_o2nofp/etc/my.cnf.cz12a_c8r32">8.4.x</a>,&nbsp;<a href="https://github.com/mdcallag/mytools/blob/master/bench/conf/arc/oct24/c8r32/my9400_rel_o2nofp/etc/my.cnf.cz12a_c8r32">9.x.0</a>.</div>
<div></div>
<div>For both Postgres and MySQL fsync on commit is disabled to avoid turning this into an fsync benchmark. The server has an SSD with <a href="https://smalldatum.blogspot.com/2026/01/ssds-power-loss-protection-and-fsync.html">high fsync latency</a>.</div>
<div></div>
<div>
<div><b>Benchmark</b>
<div></div>
</div>
<div></div>
<div>The benchmark is&nbsp;<a href="https://www.hammerdb.com/docs/ch03.html">tproc-c</a>&nbsp;from&nbsp;<a href="https://www.hammerdb.com/">HammerDB</a>. The tproc-c benchmark is derived from TPC-C.
<p>The benchmark was run for several workloads:</p></div>
<div>
<ul>
<li>vu=1, w=100 &ndash; 1 virtual user, 100 warehouses</li>
<li>vu=6, w=100 &ndash; 6 virtual users, 100 warehouses</li>
<li>vu=1, w=1000 &ndash; 1 virtual user, 1000 warehouses</li>
<li>vu=6, w=1000 &ndash; 6 virtual users, 1000 warehouses</li>
<li>vu=1, w=2000 &ndash; 1 virtual user, 2000 warehouses</li>
<li>vu=6, w=2000 &ndash; 6 virtual users, 2000 warehouses</li>
</ul>
<div>The w=100 workloads are less heavy on IO. The w=1000 and w=2000 workloads are more heavy on IO.</div>
<div></div>
<div>The benchmark for Postgres is run by&nbsp;<a href="https://github.com/mdcallag/mytools/blob/master/bench/arc/jan26.tprocc.pn53.pg/allpg.N.sh">this script</a>&nbsp;which depends on&nbsp;<a href="https://github.com/mdcallag/mytools/tree/master/bench/arc/jan26.tprocc.pn53.pg/testscripts">scripts here</a>. The MySQL scripts are similar.</div>
<div>
<ul>
<li>stored procedures are enabled</li>
<li>partitioning is used for when the warehouse count is &gt;= 1000</li>
<li>a 5 minute rampup is used</li>
<li>then performance is measured for 120 minutes</li>
</ul>
<div>
<div>
<div><b>Results</b></div>
<div><b><br></b></div>
<div>My analysis at this point is simple &mdash; I only consider average throughput. Eventually I will examine throughput over time and efficiency (CPU and IO).</div>
<div></div>
<div>On the charts that follow y-axis does not start at 0 to improve readability at the risk of overstating the differences. The y-axis shows relative throughput. There might be a regression when the relative throughput is less than 1.0. There might be an improvement when it is &gt; 1.0. The relative throughput is:</div>
</div>
<blockquote style="border: none;margin: 0px 0px 0px 40px;padding: 0px">
<div>(NOPM for <i>some-version</i> / NOPM for <i>base-version</i>)</div>
</blockquote>
<p>I provide three charts below:</p>

<ul style="text-align: left">
<li>only MySQL &ndash; <i>base-version</i> is MySQL 5.6.51</li>
<li>only Postgres &ndash; <i>base-version</i> is Postgres 12.22</li>
<li>Postgres vs MySQL &ndash; <i>base-version</i> is Postgres 18.1, <i>some-version</i> is MySQL 8.4.7</li>
</ul>
<p><b>Results: MySQL 5.6 to 8.4</b></p>
<p>Legend:</p>
<ul style="text-align: left">
<li>my5651.z12a is MySQL 5.6.51 with the z12a_c8r32 config</li>
<li>my5744.z12a is MySQL 5.7.44 with the z12a_c8r32 config</li>
<li>my8044.z12a is MySQL 8.0.44 with the z12a_c8r32 config</li>
<li>my847.z12a is MySQL 8.4.7 with the z12a_c8r32 config</li>
<li>my9400.z12a is MySQL 9.4.0 with the z12a_c8r32 config</li>
<li>my9500.z12a is MySQL 9.5.0 with the z12a_c8r32 config</li>
</ul>
<p>Summary</p>

<ul style="text-align: left">
<li>Perf regressions in MySQL 8.4 are smaller with vu=6 and wh &gt;= 1000 &mdash; the cases where there is more concurrency (vu=6) and the workload does more IO per transaction (wh=1000 &amp; 2000). Note that partitioning is used at w=1000 and 2000 but not at w=100.</li>
<li>Perf regressions in MySQL 8.4 are larger with vu=1 and even more so with wh=100 (low concurrency, less IO per transaction).</li>
<li>Performance has mostly been dropping from MySQL 5.6 to 8.4. From other benchmarks the problem is from new CPU overheads at low concurrency.</li>
<li>While perf regressions in modern MySQL at high concurrency have been less of a problem on other benchmarks, this server is too small to support high concurrency.</li>
</ul>
<div class="separator" style="clear: both;text-align: center"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3v7KO4aXH14bKepqIV2jHTNdWhyFeHWrDmvN0i8mVil_r7YXgbCHikwHqx5T8GgSBMZncljFRYbjgdItpBBmedaB49CgT07OyUNVTy8NEiR7qWHpJWc6T3Gvv0V2dThGNKeVs6sCZqGoVlibXmsuF3lqacLKndVFXIay2ggT0-NJLR2QnhKj4fV25tL27/s860/Relative%20NOPM%20on%20a%20small%20server_%20MySQL.png" style="margin-left: 1em;margin-right: 1em"><img decoding="async" loading="lazy" border="0" data-original-height="531" data-original-width="860" height="396" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3v7KO4aXH14bKepqIV2jHTNdWhyFeHWrDmvN0i8mVil_r7YXgbCHikwHqx5T8GgSBMZncljFRYbjgdItpBBmedaB49CgT07OyUNVTy8NEiR7qWHpJWc6T3Gvv0V2dThGNKeVs6sCZqGoVlibXmsuF3lqacLKndVFXIay2ggT0-NJLR2QnhKj4fV25tL27/w640-h396/Relative%20NOPM%20on%20a%20small%20server_%20MySQL.png" width="640"></a></div>
<p><b>Results: Postgres 12 to 18</b></p>
<p>Legend:</p>
<ul style="text-align: left">
<li>pg1222.x10a is Postgres 12.22 with the x10a_c8r32 config</li>
<li>pg1323.x10a is Postgres 13.23 with the x10a_c8r32 config</li>
<li>pg1420.x10a is Postgres 14.20 with the x10a_c8r32 config</li>
<li>pg1515.x10a is Postgres 15.15 with the x10a_c8r32 config</li>
<li>pg1611.x10a is Postgres 16.11 with the x10a_c8r32 config</li>
<li>pg177.x10a is Postgres 17.7 with the x10a_c8r32 config</li>
<li>pg181.x10b is Postgres 18.1 with the x10b_c8r32 config</li>
</ul>
<p>Summary</p>

<ul style="text-align: left">
<li>Modern Postgres is faster than old Postgres</li>
</ul>
<div class="separator" style="clear: both;text-align: center"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6iVfGcIfMb7MmSi_FIUQUbGoCLxPPjPiej5x8E_BlZisTKcZc0GE-0Snt0Gy-U4tTzGCkxq__UOXF194Ol3FUGLm82QjyV1xwTPuByz0LDm_xOT49GMZZrcOe1omfFv1uO3i06JJPyhgjSg2hyphenhyphenTsBoQpCyGAGIP1fbX1dwshWwLht6OlZGB1RsA0HCJSf/s787/Relative%20NOPM%20on%20a%20small%20server_%20Postgres.png" style="margin-left: 1em;margin-right: 1em"><img decoding="async" loading="lazy" border="0" data-original-height="487" data-original-width="787" height="396" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6iVfGcIfMb7MmSi_FIUQUbGoCLxPPjPiej5x8E_BlZisTKcZc0GE-0Snt0Gy-U4tTzGCkxq__UOXF194Ol3FUGLm82QjyV1xwTPuByz0LDm_xOT49GMZZrcOe1omfFv1uO3i06JJPyhgjSg2hyphenhyphenTsBoQpCyGAGIP1fbX1dwshWwLht6OlZGB1RsA0HCJSf/w640-h396/Relative%20NOPM%20on%20a%20small%20server_%20Postgres.png" width="640"></a></div>
<p></p>
<p></p>
<p><b>Results: MySQL vs Postgres</b></p>
<p>Legend:</p>
<ul>
<li>pg181.x10b is Postgres 18.1 with the x10b_c8r32 config</li>
<li>my847.z12a is MySQL 8.4.7 with the z12a_c8r32 config</li>
</ul>
<p>Summary</p>

<ul style="text-align: left">
<li>MySQL and Postgres have similar throughput for vu=6 at w=1000 and 2000. Note that partitioning is used at w=1000 and 2000 but not at w=100.</li>
<li>Otherwise Postgres is 2X faster than MySQL</li>
</ul>
<div class="separator" style="clear: both;text-align: center"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXx-zehZQnEI4NSQs_PCSLDVXPBRs49CC6-WGR0508uVzPOel8H59hqwP8AkDWxYG3d8nCujjz52qXdnxyMFLzcWIgJLG1GXeOMwQeMzy1z0tY7QrBo9uUgsvlfui_Iiu8b0KIwxf5OFClI22ZBpfCMXpzPnayphEShwgW9mnMGRNGMI297e2wH8uYe7WI/s600/Relative%20NOPM%20on%20a%20small%20server_%20Postgres%2018.1%20vs%20MySQL%208.4.7.png" style="margin-left: 1em;margin-right: 1em"><img decoding="async" loading="lazy" border="0" data-original-height="371" data-original-width="600" height="396" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXx-zehZQnEI4NSQs_PCSLDVXPBRs49CC6-WGR0508uVzPOel8H59hqwP8AkDWxYG3d8nCujjz52qXdnxyMFLzcWIgJLG1GXeOMwQeMzy1z0tY7QrBo9uUgsvlfui_Iiu8b0KIwxf5OFClI22ZBpfCMXpzPnayphEShwgW9mnMGRNGMI297e2wH8uYe7WI/w640-h396/Relative%20NOPM%20on%20a%20small%20server_%20Postgres%2018.1%20vs%20MySQL%208.4.7.png" width="640"></a></div>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p>&nbsp;</p>
</div>
</div>
</div>
</div>

<p>The post <a rel="nofollow" href="https://smalldatum.blogspot.com/2026/02/hammerdb-tproc-c-on-small-server.html">HammerDB tproc-c on a small server, Postgres and MySQL</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>MariaDB 12.3.1 and 12.2.2 now available</title>
      <link>https://mariadb.org/mariadb-12-3-1-and-12-2-2-now-available/</link>
      <pubDate>Fri, 13 Feb 2026 16:15:32 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>The MariaDB Foundation is pleased to announce the availability of MariaDB 12.3.1, the release candidate (RC) in the new long-term support (LTS) release, and MariaDB 12.2.2, the latest stable rolling release. …<br />
Continue reading \"MariaDB 12.3.1 and 12.2.2 now available\"<br />
The post MariaDB 12.3.1 and 12.2.2 now available appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-12-3-1-and-12-2-2-now-available/">MariaDB 12.3.1 and 12.2.2 now available</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>The MariaDB Foundation is pleased to announce the availability of <a href="https://mariadb.com/docs/release-notes/community-server/12.3/12.3.1">MariaDB 12.3.1</a>, the release candidate (RC) in the new <a href="https://mariadb.org/about/#maintenance-policy">long-term support (LTS) release</a>, and <a href="https://mariadb.com/docs/release-notes/community-server/12.2/12.2.2">MariaDB 12.2.2</a>, the latest stable rolling release. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/mariadb-12-3-1-and-12-2-2-now-available/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;MariaDB 12.3.1 and 12.2.2 now available&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-12-3-1-and-12-2-2-now-available/">MariaDB 12.3.1 and 12.2.2 now available</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-12-3-1-and-12-2-2-now-available/">MariaDB 12.3.1 and 12.2.2 now available</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>ClickHouse storage architecture and optimization</title>
      <link>https://severalnines.com/blog/clickhouse-storage-architecture-and-optimization/</link>
      <pubDate>Fri, 13 Feb 2026 08:00:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://severalnines.com/">Severalnines</source>
      <description><![CDATA[<p>In analytical databases, storage is the dominant factor behind performance, stability, and cost. ClickHouse is designed to scan large volumes of data efficiently, but how well it performs in production depends heavily on storage layout, disk throughput, and merge behavior.  For operations and support engineers, storage decisions determine whether ClickHouse is predictable and cost-efficient or […]<br />
The post ClickHouse storage architecture and optimization appeared first on Severalnines.</p>
<p>The post <a rel="nofollow" href="https://severalnines.com/blog/clickhouse-storage-architecture-and-optimization/">ClickHouse storage architecture and optimization</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>In analytical databases, storage is the dominant factor behind performance, stability, and cost. ClickHouse is designed to scan large volumes of data efficiently, but how well it performs in production depends heavily on storage layout, disk throughput, and merge behavior.&nbsp;</p>
<p>For operations and support engineers, storage decisions determine whether ClickHouse is predictable and cost-efficient or fragile under load.</p>
<p>This blog post focuses on ClickHouse storage architecture and optimization in production, with particular attention to hybrid on-prem and cloud deployments.</p>
<h2 class="wp-block-heading" id="h-storage-types-supported-by-clickhouse">Storage types supported by ClickHouse<a class="anchor-link" id="storage-types-supported-by-clickhouse"></a></h2>
<p>ClickHouse supports multiple storage backends, allowing flexible deployment across bare metal, virtualized environments, and cloud platforms. Since ClickHouse supports many types of table engines, we are going to focus on MergeTree-family table engines, which is the core data storage technology behind ClickHouse.</p>
<p>Base MergeTree table engine can be considered the default table engine for single-node ClickHouse instances because it is versatile and practical for a wide range of use cases. For production usage, ReplicatedMergeTree is the way to go, because it adds high-availability to all features of regular MergeTree engines. A bonus is automatic data deduplication on data ingestion, so the software can safely retry if there was some network issue during insert.&nbsp;</p>
<p>If you are wondering why is it called &ldquo;MergeTree&rdquo;? Because it stores data in sorted parts and periodically merges them in the background and is optimized for analytical workloads. The following are the supported storage type for the MergeTree table engines:</p>
<ul class="wp-block-list">
<li>Local disks</li>
<li>Network disks</li>
<li>Memory</li>
<li>Object storage</li>
</ul>
<h3 class="wp-block-heading" id="h-local-disks">Local disks<a class="anchor-link" id="local-disks"></a></h3>
<p>ClickHouse&rsquo;s local disk architecture is designed around fast, reliable block storage, with SSD or NVMe disks as the preferred foundation for production deployments. Data is written in a column-oriented format and grouped into immutable parts, which are stored sequentially on disk and later merged in the background. This access pattern minimizes random I/O and aligns well with modern SSDs. In a simple standalone setup, ClickHouse may run on a single server with one or more local disks mounted under /var/lib/clickhouse, often using a single high-capacity NVMe device to store all table data and metadata.</p>
<p>In more robust on-premises environments, local disks are often combined using RAID or JBOD depending on operational priorities. RAID 10 across multiple SSDs or NVMe drives is a common choice for production systems, providing both high throughput and resilience to disk failures. RAID 0 is sometimes used to maximize performance, but only when ClickHouse replication is in place to mitigate the risk of data loss. Alternatively, JBOD (Just a Bunch of Disks) can be used to expose individual disks directly to the filesystem, allowing ClickHouse to consume capacity without RAID overhead, but at the cost of higher operational complexity and less predictable failure handling.</p>
<p>We highly recommend reading the <a href="https://clickhouse.com/docs/operations/tips#storage-subsystem">Storage Subsystem tips</a> before installing ClickHouse.</p>
<h3 class="wp-block-heading" id="h-network-disks">Network disks<a class="anchor-link" id="network-disks"></a></h3>
<p>From a ClickHouse perspective, any &ldquo;network disk&rdquo; exposed as a standard filesystem by the operating system can be used and configured via storage_configuration, similar to local disks. However, &ldquo;usability&rdquo; does not imply &ldquo;recommendation&rdquo;.</p>
<p>Network-mounted filesystems (like NFS, SMB, and GlusterFS) are considered risky for ClickHouse. This is because ClickHouse relies heavily on features like atomic renames, reliable fsync, and predictable latency, especially during merge operations. These network filesystems often fail to provide reliable atomic renaming guarantees and consistent locking, and they are prone to latency spikes. Such issues can lead to merge stalls, unpredictable query latency, replica desynchronization, and, in the worst cases, data corruption.</p>
<p>Conversely, network block storage solutions are generally safe and production-proven. These include technologies like iSCSI, Fibre Channel, NVMe-oF, or cloud-specific block-storage services like AWS EBS, GCP Persistent Disk, and Azure Managed Disks. They are safe because they present themselves to the OS as direct, raw, local block devices. This setup provides highly predictable latency and correct fsync semantics, making it the most common and robust &ldquo;network disk&rdquo; configuration in cloud ClickHouse deployments.</p>
<h3 class="wp-block-heading" id="h-memory">Memory<a class="anchor-link" id="memory"></a></h3>
<p>ClickHouse does support memory-based storage through engines such as <strong>Memory</strong> and <strong>Buffer</strong>, but this support is limited and not designed to replace disk-based storage. A Memory table keeps all data in RAM, offers no persistence, no replication, no compression, and loses all data on restart.&nbsp;</p>
<p>The Buffer engine is often misunderstood as storage, but in practice it is an ingestion optimization layer that temporarily holds data in memory before flushing it to a disk-backed table. Neither engine participates in the MergeTree architecture that underpins ClickHouse&rsquo;s scalability and reliability.</p>
<p>Because ClickHouse is optimized for sequential disk access, compression, and background merges, using memory as primary storage works against its design. Memory tables do not benefit from merges, TTLs, or deduplication, and they scale poorly as data volume grows. In production, they introduce operational risk: data loss on restart, unpredictable memory pressure, and lack of high availability. In most real-world cases, fast local SSDs combined with proper schema design and the OS page cache deliver near-memory performance without sacrificing durability.</p>
<p>With that being said, memory-based tables do have valid, narrowly scoped use cases. They are appropriate for temporary or session-level data, small lookup or dimension tables, and controlled query acceleration scenarios where the dataset is bounded and data loss is acceptable.&nbsp;</p>
<p>For anything that represents core business data like events, logs, metrics, or analytical facts, the disk-backed MergeTree tables remain the correct default. From an operations perspective, memory storage in ClickHouse should be treated as ephemeral infrastructure, not a foundation for persistent analytics.</p>
<p>The following is an example of creating a memory table utilizing minimum of 4 KB and maximum of 16KB of RAM:</p>
<pre class="wp-block-code"><code>CREATE TABLE memory (i UInt32) 
ENGINE = Memory 
SETTINGS min_bytes_to_keep = 4096, max_bytes_to_keep = 16384;</code></pre>
<h3 class="wp-block-heading">Object Storage<a class="anchor-link" id="object-storage"></a></h3>
<p>ClickHouse has native support for S3-compatible object storage and makes it practical to store cold data outside local disks. Typical use cases are:</p>
<ul class="wp-block-list">
<li>Long-term data retention storage</li>
<li>Cost optimization</li>
<li>Elastic scaling without disk re-provisioning</li>
</ul>
<p>Object storage can be configured as a disk in ClickHouse and combined with storage policies to tier data, typically keeping hot data on local disks while moving colder data to object storage via TTL rules. From ClickHouse&rsquo;s perspective, object storage is treated as an external disk, with local storage still required for metadata and coordination.</p>
<p>In addition to leveraging major object storage providers (such as Amazon S3, Azure, Google Cloud, or Digital Ocean), organizations can deploy self-hosted solutions like SeaweedFS or Garage. Scaling these self-hosted object stores is straightforward, typically involving the addition of more servers.</p>
<p>However, utilizing object storage introduces operational trade-offs compared to local disks. While object storage offers compelling advantages like lower cost and virtually limitless capacity, these benefits are balanced by drawbacks, including higher latency, variable throughput, and a strong dependency on network stability.<br>The following is example of the storage configuration inside /etc/clickhouse-server/config.xml:</p>
<pre class="wp-block-code"><code>&lt;storage_configuration&gt;
  &lt;disks&gt;
    &lt;s3_disk1&gt;
      &lt;type&gt;object_storage&lt;/type&gt;
      &lt;object_storage_type&gt;s3&lt;/object_storage_type&gt;
      &lt;endpoint&gt;https://s3.amazonaws.com&lt;/endpoint&gt;
      &lt;bucket&gt;clickhouse-data&lt;/bucket&gt;
      &lt;access_key_id&gt;ACCESS_KEY&lt;/access_key_id&gt;
      &lt;secret_access_key&gt;SECRET_KEY&lt;/secret_access_key&gt;
      &lt;metadata_path&gt;/var/lib/clickhouse/disks/s3/&lt;/metadata_path&gt;
    &lt;/s3_disk1&gt;
  &lt;/disks&gt;
  &lt;policies&gt;
    &lt;s3_main&gt;
      &lt;volumes&gt;
        &lt;main&gt;
          &lt;disk&gt;s3_disk1&lt;/disk&gt;
        &lt;/main&gt;
      &lt;/volumes&gt;
    &lt;/s3_main&gt;
  &lt;/policies&gt;
&lt;/storage_configuration&gt;</code></pre>
<p>We can then use the S3 bucket as data storage by referencing the storage policy for a table:</p>
<pre class="wp-block-code"><code>CREATE TABLE s3_table1 (`id` UInt64, `column1` String) 
ENGINE = MergeTree
ORDER BY id
SETTINGS storage_policy = 's3_main';</code></pre>
<h2 class="wp-block-heading" id="h-clickhouse-storage-in-hybrid-environments">ClickHouse storage in hybrid  environments<a class="anchor-link" id="clickhouse-storage-in-hybrid-environments"></a></h2>
<p>MergeTree table engines can distribute data across several block devices, which is highly beneficial for implementing implicit &ldquo;hot&rdquo; and &ldquo;cold&rdquo; data tiers. Specifically, high-speed storage, such as NVMe SSDs or in-memory storage, can house the relatively small volume of frequently accessed, recent (&ldquo;hot&rdquo;) data. In contrast, the large historical volumes of rarely accessed (&ldquo;cold&rdquo;) data can be relegated to slower, more cost-effective media like HDDs or object storage.</p>
<p>This configuration can be achieved by using the storage policy. A storage policy can be defined globally, or per table settings when creating a table. Let&rsquo;s put it into an example. We can configure our storage to be as below:</p>
<pre class="wp-block-code"><code>&lt;storage_configuration&gt;
  &lt;disks&gt;
    &lt;s3_disk&gt;
      &lt;type&gt;object_storage&lt;/type&gt;
      &lt;object_storage_type&gt;s3&lt;/object_storage_type&gt;
      &lt;endpoint&gt;https://s3.amazonaws.com&lt;/endpoint&gt;
      &lt;bucket&gt;clickhouse-data&lt;/bucket&gt;
      &lt;access_key_id&gt;ACCESS_KEY&lt;/access_key_id&gt;
      &lt;secret_access_key&gt;SECRET_KEY&lt;/secret_access_key&gt;
      &lt;metadata_path&gt;/var/lib/clickhouse/disks/s3/&lt;/metadata_path&gt;
    &lt;/s3_disk&gt;
  &lt;/disks&gt;

  &lt;policies&gt;
    &lt;hybrid_policy&gt;
      &lt;volumes&gt;
        &lt;hot&gt;
          &lt;disk&gt;default&lt;/disk&gt;
        &lt;/hot&gt;
        &lt;cold&gt;
          &lt;disk&gt;s3_disk&lt;/disk&gt;
        &lt;/cold&gt;
      &lt;/volumes&gt;
    &lt;/hybrid_policy&gt;
  &lt;/policies&gt;
&lt;/storage_configuration&gt;</code></pre>
<p>The hot data will be located on the default disk, which in this case is our local file system located at /var/lib/clickhouse/data/store and once it is full, the data will be moved to our S3 bucket called &ldquo;clickhouse-data&rdquo;.&nbsp;</p>
<p>Even when data is stored in S3, local disk space is still essential for reliable metadata management and merge coordination. <strong>A common mistake is under-provisioning local disks </strong>because &ldquo;most data lives in S3&rdquo;. When merges or TTL moves run, the local disk fills up and the system degrades. It is recommended to always have 30-50% local disk free space and perform inserts in batches to avoid small parts.</p>
<h2 class="wp-block-heading" id="h-performance-optimization-for-clickhouse-s-storage-layer">Performance optimization for ClickHouse&rsquo;s storage layer<a class="anchor-link" id="performance-optimization-for-clickhouses-storage-layer"></a></h2>
<p>The performance of ClickHouse&rsquo;s storage layer heavily depends on the physical arrangement and compression of data on the disk. Partitioning is the most critical element and must be set up correctly. Partitions should be large-grained, for instance, monthly divisions based on a date column. This approach minimizes the total number of partitions, thereby reducing the overhead associated with metadata and the filesystem.&nbsp;</p>
<p>Conversely, partitioning using values with high uniqueness (high-cardinality), such as a user ID or request ID, should be avoided because it generates too many small partitions and increases the pressure on the merge process. For example, partitioning a log table by <code>toYYYYMM(event_date)</code> (monthly partitions) is generally better than partitioning by <code>user_id</code>, which would create millions of tiny partitions and slow down overall performance.</p>
<p>Compression and indexing further enhance the efficiency of data reads within these partitions. ClickHouse compresses data on a per-column basis. Utilizing stronger codecs like ZSTD for data that is accessed less often (&ldquo;colder&rdquo; data) can substantially decrease disk usage and I/O operations, though it uses slightly more CPU resources.&nbsp;</p>
<p>For time-series data, specialized codecs such as Delta, DoubleDelta, or Gorilla can achieve even better compression and cache utilization. Data skipping indexes including <code>minmax</code>, <code>set</code>, and <code>bloom_filter</code>, providing an additional layer of optimization. They enable ClickHouse to bypass entire ranges of data during query execution. These indexes are particularly beneficial for log and observability workloads, where queries often filter on specific fields or tags that are not part of the primary sorting key (<code>ORDER BY</code>), which reduces unnecessary disk reads and improves query response time.</p>
<h2 class="wp-block-heading" id="h-avoiding-common-clickhouse-storage-operations-pitfalls">Avoiding common ClickHouse storage operations pitfalls<a class="anchor-link" id="avoiding-common-clickhouse-storage-operations-pitfalls"></a></h2>
<h3 class="wp-block-heading" id="h-frequent-updates-and-deletes-mutations">Frequent updates and deletes (mutations)<a class="anchor-link" id="frequent-updates-and-deletes-mutations"></a></h3>
<ul class="wp-block-list">
<li><strong>Description:</strong> Applying frequent <code>UPDATE</code> or <code>DELETE</code> operations triggers internal &ldquo;mutations.&rdquo; These mutations are costly, as they involve rewriting the entire affected data part in the background.</li>
<li><strong>Impact:</strong> In busy, write-heavy systems, excessive mutations can overload the merge process. This leads to long queues, delayed visibility of data changes, and temporary increases in disk usage as old and new data versions coexist.</li>
<li><strong>Recommendation:</strong> Treat mutations as exceptional administrative tasks, not a standard mechanism for routine data modification.</li>
<li><strong>Example:</strong> Instead of an hourly <code>UPDATE</code> <code>employees SET salary = new_salary WHERE id = 123;</code>, adopt a pattern where the new salary record is appended with a timestamp, and queries select only the latest record for that employee ID.</li>
</ul>
<h3 class="wp-block-heading" id="h-schema-and-ingestion-suboptimization">Schema and ingestion suboptimization<a class="anchor-link" id="schema-and-ingestion-suboptimization"></a></h3>
<ul class="wp-block-list">
<li><strong>Overuse of <code>Nullable</code> Columns:</strong>
<ul class="wp-block-list">
<li><strong>Description:</strong> Using <code>Nullable</code> data types adds complexity to storage and query execution.</li>
<li><strong>Impact:</strong> This increases storage overhead and requires additional checks during query execution, reducing CPU efficiency and compression effectiveness.</li>
<li><strong>Example:</strong> Unless data sparsity is extremely high or a <code>NULL</code> value is semantically necessary, prefer non-nullable columns and use a default value (e.g., 0, an empty string, or a specific placeholder) if a value is missing.</li>
</ul>
</li>
<li><strong>Very Small, Frequent Inserts:</strong>
<ul class="wp-block-list">
<li><strong>Description:</strong> Each small <code>INSERT</code> query creates a new, tiny data part.</li>
<li><strong>Impact:</strong> Thousands of small parts dramatically increase the load on the merge subsystem and stress disk I/O. This backlog causes unpredictable query latency and can eventually stall new inserts if disk resources are exhausted.</li>
<li><strong>Recommendation:</strong> Always batch your inserts into larger transactions.</li>
<li><strong>Example:</strong> Instead of inserting one event at a time (e.g., one <code>INSERT</code> per second), buffer events and perform a single batch insert every 5-10 seconds containing thousands of records.</li>
</ul>
</li>
</ul>
<h2 class="wp-block-heading" id="h-using-disk-io-and-object-storage-latency-monitoring-in-clickhouse">Using disk IO and object storage latency monitoring in ClickHouse<a class="anchor-link" id="using-disk-io-and-object-storage-latency-monitoring-in-clickhouse"></a></h2>
<p>In ClickHouse, problems related to storage rarely manifest as clear-cut error messages; instead, they often surface indirectly through a decline in query performance or unreliable data ingestion. For instance, if users start experiencing slow queries that take 15 seconds instead of the usual 3 seconds, or intermittent timeouts during peak hours, the root cause is frequently disk or storage pressure rather than a poorly written <code>SELECT</code> statement.</p>
<p>Therefore, continuous monitoring of the storage layer is crucial for early detection. Key metrics to track include disk read and write throughput. If, for example, your disk&rsquo;s write throughput suddenly jumps from a typical 50 MB/s to over 200 MB/s during a large batch insert, it signals potential saturation or an abnormal spike that needs investigation, especially during heavy merge activity.</p>
<p>Beyond simple I/O, ClickHouse-specific metrics provide vital context. The number of active parts and the size of the merge backlog are strong indicators of storage health. If the part count for a table rapidly increases from 1,000 to 5,000 over a few hours, or if the system reports a merge backlog of &ldquo;100 merges pending&rdquo;, it indicates that the system is struggling to consolidate incoming data. These conditions increase disk I/O (e.g., more random reads/writes), increase temporary disk space usage (e.g., temporary merge files consuming an extra 500GB), and make query response times unpredictable. Monitoring these allows teams to distinguish between normal background merges and situations where the system is genuinely falling behind, requiring intervention, like increasing disk speed or adjusting merge settings.</p>
<p>For cloud deployments using object storage (like S3 or Azure Blob Storage), extra layers of monitoring are necessary. Metrics like object storage latency (e.g., reads taking 500ms instead of 50ms), request error rates (e.g., seeing 5xx errors from the storage service), and throttling events can directly slow down queries and background tasks like TTL moves. Even when data is remote, issues may appear as stalled merges or slow queries because ClickHouse still relies on local disks for coordination. Therefore, correlating all metrics from local disk I/O, local CPU, to remote object storage metrics is essential. When queries slow down, such as a report query that usually runs in 10 seconds now taking 30, the culprit is frequently a bottleneck in storage, not the query logic itself.</p>
<h2 class="wp-block-heading" id="h-clickhouse-storage-operations-best-practices-for-support-teams">ClickHouse storage operations best practices for support teams<a class="anchor-link" id="clickhouse-storage-operations-best-practices-for-support-teams"></a></h2>
<p>For support teams operating ClickHouse in production, proactive storage monitoring and alerting are essential to prevent performance degradation and outages. A minimum alerting baseline should include:</p>
<ul class="wp-block-list">
<li>Set alerts to catch capacity issues early, e.g., warning at 20% free space and critical at 10% free space remaining. This is vital given the temporary space required during merges.</li>
<li>Monitor the <code>system.merges</code> table and alert if the number of pending merges exceeds a predefined threshold (e.g., &gt; 100 merges pending for more than 15 minutes). This identifies when background merges are falling behind ingestion.</li>
<li>Alerts on <code>INSERT</code> queries failing with errors related to &ldquo;disk full&rdquo; or &ldquo;no space left on device&rdquo; are often a late warning sign that requires immediate action.</li>
<li>In environments using S3 or similar object storage, abnormal latency (e.g., PUT requests &gt; 500ms) or an increased error rate (e.g., &gt; 0.5% 5xx errors) should also trigger alerts, as they can indirectly stall queries and background operations.</li>
</ul>
<p>In day-to-day operations, ClickHouse&rsquo;s system tables are indispensable for diagnosing storage-related issues. Support engineers should regularly inspect these tables:</p>
<ul class="wp-block-list">
<li><code>system.parts</code>: Provides visibility into data part counts and sizes, helping to identify tables with excessive small parts that could lead to poor query performance (e.g., <code>SELECT table</code>, <code>count() FROM system.parts GROUP BY table HAVING count() &gt; 1000</code>).</li>
<li><code>system.merges</code>: Exposes active and pending merge operations, useful for diagnosing a backlog (e.g., <code>SELECT * FROM system.merges WHERE is_scheduled = 1 ORDER BY merge_time</code>).</li>
<li><code>system.disks</code>: Shows disk usage and available space across configured volumes, allowing engineers to correlate free space alerts with the actual physical location (e.g., <code>SELECT name, path, formatReadableSize(free_space) AS free, formatReadableSize(total_space) AS total FROM system.disks</code>).</li>
</ul>
<p>Regularly inspecting these tables allows support engineers to correlate alerts with actual internal state, distinguish transient spikes from structural problems, and take informed corrective actions before storage issues escalate into user-facing incidents.</p>
<h2 class="wp-block-heading" id="h-the-essential-role-of-routine-storage-maintenance-in-clickhouse">The essential role of routine storage maintenance in ClickHouse<a class="anchor-link" id="the-essential-role-of-routine-storage-maintenance-in-clickhouse"></a></h2>
<p>Routine maintenance is a critical but often overlooked aspect of operating ClickHouse at scale. For instance, regularly reviewing part counts helps identify unhealthy ingestion patterns, such as a high volume of <code>INSERT INTO</code> statements generating thousands of tiny data parts, which increases merge pressure. Unused tables and obsolete partitions should be dropped proactively, like removing a staging table that&rsquo;s no longer needed or dropping a partition for data older than the legally required retention period.&nbsp;</p>
<p>This not only reclaims disk space but also reduces metadata overhead and background maintenance work. Validating TTL (Time-To-Live) and tiering policies ensures that data is aging out or moving between storage tiers as intended, for example, confirming that data older than 90 days is successfully compressed and moved from fast NVMe storage to a slower, cheaper HDD-based volume. This prevents silent growth of hot storage or unexpected retention costs.</p>
<p>When storage usage or workload characteristics change, rebalancing storage volumes may also be necessary. This can involve redistributing data across disks, such as using the<strong> <code>ALTER TABLE ... MOVE PARTITION</code> </strong>command, adjusting storage policies, or revisiting which data belongs on fast local storage versus slower or cheaper tiers. Without this periodic upkeep, ClickHouse systems tend to degrade slowly: merges take longer, queries become less predictable, and resource usage creeps upward without a single clear failure point. Consistent maintenance allows support teams to keep performance stable and avoid the cumulative effects of neglect that are far harder to correct under pressure.</p>
<h2 class="wp-block-heading" id="h-hybrid-and-cloud-specific-considerations-for-optimal-storage-health-and-performance">Hybrid and cloud-specific considerations for optimal storage health and performance<a class="anchor-link" id="hybrid-and-cloud-specific-considerations-for-optimal-storage-health-and-performance"></a></h2>
<p>In hybrid and cloud environments, storage optimization is a dual concern: managing both performance and controlling costs and network traffic. When computing is separate from object storage, for instance, an on-premises ClickHouse cluster accessing data in Amazon S3, query speed, data merges, and movement depend heavily on available bandwidth and network latency.</p>
<h3 class="wp-block-heading" id="h-network-latency">Network latency<a class="anchor-link" id="network-latency"></a></h3>
<p>If the network link between the ClickHouse compute nodes and the S3 bucket has high latency (e.g., &gt;50ms), a query that needs to read 100 small files might take significantly longer than if the data were on local storage, even if total bandwidth is high.</p>
<p>Support teams must actively monitor sustained and peak bandwidth usage. A constrained network link, like a 1 Gbps connection consistently running at 90% utilization, can create &ldquo;backpressure.&rdquo; This backpressure can manifest as slow-running queries, or, in the worst case, completely stall background operations like data merges or replications.</p>
<h3 class="wp-block-heading" id="h-cost-management">Cost management<a class="anchor-link" id="cost-management"></a></h3>
<p>Cloud egress charges (the cost to move data <em>out</em> of a cloud region) can quickly balloon. If a daily reporting job reads 1TB of data from an S3 bucket in Region A and sends the final report to an application in Region B, this data movement can incur significant egress fees.</p>
<p>Your tiering strategy is equally vital to cost management. Policies for data tiering (e.g., moving old data to cheaper, slower storage tiers via TTLs) should be intentional, based on known access patterns and retention rules, not simply enabled by default. A poorly planned tiering strategy, such as moving frequently-accessed recent data to a low-cost &ldquo;Archive&rdquo; tier, can increase both cloud spending (due to expensive retrieval fees from the archive) and query latency. Conversely, a well-planned policy ensures that rarely-accessed historical data moves to the lowest-cost tier without impacting the performance of active workloads.</p>
<h2 class="wp-block-heading" id="h-a-word-on-storage-for-clickhouse-within-multi-database-stacks">A word on storage for ClickHouse within multi-database stacks<a class="anchor-link" id="a-word-on-storage-for-clickhouse-within-multi-database-stacks"></a></h2>
<p>ClickHouse typically complements systems like PostgreSQL/MySQL for transactional consistency, low-latency lookups, and row updates (e.g, order creation), and Elasticsearch for fast, unstructured data search (e.g, search products), and object storage (S3/GCS) for cost-efficient, durable archiving (e.g, archived data, old logs).&nbsp;</p>
<p>ClickHouse is optimized differently for high-throughput scans, heavy compression, and append-only data ingestion, excelling at analytical queries (e.g., aggregating sales data, sales dashboard) but not transactional guarantees or frequent row-level mutations. Teams should align storage strategies and retention policies across these systems.</p>
<h2 class="wp-block-heading">Conclusion<a class="anchor-link" id="conclusion"></a></h2>
<p>Effective ClickHouse operations depend heavily on thoughtful storage design, as storage choices directly influence system stability, performance, and operational risk. Local disks remain the most predictable option for hot data and latency-sensitive workloads, while object storage offers flexibility and cost advantages for colder data when used appropriately.&nbsp;</p>
<p>Hybrid architectures combine these approaches but require disciplined monitoring and capacity planning to avoid hidden bottlenecks. Ultimately, storage optimization in ClickHouse is not just a performance concern but also a cost-management exercise, especially in cloud and hybrid environments where storage decisions directly impact long-term operational expenses.</p>
<p>The post <a href="https://severalnines.com/blog/clickhouse-storage-architecture-and-optimization/">ClickHouse storage architecture and optimization</a> appeared first on <a href="https://severalnines.com">Severalnines</a>.</p>

<p>The post <a rel="nofollow" href="https://severalnines.com/blog/clickhouse-storage-architecture-and-optimization/">ClickHouse storage architecture and optimization</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Announcing MariaDB Community Server 12.2 GA</title>
      <link>https://mariadb.com/resources/blog/announcing-mariadb-community-server-12-2-ga/</link>
      <pubDate>Thu, 12 Feb 2026 19:52:10 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.com/">MariaDB</source>
      <description><![CDATA[<p>We are excited to announce the General Availability of MariaDB Community Server 12.2. Building upon the major foundational changes introduced […]</p>
<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/announcing-mariadb-community-server-12-2-ga/">Announcing MariaDB Community Server 12.2 GA</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>We are excited to announce the General Availability of MariaDB Community Server 12.2. Building upon the major foundational changes introduced in the MariaDB Community Server 12.1, this version focuses on delivering smarter query optimization, deeper compatibility for migrations, and enhanced metadata visibility, ensuring MariaDB remains the most versatile open-source database for developers and&hellip;</p>
<p><a href="https://mariadb.com/resources/blog/announcing-mariadb-community-server-12-2-ga/" rel="nofollow">Source</a></p>

<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/announcing-mariadb-community-server-12-2-ga/">Announcing MariaDB Community Server 12.2 GA</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>The Concepts of Forking</title>
      <link>https://mariadb.com/resources/blog/the-concepts-of-forking/</link>
      <pubDate>Wed, 11 Feb 2026 17:12:33 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.com/">MariaDB</source>
      <description><![CDATA[<p>Lately there has been a lot of discussion about “hard” or “soft” forks related to MySQL. As someone who has […]</p>
<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/the-concepts-of-forking/">The Concepts of Forking</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>Lately there has been a lot of discussion about &ldquo;hard&rdquo; or &ldquo;soft&rdquo; forks related to MySQL. As someone who has done a successful fork of MySQL, I think this is both confusing and trivialising the concept of forking. In my previous blog, I did touch a bit on this topic, but it looks like some more clarifications are needed. When we did the initial fork of MariaDB from MySQL&hellip;</p>
<p><a href="https://mariadb.com/resources/blog/the-concepts-of-forking/" rel="nofollow">Source</a></p>

<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/the-concepts-of-forking/">The Concepts of Forking</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>What is the quickest way to load data into the database?</title>
      <link>https://www.fromdual.com/blog/load-data-quick-into-the-database/</link>
      <pubDate>Wed, 11 Feb 2026 09:04:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://www.fromdual.com/aggregator/categories/9/">FromDual GmbH</source>
      <description><![CDATA[<p>We had some really exciting problems to solve for the last customer! Especially because the database wasn’t exactly small.<br />
Here are some key data: CPU: 2 sockets x 24 cores x 2 threads = 96 vCores, 756 G RAM, 2 x 10 Tbyte PCIe SSD in RAID-10 and 7 Tbyte data, several thousand clients, rapidly growing.<br />
The current throughput: 1 M SELECT/min, 56 k INSERT/min, 44 k UPDATE/min, 7 k DELETE/min averaged over 30 days. With a strong upward trend. Application and queries not consistently optimised. Database configuration: ‘state of the art’ not verified with benchmarks. CPU utilisation approx. 50% on average, more at peak times. I/O system still has available resources.<br />
The customer collects position and other device data and stores it in the database. In other words, a classic IoT problem (with time series, index clustered table, etc.).<br />
The question he has asked is: What is the fastest way to copy data from one table (pending data, a kind of queue) to another table (final data, per client)?<br />
The data flow looks something like this:<br />
+------------+<br />
&#124; IoT Device &#124;--+<br />
+------------+<br />
  +-----+<br />
+------------+  &#124; AS &#124; +--------------+ Processing +------------+<br />
&#124; IoT Device &#124;------+-- >&#124; &#124;-- >&#124; Pending data &#124;------------- >&#124; Final data &#124;<br />
+------------+ / &#124; 400 &#124; +--------------+ of data +------------+<br />
 / +-----+<br />
+------------+ /<br />
&#124; IoT Device &#124;--+<br />
+------------+<br />
3 different variants to copy the data were available for selection.<br />
Variant 1: INSERT and DELETE (simplest form)<br />
The simplest variant is a simple INSERT and DELETE. This variant is particularly problematic because MariaDB/MySQL and PostgreSQL have AUTOCOMMIT enabled by default (here, here, here and here).<br />
To help you visualise this a little better, here is some pseudocode:<br />
// 20k rows<br />
for (i = 1; i GRANT ALL ON *.* TO \'app\'@\'127.0.0.1\';</p>
<p>$ ./load_data.php --database-type=mysql --database=test --host=127.0.0.1 --port=3306 --user=app --password=secret --prepare</p>
<p>$ for i in $(seq 5) ; do<br />
 ./load_data.php --database-type=mysql --database=test --host=127.0.0.1 --port=3306 --user=app --password=secret --run --variant=1<br />
 ./load_data.php --database-type=mysql --database=test --host=127.0.0.1 --port=3306 --user=app --password=secret --run --variant=2<br />
 ./load_data.php --database-type=mysql --database=test --host=127.0.0.1 --port=3306 --user=app --password=secret --run --variant=3<br />
done</p>
<p>$ ./load_data.php --database-type=mysql --database=test --host=127.0.0.1 --port=3306 --user=app --password=secret --clean-up<br />
Everyone can work out the measured values themselves with the corresponding test script.<br />
Preparation and execution with PostgreSQL<br />
You can execute these tests yourself with the following commands:<br />
postgres# CREATE DATABASE test;<br />
postgres# CREATE USER app PASSWORD \'secret\';<br />
postgres# GRANT ALL ON DATABASE test TO app;<br />
postgres# GRANT ALL ON SCHEMA public TO app;</p>
<p>$ ./load_data.php --database-type=postgresql --database=test --host=127.0.0.1 --port=5432 --user=app --password=secret --prepare</p>
<p>$ for i in $(seq 5) ; do<br />
 ./load_data.php --database-type=postgresql --database=test --host=127.0.0.1 --port=5432 --user=app --password=secret --run --variant=1<br />
 ./load_data.php --database-type=postgresql --database=test --host=127.0.0.1 --port=5432 --user=app --password=secret --run --variant=2<br />
 ./load_data.php --database-type=postgresql --database=test --host=127.0.0.1 --port=5432 --user=app --password=secret --run --variant=3<br />
done</p>
<p>$ ./load_data.php --database-type=postgresql --database=test --host=127.0.0.1 --port=5432 --user=app --password=secret --clean-up<br />
Anyone can work out the measured values themselves with the corresponding test script.<br />
Results<br />
To avoid unnecessary discussions, we have ‘only’ listed the relative performance (runtime) here, as MarkC has been doing recently. We are happy to provide our measured values bilaterally. However, they can be easily reproduced with the test script itself.<br />
Less is better:</p>
<p> Variant 1<br />
 Variant 2<br />
 Variant 3</p>
<p> MariaDB 11.8, avg(5)<br />
 100.0%<br />
 7.5%<br />
 6.2%</p>
<p> PostgreSQL 19dev, avg(5)<br />
 100.0%<br />
 11.2%<br />
 7.0%</p>
<p>Attention: The values of MariaDB/MySQL and PostgreSQL can NOT be compared directly!</p>
<p>And here is the graphical evaluation:</p>
<p> </p>
<p>Remarks<br />
With faster discs, the difference between 1 and 2/3 would probably not have been quite so significant. Customer tests have shown a difference of ‘only’ about a factor of 5 (instead of a factor of 9 to 16).<br />
There are certainly other ways in which this loading process can be optimised. Here are a few that come to mind:</p>
<p>INSERT INTO ... SELECT * FROM<br />
LOAD DATA INFILE/COPY, if possible<br />
Prepared statements<br />
Server side Stored Language (SQL/PSM, PL/pgSQL, …) :-(<br />
PDO-Fetch of the results?<br />
etc.</p>
<p>Maybe I should look for a profiler (Xdebug or xhprof)?<br />
Further contributions</p>
<p>Load CSV files into the database<br />
MariaDB Prepared Statements, Transactions and Multi-Row Inserts<br />
How good is MySQL INSERT TRIGGER performance</p>
<p>This page was translated using deepl.com.</p>
<p>The post <a rel="nofollow" href="https://www.fromdual.com/blog/load-data-quick-into-the-database/">What is the quickest way to load data into the database?</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>We had some really exciting problems to solve for the last customer! Especially because the database wasn&rsquo;t exactly small.</p>
<p>Here are some key data: CPU: 2 sockets x 24 cores x 2 threads = 96 vCores, 756 G RAM, 2 x 10 Tbyte PCIe SSD in RAID-10 and 7 Tbyte data, several thousand clients, rapidly growing.</p>
<p>The current throughput: 1 M <code>SELECT</code>/min, 56 k <code>INSERT</code>/min, 44 k <code>UPDATE</code>/min, 7 k <code>DELETE</code>/min averaged over 30 days. With a strong upward trend. Application and queries not consistently optimised. Database configuration: &lsquo;state of the art&rsquo; not verified with benchmarks. CPU utilisation approx. 50% on average, more at peak times. I/O system still has available resources.</p>
<p>The customer collects position and other device data and stores it in the database. In other words, a classic IoT problem (with time series, index clustered table, etc.).</p>
<p>The question he has asked is: What is the fastest way to copy data from one table (pending data, a kind of queue) to another table (final data, per client)?</p>
<p>The data flow looks something like this:</p>
<pre><code>+------------+
| IoT Device |--+
+------------+ 
  +-----+
+------------+  | AS | +--------------+ Processing +------------+
| IoT Device |------+--&gt;| |--&gt;| Pending data |-------------&gt;| Final data |
+------------+ / | 400 | +--------------+ of data +------------+
 / +-----+
+------------+ /
| IoT Device |--+
+------------+
</code></pre>
<p>3 different variants to copy the data were available for selection.</p>
<h2 id="variant-1-insert-and-delete-simplest-form">Variant 1: <code>INSERT</code> and <code>DELETE</code> (simplest form)<a class="anchor-link" id="variant-1-insert-and-delete-simplest-form"></a></h2>
<p>The simplest variant is a simple <code>INSERT</code> and <code>DELETE</code>. This variant is particularly problematic because MariaDB/MySQL and PostgreSQL have <code>AUTOCOMMIT</code> enabled by default (<a href="https://dev.mysql.com/doc/refman/8.4/en/innodb-autocommit-commit-rollback.html" target="_blank" rel="noopener">here</a>, <a href="https://mariadb.com/docs/server/reference/sql-statements/transactions/start-transaction" target="_blank" rel="noopener">here</a>, <a href="https://www.postgresql.org/docs/current/ecpg-sql-set-autocommit.html" target="_blank" rel="noopener">here</a> and <a href="https://www.cybertec-postgresql.com/en/disabling-autocommit-in-postgresql-can-damage-your-health/" target="_blank" rel="noopener">here</a>).</p>
<p>To help you visualise this a little better, here is some pseudocode:</p>
<pre><code>// 20k rows
for (i = 1; i &lt;= 2000; i++) {

 SELECT * FROM pending LIMIT 10;
 foreach ( row ) {
 INSERT INTO final;
 -- implicit COMMIT
 DELETE FROM pending WHERE id = row[id];
 -- implicit COMMIT
 }
}
</code></pre>
<p>So if we want to copy 20 k rows, this variant causes: 40 k <code>COMMIT</code>s (<code>fsync</code>) and 42 k network round trips!</p>
<h2 id="variant-2-start-transaction-and-insert-ad-delete">Variant 2: <code>START TRANSACTION</code> and <code>INSERT</code> ad <code>DELETE</code><a class="anchor-link" id="variant-2-start-transaction-and-insert-ad-delete"></a></h2>
<p>This variant is used by more experienced database developers. Here is the corresponding pseudocode:</p>
<pre><code>// 20k rows
for (i = 1; i &lt;= 2000; i++) {

 SELECT * FROM pending LIMIT 10;
 START TRANSACTION;
 foreach ( row ) {
 INSERT INTO final;
 DELETE FROM pending WHERE id = row[id];
 }
 COMMIT;
}
</code></pre>
<p>If we want to copy 20 k rows in this example, this variant only causes 2 k <code>COMMIT</code>s (<code>fsync</code>)! So 20 times less! But 46 k network round trips (10% more).</p>
<h2 id="variant-3-start-transaction-and-optimised-insert-and-delete">Variant 3: <code>START TRANSACTION</code> and optimised <code>INSERT</code> and <code>DELETE</code><a class="anchor-link" id="variant-3-start-transaction-and-optimised-insert-and-delete"></a></h2>
<p>This variant is a little more demanding in terms of programming. It is used if you want to get a little closer to the limits of what is possible. Here is the pseudo code:</p>
<pre><code>// 20k rows
for (i = 1; i &lt;= 2000; i++) {

 SELECT * FROM pending LIMIT 10;
 START TRANSACTION;
 INSERT INTO final (), (), (), (), (), (), (), (), (), ();
 DELETE FROM pending WHERE id = IN (...);
 COMMIT;
}
</code></pre>
<p>And this 3rd variant also only causes 2 k <code>COMMIT</code>&rsquo;s (<code>fsync</code>) with 20 k rows, but saves the loop via the <code>INSERT</code> and <code>DELETE</code> statements in the database. So on the one hand we save network round trips (only 10 k) and CPU cycles on the database (which are difficult to scale) for parsing the queries.</p>
<h2 id="test-set-up">Test set-up<a class="anchor-link" id="test-set-up"></a></h2>
<p>To test the whole thing, we have prepared a small script: <a href="https://www.fromdual.com/code-examples/load_data.php.txt">load_data.php</a></p>
<h3 id="preparation-and-execution-with-mariadbmysql">Preparation and execution with MariaDB/MySQL<a class="anchor-link" id="preparation-and-execution-with-mariadb-mysql"></a></h3>
<p>You can execute these tests yourself with the following commands:</p>
<pre><code>SQL&gt; CREATE DATABASE test;
SQL&gt; CREATE USER 'app'@'127.0.0.1' IDENTIFIED BY 'secret';
SQL&gt; GRANT ALL ON *.* TO 'app'@'127.0.0.1';

$ ./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
</code></pre>
<p>Everyone can work out the measured values themselves with the corresponding test script.</p>
<h3 id="preparation-and-execution-with-postgresql">Preparation and execution with PostgreSQL<a class="anchor-link" id="preparation-and-execution-with-postgresql"></a></h3>
<p>You can execute these tests yourself with the following commands:</p>
<pre><code>postgres# CREATE DATABASE test;
postgres# CREATE USER app PASSWORD 'secret';
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
</code></pre>
<p>Anyone can work out the measured values themselves with the corresponding test script.</p>
<h2 id="results">Results<a class="anchor-link" id="results"></a></h2>
<p>To avoid unnecessary discussions, we have &lsquo;only&rsquo; listed the relative performance (runtime) here, as MarkC has been doing recently. We are happy to provide our measured values bilaterally. However, they can be easily reproduced with the test script itself.</p>
<p>Less is better:</p>
<table>
<thead>
<tr>
<th></th>
<th>Variant 1</th>
<th>Variant 2</th>
<th>Variant 3</th>
</tr>
</thead>
<tbody>
<tr>
<td>MariaDB 11.8, avg(5)</td>
<td>100.0%</td>
<td>7.5%</td>
<td>6.2%</td>
</tr>
<tr>
<td>PostgreSQL 19dev, avg(5)</td>
<td>100.0%</td>
<td>11.2%</td>
<td>7.0%</td>
</tr>
</tbody>
</table>
<p><strong>Attention</strong>: The values of MariaDB/MySQL and PostgreSQL can NOT be compared directly!</p>
<p></p>
<p>And here is the graphical evaluation:</p>
<p><img decoding="async" src="https://www.fromdual.com/images/mariadb-data-load.png" alt="mariadb"></p>
<p>&nbsp;</p>
<p><img decoding="async" src="https://www.fromdual.com/images/postgresql-data-load.png" alt="postgresql"></p>
<h2 id="remarks">Remarks<a class="anchor-link" id="remarks"></a></h2>
<p>With faster discs, the difference between 1 and 2/3 would probably not have been quite so significant. Customer tests have shown a difference of &lsquo;only&rsquo; about a factor of 5 (instead of a factor of 9 to 16).</p>
<p>There are certainly other ways in which this loading process can be optimised. Here are a few that come to mind:</p>
<ul>
<li><code>INSERT INTO ... SELECT * FROM</code></li>
<li><code>LOAD DATA INFILE</code>/<code>COPY</code>, if possible</li>
<li>Prepared statements</li>
<li>Server side Stored Language (SQL/PSM, PL/pgSQL, &hellip;) &#128577;</li>
<li>PDO-Fetch of the results?</li>
<li>etc.</li>
</ul>
<p>Maybe I should look for a profiler (<a href="https://xdebug.org/" target="_blank" rel="noopener">Xdebug</a> or <a href="https://www.php.net/manual/en/book.xhprof.php" target="_blank" rel="noopener">xhprof</a>)?</p>
<h2 id="further-contributions">Further contributions<a class="anchor-link" id="further-contributions"></a></h2>
<ul>
<li><a href="https://www.fromdual.com/blog/load-csv-files-into-the-database/">Load CSV files into the database</a></li>
<li><a href="https://www.fromdual.com/blog/mariadb-prepared-statements-transactions-and-multi-row-inserts/">MariaDB Prepared Statements, Transactions and Multi-Row Inserts</a></li>
<li><a href="https://www.fromdual.com/blog/how-good-is-mysql-insert-trigger-performance/">How good is MySQL INSERT TRIGGER performance</a></li>
</ul>
<p>This page was translated using <a href="https://www.deepl.com/en/translator" target="_blank" rel="noopener">deepl.com</a>.</p>

<p>The post <a rel="nofollow" href="https://www.fromdual.com/blog/load-data-quick-into-the-database/">What is the quickest way to load data into the database?</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Marko Mäkelä mentioned you on MDEV-38779</title>
      <link>https://www.fromdual.com/blog/mariadb-dynamically-configurable-buffer-pool-broken/comment-1049/</link>
      <pubDate>Wed, 11 Feb 2026 07:37:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://www.fromdual.com/aggregator/categories/9/">FromDual GmbH</source>
      <description><![CDATA[<p>[~oli], thank you for your report. There already was an existing report MDEV-38671 about mostly the same, namely that the {{innodb_buffer_pool_size}} cannot be increased anymore when MariaDB Server has been started with the default settings.<br />
I have updated MDEV-38671 with some more explanation why this change was made and why the parameter {{innodb_buffer_pool_size_max}} was introduced. My biggest motivation to implement this were the crash MDEV-35485, which we constantly hit in our internal stress testing, and the need to make MDEV-24670 behave in a meaningful and predictable way, that is, actually release memory to the operating system in the event of a memory pressure event. By default, that new 10.11 feature is disabled by default, but it can be enabled by {{SET GLOBAL innodb_buffer_pool_size_auto_min}}.</p>
<p>The post <a rel="nofollow" href="https://www.fromdual.com/blog/mariadb-dynamically-configurable-buffer-pool-broken/comment-1049/">Marko Mäkelä mentioned you on MDEV-38779</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>[~oli], thank you for your report. There already was an existing report MDEV-38671 about mostly the same, namely that the {{innodb_buffer_pool_size}} cannot be increased anymore when MariaDB Server has been started with the default settings.</p>
<p>I have updated MDEV-38671 with some more explanation why this change was made and why the parameter {{innodb_buffer_pool_size_max}} was introduced. My biggest motivation to implement this were the crash MDEV-35485, which we constantly hit in our internal stress testing, and the need to make MDEV-24670 behave in a meaningful and predictable way, that is, actually release memory to the operating system in the event of a memory pressure event. By default, that new 10.11 feature is disabled by default, but it can be enabled by {{SET GLOBAL innodb_buffer_pool_size_auto_min}}.</p>

<p>The post <a rel="nofollow" href="https://www.fromdual.com/blog/mariadb-dynamically-configurable-buffer-pool-broken/comment-1049/">Marko Mäkelä mentioned you on MDEV-38779</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>The MariaDB contribution process: a step by step guide.</title>
      <link>https://mariadb.org/the-mariadb-contribution-process-a-step-by-step-guide/</link>
      <pubDate>Tue, 10 Feb 2026 12:18:25 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>Inspired by my VERY long presentation on the topic at FOSDEM26 I thought I’d say a couple of words on how the contribution process works. …<br />
Continue reading \"The MariaDB contribution process: a step by step guide.\"<br />
The post The MariaDB contribution process: a step by step guide. appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/the-mariadb-contribution-process-a-step-by-step-guide/">The MariaDB contribution process: a step by step guide.</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>Inspired by my VERY long <a href="https://fosdem.org/2026/schedule/event/G88CD9-contributing_to_mariadb_and_posgresql/">presentation</a> on the topic at FOSDEM26 I thought I&rsquo;d say a couple of words on how the contribution process works. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/the-mariadb-contribution-process-a-step-by-step-guide/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;The MariaDB contribution process: a step by step guide.&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/the-mariadb-contribution-process-a-step-by-step-guide/">The MariaDB contribution process: a step by step guide.</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/the-mariadb-contribution-process-a-step-by-step-guide/">The MariaDB contribution process: a step by step guide.</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Accelerating Innovation: MariaDB’s Journey to a 2.5x Performance Leap</title>
      <link>https://mariadb.com/resources/blog/accelerating-innovation-mariadbs-journey-to-a-2-5x-performance-leap/</link>
      <pubDate>Mon, 09 Feb 2026 19:19:38 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.com/">MariaDB</source>
      <description><![CDATA[<p>It is hard to believe it has already been a year since I joined MariaDB as Vice President of Engineering. […]</p>
<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/accelerating-innovation-mariadbs-journey-to-a-2-5x-performance-leap/">Accelerating Innovation: MariaDB’s Journey to a 2.5x Performance Leap</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>It is hard to believe it has already been a year since I joined MariaDB as Vice President of Engineering. Looking back at my journey &ndash; from resolving customer bugs to leading elite engineering teams &ndash; coming to MariaDB felt like a natural next step. My background has always been rooted in building high-performance systems and fostering cultures of engineering excellence, and the mission here was&hellip;</p>
<p><a href="https://mariadb.com/resources/blog/accelerating-innovation-mariadbs-journey-to-a-2-5x-performance-leap/" rel="nofollow">Source</a></p>

<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/accelerating-innovation-mariadbs-journey-to-a-2-5x-performance-leap/">Accelerating Innovation: MariaDB’s Journey to a 2.5x Performance Leap</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>MariaDB has broken the concept of dynamically configurable buffer pools!</title>
      <link>https://www.fromdual.com/blog/mariadb-dynamically-configurable-buffer-pool-broken/</link>
      <pubDate>Mon, 09 Feb 2026 18:14:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://www.fromdual.com/aggregator/categories/9/">FromDual GmbH</source>
      <description><![CDATA[<p>Problem description<br />
MySQL introduced the dynamically configurable InnoDB buffer pool with 5.7.5 in September 2014 (here and here):</p>
<p>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:<br />
mysql > SET GLOBAL innodb_buffer_pool_size=402653184;</p>
<p>MariaDB 10.2.2 adopted this feature in September 2016 (source):</p>
<p>InnoDB was merged from MySQL-5.7.14 (XtraDB is disabled in MariaDB-10.2.2 pending a similar merge)</p>
<p>The problematic thing is, on the one hand, that this feature now no longer works as it did before and no longer works as expected. On the other hand, they changed the behaviour in spring 2025 within a major release series (LTS), which in my opinion is an absolute no-go (source):</p>
<p>From MariaDB 10.11.12 / 11.4.6 / 11.8.2, there are significant changes to the InnoDB buffer pool behavior.</p>
<p>And what is more, the description of this is quite poor (source):</p>
<p>decreasing innodb_buffer_pool_size at runtime does not release memory (MDEV-32339)<br />
reorganise innodb buffer pool (and remove buffer pool chunks) (MDEV-29445)<br />
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)<br />
Server crashes when resizing default innodb buffer pool after setting innodb-buffer-pool-chunk-size to 1M (MDEV-34677)</p>
<p>In the corresponding worklog (MDEV-36197) MarkoM also describes a different behaviour:</p>
<p>innodb_buffer_pool_size_auto_max (my proposal for this task) would set the maximum for the automation (default: 0 to disable the logic).</p>
<p>which would have made much more sense in my opinion.<br />
How did it work before?<br />
How did it work in the past with MariaDB and still today with MySQL:<br />
SQL > SHOW GLOBAL VARIABLES LIKE \'innodb_buffer_pool%size\';<br />
+-------------------------------+-----------+<br />
&#124; Variable_name &#124; Value &#124;<br />
+-------------------------------+-----------+<br />
&#124; innodb_buffer_pool_chunk_size &#124; 2097152 &#124;<br />
&#124; innodb_buffer_pool_size &#124; 134217728 &#124;<br />
+-------------------------------+-----------+</p>
<p>SQL > SET GLOBAL innodb_buffer_pool_size = @@innodb_buffer_pool_chunk_size * 128;</p>
<p>SQL > SHOW GLOBAL VARIABLES LIKE \'innodb_buffer_pool%size\';<br />
+-------------------------------+-----------+<br />
&#124; Variable_name &#124; Value &#124;<br />
+-------------------------------+-----------+<br />
&#124; innodb_buffer_pool_chunk_size &#124; 2097152 &#124;<br />
&#124; innodb_buffer_pool_size &#124; 268435456 &#124;<br />
+-------------------------------+-----------+<br />
Thus everything OK. Works as expected and as usual.<br />
What does MariaDB do today?<br />
What happens today with MariaDB:<br />
SQL > SHOW GLOBAL VARIABLES LIKE \'innodb_buffer_pool%size%\';<br />
+----------------------------------+-----------+<br />
&#124; Variable_name &#124; Value &#124;<br />
+----------------------------------+-----------+<br />
&#124; innodb_buffer_pool_chunk_size &#124; 0 &#124;<br />
&#124; innodb_buffer_pool_size &#124; 134217728 &#124;<br />
&#124; innodb_buffer_pool_size_auto_min &#124; 134217728 &#124;<br />
&#124; innodb_buffer_pool_size_max &#124; 134217728 &#124;<br />
+----------------------------------+-----------+</p>
<p>SQL > SET GLOBAL innodb_buffer_pool_size = 256*1024*1024;<br />
Query OK, 0 rows affected, 1 warning (0.000 sec)</p>
<p>SQL > show warnings;<br />
+---------+------+----------------------------------------------------------------+<br />
&#124; Level &#124; Code &#124; Message &#124;<br />
+---------+------+----------------------------------------------------------------+<br />
&#124; Warning &#124; 1292 &#124; Truncated incorrect innodb_buffer_pool_size value: \'268435456\' &#124;<br />
+---------+------+----------------------------------------------------------------+</p>
<p>SQL > SHOW GLOBAL VARIABLES LIKE \'innodb_buffer_pool%size%\';<br />
+----------------------------------+-----------+<br />
&#124; Variable_name &#124; Value &#124;<br />
+----------------------------------+-----------+<br />
&#124; innodb_buffer_pool_chunk_size &#124; 0 &#124;<br />
&#124; innodb_buffer_pool_size &#124; 134217728 &#124;<br />
&#124; innodb_buffer_pool_size_auto_min &#124; 134217728 &#124;<br />
&#124; innodb_buffer_pool_size_max &#124; 134217728 &#124;<br />
+----------------------------------+-----------+<br />
So nothing at all! Not even an error, just a warning. And if I do not look closely, I do not even realise that something did not work.<br />
The MariaDB error log says:<br />
[Note] InnoDB: Memory pressure event disregarded; innodb_buffer_pool_size=128m, innodb_buffer_pool_size_auto_min=128m<br />
If you then do some searching, you find out that something has changed here: Buffer Pool Changes and try intuitively:<br />
SQL > SET GLOBAL innodb_buffer_pool_size_max = 256*1024*1024;<br />
ERROR 1238 (HY000): Variable \'innodb_buffer_pool_size_max\' is a read only variable<br />
But that does not work either.<br />
This means you have to restart the database! And possibly at the very moment when you do not actually want to restart the database and need this feature…<br />
The documentation also states ((source):</p>
<p>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.</p>
<p>and (source):</p>
<p>If innodb_buffer_pool_size_max is 0 or not specified, it defaults to the innodb_buffer_pool_size value.</p>
<p>This means that I have to think again beforehand about how big I should make innodb_buffer_pool_size_max and can only correct it afterwards during operation, should I have forgotten or misjudged it.<br />
In my opinion, this is a complete step backwards from an operational point of view. This is probably another implementation for some cloud-only as a service solution (enterprise?).<br />
My suggestion is: Either, as suggested in the MDEV: 0 should switch off this feature and the behaviour should be as before or the default value should be set to 75% of the RAM size, as innodb_dedicated_server does with MySQL.<br />
I had the audacity to open a bug here: New InnoDB Buffer Pool autosize feature not so optimal implemented.<br />
FedericoR has thankfully recommended the following link: Issues with new buffer pool configuration in MariaDB Minors (10.11.12/13/14, 11.4.6/7/8, 11.8.2/3). I do not seem to be the only one who was annoyed by this change…<br />
How does PostgreSQL do this?<br />
PostgreSQL is currently not (yet) able to change shared_buffers dynamically. The default is usually 128M. The rule of thumb here, similar to MyISAM, is 25 - 40% of RAM. The lack of this feature is probably not as serious with PostgreSQL, however, as PostgreSQL relies heavily on the file system cache, similar to MyISAM.<br />
Source: Resource Consumption<br />
This page was translated using deepl.com.</p>
<p>The post <a rel="nofollow" href="https://www.fromdual.com/blog/mariadb-dynamically-configurable-buffer-pool-broken/">MariaDB has broken the concept of dynamically configurable buffer pools!</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<h2 id="problem-description">Problem description<a class="anchor-link" id="problem-description"></a></h2>
<p>MySQL introduced the dynamically configurable InnoDB buffer pool with 5.7.5 in September 2014 (<a href="https://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-5.html" target="_blank" rel="noopener">here</a> and <a href="https://dev.mysql.com/doc/refman/5.7/en/innodb-buffer-pool-resize.html#innodb-buffer-pool-online-resize" target="_blank" rel="noopener">here</a>):</p>
<blockquote>
<p>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:</p>
<p>mysql&gt; SET GLOBAL innodb_buffer_pool_size=402653184;</p>
</blockquote>
<p>MariaDB 10.2.2 adopted this feature in September 2016 (<a href="https://mariadb.com/docs/release-notes/community-server/old-releases/10.2/10.2.2#notable-changes" target="_blank" rel="noopener">source</a>):</p>
<blockquote>
<p>InnoDB was merged from MySQL-5.7.14 (XtraDB is disabled in MariaDB-10.2.2 pending a similar merge)</p>
</blockquote>
<p>The problematic thing is, on the one hand, that this feature now no longer works as it did before and no longer works as expected. On the other hand, they changed the behaviour in spring 2025 within a major release series (LTS), which in my opinion is an absolute no-go (<a href="https://mariadb.com/docs/server/server-usage/storage-engines/innodb/innodb-buffer-pool#buffer-pool-changes" target="_blank" rel="noopener">source</a>):</p>
<blockquote>
<p>From MariaDB 10.11.12 / 11.4.6 / 11.8.2, there are significant changes to the InnoDB buffer pool behavior.</p>
</blockquote>
<p>And what is more, the description of this is quite poor (<a href="https://mariadb.com/docs/release-notes/community-server/10.11/10.11.12#innodb" target="_blank" rel="noopener">source</a>):</p>
<blockquote>
<ul>
<li>decreasing innodb_buffer_pool_size at runtime does not release memory (MDEV-32339)</li>
<li>reorganise innodb buffer pool (and remove buffer pool chunks) (MDEV-29445)</li>
<li>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)</li>
<li>Server crashes when resizing default innodb buffer pool after setting innodb-buffer-pool-chunk-size to 1M (MDEV-34677)</li>
</ul>
</blockquote>
<p>In the corresponding worklog (<a href="https://jira.mariadb.org/browse/MDEV-36197" target="_blank" rel="noopener">MDEV-36197</a>) MarkoM also describes a different behaviour:</p>
<blockquote>
<p>innodb_buffer_pool_size_auto_max (my proposal for this task) would set the maximum for the automation (default: 0 to disable the logic).</p>
</blockquote>
<p>which would have made much more sense in my opinion.</p>
<h2 id="how-did-it-work-before">How did it work before?<a class="anchor-link" id="how-did-it-work-before"></a></h2>
<p>How did it work in the past with MariaDB and still today with MySQL:</p>
<pre><code>SQL&gt; SHOW GLOBAL VARIABLES LIKE 'innodb_buffer_pool%size';
+-------------------------------+-----------+
| Variable_name | Value |
+-------------------------------+-----------+
| innodb_buffer_pool_chunk_size | 2097152 |
| innodb_buffer_pool_size | 134217728 |
+-------------------------------+-----------+

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

SQL&gt; SHOW GLOBAL VARIABLES LIKE 'innodb_buffer_pool%size';
+-------------------------------+-----------+
| Variable_name | Value |
+-------------------------------+-----------+
| innodb_buffer_pool_chunk_size | 2097152 |
| innodb_buffer_pool_size | 268435456 |
+-------------------------------+-----------+
</code></pre>
<p>Thus everything OK. Works as expected and as usual.</p>
<h2 id="what-does-mariadb-do-today">What does MariaDB do today?<a class="anchor-link" id="what-does-mariadb-do-today"></a></h2>
<p>What happens today with MariaDB:</p>
<pre><code>SQL&gt; SHOW GLOBAL VARIABLES LIKE 'innodb_buffer_pool%size%';
+----------------------------------+-----------+
| 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&gt; SET GLOBAL innodb_buffer_pool_size = 256*1024*1024;
Query OK, 0 rows affected, 1 warning (0.000 sec)

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

SQL&gt; SHOW GLOBAL VARIABLES LIKE 'innodb_buffer_pool%size%';
+----------------------------------+-----------+
| 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 |
+----------------------------------+-----------+
</code></pre>
<p>So nothing at all! Not even an error, just a warning. And if I do not look closely, I do not even realise that something did not work.</p>
<p>The MariaDB error log says:</p>
<pre><code>[Note] InnoDB: Memory pressure event disregarded; innodb_buffer_pool_size=128m, innodb_buffer_pool_size_auto_min=128m
</code></pre>
<p>If you then do some searching, you find out that something has changed here: <a href="https://mariadb.com/docs/server/server-usage/storage-engines/innodb/innodb-buffer-pool#buffer-pool-changes" target="_blank" rel="noopener">Buffer Pool Changes</a> and try intuitively:</p>
<pre><code>SQL&gt; SET GLOBAL innodb_buffer_pool_size_max = 256*1024*1024;
ERROR 1238 (HY000): Variable 'innodb_buffer_pool_size_max' is a read only variable
</code></pre>
<p>But that does not work either.</p>
<p>This means you have to restart the database! And possibly at the very moment when you do not actually want to restart the database and need this feature&hellip;</p>
<p>The documentation also states ((<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" rel="noopener">source</a>):</p>
<blockquote>
<p>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.</p>
</blockquote>
<p>and (<a href="https://mariadb.com/docs/server/server-usage/storage-engines/innodb/innodb-buffer-pool#buffer-pool-changes" target="_blank" title="Buffer Pool Changes" rel="noopener">source</a>):</p>
<blockquote>
<p>If innodb_buffer_pool_size_max is 0 or not specified, it defaults to the innodb_buffer_pool_size value.</p>
</blockquote>
<p>This means that I have to think again beforehand about how big I should make <code>innodb_buffer_pool_size_max</code> and can only correct it afterwards during operation, should I have forgotten or misjudged it.</p>
<p>In my opinion, this is a complete step backwards from an operational point of view. This is probably another implementation for some cloud-only as a service solution (enterprise?).</p>
<p>My suggestion is: Either, as suggested in the MDEV: 0 should switch off this feature and the behaviour should be as before or the default value should be set to 75% of the RAM size, as <code>innodb_dedicated_server</code> does with MySQL.</p>
<p>I had the audacity to open a bug here: <a href="https://jira.mariadb.org/browse/MDEV-38779" target="_blank" rel="noopener">New InnoDB Buffer Pool autosize feature not so optimal implemented</a>.</p>
<p>FedericoR has thankfully recommended the following link: <a href="https://www.mail-archive.com/developers@lists.mariadb.org/msg00822.html" target="_blank" title="MariaDB developers mailing list" rel="noopener">Issues with new buffer pool configuration in MariaDB Minors (10.11.12/13/14, 11.4.6/7/8, 11.8.2/3)</a>. I do not seem to be the only one who was annoyed by this change&hellip;</p>
<h2 id="how-does-postgresql-do-this">How does PostgreSQL do this?<a class="anchor-link" id="how-does-postgresql-do-this"></a></h2>
<p>PostgreSQL is currently not (yet) able to change <code>shared_buffers</code> dynamically. The default is usually 128M. The rule of thumb here, similar to MyISAM, is 25 &ndash; 40% of RAM. The lack of this feature is probably not as serious with PostgreSQL, however, as PostgreSQL relies heavily on the file system cache, similar to MyISAM.</p>
<p>Source: <a href="https://www.postgresql.org/docs/current/runtime-config-resource.html#RUNTIME-CONFIG-RESOURCE-MEMORY" target="_blank" rel="noopener">Resource Consumption</a></p>
<p>This page was translated using <a href="https://www.deepl.com/en/translator" target="_blank" rel="noopener">deepl.com</a>.</p>

<p>The post <a rel="nofollow" href="https://www.fromdual.com/blog/mariadb-dynamically-configurable-buffer-pool-broken/">MariaDB has broken the concept of dynamically configurable buffer pools!</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Pre-FOSDEM &#038; FOSDEM 2026, Community, Databases, and Open Source</title>
      <link>https://percona.community/blog/2026/02/09/pre-fosdem-fosdem-2026-community-databases-and-open-source/</link>
      <pubDate>Mon, 09 Feb 2026 10:00:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://percona.community/blog/">Percona Community Blog - learn about MySQL, MariaDB, PostgreSQL, and MongoDB on Percona Community</source>
      <description><![CDATA[<p>This is a recap of Percona at preFosdem and Fosdem!</p>
<p>Before FOSDEM officially started, the database community gathered for MySQL Belgium Days (Pre-FOSDEM), a two-day event bringing together MySQL developers, DBAs, engineers, tool builders, and open-source enthusiasts. It was an excellent space for deep technical discussions, knowledge sharing, and reconnecting with the community, hosted by the amazing Frederic Descamps.<br />
The event featured strong participation from Percona and the wider MySQL ecosystem, with talks led by Peter Zaitsev, Marco Tusa, Fernando Laudares Camargos, Arunjith Aravindan, Vinicius Grippa, Pep Pla, and Yura Sorokin.</p>
<p>The post <a rel="nofollow" href="https://percona.community/blog/2026/02/09/pre-fosdem-fosdem-2026-community-databases-and-open-source/">Pre-FOSDEM &amp; FOSDEM 2026, Community, Databases, and Open Source</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>This is a recap of Percona at preFosdem and Fosdem!</p>
<figure>
<p> <img decoding="async" src="https://percona.community/blog/2026/02/fosdem-all.png" alt="Fosdem intro"></p></figure>

<p>Before FOSDEM officially started, the database community gathered for MySQL Belgium Days (Pre-FOSDEM), a two-day event bringing together MySQL developers, DBAs, engineers, tool builders, and open-source enthusiasts. It was an excellent space for deep technical discussions, knowledge sharing, and reconnecting with the community, hosted by the amazing <strong>Frederic Descamps</strong>.<br>
The event featured strong participation from <strong>Percona</strong> and the wider MySQL ecosystem, with talks led by <strong>Peter Zaitsev, Marco Tusa, Fernando Laudares Camargos, Arunjith Aravindan, Vinicius Grippa, Pep Pla, and Yura Sorokin</strong>.</p>

<p>The post <a rel="nofollow" href="https://percona.community/blog/2026/02/09/pre-fosdem-fosdem-2026-community-databases-and-open-source/">Pre-FOSDEM &amp; FOSDEM 2026, Community, Databases, and Open Source</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>The Real Operational Cost of Vacuuming in PostgreSQL</title>
      <link>https://mariadb.org/the-real-operational-cost-of-vacuuming-in-postgresql/</link>
      <pubDate>Sun, 08 Feb 2026 23:50:07 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>There was a time when PostgreSQL’s own developers were far more open about the real cost of their MVCC design. Back in the 8.1 era, the documentation spelled out the resource drain, the vacuum overhead, and the “cold comfort” …<br />
Continue reading \"The Real Operational Cost of Vacuuming in PostgreSQL\"<br />
The post The Real Operational Cost of Vacuuming in PostgreSQL appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/the-real-operational-cost-of-vacuuming-in-postgresql/">The Real Operational Cost of Vacuuming in PostgreSQL</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>There was a time when PostgreSQL&rsquo;s own developers were far more open about the real cost of their MVCC design. Back in the 8.1 era, the documentation spelled out the resource drain, the vacuum overhead, and the &ldquo;cold comfort&rdquo; &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/the-real-operational-cost-of-vacuuming-in-postgresql/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;The Real Operational Cost of Vacuuming in PostgreSQL&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/the-real-operational-cost-of-vacuuming-in-postgresql/">The Real Operational Cost of Vacuuming in PostgreSQL</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/the-real-operational-cost-of-vacuuming-in-postgresql/">The Real Operational Cost of Vacuuming in PostgreSQL</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Wireshark now can decode MySQL X Protocol</title>
      <link>https://databaseblog.myname.nl/2026/02/wireshark-now-can-decode-mysql-x.html</link>
      <pubDate>Sun, 08 Feb 2026 17:40:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://databaseblog.myname.nl/">Daniël's Database Blog</source>
      <description><![CDATA[<p>The new protocol dissector for X Protocol in MySQL was just merged to the master branch in Wireshark. To get it build Wireshark from the master branch or wait for the next release.This protocol is using Google Protobuf, which makes it much easier to work with than the regular MySQL protocol.See also: https://dev.mysql.com/doc/dev/mysql-server/latest/page_mysqlx_protocol.html If you like what Wireshark does, consider donating on https://wiresharkfoundation.org/donate/   </p>
<p>The post <a rel="nofollow" href="https://databaseblog.myname.nl/2026/02/wireshark-now-can-decode-mysql-x.html">Wireshark now can decode MySQL X Protocol</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>The new protocol dissector for X Protocol in MySQL was just merged to the master branch in Wireshark. To get it build Wireshark from the master branch or wait for the next release.</p>
<p>This protocol is using Google Protobuf, which makes it much easier to work with than the regular MySQL protocol.</p>
<p>See also: <a href="https://dev.mysql.com/doc/dev/mysql-server/latest/page_mysqlx_protocol.html" target="_blank" rel="noopener">https://dev.mysql.com/doc/dev/mysql-server/latest/page_mysqlx_protocol.html</a>&nbsp;</p>
<p>If you like what Wireshark does, consider donating on <a href="https://wiresharkfoundation.org/donate/" target="_blank" rel="noopener">https://wiresharkfoundation.org/donate/&nbsp;</a></p>
<p>&nbsp;</p>
<p>&nbsp;<img decoding="async" loading="lazy" alt="" height="499" src="https://blogger.googleusercontent.com/img/a/AVvXsEh6-KGp7Fn5XAAplfFATldBEeh6xJo01teGG5MqeB74FzBBgsWAsZDhg_J0oLZeZP64nYyTEbbw4W0cWyiUBrw4X07rrETwAdCQqSsopYGZYn2TjZBcC5FKxu59S6jbW2R-X7ft649YqVLJvLg4HV_-UnqgvluhtXkf2-4efjPtB1B6mhsUF1_3vcEX_Lah=w640-h499" width="640"></p>

<p>The post <a rel="nofollow" href="https://databaseblog.myname.nl/2026/02/wireshark-now-can-decode-mysql-x.html">Wireshark now can decode MySQL X Protocol</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>How much space does NULL need?</title>
      <link>https://www.fromdual.com/blog/how-much-space-does-null-need/</link>
      <pubDate>Sun, 08 Feb 2026 15:15:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://www.fromdual.com/aggregator/categories/9/">FromDual GmbH</source>
      <description><![CDATA[<p>The last time I consulted a customer, he came up to me beaming with joy and said that he had taken my advice and changed all the primary key columns from BIGINT (8 bytes) to INT (4 bytes) and that had made a big difference! His MySQL 8.4 database is now 750 Gbyte smaller (from 5.5 Tbyte). Nice!<br />
And yes, I know that contradicts the recommendations of some of my PostgreSQL colleagues (here and here). In the MySQL world, more emphasis is placed on such things (source):</p>
<p>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</p>
<p>Also, InnoDB works a wee bit differently (index clustered table and primary key in all secondary keys) than PostgreSQL (heap table, indices with row pointer (ctid)).<br />
But that’s not really the issue. Immediately afterwards, he asked me whether the deletion of columns of type DOUBLE (8 bytes, in PostgreSQL-speak DOUBLE PRECISION) would also save space or whether he should rather drop the columns straight away. My first reflex response to DOUBLE was: NULL is good, followed by OPTIMIZE TABLE (VACUUM FULL in PostgreSQL parlance). But the second thought was, DOUBLE is a data type of fixed length, does NULL also apply there or only for data types with variable length? Caution is the mother of the porcelain box! Love to consult the manual first…<br />
And there it says (source):</p>
<p>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.</p>
<p>and (source):</p>
<p>The variable-length part of the record header contains a bit vector for indicating NULL columns. … 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.</p>
<p>Experiment with MariaDB/MySQL<br />
Test setup<br />
Somehow the description is a bit too complicated for me. Perhaps a small sketch would help? So let’s give it a try:<br />
SQL > -- DROP TABLE IF EXISTS tracking;</p>
<p>SQL > CREATE TABLE tracking (<br />
 id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT<br />
, d0 DOUBLE, d1 DOUBLE, d2 DOUBLE, d3 DOUBLE, d4 DOUBLE<br />
, d5 DOUBLE, d6 DOUBLE, d7 DOUBLE, d8 DOUBLE, d9 DOUBLE<br />
);</p>
<p>SQL > 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;<br />
SQL > 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;<br />
... bis 16 M rows<br />
The table is approx. 1.8 Gbyte in size for both MariaDB and MySQL with 16 M rows. Since this information is only given very imprecisely in INFORMATION_SCHEMA, let’s take a look at the file system:<br />
MariaDB 11.8:<br />
SQL > system ls -l tracking.ibd<br />
-rw-rw---- 1 mysql mysql 1206 Feb 7 10:28 tracking.frm<br />
-rw-rw---- 1 mysql mysql 1933574144 Feb 7 10:32 tracking.ibd<br />
MySQL 8.4:<br />
SQL > system ls -l tracking.ibd<br />
-rw-r----- 1 mysql mysql 1929379840 Feb 7 10:33 tracking.ibd<br />
Defragment the table<br />
Then we ‘defragment’ the table with the OPTIMIZE TABLE command:<br />
SQL > OPTIMIZE TABLE tracking;<br />
+---------------+----------+----------+-------------------------------------------------------------------+<br />
&#124; Table &#124; Op &#124; Msg_type &#124; Msg_text &#124;<br />
+---------------+----------+----------+-------------------------------------------------------------------+<br />
&#124; test.tracking &#124; optimize &#124; note &#124; Table does not support optimize, doing recreate + analyze instead &#124;<br />
&#124; test.tracking &#124; optimize &#124; status &#124; OK &#124;<br />
+---------------+----------+----------+-------------------------------------------------------------------+<br />
Attention: The table is copied once! It therefore needs twice the amount of disc space for a short time! This can be observed while the OPTIMIZE TABLE command is running:<br />
MariaDB:<br />
$ watch -d -n 1 \'ls -l trac* #*\'<br />
-rw-rw---- 1 mysql mysql 1206 Feb 7 10:39 \'#sql-alter-d57-8c.frm\'<br />
-rw-rw---- 1 mysql mysql 968884224 Feb 7 10:39 \'#sql-alter-d57-8c.ibd\'<br />
-rw-rw---- 1 mysql mysql 1206 Feb 7 10:28 tracking.frm<br />
-rw-rw---- 1 mysql mysql 1933574144 Feb 7 10:32 tracking.ibd<br />
MySQL:<br />
$ watch -d -n 1 \'ls -l trac* #*\'<br />
-rw-r----- 1 mysql mysql 369098752 Feb 7 10:40 #sql-ib1594-4164062678.ibd<br />
-rw-r----- 1 mysql mysql 1929379840 Feb 7 10:33 tracking.ibd<br />
The result is amazing! With MariaDB, the table has remained somewhat the same size:<br />
-rw-rw---- 1 mysql mysql 1206 Feb 7 10:39 tracking.frm<br />
-rw-rw---- 1 mysql mysql 1912602624 Feb 7 10:39 tracking.ibd<br />
With MySQL, on the other hand, the table has actually grown after the ‘defragmentation’, namely by approx. 14%:<br />
-rw-r----- 1 mysql mysql 2197815296 Feb 7 10:41 tracking.ibd<br />
If we execute the OPTIMIZE TABLE command again, the size remains constant for both MariaDB and MySQL:<br />
MariaDB:<br />
-rw-rw---- 1 mysql mysql 1206 Feb 7 10:46 tracking.frm<br />
-rw-rw---- 1 mysql mysql 1912602624 Feb 7 10:48 tracking.ibd<br />
MySQL:<br />
-rw-r----- 1 mysql mysql 2197815296 Feb 7 10:48 tracking.ibd<br />
Attempt 1: NULL out<br />
Now we NULL out the values:<br />
SQL > UPDATE tracking<br />
SET d0 = NULL, d1 = NULL, d2 = NULL, d3 = NULL, d4 = NULL<br />
 , d5 = NULL, d6 = NULL, d7 = NULL, d8 = NULL, d9 = NULL<br />
;<br />
After this step, the sizes of the files have even grown slightly:<br />
MariaDB (+1.3%):<br />
-rw-rw---- 1 mysql mysql 1206 Feb 7 10:49 tracking.frm<br />
-rw-rw---- 1 mysql mysql 1937768448 Feb 7 11:04 tracking.ibd<br />
MySQL (+0.2%):<br />
-rw-r----- 1 mysql mysql 2202009600 Feb 7 11:04 tracking.ibd<br />
We then defragment the table again with the OPTIMIZE TABLE command. The tables shrink as expected.<br />
MariaDB (to 23%):<br />
-rw-rw---- 1 mysql mysql 1206 Feb 7 11:09 tracking.frm<br />
-rw-rw---- 1 mysql mysql 448790528 Feb 7 11:10 tracking.ibd<br />
MySQL (to 24%):<br />
-rw-r----- 1 mysql mysql 520093696 Feb 7 11:10 tracking.ibd<br />
OPTIMIZE TABLE again does NOT change the file size any more…<br />
Attempt 2: Deleting the columns<br />
Now we try the whole thing again with the DROP COLUMN command. The starting position is again the same as described above:<br />
MariaDB:<br />
-rw-rw---- 1 mysql mysql 1206 Feb 7 11:15 tracking.frm<br />
-rw-rw---- 1 mysql mysql 1933574144 Feb 7 11:18 tracking.ibd<br />
MySQL:<br />
-rw-r----- 1 mysql mysql 1929379840 Feb 7 11:19 tracking.ibd<br />
After the OPTIMIZE TABLE command, the values look similar to the first attempt:<br />
MariaDB:<br />
-rw-rw---- 1 mysql mysql 1206 Feb 7 11:20 tracking.frm<br />
-rw-rw---- 1 mysql mysql 1912602624 Feb 7 11:21 tracking.ibd<br />
MySQL:<br />
-rw-r----- 1 mysql mysql 2197815296 Feb 7 11:21 tracking.ibd<br />
OPTIMIZE TABLE again also brings no further changes, as above:<br />
MariaDB:<br />
-rw-rw---- 1 mysql mysql 1206 Feb 7 11:22 tracking.frm<br />
-rw-rw---- 1 mysql mysql 1912602624 Feb 7 11:23 tracking.ibd<br />
MySQL:<br />
-rw-r----- 1 mysql mysql 2197815296 Feb 7 11:24 tracking.ibd<br />
And now the actual second attempt with dropping the columns:<br />
SQL > ALTER TABLE tracking<br />
 DROP COLUMN d0, DROP COLUMN d1, DROP COLUMN d2, DROP COLUMN d3, DROP COLUMN d4<br />
, DROP COLUMN d5, DROP COLUMN d6, DROP COLUMN d7, DROP COLUMN d8, DROP COLUMN d9<br />
;<br />
The first thing we notice is that the command is INSTANTANEOUS, i.e. it does not make any changes to the data but only changes the metadata. On the one hand, this is good, as it minimises the impact on the application. On the other hand, it also means that no space is saved.<br />
So let’s get to grips with the whole thing again with the OPTIMIZE TABLE command:<br />
MariaDB (to 93%):<br />
-rw-rw---- 1 mysql mysql 925 Feb 7 11:28 tracking.frm<br />
-rw-rw---- 1 mysql mysql 415236096 Feb 7 11:29 tracking.ibd<br />
MySQL (to 92%):<br />
-rw-r----- 1 mysql mysql 478150656 Feb 7 11:28 tracking.ibd<br />
Conclusion<br />
Both, dropping the columns and the NULL out of columns save a significant amount of space. Dropping the columns saves about 7% more space than NULL them out. If it is possible from an application point of view, you should therefore drop columns that are no longer required, or if not possible, at least NULL them out.<br />
Experiment with PostgreSQL<br />
And now let’s take a look at the whole thing with PostgreSQL 19devel.<br />
Test setup<br />
The test setup is analogous to MariaDB/MySQL:<br />
postgres=# -- DROP TABLE IF EXISTS tracking;</p>
<p>postgres=# CREATE TABLE tracking (<br />
 id SERIAL PRIMARY KEY<br />
, d0 DOUBLE PRECISION, d1 DOUBLE PRECISION, d2 DOUBLE PRECISION, d3 DOUBLE PRECISION, d4 DOUBLE PRECISION<br />
, d5 DOUBLE PRECISION, d6 DOUBLE PRECISION, d7 DOUBLE PRECISION, d8 DOUBLE PRECISION, d9 DOUBLE PRECISION<br />
);</p>
<p>postgres=# timing</p>
<p>postgres=# INSERT INTO tracking (d0, d1, d2, d3, d4, d5, d6, d7, d8, d9)<br />
 SELECT 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0;<br />
postgres=# INSERT INTO tracking (d0, d1, d2, d3, d4, d5, d6, d7, d8, d9)<br />
 SELECT 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 FROM tracking;<br />
... bis 16 M rows<br />
Firstly, we want to know how big the table has actually become. PostgreSQL seems to know this information very precisely:<br />
postgres=# SELECT pg_relation_size(\'tracking\') AS tab_siz<br />
 , pg_size_pretty(pg_relation_size(\'tracking\')) AS tab_siz_prtty<br />
 , pg_indexes_size(\'tracking\') AS idx_siz<br />
 , pg_size_pretty(pg_indexes_size(\'tracking\')) AS idx_siz_prtty<br />
 , pg_relation_size(\'tracking\') + pg_indexes_size(\'tracking\') AS tab_and_idx_siz<br />
 , pg_size_pretty(pg_relation_size(\'tracking\') + pg_indexes_size(\'tracking\')) AS tab_and_idx_siz_prtty<br />
 , pg_total_relation_size(\'tracking\') AS tot_rel_siz<br />
 , pg_size_pretty(pg_total_relation_size(\'tracking\')) AS tot_rel_siz_prtty<br />
;<br />
 tab_siz &#124; tab_siz_prtty &#124; idx_siz &#124; idx_siz_prtty &#124; tab_and_idx_siz &#124; tab_and_idx_siz_prtty &#124; tot_rel_siz &#124; tot_rel_siz_prtty<br />
------------+---------------+-----------+---------------+-----------------+-----------------------+-------------+-------------------<br />
 1963417600 &#124; 1872 MB &#124; 376856576 &#124; 359 MB &#124; 2340274176 &#124; 2232 MB &#124; 2340798464 &#124; 2232 MB<br />
Then we want to know where these files can be found in the file system:<br />
postgres=# SELECT oid AS db_oid FROM pg_database WHERE datname = current_database();<br />
 db_oid<br />
--------<br />
 5</p>
<p>postgres=# SELECT oid AS table_oid, relname, relnamespace, relfilenode<br />
 FROM pg_class WHERE relname = \'tracking\';<br />
 table_oid &#124; relname &#124; relnamespace &#124; relfilenode<br />
-----------+----------+--------------+-------------<br />
 40965 &#124; tracking &#124; 2200 &#124; 40965</p>
<p>postgres=# SELECT i.indexrelid::regclass as index_name, i.indexrelid as index_oid<br />
 FROM pg_index i<br />
 JOIN pg_class c ON i.indrelid = c.oid<br />
 WHERE c.relname = \'tracking\';<br />
 index_name &#124; index_oid<br />
---------------+-----------<br />
 tracking_pkey &#124; 40970</p>
<p>postgres=# SELECT pg_relation_filepath(\'tracking\');<br />
 pg_relation_filepath<br />
----------------------<br />
 base/5/40965<br />
Table and index size in the file system:<br />
$ ls -ltr 40965* 40970*<br />
-rw------- 1 mysql mysql 40960 Feb 7 18:33 40965_vm<br />
-rw------- 1 mysql mysql 499712 Feb 7 18:33 40965_fsm<br />
-rw------- 1 mysql mysql 889675776 Feb 7 18:34 40965.1<br />
-rw------- 1 mysql mysql 1073741824 Feb 7 18:34 40965<br />
-rw------- 1 mysql mysql 376856576 Feb 7 18:35 40970</p>
<p>*_fsm means “free space map”<br />
*_vm means “visibility map”<br />
*.1 means 2nd segment of the object (table or index)</p>
<p>PostgreSQL seems to work with segments of 1 Gbyte by default and, unlike MariaDB/MySQL (INFORMATION_SCHEMA), knows exactly how large its files are. And the discrepancy from above (between tot_rel_siz and tab_and_idx_siz) can be explained by the fsm and vm files.<br />
The PostgreSQL equivalent of the MariaDB/MySQL OPTIMIZE TABLE is the VACUUM FULL command:<br />
postgres=# VACUUM FULL tracking;</p>
<p>$ ls -ltr<br />
-rw------- 1 mysql mysql 40960 Feb 7 18:39 40965_vm<br />
-rw------- 1 mysql mysql 499712 Feb 7 18:39 40965_fsm<br />
-rw------- 1 mysql mysql 1073741824 Feb 7 18:39 40965<br />
-rw------- 1 mysql mysql 889675776 Feb 7 18:39 40965.1<br />
-rw------- 1 mysql mysql 1073741824 Feb 7 18:39 40972<br />
-rw------- 1 mysql mysql 889675776 Feb 7 18:39 40972.1<br />
-rw------- 1 mysql mysql 0 Feb 7 18:39 40975</p>
<p>...</p>
<p>-rw------- 1 mysql mysql 49152 Feb 7 18:39 2704<br />
-rw------- 1 mysql mysql 32768 Feb 7 18:39 2703<br />
-rw------- 1 mysql mysql 32768 Feb 7 18:39 2696<br />
-rw------- 1 mysql mysql 65536 Feb 7 18:39 2674<br />
-rw------- 1 mysql mysql 81920 Feb 7 18:39 2673<br />
-rw------- 1 mysql mysql 98304 Feb 7 18:39 2659<br />
-rw------- 1 mysql mysql 139264 Feb 7 18:39 2658<br />
-rw------- 1 mysql mysql 24576 Feb 7 18:39 2619_fsm<br />
-rw------- 1 mysql mysql 163840 Feb 7 18:39 2619<br />
-rw------- 1 mysql mysql 106496 Feb 7 18:39 2608<br />
-rw------- 1 mysql mysql 491520 Feb 7 18:39 1249<br />
-rw------- 1 mysql mysql 122880 Feb 7 18:39 1247<br />
-rw------- 1 mysql mysql 32768 Feb 7 18:39 2662<br />
-rw------- 1 mysql mysql 114688 Feb 7 18:39 1259<br />
-rw------- 1 mysql mysql 16384 Feb 7 18:39 3455<br />
-rw------- 1 mysql mysql 49152 Feb 7 18:39 2663<br />
-rw------- 1 mysql mysql 1073741824 Feb 7 18:39 40972<br />
-rw------- 1 mysql mysql 889675776 Feb 7 18:39 40972.1<br />
-rw------- 1 mysql mysql 376864768 Feb 7 18:39 40975<br />
-rw------- 1 mysql mysql 0 Feb 7 18:39 40965<br />
-rw------- 1 mysql mysql 0 Feb 7 18:39 40970<br />
The first thing you notice is that PostgreSQL touches quite a few files and the ‘free space map’ file has disappeared. In contrast to MariaDB/MySQL, the table segments have remained the same size. You can also see that the old table has ‘disappeared’ (40965, 40970) and a new one has been created (40972 and 40975). The VACUUM FULL command in PostgreSQL also creates a copy of the data, as in MariaDB/MySQL.<br />
postgres=# SELECT pg_relation_size(\'tracking\') AS tab_siz<br />
 , pg_size_pretty(pg_relation_size(\'tracking\')) AS tab_siz_prtty<br />
 , pg_indexes_size(\'tracking\') AS idx_siz<br />
 , pg_size_pretty(pg_indexes_size(\'tracking\')) AS idx_siz_prtty<br />
 , pg_relation_size(\'tracking\') + pg_indexes_size(\'tracking\') AS tab_and_idx_siz<br />
 , pg_size_pretty(pg_relation_size(\'tracking\') + pg_indexes_size(\'tracking\')) AS tab_and_idx_siz_prtty<br />
 , pg_total_relation_size(\'tracking\') AS tot_rel_siz<br />
 , pg_size_pretty(pg_total_relation_size(\'tracking\')) AS tot_rel_siz_prtty<br />
;<br />
 tab_siz &#124; tab_siz_prtty &#124; idx_siz &#124; idx_siz_prtty &#124; tab_and_idx_siz &#124; tab_and_idx_siz_prtty &#124; tot_rel_siz &#124; tot_rel_siz_prtty<br />
------------+---------------+-----------+---------------+-----------------+-----------------------+-------------+-------------------<br />
 1963417600 &#124; 1872 MB &#124; 376864768 &#124; 359 MB &#124; 2340282368 &#124; 2232 MB &#124; 2340282368 &#124; 2232 MB<br />
The following query helps to understand which other files/objects have been created:<br />
postgres=# SELECT c.oid, c.relname, ns.nspname<br />
FROM pg_class AS c<br />
JOIN pg_namespace AS ns ON ns.oid = c.relnamespace<br />
WHERE c.oid IN (2704, 2703, 2696, 2674, 2673, 2659, 2658, 2619, 2608, 1249, 1247, 40972, 2662, 1259, 3455, 2663, 40975, 40965, 40970)<br />
;<br />
 oid &#124; relname &#124; nspname<br />
-------+-----------------------------------+------------<br />
 40965 &#124; tracking &#124; public<br />
 40970 &#124; tracking_pkey &#124; public<br />
 2619 &#124; pg_statistic &#124; pg_catalog<br />
 1247 &#124; pg_type &#124; pg_catalog<br />
 2703 &#124; pg_type_oid_index &#124; pg_catalog<br />
 2704 &#124; pg_type_typname_nsp_index &#124; pg_catalog<br />
 2658 &#124; pg_attribute_relid_attnam_index &#124; pg_catalog<br />
 2659 &#124; pg_attribute_relid_attnum_index &#124; pg_catalog<br />
 2662 &#124; pg_class_oid_index &#124; pg_catalog<br />
 2663 &#124; pg_class_relname_nsp_index &#124; pg_catalog<br />
 3455 &#124; pg_class_tblspc_relfilenode_index &#124; pg_catalog<br />
 2696 &#124; pg_statistic_relid_att_inh_index &#124; pg_catalog<br />
 2673 &#124; pg_depend_depender_index &#124; pg_catalog<br />
 2674 &#124; pg_depend_reference_index &#124; pg_catalog<br />
 1249 &#124; pg_attribute &#124; pg_catalog<br />
 1259 &#124; pg_class &#124; pg_catalog<br />
 2608 &#124; pg_depend &#124; pg_catalog</p>
<p>postgres=# SELECT i.indexrelid::regclass as index_name, i.indexrelid as index_oid, ns.nspname<br />
 FROM pg_index i<br />
 JOIN pg_class c ON i.indrelid = c.oid<br />
 JOIN pg_namespace AS ns ON ns.oid = c.relnamespace<br />
 WHERE c.oid IN (2704, 2703, 2696, 2674, 2673, 2659, 2658, 2619, 2608, 1249, 1247, 40972, 2662, 1259, 3455, 2663, 40975, 40965, 40970)<br />
;<br />
 index_name &#124; index_oid &#124; nspname<br />
-----------------------------------+-----------+------------<br />
 pg_type_typname_nsp_index &#124; 2704 &#124; pg_catalog<br />
 pg_attribute_relid_attnam_index &#124; 2658 &#124; pg_catalog<br />
 tracking_pkey &#124; 40970 &#124; public<br />
 pg_class_relname_nsp_index &#124; 2663 &#124; pg_catalog<br />
 pg_class_tblspc_relfilenode_index &#124; 3455 &#124; pg_catalog<br />
 pg_type_oid_index &#124; 2703 &#124; pg_catalog<br />
 pg_attribute_relid_attnum_index &#124; 2659 &#124; pg_catalog<br />
 pg_statistic_relid_att_inh_index &#124; 2696 &#124; pg_catalog<br />
 pg_depend_depender_index &#124; 2673 &#124; pg_catalog<br />
 pg_depend_reference_index &#124; 2674 &#124; pg_catalog<br />
 pg_class_oid_index &#124; 2662 &#124; pg_catalog<br />
Attempt 1: NULL out<br />
Then we also NULL the columns in PostgreSQL. From here on, we save the view of the file system, as PostgreSQL seems to know the file sizes exactly, as we have seen above:<br />
postgres=# UPDATE tracking<br />
SET d0 = NULL, d1 = NULL, d2 = NULL, d3 = NULL, d4 = NULL<br />
 , d5 = NULL, d6 = NULL, d7 = NULL, d8 = NULL, d9 = NULL<br />
;</p>
<p>postgres=# SELECT pg_relation_size(\'tracking\') AS tab_siz<br />
 , pg_size_pretty(pg_relation_size(\'tracking\')) AS tab_siz_prtty<br />
 , pg_indexes_size(\'tracking\') AS idx_siz<br />
 , pg_size_pretty(pg_indexes_size(\'tracking\')) AS idx_siz_prtty<br />
 , pg_relation_size(\'tracking\') + pg_indexes_size(\'tracking\') AS tab_and_idx_siz<br />
 , pg_size_pretty(pg_relation_size(\'tracking\') + pg_indexes_size(\'tracking\')) AS tab_and_idx_siz_prtty<br />
 , pg_total_relation_size(\'tracking\') AS tot_rel_siz<br />
 , pg_size_pretty(pg_total_relation_size(\'tracking\')) AS tot_rel_siz_prtty<br />
;<br />
 tab_siz &#124; tab_siz_prtty &#124; idx_siz &#124; idx_siz_prtty &#124; tab_and_idx_siz &#124; tab_and_idx_siz_prtty &#124; tot_rel_siz &#124; tot_rel_siz_prtty<br />
------------+---------------+-----------+---------------+-----------------+-----------------------+-------------+-------------------<br />
 2695716864 &#124; 2571 MB &#124; 753696768 &#124; 719 MB &#124; 3449413632 &#124; 3290 MB &#124; 3450101760 &#124; 3290 MB<br />
Here we see that the table segments grow massively (+37%), which is called ‘bloat’ in PostgreSQL terminology. The MVCC implementation of PostgreSQL stores both the old and never new version of the row ‘in-place’ directly in the table, in contrast to MariaDB/MySQL which stores the old version in UNDO space and the new row ‘in-place’. The index file also increases significantly (+100%). We need to do more research to find out why this is the case. In addition, a ‘free space map’ is created again (difference between tot_rel_siz and tab_and_idx_siz).<br />
A subsequent VACUUM FULL reduces the table (to 28%) and the index (to 50%) again in relation to the previous size:<br />
postgres=# VACUUM FULL tracking;</p>
<p>postgres=# SELECT pg_relation_size(\'tracking\') AS tab_siz<br />
 , pg_size_pretty(pg_relation_size(\'tracking\')) AS tab_siz_prtty<br />
 , pg_indexes_size(\'tracking\') AS idx_siz<br />
 , pg_size_pretty(pg_indexes_size(\'tracking\')) AS idx_siz_prtty<br />
 , pg_relation_size(\'tracking\') + pg_indexes_size(\'tracking\') AS tab_and_idx_siz<br />
 , pg_size_pretty(pg_relation_size(\'tracking\') + pg_indexes_size(\'tracking\')) AS tab_and_idx_siz_prtty<br />
 , pg_total_relation_size(\'tracking\') AS tot_rel_siz<br />
 , pg_size_pretty(pg_total_relation_size(\'tracking\')) AS tot_rel_siz_prtty<br />
;<br />
 tab_siz &#124; tab_siz_prtty &#124; idx_siz &#124; idx_siz_prtty &#124; tab_and_idx_siz &#124; tab_and_idx_siz_prtty &#124; tot_rel_siz &#124; tot_rel_siz_prtty<br />
-----------+---------------+-----------+---------------+-----------------+-----------------------+-------------+-------------------<br />
 742916096 &#124; 709 MB &#124; 376864768 &#124; 359 MB &#124; 1119780864 &#124; 1068 MB &#124; 1119780864 &#124; 1068 MB<br />
and also in relation to the original size, the table (to 38%) and the index (to 100%) become smaller again. Why the index has remained the same size and only the table has shrunk remains to be investigated…<br />
Experiment 2: Deleting the columns<br />
The columns are then dropped with DROP COLUMN.<br />
postgres=# ALTER TABLE tracking<br />
 DROP COLUMN d0, DROP COLUMN d1, DROP COLUMN d2, DROP COLUMN d3, DROP COLUMN d4<br />
, DROP COLUMN d5, DROP COLUMN d6, DROP COLUMN d7, DROP COLUMN d8, DROP COLUMN d9<br />
;<br />
As the response was immediate, it can be assumed that this operation is also instantaneous. Unfortunately, I couldn’t find anything about this in the PostgreSQL documentation.<br />
Nothing has changed significantly in terms of size, which is actually to be expected with an instant operation. However, the fact that the size did not change after the VACUUM FULL command was a little surprising:<br />
 tab_siz &#124; tab_siz_prtty &#124; idx_siz &#124; idx_siz_prtty &#124; tab_and_idx_siz &#124; tab_and_idx_siz_prtty &#124; tot_rel_siz &#124; tot_rel_siz_prtty<br />
-----------+---------------+-----------+---------------+-----------------+-----------------------+-------------+-------------------<br />
 742916096 &#124; 709 MB &#124; 376864768 &#124; 359 MB &#124; 1119780864 &#124; 1068 MB &#124; 1120010240 &#124; 1068 MB</p>
<p>postgres=# VACUUM FULL tracking;</p>
<p> tab_siz &#124; tab_siz_prtty &#124; idx_siz &#124; idx_siz_prtty &#124; tab_and_idx_siz &#124; tab_and_idx_siz_prtty &#124; tot_rel_siz &#124; tot_rel_siz_prtty<br />
-----------+---------------+-----------+---------------+-----------------+-----------------------+-------------+-------------------<br />
 742916096 &#124; 709 MB &#124; 376864768 &#124; 359 MB &#124; 1119780864 &#124; 1068 MB &#124; 1119780864 &#124; 1068 MB<br />
Remarks<br />
Locking in PostgreSQL works as follows:</p>
<p>VACUUM Concurrent DML commands are possible similar to the MariaDB/MySQL OPTIMIZE TABLE command. However, the result is not quite the same.<br />
VACUUM FULL causes an ACCESS EXCLUSIVE lock. Similar to the MariaDB/MySQL 5.5 and older OPTIMIZE TABLE command. DML and SELECT commands are NOT permitted.</p>
<p>Sources</p>
<p>How to Get Sizes of Database Objects in PostgreSQL<br />
Database File Layout<br />
System Administration Functions<br />
CLUSTER<br />
VACUUM<br />
Explicit Locking</p>
<p>Additional attempts</p>
<p>Instead of 0.0, NULL was filled into the columns d0 - d9. The table remained small (tot_rel_siz_prtty = 1068 MB). It is therefore also worth saving NULL instead of dummy values with PostgreSQL.<br />
The columns d0 - d9 were created with DOUBLE PRECISION NOT NULL and the values 0.0 were filled. No effect: The table remained large (tot_rel_siz_prtty = 2232 MB).</p>
<p>This page was translated using deepl.com.</p>
<p>The post <a rel="nofollow" href="https://www.fromdual.com/blog/how-much-space-does-null-need/">How much space does NULL need?</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>The last time I consulted a customer, he came up to me beaming with joy and said that he had taken my advice and changed all the primary key columns from <code>BIGINT</code> (8 bytes) to <code>INT</code> (4 bytes) and that had made a big difference! His MySQL 8.4 database is now 750 Gbyte smaller (from 5.5 Tbyte). Nice!</p>
<p>And yes, I know that contradicts the recommendations of some of my PostgreSQL colleagues (<a href="https://www.crunchydata.com/blog/postgres-serials-should-be-bigint-and-how-to-migrate" target="_blank" rel="noopener">here</a> and <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" rel="noopener">here</a>). In the MySQL world, more emphasis is placed on such things (<a href="https://dev.mysql.com/doc/refman/8.4/en/data-size.html" target="_blank" rel="noopener">source</a>):</p>
<blockquote>
<p>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</p>
</blockquote>
<p>Also, InnoDB works a wee bit differently (index clustered table and primary key in all secondary keys) than PostgreSQL (heap table, indices with row pointer (<code>ctid</code>)).</p>
<p>But that&rsquo;s not really the issue. Immediately afterwards, he asked me whether the deletion of columns of type <code>DOUBLE</code> (8 bytes, in PostgreSQL-speak <code>DOUBLE PRECISION</code>) would also save space or whether he should rather drop the columns straight away. My first reflex response to <code>DOUBLE</code> was: <code>NULL</code> is good, followed by <code>OPTIMIZE TABLE</code> (<code>VACUUM FULL</code> in PostgreSQL parlance). But the second thought was, <code>DOUBLE</code> is a data type of fixed length, does <code>NULL</code> also apply there or only for data types with variable length? Caution is the mother of the porcelain box! Love to consult the manual first&hellip;</p>
<p>And there it says (<a href="https://dev.mysql.com/doc/refman/8.4/en/data-size.html" target="_blank" rel="noopener">source</a>):</p>
<blockquote>
<p>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.</p>
</blockquote>
<p>and (<a href="https://dev.mysql.com/doc/refman/8.4/en/innodb-row-format.html" target="_blank" rel="noopener">source</a>):</p>
<blockquote>
<p>The variable-length part of the record header contains a bit vector for indicating NULL columns. &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.</p>
</blockquote>
<h2 id="experiment-with-mariadbmysql">Experiment with MariaDB/MySQL<a class="anchor-link" id="experiment-with-mariadb-mysql"></a></h2>
<h3 id="test-setup">Test setup<a class="anchor-link" id="test-setup"></a></h3>
<p>Somehow the description is a bit too complicated for me. Perhaps a small sketch would help? So let&rsquo;s give it a try:</p>
<pre><code>SQL&gt; -- DROP TABLE IF EXISTS tracking;

SQL&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&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&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
</code></pre>
<p>The table is approx. 1.8 Gbyte in size for both MariaDB and MySQL with 16 M rows. Since this information is only given very imprecisely in <code>INFORMATION_SCHEMA</code>, let&rsquo;s take a look at the file system:</p>
<p>MariaDB 11.8:</p>
<pre><code>SQL&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
</code></pre>
<p>MySQL 8.4:</p>
<pre><code>SQL&gt; system ls -l tracking.ibd
-rw-r----- 1 mysql mysql 1929379840 Feb 7 10:33 tracking.ibd
</code></pre>
<h3 id="defragment-the-table">Defragment the table<a class="anchor-link" id="defragment-the-table"></a></h3>
<p>Then we &lsquo;defragment&rsquo; the table with the <code>OPTIMIZE TABLE</code> command:</p>
<pre><code>SQL&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 |
+---------------+----------+----------+-------------------------------------------------------------------+
</code></pre>
<p><strong>Attention</strong>: The table is copied once! It therefore needs twice the amount of disc space for a short time! This can be observed while the <code>OPTIMIZE TABLE</code> command is running:</p>
<p>MariaDB:</p>
<pre><code>$ watch -d -n 1 'ls -l trac* #*'
-rw-rw---- 1 mysql mysql 1206 Feb 7 10:39 '#sql-alter-d57-8c.frm'
-rw-rw---- 1 mysql mysql 968884224 Feb 7 10:39 '#sql-alter-d57-8c.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
</code></pre>
<p>MySQL:</p>
<pre><code>$ watch -d -n 1 'ls -l trac* #*'
-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
</code></pre>
<p>The result is amazing! With MariaDB, the table has remained somewhat the same size:</p>
<pre><code>-rw-rw---- 1 mysql mysql 1206 Feb 7 10:39 tracking.frm
-rw-rw---- 1 mysql mysql 1912602624 Feb 7 10:39 tracking.ibd
</code></pre>
<p>With MySQL, on the other hand, the table has actually grown after the &lsquo;defragmentation&rsquo;, namely by approx. 14%:</p>
<pre><code>-rw-r----- 1 mysql mysql 2197815296 Feb 7 10:41 tracking.ibd
</code></pre>
<p>If we execute the <code>OPTIMIZE TABLE</code> command again, the size remains constant for both MariaDB and MySQL:</p>
<p>MariaDB:</p>
<pre><code>-rw-rw---- 1 mysql mysql 1206 Feb 7 10:46 tracking.frm
-rw-rw---- 1 mysql mysql 1912602624 Feb 7 10:48 tracking.ibd
</code></pre>
<p>MySQL:</p>
<pre><code>-rw-r----- 1 mysql mysql 2197815296 Feb 7 10:48 tracking.ibd
</code></pre>
<h3 id="attempt-1-null-out">Attempt 1: <code>NULL</code> out<a class="anchor-link" id="attempt-1-null-out"></a></h3>
<p>Now we <code>NULL</code> out the values:</p>
<pre><code>SQL&gt; UPDATE tracking
SET d0 = NULL, d1 = NULL, d2 = NULL, d3 = NULL, d4 = NULL
 , d5 = NULL, d6 = NULL, d7 = NULL, d8 = NULL, d9 = NULL
;
</code></pre>
<p>After this step, the sizes of the files have even grown slightly:</p>
<p>MariaDB (+1.3%):</p>
<pre><code>-rw-rw---- 1 mysql mysql 1206 Feb 7 10:49 tracking.frm
-rw-rw---- 1 mysql mysql 1937768448 Feb 7 11:04 tracking.ibd
</code></pre>
<p>MySQL (+0.2%):</p>
<pre><code>-rw-r----- 1 mysql mysql 2202009600 Feb 7 11:04 tracking.ibd
</code></pre>
<p>We then defragment the table again with the <code>OPTIMIZE TABLE</code> command. The tables shrink as expected.</p>
<p>MariaDB (to 23%):</p>
<pre><code>-rw-rw---- 1 mysql mysql 1206 Feb 7 11:09 tracking.frm
-rw-rw---- 1 mysql mysql 448790528 Feb 7 11:10 tracking.ibd
</code></pre>
<p>MySQL (to 24%):</p>
<pre><code>-rw-r----- 1 mysql mysql 520093696 Feb 7 11:10 tracking.ibd
</code></pre>
<p><code>OPTIMIZE TABLE</code> again does NOT change the file size any more&hellip;</p>
<h3 id="attempt-2-deleting-the-columns">Attempt 2: Deleting the columns<a class="anchor-link" id="attempt-2-deleting-the-columns"></a></h3>
<p>Now we try the whole thing again with the <code>DROP COLUMN</code> command. The starting position is again the same as described above:</p>
<p>MariaDB:</p>
<pre><code>-rw-rw---- 1 mysql mysql 1206 Feb 7 11:15 tracking.frm
-rw-rw---- 1 mysql mysql 1933574144 Feb 7 11:18 tracking.ibd
</code></pre>
<p>MySQL:</p>
<pre><code>-rw-r----- 1 mysql mysql 1929379840 Feb 7 11:19 tracking.ibd
</code></pre>
<p>After the <code>OPTIMIZE TABLE</code> command, the values look similar to the first attempt:</p>
<p>MariaDB:</p>
<pre><code>-rw-rw---- 1 mysql mysql 1206 Feb 7 11:20 tracking.frm
-rw-rw---- 1 mysql mysql 1912602624 Feb 7 11:21 tracking.ibd
</code></pre>
<p>MySQL:</p>
<pre><code>-rw-r----- 1 mysql mysql 2197815296 Feb 7 11:21 tracking.ibd
</code></pre>
<p><code>OPTIMIZE TABLE</code> again also brings no further changes, as above:</p>
<p>MariaDB:</p>
<pre><code>-rw-rw---- 1 mysql mysql 1206 Feb 7 11:22 tracking.frm
-rw-rw---- 1 mysql mysql 1912602624 Feb 7 11:23 tracking.ibd
</code></pre>
<p>MySQL:</p>
<pre><code>-rw-r----- 1 mysql mysql 2197815296 Feb 7 11:24 tracking.ibd
</code></pre>
<p>And now the actual second attempt with dropping the columns:</p>
<pre><code>SQL&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
;
</code></pre>
<p>The first thing we notice is that the command is <code>INSTANTANEOUS</code>, i.e. it does not make any changes to the data but only changes the metadata. On the one hand, this is good, as it minimises the impact on the application. On the other hand, it also means that no space is saved.</p>
<p>So let&rsquo;s get to grips with the whole thing again with the <code>OPTIMIZE TABLE</code> command:</p>
<p>MariaDB (to 93%):</p>
<pre><code>-rw-rw---- 1 mysql mysql 925 Feb 7 11:28 tracking.frm
-rw-rw---- 1 mysql mysql 415236096 Feb 7 11:29 tracking.ibd
</code></pre>
<p>MySQL (to 92%):</p>
<pre><code>-rw-r----- 1 mysql mysql 478150656 Feb 7 11:28 tracking.ibd
</code></pre>
<h3 id="conclusion">Conclusion<a class="anchor-link" id="conclusion"></a></h3>
<p>Both, dropping the columns and the <code>NULL</code> out of columns save a significant amount of space. Dropping the columns saves about 7% more space than <code>NULL</code> them out. If it is possible from an application point of view, you should therefore drop columns that are no longer required, or if not possible, at least <code>NULL</code> them out.</p>
<h2 id="experiment-with-postgresql">Experiment with PostgreSQL<a class="anchor-link" id="experiment-with-postgresql"></a></h2>
<p>And now let&rsquo;s take a look at the whole thing with PostgreSQL 19devel.</p>
<h3 id="test-setup-1">Test setup<a class="anchor-link" id="test-setup"></a></h3>
<p>The test setup is analogous to MariaDB/MySQL:</p>
<pre><code>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
</code></pre>
<p>Firstly, we want to know how big the table has actually become. PostgreSQL seems to know this information very precisely:</p>
<pre><code>postgres=# SELECT pg_relation_size('tracking') AS tab_siz
 , pg_size_pretty(pg_relation_size('tracking')) AS tab_siz_prtty
 , pg_indexes_size('tracking') AS idx_siz
 , pg_size_pretty(pg_indexes_size('tracking')) AS idx_siz_prtty
 , pg_relation_size('tracking') + pg_indexes_size('tracking') AS tab_and_idx_siz
 , pg_size_pretty(pg_relation_size('tracking') + pg_indexes_size('tracking')) AS tab_and_idx_siz_prtty
 , pg_total_relation_size('tracking') AS tot_rel_siz
 , pg_size_pretty(pg_total_relation_size('tracking')) 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
</code></pre>
<p>Then we want to know where these files can be found in the file system:</p>
<pre><code>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 = 'tracking';
 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 = 'tracking';
 index_name | index_oid
---------------+-----------
 tracking_pkey | 40970

postgres=# SELECT pg_relation_filepath('tracking');
 pg_relation_filepath
----------------------
 base/5/40965
</code></pre>
<p>Table and index size in the file system:</p>
<pre><code>$ 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
</code></pre>
<ul>
<li><code>*_fsm</code> means &ldquo;free space map&rdquo;</li>
<li><code>*_vm</code> means &ldquo;visibility map&rdquo;</li>
<li><code>*.1</code> means 2nd segment of the object (table or index)</li>
</ul>
<p>PostgreSQL seems to work with segments of 1 Gbyte by default and, unlike MariaDB/MySQL (<code>INFORMATION_SCHEMA</code>), knows exactly how large its files are. And the discrepancy from above (between <code>tot_rel_siz</code> and <code>tab_and_idx_siz</code>) can be explained by the <code>fsm</code> and <code>vm</code> files.</p>
<p>The PostgreSQL equivalent of the MariaDB/MySQL <code>OPTIMIZE TABLE</code> is the <code>VACUUM FULL</code> command:</p>
<pre><code>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
</code></pre>
<p>The first thing you notice is that PostgreSQL touches quite a few files and the &lsquo;free space map&rsquo; file has disappeared. In contrast to MariaDB/MySQL, the table segments have remained the same size. You can also see that the old table has &lsquo;disappeared&rsquo; (40965, 40970) and a new one has been created (40972 and 40975). The <code>VACUUM FULL</code> command in PostgreSQL also creates a copy of the data, as in MariaDB/MySQL.</p>
<pre><code>postgres=# SELECT pg_relation_size('tracking') AS tab_siz
 , pg_size_pretty(pg_relation_size('tracking')) AS tab_siz_prtty
 , pg_indexes_size('tracking') AS idx_siz
 , pg_size_pretty(pg_indexes_size('tracking')) AS idx_siz_prtty
 , pg_relation_size('tracking') + pg_indexes_size('tracking') AS tab_and_idx_siz
 , pg_size_pretty(pg_relation_size('tracking') + pg_indexes_size('tracking')) AS tab_and_idx_siz_prtty
 , pg_total_relation_size('tracking') AS tot_rel_siz
 , pg_size_pretty(pg_total_relation_size('tracking')) 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
</code></pre>
<p>The following query helps to understand which other files/objects have been created:</p>
<pre><code>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
</code></pre>
<h3 id="attempt-1-null-out-1">Attempt 1: <code>NULL</code> out<a class="anchor-link" id="attempt-1-null-out"></a></h3>
<p>Then we also <code>NULL</code> the columns in PostgreSQL. From here on, we save the view of the file system, as PostgreSQL seems to know the file sizes exactly, as we have seen above:</p>
<pre><code>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('tracking') AS tab_siz
 , pg_size_pretty(pg_relation_size('tracking')) AS tab_siz_prtty
 , pg_indexes_size('tracking') AS idx_siz
 , pg_size_pretty(pg_indexes_size('tracking')) AS idx_siz_prtty
 , pg_relation_size('tracking') + pg_indexes_size('tracking') AS tab_and_idx_siz
 , pg_size_pretty(pg_relation_size('tracking') + pg_indexes_size('tracking')) AS tab_and_idx_siz_prtty
 , pg_total_relation_size('tracking') AS tot_rel_siz
 , pg_size_pretty(pg_total_relation_size('tracking')) 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
</code></pre>
<p>Here we see that the table segments grow massively (+37%), which is called &lsquo;bloat&rsquo; in PostgreSQL terminology. The MVCC implementation of PostgreSQL stores both the old and never new version of the row &lsquo;in-place&rsquo; directly in the table, in contrast to MariaDB/MySQL which stores the old version in UNDO space and the new row &lsquo;in-place&rsquo;. The index file also increases significantly (+100%). We need to do more research to find out why this is the case. In addition, a &lsquo;free space map&rsquo; is created again (difference between <code>tot_rel_siz</code> and <code>tab_and_idx_siz</code>).</p>
<p>A subsequent <code>VACUUM FULL</code> reduces the table (to 28%) and the index (to 50%) again in relation to the previous size:</p>
<pre><code>postgres=# VACUUM FULL tracking;

postgres=# SELECT pg_relation_size('tracking') AS tab_siz
 , pg_size_pretty(pg_relation_size('tracking')) AS tab_siz_prtty
 , pg_indexes_size('tracking') AS idx_siz
 , pg_size_pretty(pg_indexes_size('tracking')) AS idx_siz_prtty
 , pg_relation_size('tracking') + pg_indexes_size('tracking') AS tab_and_idx_siz
 , pg_size_pretty(pg_relation_size('tracking') + pg_indexes_size('tracking')) AS tab_and_idx_siz_prtty
 , pg_total_relation_size('tracking') AS tot_rel_siz
 , pg_size_pretty(pg_total_relation_size('tracking')) 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
</code></pre>
<p>and also in relation to the original size, the table (to 38%) and the index (to 100%) become smaller again. Why the index has remained the same size and only the table has shrunk remains to be investigated&hellip;</p>
<h3 id="experiment-2-deleting-the-columns">Experiment 2: Deleting the columns<a class="anchor-link" id="experiment-2-deleting-the-columns"></a></h3>
<p>The columns are then dropped with <code>DROP COLUMN</code>.</p>
<pre><code>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
;
</code></pre>
<p>As the response was immediate, it can be assumed that this operation is also instantaneous. Unfortunately, I couldn&rsquo;t find anything about this in the PostgreSQL documentation.</p>
<p>Nothing has changed significantly in terms of size, which is actually to be expected with an instant operation. However, the fact that the size did not change after the <code>VACUUM FULL</code> command was a little surprising:</p>
<pre><code> 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
</code></pre>
<h2 id="remarks">Remarks<a class="anchor-link" id="remarks"></a></h2>
<p>Locking in PostgreSQL works as follows:</p>
<ul>
<li><code>VACUUM</code> Concurrent DML commands are possible similar to the MariaDB/MySQL <code>OPTIMIZE TABLE</code> command. However, the result is not quite the same.</li>
<li><code>VACUUM FULL</code> causes an <code>ACCESS EXCLUSIVE</code> lock. Similar to the MariaDB/MySQL 5.5 and older <code>OPTIMIZE TABLE</code> command. DML and <code>SELECT</code> commands are NOT permitted.</li>
</ul>
<h2 id="sources">Sources<a class="anchor-link" id="sources"></a></h2>
<ul>
<li><a href="https://neon.com/postgresql/postgresql-administration/postgresql-database-indexes-table-size" target="_blank" rel="noopener">How to Get Sizes of Database Objects in PostgreSQL</a></li>
<li><a href="https://www.postgresql.org/docs/current/storage-file-layout.html" target="_blank" rel="noopener">Database File Layout</a></li>
<li><a href="https://www.postgresql.org/docs/current/functions-admin.html" target="_blank" rel="noopener">System Administration Functions</a></li>
<li><a href="https://www.postgresql.org/docs/current/sql-cluster.html" target="_blank" rel="noopener">CLUSTER</a></li>
<li><a href="https://www.postgresql.org/docs/current/sql-vacuum.html" target="_blank" rel="noopener">VACUUM</a></li>
<li><a href="https://www.postgresql.org/docs/current/explicit-locking.html" target="_blank" rel="noopener">Explicit Locking</a></li>
</ul>
<h2 id="additional-attempts">Additional attempts<a class="anchor-link" id="additional-attempts"></a></h2>
<ol>
<li>Instead of <code>0.0</code>, <code>NULL</code> was filled into the columns <code>d0</code> &ndash; <code>d9</code>. The table remained small (<code>tot_rel_siz_prtty = 1068 MB</code>). It is therefore also worth saving <code>NULL</code> instead of dummy values with PostgreSQL.</li>
<li>The columns <code>d0</code> &ndash; <code>d9</code> were created with <code>DOUBLE PRECISION NOT NULL</code> and the values <code>0.0</code> were filled. No effect: The table remained large (<code>tot_rel_siz_prtty = 2232 MB</code>).</li>
</ol>
<p>This page was translated using <a href="https://www.deepl.com/en/translator" target="_blank" rel="noopener">deepl.com</a>.</p>

<p>The post <a rel="nofollow" href="https://www.fromdual.com/blog/how-much-space-does-null-need/">How much space does NULL need?</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Someone is deleting my shared memory segments!</title>
      <link>https://www.fromdual.com/blog/deleted-postgresql-shared-memory-segments/</link>
      <pubDate>Sun, 08 Feb 2026 05:27:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://www.fromdual.com/aggregator/categories/9/">FromDual GmbH</source>
      <description><![CDATA[<p>When we work with PostgreSQL under our myEnv, we regularly get shared memory segment errors. Example:<br />
psql: error: connection to server on socket \"/tmp/.s.PGSQL.5433\" failed:<br />
FATAL: could not open shared memory segment \"/PostgreSQL.4220847662\":<br />
No such file or directory<br />
or we see similar messages in the PostgreSQL error log:<br />
ERROR: could not open shared memory segment \"/PostgreSQL.4220847662\":<br />
No such file or directory<br />
Because I am a MariaDB/MySQL admin, I am not very familiar with shared memory problems (MariaDB/MySQL does not work with shared memory). Fortunately, a search on the Internet led us on the right track (source). It is noted there:</p>
<p>The documentation of systemd states that this only happens for<br />
non-system users. Can you check whether your “postgres” user (or<br />
whatever you are using) is a system user?</p>
<p>Linux System User<br />
First I had to find out what a system user under Linux actually is. I found an answer here: What’s the difference between a normal user and a system user?.</p>
<p>That is not a technical difference but an organizational decision. E.g. it makes sense to show normal users in a login dialog (so that you can click them instead of having to type the user name) but it wouldn’t to show system accounts (the UIDs under which daemons and other automatic processes run) there.</p>
<p>The LSB standard says: User ID Ranges:</p>
<p>The system User IDs from 0 to 99 should be statically allocated by the system, and shall not be created by applications.<br />
The system User IDs from 100 to 499 should be reserved for dynamic allocation by system administrators and post install scripts using useradd.</p>
<p>On my Ubuntu system it looks like this:<br />
$ grep SYS_ /etc/login.defs<br />
#SYS_UID_MIN 100<br />
#SYS_UID_MAX 999<br />
#SYS_GID_MIN 100<br />
#SYS_GID_MAX 999<br />
This would be correct for the PostgreSQL user:<br />
$ id postgres<br />
uid=130(postgres) gid=142(postgres) groups=142(postgres),116(ssl-cert)<br />
But since the PostgreSQL instance in question runs under our myEnv, that’s different:<br />
$ id dba<br />
uid=1001(dba) gid=1001(dba) groups=1001(dba)<br />
So now we have two options:</p>
<p>We change SYS_UID_MAX and SYS_GID_MAX to 1001 (simple variant).<br />
Or we change the UID and the GID of our user dba to less than 1000.</p>
<p>Simple variant: Change SYS_UID_MAX and SYS_GID_MAX to 1001<br />
# /etc/login.defs<br />
SYS_UID_MAX 1001<br />
SYS_GID_MAX 1001<br />
To be on the safe side, the machine was rebooted. But that did not help: After a short time, the same errors occur again.<br />
More complicated variant: Changing the UID from 1001 to 990<br />
$ cat /etc/passwd<br />
...<br />
polkitd:x:997:997:User for polkitd:/:/usr/sbin/nologin<br />
systemd-coredump:x:998:998:systemd Core Dumper:/:/usr/sbin/nologin<br />
tomcat:x:999:999:Apache Tomcat:/:/sbin/nologin<br />
oli:x:1000:1000:Oli Sennhauser,,,:/home/oli:/bin/bash<br />
dba:x:1001:1001:DBA user:/home/dba:/bin/bash<br />
...</p>
<p>$ id dba<br />
uid=1001(dba) gid=1001(dba) groups=1001(dba)<br />
To do this, all processes of this user must be stopped!<br />
$ usermod --uid 990 dba<br />
$ groupmod --gid 990 dba</p>
<p>$ find / -user 1001 -exec chown --no-dereference dba {} ;<br />
$ find / -group 1001 -exec chgrp --no-dereference dba {} ;<br />
This seems to have solved the problem!<br />
Additional information<br />
A source mentioned above also recommended setting the following parameters in systemd-logind:<br />
# /etc/systemd/system/systemd-logind.service.d/override.conf<br />
RemoveIPC=no<br />
RuntimeDirectorySize=1%<br />
However, this measure is no longer necessary as it works without this change.<br />
This page was translated using deepl.com.</p>
<p>The post <a rel="nofollow" href="https://www.fromdual.com/blog/deleted-postgresql-shared-memory-segments/">Someone is deleting my shared memory segments!</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>When we work with PostgreSQL under our <a href="https://www.fromdual.com/myenv/">myEnv</a>, we regularly get shared memory segment errors. Example:</p>
<pre><code>psql: error: connection to server on socket "/tmp/.s.PGSQL.5433" failed:
FATAL: could not open shared memory segment "/PostgreSQL.4220847662":
No such file or directory
</code></pre>
<p>or we see similar messages in the PostgreSQL error log:</p>
<pre><code>ERROR: could not open shared memory segment "/PostgreSQL.4220847662":
No such file or directory
</code></pre>
<p>Because I am a MariaDB/MySQL admin, I am not very familiar with shared memory problems (MariaDB/MySQL does not work with shared memory). Fortunately, a search on the Internet led us on the right track (<a href="https://www.postgresql.org/message-id/56A52018.1030001%40gmx.net" target="_blank" title="Re: systemd deletes shared memory segment in /dev/shm/Postgresql.NNNNNN" rel="noopener">source</a>). It is noted there:</p>
<blockquote>
<p>The documentation of systemd states that this only happens for<br>
non-system users. Can you check whether your &ldquo;postgres&rdquo; user (or<br>
whatever you are using) is a system user?</p>
</blockquote>
<h2 id="linux-system-user">Linux System User<a class="anchor-link" id="linux-system-user"></a></h2>
<p>First I had to find out what a system user under Linux actually is. I found an answer here: <a href="https://unix.stackexchange.com/questions/80277/whats-the-difference-between-a-normal-user-and-a-system-user" target="_blank" rel="noopener">What&rsquo;s the difference between a normal user and a system user?</a>.</p>
<blockquote>
<p>That is not a technical difference but an organizational decision. E.g. it makes sense to show normal users in a login dialog (so that you can click them instead of having to type the user name) but it wouldn&rsquo;t to show system accounts (the UIDs under which daemons and other automatic processes run) there.</p>
</blockquote>
<p>The LSB standard says: <a href="https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/uidrange.html" target="_blank" rel="noopener">User ID Ranges</a>:</p>
<blockquote>
<p>The system User IDs from 0 to 99 should be statically allocated by the system, and shall not be created by applications.<br>
The system User IDs from 100 to 499 should be reserved for dynamic allocation by system administrators and post install scripts using useradd.</p>
</blockquote>
<p>On my Ubuntu system it looks like this:</p>
<pre><code>$ grep SYS_ /etc/login.defs
#SYS_UID_MIN 100
#SYS_UID_MAX 999
#SYS_GID_MIN 100
#SYS_GID_MAX 999
</code></pre>
<p>This would be correct for the PostgreSQL user:</p>
<pre><code>$ id postgres
uid=130(postgres) gid=142(postgres) groups=142(postgres),116(ssl-cert)
</code></pre>
<p>But since the PostgreSQL instance in question runs under our <a href="https://www.fromdual.com/myenv/">myEnv</a>, that&rsquo;s different:</p>
<pre><code>$ id dba
uid=1001(dba) gid=1001(dba) groups=1001(dba)
</code></pre>
<p>So now we have two options:</p>
<ol>
<li>We change <code>SYS_UID_MAX</code> and <code>SYS_GID_MAX</code> to 1001 (simple variant).</li>
<li>Or we change the <code>UID</code> and the <code>GID</code> of our user <code>dba</code> to less than 1000.</li>
</ol>
<h2 id="simple-variant-change-sys_uid_max-and-sys_gid_max-to-1001">Simple variant: Change <code>SYS_UID_MAX</code> and <code>SYS_GID_MAX</code> to 1001<a class="anchor-link" id="simple-variant-change-sys_uid_max-and-sys_gid_max-to-1001"></a></h2>
<pre><code># /etc/login.defs
SYS_UID_MAX 1001
SYS_GID_MAX 1001
</code></pre>
<p>To be on the safe side, the machine was rebooted. But that did not help: After a short time, the same errors occur again.</p>
<h2 id="more-complicated-variant-changing-the-uid-from-1001-to-990">More complicated variant: Changing the <code>UID</code> from 1001 to 990<a class="anchor-link" id="more-complicated-variant-changing-the-uid-from-1001-to-990"></a></h2>
<pre><code>$ cat /etc/passwd
...
polkitd:x:997:997:User for polkitd:/:/usr/sbin/nologin
systemd-coredump:x:998:998:systemd Core Dumper:/:/usr/sbin/nologin
tomcat:x:999:999:Apache Tomcat:/:/sbin/nologin
oli:x:1000:1000:Oli Sennhauser,,,:/home/oli:/bin/bash
dba:x:1001:1001:DBA user:/home/dba:/bin/bash
...

$ id dba
uid=1001(dba) gid=1001(dba) groups=1001(dba)
</code></pre>
<p>To do this, all processes of this user must be stopped!</p>
<pre><code>$ usermod --uid 990 dba
$ groupmod --gid 990 dba

$ find / -user 1001 -exec chown --no-dereference dba {} ;
$ find / -group 1001 -exec chgrp --no-dereference dba {} ;
</code></pre>
<p>This seems to have solved the problem!</p>
<h2 id="additional-information">Additional information<a class="anchor-link" id="additional-information"></a></h2>
<p>A source mentioned above also recommended setting the following parameters in <code>systemd-logind</code>:</p>
<pre><code># /etc/systemd/system/systemd-logind.service.d/override.conf
RemoveIPC=no
RuntimeDirectorySize=1%
</code></pre>
<p>However, this measure is no longer necessary as it works without this change.</p>
<p>This page was translated using <a href="https://www.deepl.com/en/translator" target="_blank" rel="noopener">deepl.com</a>.</p>

<p>The post <a rel="nofollow" href="https://www.fromdual.com/blog/deleted-postgresql-shared-memory-segments/">Someone is deleting my shared memory segments!</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Load CSV files into the database</title>
      <link>https://www.fromdual.com/blog/load-csv-files-into-the-database/</link>
      <pubDate>Fri, 06 Feb 2026 17:04:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://www.fromdual.com/aggregator/categories/9/">FromDual GmbH</source>
      <description><![CDATA[<p>Recently, I wanted to display the places of residence of the members of my club on a map for a personal gimmick (IGOC members). I knew the addresses of the club members. But not the coordinates of their places of residence.<br />
So I went in search of the coordinates and found what I was looking for at the Federal Office of Topography (swisstopo).<br />
The data is available there as a CSV file. Details here: Swiss town coordinates.<br />
How do I load this data into a database?<br />
Loading the data with MariaDB/MySQL<br />
MariaDB and MySQL have the LOAD DATA INFILE command:<br />
SQL > DROP TABLE IF EXISTS wgs84;</p>
<p>SQL > -- SET GLOBAL local_infile = ON; -- Only needed with MySQL</p>
<p>SQL > CREATE TABLE wgs84 (<br />
 ortschaftsname VARCHAR(32)<br />
, plz4 SMALLINT<br />
, zusatzziffer SMALLINT<br />
, zip_id SMALLINT UNSIGNED<br />
, gemeindename VARCHAR(32)<br />
, bfs_nr SMALLINT<br />
, kantonskuerzel CHAR(2)<br />
, adressenanteil varchar(8)<br />
, e DOUBLE<br />
, n DOUBLE<br />
, sprache VARCHAR(8)<br />
, validity VARCHAR(12)<br />
);</p>
<p>SQL > -- TRUNCATE TABLE wgs84;</p>
<p>SQL > LOAD DATA LOCAL INFILE \'/tmp/AMTOVZ_CSV_WGS84/AMTOVZ_CSV_WGS84.csv\'<br />
INTO TABLE wgs84<br />
FIELDS TERMINATED BY \';\'<br />
LINES TERMINATED BY \'rn\'<br />
IGNORE 1 LINES<br />
;<br />
Query OK, 5713 rows affected<br />
Records: 5713 Deleted: 0 Skipped: 0 Warnings: 0<br />
You can then query the data in the database:<br />
SQL > SELECT * FROM wgs84 ORDER BY ortschaftsname LIMIT 5;<br />
+----------------+------+--------------+--------+--------------+--------+----------------+----------------+-------------------+--------------------+---------+------------+<br />
&#124; ortschaftsname &#124; plz4 &#124; zusatzziffer &#124; zip_id &#124; gemeindename &#124; bfs_nr &#124; kantonskuerzel &#124; adressenanteil &#124; e &#124; n &#124; sprache &#124; validity &#124;<br />
+----------------+------+--------------+--------+--------------+--------+----------------+----------------+-------------------+--------------------+---------+------------+<br />
&#124; Aadorf &#124; 8355 &#124; 0 &#124; 4672 &#124; Aadorf &#124; 4551 &#124; TG &#124; 96.802 % &#124; 8.903193007810433 &#124; 47.491079014637265 &#124; de &#124; 2008-07-01 &#124;<br />
&#124; Aadorf &#124; 8355 &#124; 0 &#124; 4672 &#124; Elgg &#124; 294 &#124; ZH &#124; 3.198 % &#124; 8.89206766645808 &#124; 47.4933781685032 &#124; de &#124; 2008-07-01 &#124;<br />
&#124; Aarau &#124; 5000 &#124; 0 &#124; 2913 &#124; Aarau &#124; 4001 &#124; AG &#124; 99.713 % &#124; 8.048148371736266 &#124; 47.38973523857376 &#124; de &#124; 2008-07-01 &#124;<br />
&#124; Aarau &#124; 5000 &#124; 0 &#124; 2913 &#124; Suhr &#124; 4012 &#124; AG &#124; 0.287 % &#124; 8.059410934099922 &#124; 47.383298214804334 &#124; de &#124; 2008-07-01 &#124;<br />
&#124; Aarau &#124; 5004 &#124; 0 &#124; 2932 &#124; Aarau &#124; 4001 &#124; AG &#124; 100 % &#124; 8.060698546432551 &#124; 47.400587704180744 &#124; de &#124; 2008-07-01 &#124;<br />
+----------------+------+--------------+--------+--------------+--------+----------------+----------------+-------------------+--------------------+---------+------------+<br />
5 rows in set<br />
Or something more precise:<br />
SQL > SELECT ortschaftsname AS city, plz4 AS city_code, e AS lon, n AS lat<br />
 FROM wgs84 WHERE plz4 IN (8280, 4663, 6043);<br />
+-------------+-----------+-------------------+--------------------+<br />
&#124; city &#124; city_code &#124; lon &#124; lat &#124;<br />
+-------------+-----------+-------------------+--------------------+<br />
&#124; Aarburg &#124; 4663 &#124; 7.904271716719409 &#124; 47.321443418782955 &#124;<br />
&#124; Aarburg &#124; 4663 &#124; 7.889249714098425 &#124; 47.313536073562474 &#124;<br />
&#124; Aarburg &#124; 4663 &#124; 7.880309179095798 &#124; 47.31255194439023 &#124;<br />
&#124; Adligenswil &#124; 6043 &#124; 8.364849060491428 &#124; 47.07037816052481 &#124;<br />
&#124; Kreuzlingen &#124; 8280 &#124; 9.173740257895282 &#124; 47.64491046067056 &#124;<br />
&#124; Kreuzlingen &#124; 8280 &#124; 9.159171428030783 &#124; 47.654149879509134 &#124;<br />
&#124; Kreuzlingen &#124; 8280 &#124; 9.204470741840725 &#124; 47.639949130372145 &#124;<br />
+-------------+-----------+-------------------+--------------------+<br />
7 rows in set (0.003 sec)<br />
I will leave it to the reader to clean out the duplicates… :-)<br />
So far so good, now to the finer points::<br />
Differences between MariaDB and MySQL<br />
The procedure described above works perfectly with MariaDB 11.4 and 11.8. There are small differences with MySQL 8.4:<br />
The first error message that prevents loading is this one:<br />
ERROR 3948 (42000): Loading local data is disabled; this must be enabled on both the client and server sides<br />
It can be bypassed relatively easily with the command:<br />
SQL > SET GLOBAL local_infile = ON;<br />
The next attempt will fail as follows:<br />
ERROR 2068 (HY000): LOAD DATA LOCAL INFILE file request rejected due to restrictions on access.<br />
This problem can be solved by starting the MySQL client as follows:<br />
$ mysql --local-infile=1 --user=root test<br />
Sources</p>
<p>MariaDB: LOAD DATA INFILE<br />
MySQL: LOAD DATA Statement</p>
<p>Loading the data with PostgreSQL<br />
PostgreSQL has the command COPY ... FROM:<br />
postgres=# DROP TABLE IF EXISTS wgs84;</p>
<p>postgres=# CREATE TABLE wgs84 (<br />
 ortschaftsname VARCHAR(32)<br />
, plz4 SMALLINT<br />
, zusatzziffer SMALLINT<br />
, zip_id INT<br />
, gemeindename VARCHAR(32)<br />
, bfs_nr SMALLINT<br />
, kantonskuerzel CHAR(2)<br />
, adressenanteil varchar(8)<br />
, e DOUBLE PRECISION<br />
, n DOUBLE PRECISION<br />
, sprache VARCHAR(8)<br />
, validity VARCHAR(12)<br />
);</p>
<p>postgres=# -- TRUNCATE TABLE wgs84;</p>
<p>postgres=# COPY wgs84<br />
FROM \'/tmp/AMTOVZ_CSV_WGS84/AMTOVZ_CSV_WGS84.csv\'<br />
DELIMITER \';\'<br />
CSV HEADER<br />
;<br />
COPY 5713<br />
Here, too, we receive the result as expected in the usual PostgreSQL form:<br />
postgres=# SELECT ortschaftsname AS city, plz4 AS city_code, e AS lon, n AS lat<br />
 FROM wgs84 WHERE plz4 IN (8280, 4663, 6043);<br />
 city &#124; city_code &#124; lon &#124; lat<br />
-------------+-----------+-------------------+--------------------<br />
 Aarburg &#124; 4663 &#124; 7.904271716719409 &#124; 47.321443418782955<br />
 Aarburg &#124; 4663 &#124; 7.889249714098425 &#124; 47.313536073562474<br />
 Aarburg &#124; 4663 &#124; 7.880309179095798 &#124; 47.31255194439023<br />
 Adligenswil &#124; 6043 &#124; 8.36487538940682 &#124; 47.07037794822416<br />
 Kreuzlingen &#124; 8280 &#124; 9.173740257895282 &#124; 47.64491046067056<br />
 Kreuzlingen &#124; 8280 &#124; 9.159171428030783 &#124; 47.654149879509134<br />
 Kreuzlingen &#124; 8280 &#124; 9.204470741840725 &#124; 47.639949130372145<br />
(7 rows)<br />
Sources</p>
<p>PostgreSQL: COPY</p>
<p>Small differences between MariaDB/MySQL and PostgreSQL<br />
Basically, the load command is completely different in the two database worlds.<br />
With MariaDB and PostgreSQL, the commands run “out-of-the-box”. MySQL has two additional security hurdles built in here.<br />
PostgreSQL does not recognise UNSIGNED integer data types, so the next largest data type (INT) must be used, which is a little less space-saving than with MariaDB/MySQL.<br />
Remarks<br />
When we did the same test a few days ago, there was still a loading error. So it seems that the data source has also changed slightly…<br />
I have not found out quickly whether there is an SQL standard for these load commands and if so, whether MariaDB/MySQL or PostgreSQL are standard-compliant here.<br />
And of course there are other ways to get your CSV data into the database…<br />
The tools mariadb-import/mysqlimport are used if you want to do this from the command line. The CSV Storage Engine can also be misused for this purpose (see here for details). An officially supported variant is the MariaDB CONNECT Storage Engine with the CSV type (see here):<br />
SQL > INSTALL SONAME \'ha_connect\';</p>
<p>SQL > CREATE TABLE wgs84_fdw<br />
ENGINE = CONNECT<br />
table_type = CSV<br />
file_name=\'/tmp/AMTOVZ_CSV_WGS84/AMTOVZ_CSV_WGS84.csv\'<br />
header = 1<br />
sep_char = \';\'<br />
quoted = 0;</p>
<p>SQL > INSERT INTO wgs84 SELECT * FROM wgs84_fdw;<br />
Unfortunately, it looks like the CONNECT Storage Engine will no longer be supported by MariaDB! And the mydumper/myloader tool also seems to be able to handle CSV files.<br />
And of course the whole thing can also be solved using applications…<br />
With PostgreSQL there are the following options:<br />
postgres=# copy wgs84 FROM \'/tmp/AMTOVZ_CSV_WGS84/AMTOVZ_CSV_WGS84.csv\' DELIMITER \';\' CSV HEADER<br />
then from the shell:<br />
$ psql --user=dba -c \"copy wgs84 FROM \'/tmp/AMTOVZ_CSV_WGS84/AMTOVZ_CSV_WGS84.csv\' DELIMITER \';\' CSV HEADER\"<br />
And the variant via the Foreign Data Wrapper (FWD). But I have not tried this:<br />
postgres=# CREATE EXTENSION postgres_fdw;</p>
<p>postgres=# CREATE SERVER foreign_server<br />
 FOREIGN DATA WRAPPER postgres_fdw<br />
 OPTIONS (<br />
 datasource \'CSV:/tmp/AMTOVZ_CSV_WGS84/AMTOVZ_CSV_WGS84.csv\',<br />
 format \'CSV\'<br />
 )<br />
;</p>
<p>postgres=# CREATE USER MAPPING FOR local_user<br />
 SERVER foreign_server<br />
 OPTIONS (user \'foreign_user\', password \'password\')<br />
;</p>
<p>postgres=# CREATE FOREIGN TABLE foreign_table (<br />
 id integer NOT NULL,<br />
 data text<br />
)<br />
 SERVER foreign_server<br />
 OPTIONS (schema_name \'some_schema\', table_name \'some_table\')<br />
;<br />
This page was translated using deepl.com.</p>
<p>The post <a rel="nofollow" href="https://www.fromdual.com/blog/load-csv-files-into-the-database/">Load CSV files into the database</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>Recently, I wanted to display the places of residence of the members of my club on a map for a personal gimmick (<a href="https://www.shinguz.ch/computer/gis/igoc-mitglieder/" target="_blank" rel="noopener">IGOC members</a>). I knew the addresses of the club members. But not the coordinates of their places of residence.</p>
<p>So I went in search of the coordinates and found what I was looking for at the Federal Office of Topography (<a href="https://www.swisstopo.admin.ch/en" target="_blank" rel="noopener">swisstopo</a>).</p>
<p>The data is available there as a CSV file. Details here: <a href="https://www.shinguz.ch/computer/gis/schweizer-ortschafts-koordinaten/" target="_blank" rel="noopener">Swiss town coordinates</a>.</p>
<p>How do I load this data into a database?</p>
<h2 id="loading-the-data-with-mariadbmysql">Loading the data with MariaDB/MySQL<a class="anchor-link" id="loading-the-data-with-mariadb-mysql"></a></h2>
<p>MariaDB and MySQL have the <code>LOAD DATA INFILE</code> command:</p>
<pre><code>SQL&gt; DROP TABLE IF EXISTS wgs84;

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

SQL&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&gt; -- TRUNCATE TABLE wgs84;

SQL&gt; LOAD DATA LOCAL INFILE '/tmp/AMTOVZ_CSV_WGS84/AMTOVZ_CSV_WGS84.csv'
INTO TABLE wgs84
FIELDS TERMINATED BY ';'
LINES TERMINATED BY 'rn'
IGNORE 1 LINES
;
Query OK, 5713 rows affected
Records: 5713 Deleted: 0 Skipped: 0 Warnings: 0
</code></pre>
<p>You can then query the data in the database:</p>
<pre><code>SQL&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
</code></pre>
<p>Or something more precise:</p>
<pre><code>SQL&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)
</code></pre>
<p>I will leave it to the reader to clean out the duplicates&hellip; &#128578;</p>
<p>So far so good, now to the finer points::</p>
<h3 id="differences-between-mariadb-and-mysql">Differences between MariaDB and MySQL<a class="anchor-link" id="differences-between-mariadb-and-mysql"></a></h3>
<p>The procedure described above works perfectly with MariaDB 11.4 and 11.8. There are small differences with MySQL 8.4:</p>
<p>The first error message that prevents loading is this one:</p>
<pre><code>ERROR 3948 (42000): Loading local data is disabled; this must be enabled on both the client and server sides
</code></pre>
<p>It can be bypassed relatively easily with the command:</p>
<pre><code>SQL&gt; SET GLOBAL local_infile = ON;
</code></pre>
<p>The next attempt will fail as follows:</p>
<pre><code>ERROR 2068 (HY000): LOAD DATA LOCAL INFILE file request rejected due to restrictions on access.
</code></pre>
<p>This problem can be solved by starting the MySQL client as follows:</p>
<pre><code>$ mysql --local-infile=1 --user=root test
</code></pre>
<h3 id="sources">Sources<a class="anchor-link" id="sources"></a></h3>
<ul>
<li>MariaDB: <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" rel="noopener">LOAD DATA INFILE</a></li>
<li>MySQL: <a href="https://dev.mysql.com/doc/refman/8.4/en/load-data.html" target="_blank" rel="noopener">LOAD DATA Statement</a></li>
</ul>
<h2 id="loading-the-data-with-postgresql">Loading the data with PostgreSQL<a class="anchor-link" id="loading-the-data-with-postgresql"></a></h2>
<p>PostgreSQL has the command <code>COPY ... FROM</code>:</p>
<pre><code>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 '/tmp/AMTOVZ_CSV_WGS84/AMTOVZ_CSV_WGS84.csv'
DELIMITER ';'
CSV HEADER
;
COPY 5713
</code></pre>
<p>Here, too, we receive the result as expected in the usual PostgreSQL form:</p>
<pre><code>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)
</code></pre>
<h3 id="sources-1">Sources<a class="anchor-link" id="sources"></a></h3>
<ul>
<li>PostgreSQL: <a href="https://www.postgresql.org/docs/current/sql-copy.html" target="_blank" rel="noopener">COPY</a></li>
</ul>
<h2 id="small-differences-between-mariadbmysql-and-postgresql">Small differences between MariaDB/MySQL and PostgreSQL<a class="anchor-link" id="small-differences-between-mariadb-mysql-and-postgresql"></a></h2>
<p>Basically, the load command is completely different in the two database worlds.</p>
<p>With MariaDB and PostgreSQL, the commands run &ldquo;out-of-the-box&rdquo;. MySQL has two additional security hurdles built in here.</p>
<p>PostgreSQL does not recognise <code>UNSIGNED</code> integer data types, so the next largest data type (<code>INT</code>) must be used, which is a little less space-saving than with MariaDB/MySQL.</p>
<h2 id="remarks">Remarks<a class="anchor-link" id="remarks"></a></h2>
<p>When we did the same test a few days ago, there was still a loading error. So it seems that the data source has also changed slightly&hellip;</p>
<p>I have not found out quickly whether there is an SQL standard for these load commands and if so, whether MariaDB/MySQL or PostgreSQL are standard-compliant here.</p>
<p>And of course there are other ways to get your CSV data into the database&hellip;</p>
<p>The tools <code>mariadb-import</code>/<code>mysqlimport</code> are used if you want to do this from the command line. The CSV Storage Engine can also be misused for this purpose (see <a href="https://www.fromdual.com/blog/csv-storage-engine/">here</a> for details). An officially supported variant is the MariaDB CONNECT Storage Engine with the CSV type (see <a href="https://mariadb.com/docs/server/server-usage/storage-engines/connect/connect-table-types/connect-csv-and-fmt-table-types" target="_blank" rel="noopener">here</a>):</p>
<pre><code>SQL&gt; INSTALL SONAME 'ha_connect';

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

SQL&gt; INSERT INTO wgs84 SELECT * FROM wgs84_fdw;
</code></pre>
<p>Unfortunately, it looks like the CONNECT Storage Engine will no longer be supported by MariaDB! And the <code>mydumper</code>/<code>myloader</code> tool also seems to be able to handle CSV files.</p>
<p>And of course the whole thing can also be solved using applications&hellip;</p>
<p>With PostgreSQL there are the following options:</p>
<pre><code>postgres=# copy wgs84 FROM '/tmp/AMTOVZ_CSV_WGS84/AMTOVZ_CSV_WGS84.csv' DELIMITER ';' CSV HEADER
</code></pre>
<p>then from the shell:</p>
<pre><code>$ psql --user=dba -c "copy wgs84 FROM '/tmp/AMTOVZ_CSV_WGS84/AMTOVZ_CSV_WGS84.csv' DELIMITER ';' CSV HEADER"
</code></pre>
<p>And the variant via the Foreign Data Wrapper (FWD). But I have not tried this:</p>
<pre><code>postgres=# CREATE EXTENSION postgres_fdw;

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

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

postgres=# CREATE FOREIGN TABLE foreign_table (
 id integer NOT NULL,
 data text
)
 SERVER foreign_server
 OPTIONS (schema_name 'some_schema', table_name 'some_table')
;
</code></pre>
<p>This page was translated using <a href="https://www.deepl.com/en/translator" target="_blank" rel="noopener">deepl.com</a>.</p>

<p>The post <a rel="nofollow" href="https://www.fromdual.com/blog/load-csv-files-into-the-database/">Load CSV files into the database</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>ClickHouse scaling and sharding best practices</title>
      <link>https://severalnines.com/blog/clickhouse-scaling-and-sharding-best-practices/</link>
      <pubDate>Fri, 06 Feb 2026 15:39:52 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://severalnines.com/">Severalnines</source>
      <description><![CDATA[<p>ClickHouse is a core engine for real-time analytics, powering critical functions like event ingestion, observability, customer analytics, and large-scale dashboards. However, scaling a ClickHouse cluster predictably while ensuring high availability, peak performance, and efficient resource utilization is challenging as workloads increase. Scaling involves complex planning beyond just adding nodes, and covers replication, sharding, storage, partitioning, […]<br />
The post ClickHouse scaling and sharding best practices appeared first on Severalnines.</p>
<p>The post <a rel="nofollow" href="https://severalnines.com/blog/clickhouse-scaling-and-sharding-best-practices/">ClickHouse scaling and sharding best practices</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>ClickHouse is a core engine for real-time analytics, powering critical functions like event ingestion, observability, customer analytics, and large-scale dashboards.</p>
<p>However, scaling a ClickHouse cluster predictably while ensuring high availability, peak performance, and efficient resource utilization is challenging as workloads increase. Scaling involves complex planning beyond just adding nodes, and covers replication, sharding, storage, partitioning, and operational workflows. Without proper processes, clusters risk performance regressions, replication lag, uneven merges, and data hotspots.</p>
<p>This post offers practical, ops-focused guidance for scaling ClickHouse clusters in any environment, from bare metal to cloud instances. We will use a two-node replica topology as a starting point to explore scaling, replication, and sharding.</p>
<figure class="wp-block-image aligncenter size-full"><img decoding="async" loading="lazy" width="506" height="705" src="https://severalnines.com/wp-content/uploads/2026/01/diagram-ch_2_node_1_shard_topology.png" alt="clickhouse 2-node, 1-shard topology  diagram" class="wp-image-42611"></figure>
<p>Topology IP detail,&nbsp;</p>
<pre class="wp-block-code"><code>$ cat /etc/hosts

10.10.10.101 rndsvr1 # replica 1
10.10.10.102 rndsvr2 # replica 2</code></pre>
<p>The ClickHouse cluster is currently running with 1 shard and 2 replicas. We&rsquo;ll beef this up to a 2-shard setup, and maintain a 1:1 shard to replica ratio . The new cluster topology will look like this:</p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="1024" height="706" src="https://severalnines.com/wp-content/uploads/2026/01/diagram-ch_2_node_2_shard_topology-1024x706.png" alt="clickhouse 2-node, 2-shard topology  diagram" class="wp-image-42612"></figure>
<h2 class="wp-block-heading" id="h-sharding-and-replication-in-clickhouse">Sharding and replication in ClickHouse<a class="anchor-link" id="sharding-and-replication-in-clickhouse"></a></h2>
<p>Before we continue, let&rsquo;s figure out what shards and replicas are, and how distributed tables work in ClickHouse. Understanding these core concepts is crucial for designing a performant, fault-tolerant, and scalable analytical infrastructure.</p>
<p>In Clickhouse a <strong>Sharding</strong> is essentially slicing or partitioning the overall dataset into smaller pieces. Instead of storing the entire dataset on a single server (vertical scaling), sharding involves distributing different subsets of the data across multiple, independent servers (horizontal scaling). Meanwhile, Replication is the process of creating and maintaining identical copies of data across multiple servers. It directly addresses the challenges of fault tolerance and high availability.</p>
<figure class="wp-block-table">
<table class="has-fixed-layout">
<tbody>
<tr>
<td></td>
<td><strong>Sharding</strong></td>
<td><strong>Replication</strong></td>
</tr>
<tr>
<td>Purpose&nbsp;</td>
<td>To handle datasets that are too large to fit on a single server, to improve write performance by distributing the load, and to increase query throughput by allowing parallel execution.</td>
<td>To ensure that the system remains operational even if one server fails (fault tolerance), to maintain continuous data access (high availability), and to improve read performance by distributing read queries across multiple copies.</td>
</tr>
<tr>
<td>Mechanism</td>
<td>Data is partitioned based on a chosen shard key (e.g., customer ID, geographical region, or a hash of a primary key). This key determines which specific subset of the total data resides on which server.</td>
<td>An entire shard&rsquo;s data is copied to one or more other servers, creating a set of replica servers for that specific shard. Changes made to the primary copy are asynchronously or synchronously propagated to all replica copies.</td>
</tr>
<tr>
<td>Outcome</td>
<td>The entire dataset is logically divided into different, smaller subsets, and each server (shard) holds a unique portion of the total data.</td>
<td>Each server designated as a replica holds an exact, redundant copy of the data present on its corresponding primary shard.</td>
</tr>
</tbody>
</table>
</figure>
<p>ClickHouse uses <strong>Distributed tables</strong> to provide a unified view over sharded and replicated data. Acting as a logical entry point, they&nbsp;</p>
<ul class="wp-block-list">
<li>Route INSERT operations to the correct shard,&nbsp;</li>
<li>Parallelize SELECT queries across replicas, and&nbsp;</li>
<li>Offer transparent read failover.&nbsp;</li>
<li>This allows applications to interact with the cluster as if it were a single database while improving performance and enabling future scale out. Even in single shard deployments, Distributed tables increase resiliency and reduce query latency.</li>
</ul>
<p>To understand how ClickHouse exposes a unified view of data in a sharded and replicated cluster, consider the following example. We define a <strong>local replicated table</strong> on each node and a <strong>Distributed table</strong> that provides a global entry point for queries across the cluster.</p>
<pre class="wp-block-code"><code>-- Local replicated table on each shard/replica
CREATE TABLE IF NOT EXISTS test_log.log_local ON CLUSTER my_cluster
(
    date Date,
    town LowCardinality(String),
    street LowCardinality(String),
    price UInt32
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/log_local', '{replica}')
PARTITION BY toYYYYMM(date)
ORDER BY (town, street, date);

-- Distributed table providing global view
CREATE TABLE IF NOT EXISTS test_log.log_distributed 
  ON CLUSTER my_cluster
  AS test_log.log_local
ENGINE = Distributed('my_cluster', 'test_log', 'log_local', rand());
</code></pre>
<h3 class="wp-block-heading" id="h-logical-relationship-between-local-and-distributed-tables">Logical Relationship Between Local and Distributed Tables<a class="anchor-link" id="logical-relationship-between-local-and-distributed-tables"></a></h3>
<p>In this example, <strong>log_local</strong> represents the actual physical storage table on each node. This table uses the <code>ReplicatedMergeTree</code> engine, meaning the data is replicated across the replicas of the same shard through ClickHouse Keeper or ZooKeeper. Every node stores its own part of the dataset, and replication ensures consistency among replicas inside the same shard.</p>
<p>The <strong>log_distributed</strong> table, on the other hand, is a <strong>logical routing layer</strong>. It does not store any data by itself. Instead, it forwards queries to all <code>log_local</code> tables across the cluster and aggregates the results before returning them to the client. In other words, the Distributed table acts as a cluster-wide &ldquo;global view,&rdquo; while the local tables remain the actual storage layer.</p>
<p>You can think of it like this:</p>
<ul class="wp-block-list">
<li><strong>Local perspective:</strong> <code>log_local</code> &rarr; stores data physically</li>
<li><strong>Cluster perspective:</strong> <code>log_distributed</code> &rarr; routes and merges queries across the cluster</li>
</ul>
<p>Both tables represent the same dataset but at different operational layers.</p>
<h4 class="wp-block-heading" id="h-where-reads-and-writes-should-go">Where Reads and Writes Should Go</h4>
<p>A common pattern in ClickHouse clusters is:</p>
<h4 class="wp-block-heading" id="h-writes-insert">Writes (INSERT)</h4>
<p>Applications typically insert data into the <strong>Distributed table</strong>:</p>
<pre class="wp-block-code"><code>INSERT INTO test_log.log_distributed (...) VALUES (...);
</code></pre>
<p>The Distributed engine decides which shard should receive the row based on the sharding key and then forwards it to the appropriate <code>log_local</code> table. From there, ReplicatedMergeTree handles replication across replicas of that shard.</p>
<h4 class="wp-block-heading" id="h-reads-select">Reads (SELECT)</h4>
<p>For cluster-wide analytics or any query requiring a global dataset, the query is executed against the Distributed table:</p>
<pre class="wp-block-code"><code>SELECT town, sum(price) FROM test_log.log_distributed GROUP BY town;
</code></pre>
<p>If you want to debug a specific node or inspect local data, you can query log_local directly.</p>
<p>In simple terms:</p>
<ul class="wp-block-list">
<li><strong>log_distributed &rarr;</strong> cluster entry point</li>
<li><strong>log_local &rarr;</strong> actual storage on each node</li>
</ul>
<h4 class="wp-block-heading" id="h-understanding-the-shard-and-replica-macros">Understanding the <code>{shard}</code> and <code>{replica}</code> Macros</h4>
<p>In this example, the local table uses:</p>
<pre class="wp-block-code"><code>ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/log_local', '{replica}')</code></pre>
<p>This pattern is essential in multi-shard clusters because:</p>
<ul class="wp-block-list">
<li><code>{shard}</code> separates the data directory for each shard</li>
<li><code>{replica}</code> identifies the replica inside that shard</li>
</ul>
<p>If you define a table without <code>{shard}</code>, such as:</p>
<pre class="wp-block-code"><code>ENGINE = ReplicatedMergeTree('/clickhouse/tables/log_local', '{replica}')</code></pre>
<p>It will still work but only if you have <strong>one shard</strong>. As soon as you expand the cluster and introduce additional shards, this layout will cause conflicts because all shards would try to use the same storage path.</p>
<p>N.B. Using <code>{shard}</code> is therefore considered best practice for scalable clusters.</p>
<h4 class="wp-block-heading" id="h-how-on-cluster-works-in-the-ddl">How <code>ON CLUSTER</code> Works in the DDL</h4>
<p>In the second DDL, we used:</p>
<pre class="wp-block-code"><code>CREATE TABLE test_log.log_distributed ON CLUSTER my_cluster AS test_log.log_local ...</code></pre>
<p>This means the statement is executed <strong>on every node in </strong><strong>my_cluster</strong>, and on each node ClickHouse will:</p>
<ol class="wp-block-list">
<li>Look for the existing <code>test_log.log_local</code> table</li>
<li>Create a <code>log_distributed</code> table that references the local table</li>
</ol>
<p>For a clean cluster wide deployment, the recommended pattern is:</p>
<ol class="wp-block-list">
<li>Create the <strong>local replicated table</strong> on all nodes:</li>
</ol>
<pre class="wp-block-code"><code>CREATE TABLE test_log.log_local ... ON CLUSTER my_cluster;</code></pre>
<ol start="2" class="wp-block-list">
<li>Then create the <strong>distributed table</strong>:</li>
</ol>
<pre class="wp-block-code"><code>CREATE TABLE test_log.log_distributed ON CLUSTER my_cluster AS test_log.log_local ENGINE = Distributed(...);</code></pre>
<p>This ensures consistent table structure and metadata across all nodes in the cluster.</p>
<h3 class="wp-block-heading" id="h-scaling-out-adding-a-new-shard-replicas-to-my-cluster">Scaling Out: Adding a New Shard + Replicas to <code>my_cluster</code><a class="anchor-link" id="scaling-out-adding-a-new-shard-replicas-to-my_cluster"></a></h3>
<p>Scaling out by adding a shard in ClickHouse is more complex than adding a replica. This is because a shard represents a horizontal data partition, meaning <strong>data locality changes</strong> when a new shard is introduced. Consequently, scaling at the shard level requires modifications to both the <strong>cluster configuration</strong> and the <strong>table layout</strong>. This post will detail the traditional steps for adding or scaling out shards and replicas.</p>
<p>Below are the required steps.</p>
<ol class="wp-block-list">
<li>Prepare /etc/hosts on ALL nodes to avoid cluster issues caused by DNS mismatches. Add new nodes rndsvr3 and rndsvr4 to the <code>/etc/hosts</code> file on every node..</li>
</ol>
<pre class="wp-block-code"><code>$ cat /etc/hosts

10.10.10.101	rndsvr1
10.10.10.102	rndsvr2
10.10.10.103	rndsvr3
10.10.10.104	rndsvr4</code></pre>
<ol start="2" class="wp-block-list">
<li>Install ClickHouse on the new nodes (rndsvr3 &amp; rndsvr4) On both new nodes and make sure the new node.
<ol class="wp-block-list">
<li>The required port must be open :
<ul class="wp-block-list">
<li>9000 : ClickHouse &harr; ClickHouse; clients &harr; ClickHouse</li>
<li>9009 : ClickHouse &harr; ClickHouse</li>
<li>9181 : ClickHouse &harr; Keeper</li>
<li>9234 : Keeper &harr; Keeper</li>
</ul>
</li>
<li>Uses the same ClickHouse version as the existing cluster,&nbsp;</li>
<li>Has proper time synchronization,&nbsp;</li>
<li>Runs on the same filesystem.</li>
<li>User permissions (<code>clickhouse:clickhouse</code>)</li>
</ol>
</li>
<li>Set shard &amp; replica macros on each node and you <strong>must</strong> define <code>shard</code> and <code>replica</code> id macros per node on <code>/etc/clickhouse-server/config.d/macros.xml</code>.</li>
</ol>
<pre class="wp-block-code"><code>-- rndsvr3
&lt;clickhouse&gt;
    &lt;macros&gt;
        &lt;shard&gt;2&lt;/shard&gt;
        &lt;replica&gt;replica1&lt;/replica&gt;
    &lt;/macros&gt;
&lt;/clickhouse&gt;

-- rndsvr4
&lt;clickhouse&gt;
    &lt;macros&gt;
        &lt;shard&gt;2&lt;/shard&gt;
        &lt;replica&gt;replica2&lt;/replica&gt;
    &lt;/macros&gt;
&lt;/clickhouse&gt;</code></pre>
<ol start="4" class="wp-block-list">
<li>Extend the <code>my_cluster</code> definition (remote_servers) on <strong>all nodes</strong>, update your <code>remote_servers</code> config (e.g. <code>config.d/clusters.xml</code>) to include the <strong>new shard.</strong></li>
</ol>
<pre class="wp-block-code"><code>&lt;clickhouse&gt;
    &lt;remote_servers&gt;
        &lt;my_cluster&gt;
            &lt;shard&gt;
                &lt;internal_replication&gt;true&lt;/internal_replication&gt;
                &lt;replica&gt;
                    &lt;host&gt;rndsvr1&lt;/host&gt;
                    &lt;port&gt;9000&lt;/port&gt;
                &lt;/replica&gt;
                &lt;replica&gt;
                    &lt;host&gt;rndsvr2&lt;/host&gt;
                    &lt;port&gt;9000&lt;/port&gt;
                &lt;/replica&gt;
            &lt;/shard&gt;

            &lt;shard&gt;
                &lt;internal_replication&gt;true&lt;/internal_replication&gt;
                &lt;replica&gt;
                    &lt;host&gt;rndsvr3&lt;/host&gt;
                    &lt;port&gt;9000&lt;/port&gt;
                &lt;/replica&gt;
                &lt;replica&gt;
                    &lt;host&gt;rndsvr4&lt;/host&gt;
                    &lt;port&gt;9000&lt;/port&gt;
                &lt;/replica&gt;
            &lt;/shard&gt;

        &lt;/my_cluster&gt;
    &lt;/remote_servers&gt;
&lt;/clickhouse&gt;</code></pre>
<ol start="5" class="wp-block-list">
<li>Extend the clickhouse keeper on each replica to make it have quorum (minimal 3 replicas) since the previous setup only had 2 replicas.</li>
</ol>
<pre class="wp-block-code"><code>&lt;keeper_server&gt;
            &lt;tcp_port&gt;9181&lt;/tcp_port&gt;
            &lt;server_id&gt;101&lt;/server_id&gt;
            &lt;log_storage_path&gt;/var/lib/clickhouse/coordination/logs&lt;/log_storage_path&gt;
            &lt;snapshot_storage_path&gt;/var/lib/clickhouse/coordination/snapshots&lt;/snapshot_storage_path&gt;

            &lt;coordination_settings&gt;
                &lt;operation_timeout_ms&gt;10000&lt;/operation_timeout_ms&gt;
                &lt;min_session_timeout_ms&gt;10000&lt;/min_session_timeout_ms&gt;
                &lt;session_timeout_ms&gt;100000&lt;/session_timeout_ms&gt;
                &lt;raft_logs_level&gt;information&lt;/raft_logs_level&gt;
                &lt;compress_logs&gt;false&lt;/compress_logs&gt;
            &lt;/coordination_settings&gt;
            &lt;hostname_checks_enabled&gt;true&lt;/hostname_checks_enabled&gt;
            &lt;raft_configuration&gt;
                &lt;server&gt;
                    &lt;id&gt;101&lt;/id&gt;
                    &lt;hostname&gt;rndsvr1&lt;/hostname&gt;
                    &lt;port&gt;9234&lt;/port&gt;
                &lt;/server&gt;
                &lt;server&gt;
                    &lt;id&gt;102&lt;/id&gt;
                    &lt;hostname&gt;rndsvr2&lt;/hostname&gt;
                    &lt;port&gt;9234&lt;/port&gt;
                &lt;/server&gt;
                &lt;server&gt;
                    &lt;id&gt;103&lt;/id&gt;
                    &lt;hostname&gt;rndsvr3&lt;/hostname&gt;
                    &lt;port&gt;9234&lt;/port&gt;
                &lt;/server&gt;
                &lt;server&gt;
                    &lt;id&gt;104&lt;/id&gt;
                    &lt;hostname&gt;rndsvr4&lt;/hostname&gt;
                    &lt;port&gt;9234&lt;/port&gt;
                &lt;/server&gt;

            &lt;/raft_configuration&gt;
    &lt;/keeper_server&gt;</code></pre>
<ol start="6" class="wp-block-list">
<li>Recreate the local replicated table and Distributed table. You can show the creation script of the tables (replicated and distributed) by using <code>show create table table_name</code> then you can recreate by using the same script but don&rsquo;t forget to add&nbsp; <code>IF NOT EXISTS</code> while recreate it.</li>
<li>Validate the new shard and replicas</li>
</ol>
<pre class="wp-block-code"><code>-- verify replicated tables and distributed tables
SELECT hostName(), name, engine
  FROM system.tables
WHERE database = 'test_log'
  AND name IN ('log_local','log_distributed')
ORDER BY hostName(), name;

-- verify count data every shard.
SELECT hostName(), count(*)
  FROM test_log.log_local
GROUP BY hostName()
ORDER BY hostName();</code></pre>
<ol start="8" class="wp-block-list">
<li>One important note: data rebalancing</li>
</ol>
<p>Adding a new shard <strong>does not automatically move old data</strong>.</p>
<ul class="wp-block-list">
<li>All <strong>new</strong> INSERTs will be distributed according to your sharding key.</li>
<li>Existing data remains on shard 1 unless you manually rebalance e.g. <code>INSERT SELECT</code> through the Distributed table into a new cluster, or using shard-migration tools(discussed later).</li>
</ul>
<h1 class="wp-block-heading" id="h-operational-nuances-for-self-managed-clickhouse">Operational nuances for self-managed ClickHouse<a class="anchor-link" id="operational-nuances-for-self-managed-clickhouse"></a></h1>
<p>Scaling ClickHouse across hybrid environments mixing on-premise servers with cloud instances introduces operational nuances that engineers must evaluate before expanding the cluster. The following considerations help ensure stable replication, predictable performance, and consistent behavior across diverse infrastructure types; let&rsquo;s start with node management:</p>
<ol class="wp-block-list">
<li><strong>Cluster membership</strong>
<ul class="wp-block-list">
<li><strong>Traditionally</strong>, you list every shard/replica in <code>remote_servers</code>:</li>
</ul>
</li>
</ol>
<pre class="wp-block-code"><code>&lt;remote_servers&gt;
    &lt;cluster_name&gt;
        &lt;shard&gt;
            &lt;replica&gt;
                &lt;host&gt;node1&lt;/host&gt;
                &lt;port&gt;9000&lt;/port&gt;
            &lt;/replica&gt;
            ...
        &lt;/shard&gt;
    &lt;/cluster_name&gt;
&lt;/remote_servers&gt;</code></pre>
<ul class="wp-block-list">
<li><strong>Cluster discovery config</strong></li>
</ul>
<p>With Cluster Discovery, nodes auto register via Keeper / ZooKeeper under a path; adding / removing a node is done by starting/stopping a ClickHouse server that registers under that path. The cluster view updates without config changes or restarts.</p>
<ol start="2" class="wp-block-list">
<li><strong>Data vs cluster config</strong>
<ul class="wp-block-list">
<li>Cluster Discovery only changes the cluster topology (what nodes are visible in <code>system.clusters</code>), not where data actually lives.</li>
<li>If you add a 4th node to a 3&#8209;node cluster, existing replicated tables are still only on the original 3 nodes unless you explicitly create/attach them on the new node (or use a Replicated database engine to manage this).&nbsp;</li>
</ul>
</li>
<li><strong>Replication behavior</strong>
<ul class="wp-block-list">
<li>Replicated tables use Keeper to coordinate inserts and merges; new nodes can catch up by copying state from an existing node or replaying logs, with configurable trade&#8209;offs between CPU and network.</li>
</ul>
</li>
<li><strong>Separate storage &amp; compute</strong></li>
</ol>
<p>You can approximate ClickHouse Cloud&rsquo;s model by using S3 / GCS backed MergeTree: data in object storage, only metadata on nodes. Then adding / removing compute nodes is mostly about metadata replication, making scaling easier.</p>
<h3 class="wp-block-heading" id="h-high-availability-and-failover-nbsp">High availability and failover&nbsp;<a class="anchor-link" id="high-availability-and-failover"></a></h3>
<p>While sharding is meant for scale and load distribution, ClickHouse leverages replication for high availability (HA):</p>
<ul class="wp-block-list">
<li><strong>Sharding</strong> for scale and load distribution.</li>
<li><strong>Replication</strong> for availability and data integrity. Each shard can have multiple replicas, implemented via <code>ReplicatedMergeTree</code> engines coordinated by ClickHouse Keeper (Raft).</li>
</ul>
<h3 class="wp-block-heading" id="h-replica-health-amp-automated-failover-patterns">Replica health &amp; automated failover patterns<a class="anchor-link" id="replica-health-automated-failover-patterns"></a></h3>
<p>Replication characteristics:</p>
<ul class="wp-block-list">
<li>Asynchronous multi&#8209;master: writes can go to any replica; other replicas pull data in the background.&nbsp;</li>
<li>Eventual consistency by default; stronger guarantees (quorum / sequential consistency) are possible but have performance cost. Health and failover mechanisms mentioned:</li>
</ul>
<ul class="wp-block-list">
<li>Replica health endpoint:
<ul class="wp-block-list">
<li>Configure <code>max_replica_delay_for_distributed_queries</code>.</li>
<li>Use HTTP <code>/replicas_status</code>: returns <code>200 OK</code> if replica is available and not delayed; returns <code>503</code> with delay info if it is lagging.</li>
<li>Automatic recovery: replicas track table state via parts and checksums; if local state diverges, a replica downloads missing/broken parts from others and quarantines unexpected data.</li>
</ul>
</li>
<li>Leader election for merges: multiple replicas can be leaders; leaders schedule merges, ensuring byte&#8209;identical parts across replicas.</li>
</ul>
<h3 class="wp-block-heading" id="h-multi-region-cloud-region-considerations">Multi&#8209;region / cloud region considerations<a class="anchor-link" id="multi%e2%80%91region-cloud-region-considerations"></a></h3>
<p>Multi&#8209;region replication is supported, <strong>but</strong>:</p>
<ul class="wp-block-list">
<li>Recommended to keep inter&#8209;region latency in the <strong>two&#8209;digit millisecond range</strong>; otherwise write performance suffers due to consensus overhead.</li>
<li>Replication between US coasts is likely fine; US &ndash; Europe is discouraged for synchronous&#8209;style patterns.</li>
<li>Configuration is the same as single&#8209;region deployments; you just place replicas in different locations.</li>
</ul>
<h3 class="wp-block-heading" id="h-support-team-monitoring-and-operations">Support Team Monitoring and Operations<a class="anchor-link" id="support-team-monitoring-and-operations"></a></h3>
<h4 class="wp-block-heading" id="h-monitoring-health-and-availability"><strong>Monitoring health and availability</strong></h4>
<ul class="wp-block-list">
<li>Built&#8209;in observability dashboard at <code>$HOST:$PORT/dashboard</code>:
<ul class="wp-block-list">
<li>Shows QPS, CPU, merges, IO, memory, parts, etc.</li>
</ul>
</li>
<li>System tables:
<ul class="wp-block-list">
<li><code>system.metrics</code>, <code>system.events</code>, <code>system.asynchronous_metrics</code>, and <code>system.asynchronous_metric_log</code> for resource and server metrics.&nbsp;</li>
</ul>
</li>
<li>External monitoring integrations:
<ul class="wp-block-list">
<li>Export metrics to Graphite or Prometheus via server config.</li>
</ul>
</li>
<li>Liveness checks:
<ul class="wp-block-list">
<li><code>/ping</code> HTTP endpoint returns <code>200 OK</code> if server is up.&nbsp;</li>
</ul>
</li>
<li>Replica health:
<ul class="wp-block-list">
<li><code>/replicas_status</code> + <code>max_replica_delay_for_distributed_queries</code> to detect lagging replicas and drive routing/failover decisions.</li>
</ul>
</li>
</ul>
<p><strong>Operationally, support teams need to:</strong></p>
<ul class="wp-block-list">
<li>Watch replica lag and availability to avoid routing queries to stale or unhealthy replicas.</li>
<li>Monitor merges, parts count, and resource utilization to prevent degraded performance that can look like availability issues.</li>
<li>Manage Keeper (or ZooKeeper) health, since replication and cluster coordination depend on it.</li>
</ul>
<h3 class="wp-block-heading" id="h-performance-optimisation-when-scaling">Performance optimisation when scaling<a class="anchor-link" id="performance-optimisation-when-scaling"></a></h3>
<p>As ClickHouse clusters expand with more shards, replicas or data, their performance characteristics change. While scaling enhances parallelism and capacity, it also makes the system more susceptible to issues related to data distribution, merge efficiency, storage latency, and background workload. Achieving optimal performance in a scaled-out ClickHouse environment necessitates identifying where bottlenecks occur and proactively mitigating problems like hotspots, replication lag, or merge backlogs that can degrade cluster performance.</p>
<p>The following sections highlight key operational practices for maintaining performance in modern ClickHouse versions.</p>
<h4 class="wp-block-heading" id="h-avoiding-hotspots-and-balancing-shards">Avoiding Hotspots and Balancing Shards</h4>
<p>Hotspots occur when a sharding key causes a disproportionate amount of data or query load to be directed to a single shard. This leads to uneven resource utilization, making the overloaded shard a bottleneck for the entire cluster.</p>
<h5 class="wp-block-heading" id="h-strategies-for-avoiding-hotspots">Strategies for Avoiding Hotspots</h5>
<ol class="wp-block-list">
<li><strong>Choose an Effective Sharding Key:</strong> The quality of the sharding key is the single most important factor for balancing load.
<ul class="wp-block-list">
<li><strong>High Cardinality:</strong> The key should have a large number of unique values (e.g., UUID, User ID, Session ID). Keys with low cardinality (e.g., date, city, event type) will group too many records onto a single shard.</li>
<li><strong>Even Distribution:</strong> The distribution of the key&rsquo;s values should be uniform. If a few keys account for most of the data (e.g., a few super-users in a user ID key), the cluster will still be unbalanced.</li>
<li><strong>Common Techniques:</strong>
<ul class="wp-block-list">
<li><strong><code>rand()</code>:</strong> Using <code>rand()</code> as the sharding key provides perfect, uniform distribution but severely limits shard-local queries, as related data might be spread across all shards.</li>
<li><strong>sipHash64(key<code>)</code>:</strong> Hashing a high-cardinality key (like a user ID) is often the best compromise, providing even distribution while ensuring all data for a specific entity resides on one shard.</li>
</ul>
</li>
</ul>
</li>
<li><strong>Evaluate and Adjust the Sharding Expression:</strong> When defining the Distributed table, the sharding key is specified in the<code> rand()</code> position of the engine definition.</li>
</ol>
<figure class="wp-block-table">
<table class="has-fixed-layout">
<tbody>
<tr>
<td><strong>Current Sharding Expression (Uniform but Query-Inefficient)</strong></td>
<td><code>ENGINE = Distributed('my_cluster', 'test_log', 'log_local', rand());</code></td>
</tr>
<tr>
<td><strong>Improved Sharding Expression (Entity-local &amp; Distributed)</strong></td>
<td><code>ENGINE = Distributed('my_cluster', 'test_log', 'log_local', sipHash64(user_id));</code></td>
</tr>
</tbody>
</table>
</figure>
<ol start="3" class="wp-block-list">
<li><strong>Monitor Shard Balance:</strong> Regularly check the data distribution and query load across shards using system tables.</li>
</ol>
<figure class="wp-block-table">
<table class="has-fixed-layout">
<thead>
<tr>
<th>Metric</th>
<th>Query</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Data Size Balance</strong></td>
<td><code>SELECT hostName(), sum(bytes_on_disk) AS total_disk_usage, formatReadableSize(total_disk_usage) FROM system.parts GROUP BY hostName() ORDER BY total_disk_usage DESC;</code></td>
</tr>
<tr>
<td><strong>Row Count Balance</strong></td>
<td><code>SELECT hostName(), count() FROM test_log.log_local GROUP BY hostName();</code></td>
</tr>
<tr>
<td><strong>Query Load Balance</strong></td>
<td><code>SELECT hostName(), count() FROM system.query_log WHERE query_start_time &gt;= now() - INTERVAL 1 HOUR GROUP BY hostName();</code></td>
</tr>
</tbody>
</table>
</figure>
<h5 class="wp-block-heading" id="h-post-deployment-data-rebalancing">Post-Deployment Data Rebalancing</h5>
<p>As noted previously, adding a new shard does not automatically rebalance existing data. If monitoring shows significant imbalance after scaling out, you must manually rebalance the historical data.</p>
<ul class="wp-block-list">
<li><strong>INSERT INTO SELECT (The Traditional Method):</strong> The most common method is to perform a cluster-wide re-ingestion.</li>
</ul>
<ul class="wp-block-list">
<li>Create a new set of distributed and local tables (e.g., <code>log_local_v2</code>, <code>log_distributed_v2</code>) using the optimal sharding key/expression.</li>
<li>Execute an <code>INSERT INTO SELECT</code> statement from the old distributed table into the new one. Since the new Distributed table is aware of all shards (including the new one), the data will be re-sharded and inserted according to the new logic.</li>
</ul>
<figure class="wp-block-table">
<table class="has-fixed-layout">
<tbody>
<tr>
<td><strong>Rebalancing Command</strong></td>
<td><code>INSERT INTO test_log.log_distributed_v2 SELECT * FROM test_log.log_distributed;</code></td>
</tr>
<tr>
<td><strong>Considerations</strong></td>
<td>This is resource-intensive and requires significant free storage space and downtime or maintenance windows.</td>
</tr>
</tbody>
</table>
</figure>
<h4 class="wp-block-heading" id="h-storage-concerns-free-space-and-merge-behavior">Storage Concerns: Free Space and Merge Behavior</h4>
<p>When scaling a ClickHouse cluster, managing storage efficiently and ensuring proper merge behavior are critical operational responsibilities, particularly for the <code>MergeTree</code> family of tables (including <code>ReplicatedMergeTree</code>).</p>
<h5 class="wp-block-heading" id="h-managing-free-space-and-part-merges">Managing Free Space and Part Merges</h5>
<p>The <code>MergeTree</code> engine stores data in immutable &ldquo;parts.&rdquo; Background processes constantly merge smaller parts into larger ones to optimize read performance and reduce file count. This merge process temporarily requires significant free space.</p>
<ul class="wp-block-list">
<li><strong>Free Space Requirement:</strong> A common rule of thumb is that a node should have at least <strong>2x to 3x the size of the largest unmerged data part</strong> available as free space on the disk where ClickHouse stores data.
<ul class="wp-block-list">
<li>If insufficient space is available, merges will stall, leading to a large number of small data parts.</li>
</ul>
</li>
<li><strong>The Problem of Too Many Parts:</strong> A high number of small parts (<strong>part explosion</strong>) dramatically degrades query performance because ClickHouse must open and scan many files. It also increases metadata overhead.</li>
<li><strong>Monitoring:</strong> Use the <code>system.parts </code>table to monitor the number of parts and the size of the largest parts.</li>
</ul>
<figure class="wp-block-table">
<table class="has-fixed-layout">
<thead>
<tr>
<th>Metric</th>
<th>Query</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Parts Count</strong></td>
<td><code>SELECT count() FROM system.parts WHERE active;</code></td>
</tr>
<tr>
<td><strong>Parts per Table</strong></td>
<td><code>SELECT database, table, count() AS parts FROM system.parts WHERE active GROUP BY database, table ORDER BY parts DESC;</code></td>
</tr>
<tr>
<td><strong>Disk Space Available</strong></td>
<td><code>SELECT name, path, formatReadableSize(free_space) FROM system.disks;</code></td>
</tr>
</tbody>
</table>
</figure>
<h5 class="wp-block-heading" id="h-optimizing-merge-strategy">Optimizing Merge Strategy</h5>
<p>ClickHouse attempts to merge parts efficiently, but high write throughput or uneven data distribution can overwhelm the merge scheduler.</p>
<ul class="wp-block-list">
<li><strong>Prioritizing Merges:</strong> You can influence the merge process using settings, although ClickHouse&rsquo;s default scheduling is usually robust.
<ul class="wp-block-list">
<li><strong><code>background_pool_size:</code></strong> Controls the number of threads available for background tasks (merges, fetches, mutations). Increasing this can speed up merges if CPU/IO is the bottleneck.</li>
</ul>
</li>
<li><strong>Mitigating Part Explosion:</strong>
<ul class="wp-block-list">
<li><strong>Tune Write Frequency:</strong> Try to batch inserts into larger chunks to reduce the number of initial small parts.</li>
<li><strong>Set <code>max_parts_in_total</code>:</strong> This setting defines the maximum number of parts allowed in a single partition before ClickHouse will aggressively block future inserts to force merges. This acts as a protective mechanism against part explosion.</li>
</ul>
</li>
</ul>
<h5 class="wp-block-heading" id="h-separate-storage-volume-configuration">Separate Storage (Volume Configuration)</h5>
<p>For servers with heterogeneous storage (e.g., fast SSDs for hot data, slower HDDs for cold archives), ClickHouse allows defining multiple storage volumes.</p>
<ul class="wp-block-list">
<li><strong>Configuration:</strong> You define volumes and policies in the server configuration (e.g., <code>storage_configuration</code>).</li>
<li><strong>Tiered Storage:</strong> Using policies, you can automatically move older, colder data parts from fast storage (e.g., d<code>efault </code>volume) to slower, cheaper storage (e.g., cold volume) based on part age. This optimizes the utilization of expensive, high-speed disks for active data while maintaining the total capacity of the cluster.</li>
</ul>
<figure class="wp-block-table">
<table class="has-fixed-layout">
<thead>
<tr>
<th>Policy Parameter</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><code>move_factor</code></strong></td>
<td>The disk usage ratio at which data migration to the next volume in the policy will begin.</td>
</tr>
<tr>
<td><strong><code>prefer_not_to_merge</code></strong></td>
<td>If set to 1, merges will not occur on parts stored in this volume (useful for archival/cold storage).</td>
</tr>
</tbody>
</table>
</figure>
<h3 class="wp-block-heading" id="h-integrating-into-a-unified-ops-flow">Integrating into a unified ops flow<a class="anchor-link" id="integrating-into-a-unified-ops-flow"></a></h3>
<p>As ClickHouse clusters grow with more shards, replicas, and storage layers, the daily burden intensifies. Routine tasks like provisioning, configuration, schema sync, operational replication monitoring, and failure handling become more complex. To reduce overhead and ensure reliability, ClickHouse should be integrated into a unified operational workflow. This provides consistent management across environments (on-prem, cloud, hybrid), offers cross-database visibility, and enables automated maintenance.&nbsp;</p>
<p>A unified operations flow is essential for ensuring predictable scaling, reproducible cluster changes, and consistent operational standards.</p>
<h4 class="wp-block-heading" id="h-how-a-multi-database-operations-platform-fits-in">How a Multi-Database Operations Platform Fits In</h4>
<p>Managing distributed databases, such as ClickHouse, manually with CLI commands and adhoc automation can be complex. A dedicated platform provides a centralized management layer, allowing operators to execute, monitor, and audit critical lifecycle operations from a single control plane. This approach significantly reduces human error and streamlines repetitive tasks, especially as database clusters scale in size and complexity.</p>
<h5 class="wp-block-heading" id="h-key-value-propositions-of-a-management-platform"><strong>Key Value Propositions of a Management Platform:</strong></h5>
<ul class="wp-block-list">
<li><strong>Simplified Operations:</strong> Provides a consistent operational interface that reduces complexity.</li>
<li><strong>Automated Lifecycle Management:</strong>
<ul class="wp-block-list">
<li><strong>Deployment &amp; Scaling:</strong> Simplifies adding new nodes, whether expanding an existing shard or creating a new one.</li>
<li><strong>Failure Handling and Repair:</strong> Automates fixes for desynced replicas, lost parts, or corrupted tables.</li>
</ul>
</li>
<li><strong>Cluster Consistency &amp; Awareness:</strong>
<ul class="wp-block-list">
<li><strong>Topology-aware Management:</strong> Automatically understands the cluster structure, including shards, replicas, Keeper nodes, and distributed table configurations.</li>
<li><strong>Configuration Enforcement:</strong> Ensures consistency for macros, <code>remote_servers</code> definitions, storage paths, and replication settings across all nodes.</li>
</ul>
</li>
<li><strong>Comprehensive Visibility:</strong>
<ul class="wp-block-list">
<li><strong>Observability &amp; Alerting:</strong> Tracks key metrics like replication lag, merge queue size, disk usage, and background pool pressure.</li>
<li><strong>Cross-database Management:</strong> Offers a unified workflow for operators managing heterogeneous databases (e.g., MySQL, PostgreSQL, MongoDB, or ClickHouse).</li>
</ul>
</li>
</ul>
<h4 class="wp-block-heading" id="h-automating-node-addition-rebalancing-and-scaling-tasks-nbsp"><strong>Automating Node Addition, Rebalancing, and Scaling Tasks&nbsp;</strong></h4>
<p>Manually scaling ClickHouse requires a multi-step process, encompassing preparing new nodes, updating cluster configurations, creating replicas, provisioning tables, validating ClickHouse Keeper connectivity, and verifying replication health. Automating these steps is key to achieving greater consistency, minimizing risks, and speeding up deployment cycles.</p>
<h5 class="wp-block-heading" id="h-automating-node-provisioning-and-cluster-discovery">Automating Node Provisioning and Cluster Discovery</h5>
<p>Automation can be used to:</p>
<ul class="wp-block-list">
<li>Pre-configure OS dependencies and sysctl parameters</li>
<li>Install ClickHouse packages</li>
<li>Deploy macros (<code>shard</code>, <code>replica</code>)</li>
<li>Generate and distribute configuration files</li>
<li>Register new nodes into <code>remote_servers</code> definitions</li>
</ul>
<h5 class="wp-block-heading" id="h-automating-schema-propagation">Automating Schema Propagation</h5>
<p>As clusters grow, keeping schemas consistent across nodes becomes more important:</p>
<ul class="wp-block-list">
<li>Distributed tables must reflect updated cluster topology</li>
<li>Schema drift becomes more likely as cluster size grows</li>
</ul>
<p>Automation ensures:</p>
<ul class="wp-block-list">
<li><code>CREATE DATABASE &hellip; ON CLUSTER</code> and <code>CREATE TABLE &hellip; ON CLUSTER</code> commands run reliably</li>
<li>All nodes remain synchronized when introducing schema changes</li>
<li>Distributed tables update correctly after adding new shards</li>
</ul>
<h5 class="wp-block-heading" id="h-automating-rebalancing-operations">Automating Rebalancing Operations</h5>
<p>ClickHouse does not automatically redistribute old data after scaling out. Automation helps plan and execute controlled rebalancing:</p>
<ul class="wp-block-list">
<li>Selectively moving partitions</li>
<li>Rebuilding parts by using <code>INSERT INTO &hellip; SELECT&nbsp;</code></li>
<li>Tiering cold partitions into object storage automatically</li>
<li>Scheduling rebalancing at off-peak hours</li>
</ul>
<p>Platforms can track rebalancing progress, ensuring minimal performance impact.</p>
<p><strong>Automating Scaling and Background Maintenance</strong></p>
<p>Tasks that are ideal candidates for automation:</p>
<ul class="wp-block-list">
<li>Auto-recovering replicas with missing or corrupted parts</li>
<li>Scaling read replicas for analytics workloads</li>
<li>Refreshing Keeper/Zookeeper configuration</li>
</ul>
<p>Automation ensures the cluster stays healthy without constant operator intervention.</p>
<h3 class="wp-block-heading" id="h-conclusion"><strong>Conclusion</strong><a class="anchor-link" id="conclusion"></a></h3>
<p>Scaling ClickHouse from a single shard to a robust, multi-shard, replicated cluster is complex but necessary for growing analytical needs.</p>
<p>The process requires configuration changes updating the server (network,storage), deploying shard/replica macros, extending the <code>remote_servers</code> cluster definition, and expanding the ClickHouse Keeper ensemble.</p>
<p>Maintaining performance requires proactive strategies:</p>
<ul class="wp-block-list">
<li><strong>Sharding Optimization:</strong> Use a high-cardinality sharding key (e.g., <code>sipHash64(user_id))</code> to prevent hotspots.</li>
<li><strong>Storage Management:</strong> Monitor disk space and manage merges carefully to avoid &ldquo;part explosion,&rdquo; which degrades query performance.</li>
<li><strong>High Availability:</strong> Fault tolerance in self-managed environments relies on replication (Cloud abstracts this).</li>
<li><strong>Automation:</strong> Integrate ClickHouse with an operational platform for automated scaling, configuration management, schema consistency, and data rebalancing to ensure reliability as clusters grow.</li>
</ul>
<p>Meticulous planning and robust operations ensure the ClickHouse infrastructure remains performant, predictable, and scalable despite increasing data ingestion.</p>
<p>The post <a href="https://severalnines.com/blog/clickhouse-scaling-and-sharding-best-practices/">ClickHouse scaling and sharding best practices</a> appeared first on <a href="https://severalnines.com">Severalnines</a>.</p>

<p>The post <a rel="nofollow" href="https://severalnines.com/blog/clickhouse-scaling-and-sharding-best-practices/">ClickHouse scaling and sharding best practices</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>MariaDB Community Server Q1 2026 maintenance releases</title>
      <link>https://mariadb.com/resources/blog/mariadb-community-server-q1-2026-maintenance-releases/</link>
      <pubDate>Fri, 06 Feb 2026 15:06:37 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.com/">MariaDB</source>
      <description><![CDATA[<p>MariaDB is pleased to announce the immediate availability of MariaDB Community Server 11.8.6, 11.4.10, 10.11.16, and 10.6.25 maintenance releases. See […]</p>
<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/mariadb-community-server-q1-2026-maintenance-releases/">MariaDB Community Server Q1 2026 maintenance releases</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>MariaDB is pleased to announce the immediate availability of MariaDB Community Server 11.8.6, 11.4.10, 10.11.16, and 10.6.25 maintenance releases. See the release notes and changelogs for additional details on each release and visit mariadb.com/downloads to download.</p>
<p><a href="https://mariadb.com/resources/blog/mariadb-community-server-q1-2026-maintenance-releases/" rel="nofollow">Source</a></p>

<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/mariadb-community-server-q1-2026-maintenance-releases/">MariaDB Community Server Q1 2026 maintenance releases</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>And The Winners Of The Inaugural Top External Contributor To MariaDB Award are …</title>
      <link>https://mariadb.org/and-the-winner-of-the-inaugural-top-external-contributor-to-mariadb-award-is/</link>
      <pubDate>Fri, 06 Feb 2026 13:10:10 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>Announcing the winners of the first Top Contributor to MariaDB award<br />
The post And The Winners Of The Inaugural Top External Contributor To MariaDB Award are … appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/and-the-winner-of-the-inaugural-top-external-contributor-to-mariadb-award-is/">And The Winners Of The Inaugural Top External Contributor To MariaDB Award are …</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>Announcing the winners of the first Top Contributor to MariaDB award</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/and-the-winner-of-the-inaugural-top-external-contributor-to-mariadb-award-is/">And The Winners Of The Inaugural Top External Contributor To MariaDB Award are &hellip;</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/and-the-winner-of-the-inaugural-top-external-contributor-to-mariadb-award-is/">And The Winners Of The Inaugural Top External Contributor To MariaDB Award are …</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>MariaDB 11.8.6, 11.4.10, 10.11.16 and 10.6.25 now available</title>
      <link>https://mariadb.org/mariadb-11-8-6-11-4-10-10-11-16-and-10-6-25-now-available/</link>
      <pubDate>Fri, 06 Feb 2026 11:20:12 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>The MariaDB Foundation is pleased to announce the availability of MariaDB 11.8.6, MariaDB 11.4.10, MariaDB 10.11.16 and MariaDB 10.6.25, the latest stable releases in their respective long-term series (receiving regular maintenance and support for three years from their first stable release dates, and critical security fixes as source code releases for two additional years beyond). …<br />
Continue reading \"MariaDB 11.8.6, 11.4.10, 10.11.16 and 10.6.25 now available\"<br />
The post MariaDB 11.8.6, 11.4.10, 10.11.16 and 10.6.25 now available appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-11-8-6-11-4-10-10-11-16-and-10-6-25-now-available/">MariaDB 11.8.6, 11.4.10, 10.11.16 and 10.6.25 now available</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>The MariaDB Foundation is pleased to announce the availability of <a href="https://mariadb.com/docs/release-notes/community-server/11.8/11.8.6">MariaDB 11.8.6</a>, <a href="https://mariadb.com/docs/release-notes/community-server/11.4/11.4.10">MariaDB 11.4.10</a>, <a href="https://mariadb.com/docs/release-notes/community-server/10.11/10.11.16">MariaDB 10.11.16</a> and <a href="https://mariadb.com/docs/release-notes/community-server/10.6/10.6.25">MariaDB 10.6.25</a>, the latest stable releases in their respective <a href="https://mariadb.org/about/#maintenance-policy">long-term series</a> (receiving regular maintenance and support for three years from their first stable release dates, and critical security fixes as source code releases for two additional years beyond). &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/mariadb-11-8-6-11-4-10-10-11-16-and-10-6-25-now-available/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;MariaDB 11.8.6, 11.4.10, 10.11.16 and 10.6.25 now available&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-11-8-6-11-4-10-10-11-16-and-10-6-25-now-available/">MariaDB 11.8.6, 11.4.10, 10.11.16 and 10.6.25 now available</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/mariadb-11-8-6-11-4-10-10-11-16-and-10-6-25-now-available/">MariaDB 11.8.6, 11.4.10, 10.11.16 and 10.6.25 now available</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>New binlog implementation in MariaDB 12.3</title>
      <link>https://mariadb.org/new-binlog-implementation-in-mariadb-12-3/</link>
      <pubDate>Fri, 06 Feb 2026 09:02:28 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>I have recently completed a large project to implement a new improved binlog format for MariaDB. The result will be available shortly in the upcoming MariaDB 12.3.1 release. …<br />
Continue reading \"New binlog implementation in MariaDB 12.3\"<br />
The post New binlog implementation in MariaDB 12.3 appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/new-binlog-implementation-in-mariadb-12-3/">New binlog implementation in MariaDB 12.3</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>I have recently completed a large project to implement a new improved binlog format for MariaDB. The result will be available shortly in the upcoming MariaDB 12.3.1 release. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/new-binlog-implementation-in-mariadb-12-3/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;New binlog implementation in MariaDB 12.3&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/new-binlog-implementation-in-mariadb-12-3/">New binlog implementation in MariaDB 12.3</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/new-binlog-implementation-in-mariadb-12-3/">New binlog implementation in MariaDB 12.3</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>DB Fiddle – SQL Database Playground – now has MariaDB</title>
      <link>https://mariadb.org/db-fiddle-sql-database-playground-now-has-mariadb/</link>
      <pubDate>Thu, 05 Feb 2026 22:05:37 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>When choosing a database, many times you want to play and see what it can do to see if it’s relevant. Containers are easy, but a web page is even easier. …<br />
Continue reading \"DB Fiddle – SQL Database Playground – now has MariaDB\"<br />
The post DB Fiddle – SQL Database Playground – now has MariaDB appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/db-fiddle-sql-database-playground-now-has-mariadb/">DB Fiddle – SQL Database Playground – now has MariaDB</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>When choosing a database, many times you want to play and see what it can do to see if it&rsquo;s relevant. Containers are easy, but a web page is even easier. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/db-fiddle-sql-database-playground-now-has-mariadb/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;DB Fiddle &ndash; SQL Database Playground &ndash; now has MariaDB&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/db-fiddle-sql-database-playground-now-has-mariadb/">DB Fiddle &ndash; SQL Database Playground &ndash; now has MariaDB</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/db-fiddle-sql-database-playground-now-has-mariadb/">DB Fiddle – SQL Database Playground – now has MariaDB</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Announcing MariaDB Cloud BYOA: Fully Managed MariaDB in Your Azure Subscription</title>
      <link>https://mariadb.com/resources/blog/announcing-mariadb-cloud-byoa-fully-managed-mariadb-in-your-azure-subscription/</link>
      <pubDate>Wed, 04 Feb 2026 16:42:53 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.com/">MariaDB</source>
      <description><![CDATA[<p>The Evolution of Cloud Databases The shift to cloud databases fundamentally changed how we run production environments. By offloading the […]</p>
<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/announcing-mariadb-cloud-byoa-fully-managed-mariadb-in-your-azure-subscription/">Announcing MariaDB Cloud BYOA: Fully Managed MariaDB in Your Azure Subscription</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>The shift to cloud databases fundamentally changed how we run production environments. By offloading the burden of hardware management, software patching, and 24&times;7 operations, organizations gained instant scale, built-in resilience, and a consumption-based cost model. For many teams, this operational simplicity was the primary catalyst for cloud migration. However, as workloads grow more&hellip;</p>
<p><a href="https://mariadb.com/resources/blog/announcing-mariadb-cloud-byoa-fully-managed-mariadb-in-your-azure-subscription/" rel="nofollow">Source</a></p>

<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/announcing-mariadb-cloud-byoa-fully-managed-mariadb-in-your-azure-subscription/">Announcing MariaDB Cloud BYOA: Fully Managed MariaDB in Your Azure Subscription</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>PGDay and FOSDEM Report from Kai</title>
      <link>https://percona.community/blog/2026/02/04/pgday-and-fosdem-report-from-kai/</link>
      <pubDate>Wed, 04 Feb 2026 10:00:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://percona.community/blog/">Percona Community Blog - learn about MySQL, MariaDB, PostgreSQL, and MongoDB</source>
      <description><![CDATA[<p>The following thoughts and comments are completely my personal opinion and do not reflect my employers thoughts or beliefs. If you don’t like anything in this post, reach out to me directly, so I can ignore it ;-).</p>
<p>The post <a rel="nofollow" href="https://percona.community/blog/2026/02/04/pgday-and-fosdem-report-from-kai/">PGDay and FOSDEM Report from Kai</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>The following thoughts and comments are completely my personal opinion and do not reflect my employers thoughts or beliefs. If you don&rsquo;t like anything in this post, reach out to me directly, so I can ignore it ;-).</p>
<p>I&rsquo;m currently on the train on my way back home from FOSDEM this year and man, I&rsquo;m exhausted but also happy. Why? Because the PG and FOSDEM community is just crazily awesome. While it&rsquo;s always too much of everything, it&rsquo;s at the same time inspiring to see so many enthusiastic IT nerds in one place, discussing and working on what they love &ndash; technology and engineering challenges.</p>
<h2 id="pgday-fosdem">PGDay FOSDEM<a class="anchor-link" id="pgday-fosdem"></a></h2>
<p>It all started with the usual PGDay FOSDEM the day before FOSDEM. Just in case &ndash; this has been happening for over 15 years and if you read this as a little blame that you didn&rsquo;t know about it, that&rsquo;s absolutely correct, as you should. It&rsquo;s been a great event as usual: around 150 Postgres enthusiasts collaborating with each other. There was a great set of talks (no recording available, so yes, just join next year to not miss anything), as well as the hallway track conversations.</p>
<p><figure>
<img decoding="async" src="https://percona.community/blog/2026/02/pgday-slonik.jpeg" alt="PGDay Kai and Slonik"></figure>
</p>
<p>I was able and accepted again as a volunteer helping to make the event happen. While you might think, what&rsquo;s special about it, I cannot express my gratitude for being able to help in any way. I simply love it. I&rsquo;m not a great coder and I&rsquo;ve never been one. I&rsquo;m the one that looks at his code from a year ago and questions his technical existence and overall abilities if I should rather do something without touching a keyboard. What I am very well capable of is helping and supporting events. So it was my pleasure and I hope you do feel inspired to do the same next year or at any future event, not only in the Postgres ecosystem but in general. I strongly believe in this: doing good things will get you good things back.</p>
<p>After the wrap up to the PGDay and a great community dinner to collaborate and discuss further, I simply fell completely tired asleep, as the next day and FOSDEM was already waiting.</p>
<h2 id="fosdem-day-1">FOSDEM Day 1<a class="anchor-link" id="fosdem-day-1"></a></h2>
<p><figure>
<img decoding="async" src="https://percona.community/blog/2026/02/fosdem-pgbooth-volunteering.jpg" alt="PGDay Kai PG Booth Volunteering"></figure>
</p>
<p>The next day started with volunteering at the Postgres booth. As usual, Saturday was simply crazy. The Postgres swag like hoodies, caps, mugs, shirts, etc. was almost ripped out of our living hands. We had people waiting in line just to be able to get some swag. That fact alone shows how Postgres is viewed outside of the internal PG ecosystem community. How many times I heard the sentence &ldquo;Thanks a lot for the great work you do&rdquo; or &ldquo;Postgres just works.&rdquo; Yeah, we can all argue about the details and scenarios, but what this is about is the overall ease of use. Not everyone has terabytes of data or the most complex HA and replication scenarios on this planet. Some just need a functional and boring database and, in the best case, open source &ndash; and we all know, looking at real open source, not single-vendor owned, Postgres is the king and here to stay.</p>
<p>After all of this, I switched clothes and helped at the Percona booth. This wasn&rsquo;t any less interesting in comparison to the PG booth. How many people stepped by, asking about what we do or thanking us for our projects and that we remained open source even after all these years and so many other companies not withstanding the quick and easy money to go with open-core or closed offerings. That&rsquo;s the reason I&rsquo;m proud to be part of this company. We walk the talk, since 20y and we have no incentive ever changing it. Thanks to Peter Zaitsev and Peter Farkas aka P&sup2; &ndash; for those who know, just know.</p>
<p>Following that I had the pleassure of being the Slonik guide again. What is a Slonik guide you might ask? Slonik, the mascot of Postgres (big blue elephant), needs some help and guidance while walking throught the crowd, as you can barely see anything while inside the costome. As usual, Slonik is a celebraty. Everyone wants a picture and taking their chance to photograph Slonik in the &ldquo;wild&rdquo;. As you can see, even MySQL&rsquo;s Sakila couldn&rsquo;t resist and had to take a picture with Slonik.</p>
<p><figure>
<img decoding="async" src="https://percona.community/blog/2026/02/fosdem-slonik.jpeg" alt="FOSDEM Sakila and Slonik"></figure>
</p>
<p>If you&rsquo;re wondering, like many others, why Slonik and why an Elephant? <a href="https://learnsql.com/blog/the-history-of-slonik-the-postgresql-elephant-logo/" target="_blank" rel="noopener noreferrer">Click here for some nice written down history lesson</a></p>
<p>After an exciting but also energy-draining day, I enjoyed a Percona crew/team dinner at BrewDog, with some great conversations and good food. <a href="https://www.reddit.com/r/Homebrewing/comments/47icau/brewdog_just_open_sourced_all_their_recipes/" target="_blank" rel="noopener noreferrer">Fun Fact: Did you know that BrewDog is also open source?</a>. I couldn&rsquo;t stay too long &ndash; sorry about that &ndash; but I had another date. The famous Floor Drees organized in tradition another karaoke event that I couldn&rsquo;t miss. As I couldn&rsquo;t make it to earlier versions of it, I definitely wanted to join. What should I say apart from thanks, Floor, for this great tradition. Yes, I had a hard time talking the next day, but damn I had fun singing Swedish, Polish, German, and English songs &ndash; and yes, I most likely misunderstood all of them as usual.</p>
<p>Too many songs for my voice and maybe a &ldquo;soft drink or two&rdquo; later, I felt in my bed like a stone, and couldn&rsquo;t really accept the fact that my alarm clock went off almost five minutes later (at least that&rsquo;s how it felt to me).</p>
<h2 id="fosdem-day-2">FOSDEM Day 2<a class="anchor-link" id="fosdem-day-2"></a></h2>
<p><figure>
<img decoding="async" src="https://percona.community/blog/2026/02/fosdem-perconabooth-volunteering.jpeg" alt="PGDay Kai PG Booth Volunteering"></figure>
</p>
<p>No whining helped, just getting up and making myself ready for Day 2 of FOSDEM, which started with another round of volunteering at the Postgres and Percona booths. Both basically matched the previous feedback, apart from a definitely dropped and less crowded space &ndash; seems I wasn&rsquo;t the only one singing last night ;-).</p>
<p>With that, thanks a lot to everyone making this great FOSDEM happen. I&rsquo;ll try now if the Deutsche Bahn restaurant actually works this time, as I need coffee, a big one, maybe two&hellip; See all of you next year again or at another event this year.</p>
<blockquote>
<p>Stay on top of Postgres development without the inbox overwhelm. Explore <a href="https://hackorum.dev/" target="_blank" rel="noopener noreferrer">hackorum.dev</a> today and share your feedback with us.</p>
</blockquote>

<p>The post <a rel="nofollow" href="https://percona.community/blog/2026/02/04/pgday-and-fosdem-report-from-kai/">PGDay and FOSDEM Report from Kai</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>New binlog implementation in MariaDB 12.3</title>
      <link>https://knielsen-hq.org/w/new-binlog-implementation-in-mariadb-12-3/</link>
      <pubDate>Tue, 03 Feb 2026 19:08:37 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://knielsen-hq.org/w">Kristian Nielsen's blog</source>
      <description><![CDATA[<p>I have recently completed a large project to implement a new improved binlog format for MariaDB. The result will be available shortly in the upcoming MariaDB 12.3.1 release. In this article, I will give a short overview of the new binlog implementation. For more details, check the documentation which is in the source tree as… Continue reading New binlog implementation in MariaDB 12.3</p>
<p>The post <a rel="nofollow" href="https://knielsen-hq.org/w/new-binlog-implementation-in-mariadb-12-3/">New binlog implementation in MariaDB 12.3</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>I have recently completed a large project to implement a new improved binlog format for MariaDB. The result will be available shortly in the upcoming MariaDB 12.3.1 release.</p>
<p>In this article, I will give a short overview of the new binlog implementation. For more details, check the documentation which is in the source tree as the file <code>Docs/replication/binlog.md</code>, or here: <a href="https://github.com/MariaDB/server/blob/knielsen_binlog_in_engine/Docs/replication/binlog.md">https://github.com/MariaDB/server/blob/knielsen_binlog_in_engine/Docs/replication/binlog.md</a></p>
<h2>Using the new binlog<a class="anchor-link" id="using-the-new-binlog"></a></h2>
<p>To enable the new binlog, configure the MariaDB server with <code>binlog_storage_engine=innodb</code>.</p>
<p>Additionally, the binlog must itself be enabled as usual using the option <code>log_bin</code>. Note that no argument can be given to the <code>log_bin</code> option (this is to avoid confusion with the meaning of such argument as the name to use for the old binlog format, as the new binlog file names are fixed).</p>
<pre class="wp-block-preformatted">    binlog_storage_engine=innodb
    log_bin</pre>
<p>When the new binlog file is enabled and the server restarted, any old binlog files are no longer available. See the above-referenced documentation for options on how to migrate old binlogs of an existing server.</p>
<h2>Benefits of the new binlog<a class="anchor-link" id="benefits-of-the-new-binlog"></a></h2>
<p>For the user, the new binlog format brings two main benefits.</p>
<p>First, for users that are running with<code> --innodb-flush-log-at-trx-commit</code> set to 2 or 0 for performance reasons, the new binlog will make the binlog crash-safe (when used with InnoDB tables). This means that if the server crashes or the machine loses power, the restarted server will recover itself into a consistent state, including the state of replication and consistency between the binlog and the InnoDB table contents. With the old binlog format, such a crash could easily leave the binlog in a different state than the InnoDB table data, which then causes replication slaves to diverge from the master. To have the old binlog be crash-safe required setting both <code>--sync-binlog=1</code> and <code>--innodb-flush-log-at-trx-commit=1</code>.</p>
<p>Second, for users that are running with <code>--innodb-flush-log-at-trx-commit</code> set to 1 because they need durability of commits, the new binlog will provide a large speedup of the time taken to commit. Because the new binlog is integrated with InnoDB, only half as many flushes to disk of buffers are needed per commit as with the old binlog.</p>
<p>Thus, the primary user-visible benefits of the new binlog is greatly improved speedup of transaction commits.</p>
<p>The speedup that will be obtained will be completely dependent on the actual workload of the application and on the hardware used for running the database. The speedup will be greater when transactions are small; when the transaction parallelism is modest; and when disk writes have a higher latency (like consumer-grade SSDs or network-attached storage). This is because the new binlog particularly reduces the amount of disk writes that have to happen during commit of a batch of parallel transactions. So if there are many small individual transactions and writes are expensive, the speedup can be huge. If there are few individual transactions, most transactions run in parallel and batch up in a single group commit, and/or disk writes are fast, the speedup will be smaller (but can still be significant).</p>
<h2>Technical background<a class="anchor-link" id="technical-background"></a></h2>
<p>The core of a transactional system like a database &ndash; but also for example a file-system &ndash; is its transactional log, also referred to as the write-ahead log or redo log, amongst others:</p>
<p><a href="https://en.wikipedia.org/wiki/Transaction_log">https://en.wikipedia.org/wiki/Transaction_log</a></p>
<p>This log is the core of how the database achieves a high throughput of updates to data stored on its disks, while simultaneously being able to gracefully recover into a consistent state if the system crashes during operation.</p>
<p>Unfortunately MariaDB does not have a central implementation of its Transaction Log. The main storage engine, InnoDB, has its own implementation, which is separate from the log used by other parts of the server; in particular the (old) binlog is a separate &ldquo;transaction log&rdquo;, and there are other logs used by the Aria storage engine, by DDL operations, etc. Some parts do not even have any transaction log backing them, and are thus not crash-safe. Arguably, this lack of a central transaction log is the biggest architectural limitation of MariaDB currently.</p>
<p>For the binlog in particular, having the binlog separate from the InnoDB write-ahead log causes not just a lot of code complexity, but also a huge performance cost. Because of the two separate logs, it is necessary to use a two-phase commit protocol between the two. This requires two separate synchronous disk writes per (group) commit, otherwise a crash would leave the date in one inconsistent with the other, and replication would break. The need to have these two disk flushes is a <em>huge</em> overhead.</p>
<p>The new binlog implementation fixes this, by re-implementing the binlog data format inside of InnoDB. Similar to InnoDB tablespace files, the new binlog files are now being handled through the InnoDB write-ahead log. This means that when a transaction commit happens, both the table data <em>and</em> the binlog data get written through the InnoDB write-ahead log. The write of data to binlog files can happen later, asynchronously and in an efficient manner. The InnoDB write-ahead log will be used to recover both table data and binlog data into a consistent state, and the overhead of being able to do so is being re-used for the binlog part. Thus, the overhead of two-phase commit and binlog disk flushes is gone, which is a major contribution to the performance improvements of the new binlog.</p>
<p>More subtle, but at least as important, is the improvements under the hood of the code implementing the new binlog.</p>
<p>The new binlog is implemented in InnoDB through an extension of the storage engine API. This means that another storage engine could in principle implement its own version of the binlog, which would be beneficial for users that were mainly using that storage engine for their data. But perhaps more importantly, it means that there is now a well-defined API for <em>how</em> the binlog writes work and what operations are possible on it. This gives a much cleaner separation between the file format and operations used to store the binlog on disk and read it back, as opposed to the actual contents of the binlog in the form of replication events used by slaves to replicate the master&rsquo;s data.</p>
<p>And the actual file format of the new binlog is also greatly improved.</p>
<p>The old binlog is a very naive implementation, it is just a flat file with each individual binlog event written as just a raw sequence of bytes one after the other. This is inefficient for the underlying file system, as each write has to update in two places on disk: the actual data written to the end of the file; and the metadata recording the increase in file length. It also makes it impossible to start reading the binlog file from an arbitrary place, since the start of a new event cannot be distinguished from arbitrary data contained inside an event.</p>
<p>The new binlog has a proper page-based file, which can be pre-allocated efficiently on the file system using eg. <code>posix_fallocate()</code>, and written efficiently page-by-page to the disk. And the binlog data records have proper framing within pages, so that it is possible to look at an arbitrary page in the file and understand what kind of data is there and where one record ends and the next one begins. Having a good page-based file format for the binlog is a great improvement, and something that I have desired for many years.</p>
<p>In many ways, the main benefits to me of the new binlog format is not so much the immediate performance gains, though these are quite substantial already. The really important benefits are the possibilities that are now open for future development and improvements of the binlog and replication, many things that were previously impossible to achieve due to the limitations and convoluted code and design.</p>
<p>For example, with the new binlog, large transactions are now no longer constrained to be written into the binlog as a single block at commit time; they can be written in pieces spread out over the binlog files as the transaction executes. This opens the possibility for having the slaves replicate these pieces optimistically in parallel with the transaction running on the master. This has the potential to greatly reduce the replication lag caused by long-running transactions.</p>
<p>Another example is if and when InnoDB is extended with an option for log archiving, so that the InnoDB write-ahead log is not overwritten cyclicly, but written as a sequence of files containing the complete redo data. Then the new binlog API could be used to implement the binlog data completely inside the InnoDB write-ahead log, so that replication could simply read the binlog data out of the archived log files, and the overhead of having separate binlog files could be eliminated completely.</p>
<p>And there are many other improvements, small and large, that will now be possible to do going forward, based on the improvements done in this project.</p>
<h2>Final words<a class="anchor-link" id="final-words"></a></h2>
<p>Thanks for reading this far! I encourage you to try out the new binlog and see how it works. Any questions or reports of problems are welcome, please direct all queries to the developers@ or discuss@ mailing lists:</p>
<ul>
<li><a href="https://lists.mariadb.org/hyperkitty/list/developers@lists.mariadb.org/">https://lists.mariadb.org/hyperkitty/list/developers@lists.mariadb.org/</a></li>
<li><a href="https://lists.mariadb.org/hyperkitty/list/discuss@lists.mariadb.org/">https://lists.mariadb.org/hyperkitty/list/discuss@lists.mariadb.org/</a></li>
</ul>

<p>The post <a rel="nofollow" href="https://knielsen-hq.org/w/new-binlog-implementation-in-mariadb-12-3/">New binlog implementation in MariaDB 12.3</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Reading the Room: What Europe’s MySQL Community Is Really Saying</title>
      <link>https://mariadb.org/reading-the-room-what-europes-mysql-community-is-really-saying/</link>
      <pubDate>Tue, 03 Feb 2026 18:55:13 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.org/">MariaDB.org</source>
      <description><![CDATA[<p>FOSDEM was exciting from a MariaDB perspective for many reasons this year. For this blog, let me concentrate on one aspect: The discussions at what was called the “Summit for MySQL Community, Europe”, hosted by Percona on Monday 2 Feb 2026 at the Marriott Grand Place in central Brussels. …<br />
Continue reading \"Reading the Room: What Europe’s MySQL Community Is Really Saying\"<br />
The post Reading the Room: What Europe’s MySQL Community Is Really Saying appeared first on MariaDB.org.</p>
<p>The post <a rel="nofollow" href="https://mariadb.org/reading-the-room-what-europes-mysql-community-is-really-saying/">Reading the Room: What Europe’s MySQL Community Is Really Saying</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>FOSDEM was exciting from a MariaDB perspective for many reasons this year. For this blog, let me concentrate on one aspect: The discussions at what was called the &ldquo;Summit for MySQL Community, Europe&rdquo;, hosted by Percona on Monday 2 Feb 2026 at the Marriott Grand Place in central Brussels. &hellip; </p>
<p class="link-more"><a href="https://mariadb.org/reading-the-room-what-europes-mysql-community-is-really-saying/" class="more-link">Continue reading<span class="screen-reader-text"> &ldquo;Reading the Room: What Europe&rsquo;s MySQL Community Is Really Saying&rdquo;</span></a></p>
<p>The post <a rel="nofollow" href="https://mariadb.org/reading-the-room-what-europes-mysql-community-is-really-saying/">Reading the Room: What Europe&rsquo;s MySQL Community Is Really Saying</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>

<p>The post <a rel="nofollow" href="https://mariadb.org/reading-the-room-what-europes-mysql-community-is-really-saying/">Reading the Room: What Europe’s MySQL Community Is Really Saying</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Hackorum &#8211; A Forum-Style View of pg-hackers</title>
      <link>https://percona.community/blog/2026/02/02/hackorum-a-forum-style-view-of-pg-hackers/</link>
      <pubDate>Mon, 02 Feb 2026 00:00:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://percona.community/blog/">Percona Community Blog - learn about MySQL, MariaDB, PostgreSQL, and MongoDB</source>
      <description><![CDATA[<p>Last year at pgconf.dev, there was a discussion about improving the user interface for the PostgreSQL hackers mailing list, which is the main communication channel for PostgreSQL core development. Based on that discussion, I want to share a small project we have been working on:</p>
<p>The post <a rel="nofollow" href="https://percona.community/blog/2026/02/02/hackorum-a-forum-style-view-of-pg-hackers/">Hackorum &#8211; A Forum-Style View of pg-hackers</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>Last year at pgconf.dev, there was a discussion about improving the user interface for the PostgreSQL hackers mailing list, which is the main communication channel for PostgreSQL core development. Based on that discussion, I want to share a small project we have been working on:</p>
<p><a href="https://hackorum.dev/" target="_blank" rel="noopener noreferrer">https://hackorum.dev/</a></p>
<p>Hackorum provides a <strong>read-only (for now)</strong> web view of the mailing list with a more forum-like presentation. It is a <strong>work-in-progress proof of concept</strong>, and we are primarily looking for feedback on whether this approach is useful and what we should improve next.</p>
<h2 id="what-hackorum-already-does">What Hackorum already does<a class="anchor-link" id="what-hackorum-already-does"></a></h2>
<p>Hackorum focuses on readability, navigation, and workflow improvements for people who follow pg-hackers. Some highlights:</p>
<ul>
<li><strong>Continuous mailing list synchronization</strong>: The site is subscribed to the list</li>
<li><strong>Commitfest integration</strong>: See commitfest context next to threads &ndash; you directly know what&rsquo;s the state of this commit/thread.</li>
<li><strong>User profiles</strong>: Contributor/committer status from the main website</li>
<li><strong>Statistics</strong>: Per-user and mailing lists insights</li>
<li><strong>Easy download of attached patches</strong>: Including helper script for easy rebase and merge</li>
<li><strong>Additional logged-in user features</strong>: Per-message read status, starring threads, tags, notes, mentions on messages and threads</li>
<li><strong>Basic team support</strong>: Shared reading status, shared mentioned tags and notes &ndash; mention someone underneath an email an the person gets notified</li>
<li><strong>Resend email</strong>: Integration from the official archive</li>
<li><strong>Importing read status / tags via CSV files</strong>: To help migration from email-based workflows</li>
</ul>
<p><figure>
<img decoding="async" src="https://percona.community/blog/2026/02/hackorum-topics.png" alt="Hackorum topics overview"></figure>
</p>
<h2 id="what-we-plan-next">What we plan next<a class="anchor-link" id="what-we-plan-next"></a></h2>
<ul>
<li><strong>Sending emails from the web UI</strong>: Initially via Gmail API for Google-authenticated users who authorize sending</li>
<li><strong>Advanced search functionality</strong></li>
<li><strong>Integrating other mailing lists</strong></li>
</ul>
<h2 id="try-it-and-share-feedback">Try it and share feedback<a class="anchor-link" id="try-it-and-share-feedback"></a></h2>
<p>If you want to take a look, just got to <a href="https://hackorum.dev/" target="_blank" rel="noopener noreferrer">https://hackorum.dev/</a></p>
<p>The repository, including a simple dev setup, can be found here: <a href="https://github.com/hackorum-dev/hackorum" target="_blank" rel="noopener noreferrer">https://github.com/hackorum-dev/hackorum</a></p>
<p>Is this useful? What is missing? What would you change? Bug reports, feature requests, and contributions are all welcome. <a href="https://github.com/hackorum-dev/hackorum/issues" target="_blank" rel="noopener noreferrer">https://github.com/hackorum-dev/hackorum/issues</a></p>
<p>Thanks for taking a look, and we appreciate any feedback.</p>

<p>The post <a rel="nofollow" href="https://percona.community/blog/2026/02/02/hackorum-a-forum-style-view-of-pg-hackers/">Hackorum &#8211; A Forum-Style View of pg-hackers</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>Tuning MySQL for Performance: The Variables That Actually Matter</title>
      <link>https://percona.community/blog/2026/02/01/tuning-mysql-for-performance-the-variables-that-actually-matter/</link>
      <pubDate>Sun, 01 Feb 2026 00:00:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://percona.community/blog/">Percona Community Blog - learn about MySQL, MariaDB, PostgreSQL, and MongoDB on Percona Community</source>
      <description><![CDATA[<p>There is a special kind of boredom that only database people know. The kind where you stare at a server humming along and think, surely there is something here I can tune. Good news: there is.<br />
This post walks through the most important MySQL variables to tune for performance, why they matter, and when touching them helps versus when it quietly makes things worse. This is written with InnoDB-first workloads in mind, because let’s be honest, that’s almost everyone.</p>
<p>The post <a rel="nofollow" href="https://percona.community/blog/2026/02/01/tuning-mysql-for-performance-the-variables-that-actually-matter/">Tuning MySQL for Performance: The Variables That Actually Matter</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>There is a special kind of boredom that only database people know. The kind where you stare at a server humming along and think, <em>surely there is something here I can tune</em>. Good news: there is.</p>
<p>This post walks through the <strong>most important MySQL variables to tune for performance</strong>, why they matter, and when touching them helps versus when it quietly makes things worse. This is written with <strong>InnoDB-first workloads</strong> in mind, because let&rsquo;s be honest, that&rsquo;s almost everyone.</p>

<p>The post <a rel="nofollow" href="https://percona.community/blog/2026/02/01/tuning-mysql-for-performance-the-variables-that-actually-matter/">Tuning MySQL for Performance: The Variables That Actually Matter</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>PostgreSQL Multi-Master Operational and Consistency Issues in Brief</title>
      <link>https://severalnines.com/blog/postgresql-multi-master-operational-and-consistency-issues-in-brief/</link>
      <pubDate>Fri, 30 Jan 2026 08:00:00 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://severalnines.com/">Severalnines</source>
      <description><![CDATA[<p>In ommon PostgreSQL setups, one server acts as the master, handling all write operations. Other servers act as read replicas, replicating data from the master. Instead, multi-master replication, or Bi-Directional Replication (BDR), allows multiple Postgres servers to act as primaries, each capable of accepting and processing write requests. Changes made on one master are replicated […]<br />
The post PostgreSQL Multi-Master Operational and Consistency Issues in Brief appeared first on Severalnines.</p>
<p>The post <a rel="nofollow" href="https://severalnines.com/blog/postgresql-multi-master-operational-and-consistency-issues-in-brief/">PostgreSQL Multi-Master Operational and Consistency Issues in Brief</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>In ommon <a href="https://severalnines.com/clustercontrol/databases/postgresql">PostgreSQL</a> setups, one server acts as the master, handling all write operations. Other servers act as read replicas, replicating data from the master. Instead, multi-master replication, or Bi-Directional Replication (BDR), allows multiple Postgres servers to act as primaries, each capable of accepting and processing write requests. Changes made on one master are replicated to the other masters, ensuring data consistency across the cluster.</p>
<p>This system incorporates mechanisms to address conflicts arising from simultaneous modifications. The primary motivations for employing multi-master replication are High Availability (HA), write scalability and performance, and geographical distribution. Typically, certain nodes are specifically designated for handling high-write workloads, while others are either reserved for read operations or backup nodes in case of failures.</p>
<h2 class="wp-block-heading" id="h-challenges-of-multi-master-replication">Challenges of Multi-Master Replication<a class="anchor-link" id="challenges-of-multi-master-replication"></a></h2>
<p>Multi-Master replication in PostgreSQL, while offering significant advantages in terms of high availability and scalability, presents several challenges that require careful consideration and robust solutions.</p>
<p>Firstly, <strong>conflict resolution</strong> poses a major hurdle. Concurrent writes on different master nodes inevitably lead to conflicts, requiring sophisticated mechanisms to determine the correct order of operations and ensure data consistency. Implementing and managing these conflict resolution strategies can be complex and resource-intensive, especially in high-traffic environments.</p>
<p>Secondly, maintaining <strong>data consistency</strong> across all master nodes is crucial. Network latency, communication failures, and unforeseen events can disrupt replication and potentially lead to data divergence. Ensuring that all nodes maintain an accurate and up-to-date view of the data requires robust mechanisms for detecting and resolving inconsistencies, which can add overhead and complexity to the system.</p>
<p>Thirdly, <strong>operational complexity</strong> increases significantly in a multi-master setup. Managing multiple master nodes, coordinating replication, and monitoring the health of the entire cluster requires specialized expertise and careful planning. This can increase operational overhead and potentially lead to human error if not managed effectively.</p>
<p>Fourthly, <strong>testing and debugging</strong> can be challenging. Replicating complex scenarios and identifying subtle bugs in a multi-master environment can be time-consuming and difficult. Thorough testing is crucial to ensure the stability and reliability of the system, but it can require significant effort and resources.</p>
<p>Finally, <strong>limited native support</strong> within PostgreSQL itself presents challenges. While third-party extensions and tools are available, they may not always provide the level of functionality, performance, and support required for demanding applications. This can lead to increased reliance on external solutions, which may introduce additional dependencies and potential risks.</p>
<h2 class="wp-block-heading" id="h-importance-of-addressing-consistency-issues">Importance of Addressing Consistency Issues<a class="anchor-link" id="importance-of-addressing-consistency-issues"></a></h2>
<p>Addressing consistency issues in PostgreSQL Multi-Master replication is paramount for several critical reasons. Firstly, <strong>data integrity</strong> is fundamental for any application. Inconsistent data can lead to incorrect business decisions, erroneous reports, and even financial losses. By ensuring that all master nodes maintain an accurate and up-to-date view of the data, organizations can safeguard the reliability and trustworthiness of their applications.&nbsp;</p>
<p>Secondly, <strong>application correctness</strong> heavily relies on consistent data. Inconsistent data can cause unexpected application behavior, leading to errors, crashes, and data corruption. Addressing consistency issues is crucial for ensuring that applications function as expected and provide a seamless user experience.&nbsp;</p>
<p>Thirdly, <strong>regulatory compliance</strong> often mandates data consistency and accuracy. Many industries have strict regulations regarding data integrity and security. By maintaining data consistency in a multi-master environment, organizations can ensure compliance with relevant regulations and avoid costly penalties.&nbsp;</p>
<p>Finally, <strong>user trust</strong> is directly impacted by data consistency. Users expect applications to provide accurate and reliable information. Inconsistent data can erode user trust, leading to dissatisfaction, churn, and reputational damage. By prioritizing data consistency, organizations can build and maintain trust with their users, fostering long-term relationships and ensuring the continued success of their applications.</p>
<h2 class="wp-block-heading" id="h-how-clustercontrol-can-help">How ClusterControl can help<a class="anchor-link" id="how-clustercontrol-can-help"></a></h2>
<p>ClusterControl exposes Multi Master Replication features that will automatically reduce the time and expertise required to manage Multi-Master PostgreSQL clusters. By abstracting away the complexities of configuration and management, ClusterControl enables organizations to leverage the full power of Multi-Master PostgreSQL without needing deep expertise in database administration. This democratizes access to advanced database architectures, allowing more organizations to benefit from the scalability, availability, and resilience that Multi-Master PostgreSQL provides.</p>
<h2 class="wp-block-heading" id="h-wrapping-up">Wrapping up<a class="anchor-link" id="wrapping-up"></a></h2>
<p>Multi-Master replication in PostgreSQL offers a compelling solution for organizations seeking to enhance their database systems&rsquo; availability, scalability, and performance. By enabling multiple nodes to accept writes and distribute read traffic independently, multi-master setups can significantly improve system responsiveness and reduce latency. </p>
<p>However, careful consideration must be given to the inherent challenges, such as conflict resolution, data consistency, and operational complexity. By implementing robust solutions and carefully managing the system, organizations can leverage the power of multi-master replication to build highly available, scalable, and resilient database systems that meet the demands of modern applications.</p>
<p>Ready to make PostgreSQL management easier and more reliable in any environment?</p>
<h2 class="wp-block-heading" id="h-install-clustercontrol-in-10-minutes-nbsp-free-30-day-nbsp-enterprise-trial-included">Install ClusterControl in 10-minutes.&nbsp;<strong>Free 30-day&nbsp;</strong>Enterprise trial included!<a class="anchor-link" id="install-clustercontrol-in-10-minutes-free-30-day-enterprise-trial-included"></a></h2>
<h3 class="wp-block-heading" id="h-script-installation-instructions">Script Installation Instructions<a class="anchor-link" id="script-installation-instructions"></a></h3>
<p>The installer script is the simplest way to get ClusterControl up and running. Run it on your chosen host, and it will take care of installing all required packages and dependencies.</p>
<p>Offline environments are supported as well. See the&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/offline-installation/">Offline Installation</a>&nbsp;guide for more details.</p>
<p>On the ClusterControl server, run the following commands:</p>
<pre class="wp-block-code"><code>wget https://severalnines.com/downloads/cmon/install-cc
chmod +x install-cc</code></pre>
<p>With your install script ready, run the command below. Replace&nbsp;<code>S9S_CMON_PASSWORD</code>&nbsp;and&nbsp;<code>S9S_ROOT_PASSWORD</code>&nbsp;placeholders with your choice password, or remove the environment variables from the command to interactively set the passwords. If you have multiple network interface cards, assign one IP address for the&nbsp;<code>HOST</code>&nbsp;variable in the command using&nbsp;<code>HOST=&lt;ip_address&gt;</code>.</p>
<pre class="wp-block-code"><code>S9S_CMON_PASSWORD=&lt;your_password&gt; S9S_ROOT_PASSWORD=&lt;your_password&gt; HOST=&lt;ip_address&gt; ./install-cc # as root or sudo user</code></pre>
<p>After the installation is complete, open a web browser, navigate to&nbsp;<code>https://&lt;ClusterControl_host&gt;/</code>, and create the first admin user by entering a username (note that &ldquo;admin&rdquo; is reserved) and a password on the&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/quickstart/#step-2-create-the-first-admin-user">welcome page</a>. Once you&rsquo;re in, you can&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/user-guide/deployment/create-database-cluster/">deploy</a>&nbsp;a new database cluster or&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/user-guide/deployment/import-database-cluster/">import</a>&nbsp;an existing one.</p>
<p>The installer script supports a range of environment variables for advanced setup. You can define them using export or by prefixing the install command.</p>
<p>See the&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/online-installation/#environment-variables">list of supported variables</a>&nbsp;and&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/online-installation/#example-use-cases">example use cases</a>&nbsp;to tailor your installation.</p>
<h4 class="wp-block-heading" id="h-other-installation-options">Other Installation Options</h4>
<p><strong>Helm Chart</strong></p>
<p>Deploy ClusterControl on Kubernetes using our&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/online-installation/#helm-chart">official Helm chart</a>.</p>
<p><strong>Ansible Role</strong></p>
<p>Automate installation and configuration using our&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/online-installation/#ansible-role">Ansible playbooks</a>.</p>
<p><strong>Puppet Module</strong></p>
<p>Manage your ClusterControl deployment with the&nbsp;<a href="https://docs.severalnines.com/clustercontrol/latest/getting-started/installation/online-installation/#puppet-module">Puppet module</a>.</p>
<h4 class="wp-block-heading" id="h-clustercontrol-on-marketplaces">ClusterControl on Marketplaces</h4>
<p>Prefer to launch ClusterControl directly from the cloud? It&rsquo;s available on these platforms:</p>
<ul class="wp-block-list">
<li><a href="https://marketplace.digitalocean.com/apps/clustercontrol">DigitalOcean Marketplace</a></li>
<li><a href="https://gridscale.io/en/marketplace">gridscale.io Marketplace</a></li>
<li><a href="https://www.vultr.com/marketplace/apps/clustercontrol/">Vultr Marketplace</a></li>
<li><a href="https://www.linode.com/marketplace/apps/severalnines/clustercontrol/">Linode Marketplace</a></li>
<li><a href="https://console.cloud.google.com/marketplace/product/severalnines-public/clustercontrol">Google Cloud Platform</a></li>
</ul>
<p>The post <a href="https://severalnines.com/blog/postgresql-multi-master-operational-and-consistency-issues-in-brief/">PostgreSQL Multi-Master Operational and Consistency Issues in Brief</a> appeared first on <a href="https://severalnines.com">Severalnines</a>.</p>

<p>The post <a rel="nofollow" href="https://severalnines.com/blog/postgresql-multi-master-operational-and-consistency-issues-in-brief/">PostgreSQL Multi-Master Operational and Consistency Issues in Brief</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
      <item>
      <title>What if OpenAI Used MariaDB Instead of PostgreSQL to Handle 800 Million Users?</title>
      <link>https://mariadb.com/resources/blog/what-if-openai-used-mariadb-instead-of-postgresql-to-handle-800-million-users/</link>
      <pubDate>Thu, 29 Jan 2026 19:19:44 +0000</pubDate>
      <dc:creator></dc:creator>
      <guid isPermaLink="false"></guid>
      <source url="https://mariadb.com/">MariaDB</source>
      <description><![CDATA[<p>OpenAI’s journey to scale its infrastructure has provided valuable insights into the limitations and scaling challenges of traditional database systems, […]</p>
<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/what-if-openai-used-mariadb-instead-of-postgresql-to-handle-800-million-users/">What if OpenAI Used MariaDB Instead of PostgreSQL to Handle 800 Million Users?</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></description>
      <content:encoded><![CDATA[<p>OpenAI&rsquo;s journey to scale its infrastructure has provided valuable insights into the limitations and scaling challenges of traditional database systems, particularly PostgreSQL (https://openai.com/index/scaling-postgresql/). As detailed by their engineering teams, several core areas became bottlenecks as they navigated explosive growth and massive data volumes. Interestingly&hellip;</p>
<p><a href="https://mariadb.com/resources/blog/what-if-openai-used-mariadb-instead-of-postgresql-to-handle-800-million-users/" rel="nofollow">Source</a></p>

<p>The post <a rel="nofollow" href="https://mariadb.com/resources/blog/what-if-openai-used-mariadb-instead-of-postgresql-to-handle-800-million-users/">What if OpenAI Used MariaDB Instead of PostgreSQL to Handle 800 Million Users?</a> appeared first on <a rel="nofollow" href="https://mariadb.org">MariaDB.org</a>.</p>
]]></content:encoded>
                </item>
    </channel>
</rss>