<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.2">Jekyll</generator><link href="https://mysqlonarm.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://mysqlonarm.github.io/" rel="alternate" type="text/html" /><updated>2022-08-23T05:46:15+00:00</updated><id>https://mysqlonarm.github.io/feed.xml</id><title type="html">MySQL On ARM</title><subtitle>All you need to know about MySQL (and its variants) on ARM.</subtitle><entry><title type="html">openGauss on ARM</title><link href="https://mysqlonarm.github.io/opengauss-on-arm/" rel="alternate" type="text/html" title="openGauss on ARM" /><published>2022-08-23T00:00:00+00:00</published><updated>2022-08-23T00:00:00+00:00</updated><id>https://mysqlonarm.github.io/opengauss-on-arm</id><content type="html" xml:base="https://mysqlonarm.github.io/opengauss-on-arm/">&lt;p&gt;openGauss is an open-source relational database built for internet-scale and optimized for ARM architecture (retaining compatibility with x86). DB kernel is derived from PostgreSQL so PgSQL users will find a lot of things familiar but a series of optimizations are added to make it faster and support distributed setup (cluster ecosystem).  It supports a row-based, column-based, and in-memory storage engine with full ACID compliance. The ecosystem and contribution from the community continue to grow.&lt;/p&gt;

&lt;p&gt;Given the DB is optimized for ARM, it sparked my interest and so I decided to evaluate it.&lt;/p&gt;

&lt;h2 id=&quot;key-features&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Key Features&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;openGauss was derived from PostgreSQL 9.2 but post that many features were added to openGauss to make it more enterprise-ready.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Multi-threaded (vs multi-process):&lt;/strong&gt; One of the most discussed features of PgSQL is the need for multi-threading. Fortunately, openguass has been ported to use a multi-threaded model. Each process in PgSQL maps directly to a thread.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Thread Pool:&lt;/strong&gt; PgSQL still doesn’t have a thread pool and needs an additional component like pgpool or so. openGauss has an inherent thread pool allowing it to scale and handle the multiple active short-lived connections effectively.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Incremental Checkpoint:&lt;/strong&gt; Given the time it takes for the time-based checkpoint this feature comes as a savior with continuous checkpointing (more on lines of MySQL).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Doublewrite:&lt;/strong&gt; To avoid half-cooked pages (again on lines of MySQL).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Optimization of Global Counter:&lt;/strong&gt; Most global counters in opengauss use thread local storage copy that is then aggregated to the main counter.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;NUMA scalability:&lt;/strong&gt; openguass has been designed/optimized/tuned to ensure it scales well with multiple NUMA nodes and increases scalability and that is quite evident with broader spread use of thread local storage, multiple threads, threadpool, etc… allowing it to scale better with multi-numa node machines.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While a lot of these features are done a lot more is being done especially around IO.&lt;/p&gt;

&lt;h2 id=&quot;setup&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Setup&lt;/span&gt;&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Machine Configuration:
    &lt;ul&gt;
      &lt;li&gt;ARM: 96 vCPU (4 NUMA) ARM Kunpeng 920 CPU @ 2.6 Ghz&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Workload (using sysbench):
    &lt;ul&gt;
      &lt;li&gt;CPU bound workload&lt;/li&gt;
      &lt;li&gt;pgbench: select, update workload&lt;/li&gt;
      &lt;li&gt;sysbench: read-only, read-write, write-only workload (pattern: uniform, zipfian)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Other configuration details here
    &lt;ul&gt;
      &lt;li&gt;Shared Buffer: 80GB&lt;/li&gt;
      &lt;li&gt;Data: 32 GB (pgbench)&lt;/li&gt;
      &lt;li&gt;Data: 75 GB (sysbench)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Storage: SATA SSD
    &lt;ul&gt;
      &lt;li&gt;sequential read/write IOPS: 65K/44+K (8K blocks)&lt;/li&gt;
      &lt;li&gt;random read/write IOPS: 51+K/38+K (8K blocks)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;openGauss Version: 3.0.1 [compile from source]&lt;/li&gt;
  &lt;li&gt;Scalability: 1-1024 threads: 21/42/84 threads for server and 3/6/12 threads for sysbench (core-binding).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;benchmark&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Benchmark&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;Let’s first explore the benchmark using some standard suits like pgbench, and sysbench. We will then discuss more specific configuration, NUMA scalability, threadpool, how it performs compared to pgsql, etc…&lt;/p&gt;

&lt;h3 id=&quot;pgbench&quot;&gt;&lt;span style=&quot;color:#0F9D58&quot;&gt;pgbench&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog34/pgbench.png&quot; height=&quot;400&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observations:&lt;/strong&gt;&lt;br /&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;pgbench read-only workload continues to scale well with increasing scalability but does hit a contention after a certain point that indeed suggests a scope of further improvement.&lt;/li&gt;
  &lt;li&gt;pgbench update workload continues to scale well with increasing scalability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;drop in the read-only workload of pgbench could be attributed to the memory allocation routine.&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+    3.40%          6236  worker           gaussdb               [.] GenericMemoryAllocator::AllocSetAlloc&amp;lt;true, false, false&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;hr /&gt;

&lt;h3 id=&quot;sysbench&quot;&gt;&lt;span style=&quot;color:#0F9D58&quot;&gt;sysbench&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog34/sysbench.png&quot; height=&quot;400&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observations:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;sysbench read/update workload continues to scale linearly before hitting a threshold, post that point the performance is almost flat (better than going down due to increased contention).&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;

&lt;h3 id=&quot;numa-scalability&quot;&gt;&lt;span style=&quot;color:#0F9D58&quot;&gt;numa scalability&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;opengauss scales well with the increasing scalability. Let’s now explore how it performs with increasing NUMA nodes.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog34/pgbench-numa.png&quot; height=&quot;400&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;br /&gt;
 &lt;img src=&quot;/images/blog34/sysbench-numa.png&quot; height=&quot;400&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observations:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;opengauss is well optimized for NUMA nodes. With increasing NUMA for different variants of workload, it continues to scale well. Update workload could be further improved but like other databases, with 4 NUMA, performance doesn’t regress.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;

&lt;h3 id=&quot;effect-of-threadpool&quot;&gt;&lt;span style=&quot;color:#0F9D58&quot;&gt;effect of threadpool&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;threadpool is best suited for the environment where there are a lot of short-lived connections. Let’s see the effect of threadpool.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog34/pgbench-tpool.png&quot; height=&quot;400&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;br /&gt;
&lt;img src=&quot;/images/blog34/sysbench-tpool.png&quot; height=&quot;400&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observations:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;threadpool is ideally expected to improve performance and it does in opengauss case too but only for the pgbench-ro workload. For all other workloads including sysbench ro workload, it failed to show the improvement. Infact, for update workload, a serious regression is observed with threadpool.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;

&lt;h3 id=&quot;pgsql-vs-opengauss&quot;&gt;&lt;span style=&quot;color:#0F9D58&quot;&gt;pgsql vs opengauss&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;Opengauss is inherited from PgSQL so it would be interesting to see how these both perform. We tried our best to match the configuration using the latest release (PgSQL 14.5/OpenGauss 13.0.1) of both the databases and on comparable servers (24 ARM cores, 48 GB of memory, and similar IO volumes).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog34/pgsqlvsopengauss.png&quot; height=&quot;400&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observations:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Read-only workload performance looks comparable.&lt;/li&gt;
  &lt;li&gt;Update workload performance seems to show a significant difference despite of the multiple enhancement in opengauss. (Note: opengauss is operated with threadpool and incremental checkpoint turned off so things are comparable).&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Conclusion&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;Based on the overall evaluation, numa optimization, features, etc.. opengauss seems promising. Given it is relatively new, there is ample scope for improvement.
&lt;br /&gt;&lt;br /&gt;
&lt;em&gt;If you have more questions/queries do let me know. Will try to answer them.&lt;/em&gt;&lt;/p&gt;</content><author><name>Krunal Bauskar</name></author><summary type="html">openGauss is an open-source relational database built for internet-scale and optimized for ARM architecture (retaining compatibility with x86). DB kernel is derived from PostgreSQL so PgSQL users will find a lot of things familiar but a series of optimizations are added to make it faster and support distributed setup (cluster ecosystem). It supports a row-based, column-based, and in-memory storage engine with full ACID compliance. The ecosystem and contribution from the community continue to grow.</summary></entry><entry><title type="html">MariaDB on openEuler-ARM stack - Multi-NUMA study</title><link href="https://mysqlonarm.github.io/mdb-openeuler-multinuma/" rel="alternate" type="text/html" title="MariaDB on openEuler-ARM stack - Multi-NUMA study" /><published>2022-07-19T00:00:00+00:00</published><updated>2022-07-19T00:00:00+00:00</updated><id>https://mysqlonarm.github.io/mdb-openeuler-multinuma</id><content type="html" xml:base="https://mysqlonarm.github.io/mdb-openeuler-multinuma/">&lt;p&gt;MariaDB on the openEuler-ARM stack has shown promising results during the initial evaluation. (If you have missed that blog click &lt;a href=&quot;https://mysqlonarm.github.io/mdb-on-openeuler/&quot;&gt;here&lt;/a&gt;). Taking the assessment further we decided to evaluate the setup in a multi-numa environment since it represents enterprise deployment. The aim is to find out if things scale like other OS-ARM stacks and if the bottleneck continues to remain the same or if something else pops up on the openEuler-ARM stack.&lt;/p&gt;

&lt;h2 id=&quot;setup&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Setup&lt;/span&gt;&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Machine Configuration
    &lt;ul&gt;
      &lt;li&gt;ARM: 96 vCPU (4 NUMA) ARM Kunpeng 920 CPU @ 2.6 Ghz (openEuler - ARM stack)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Workload:
    &lt;ul&gt;
      &lt;li&gt;sysbench: point-select, read-only, update-index, update-non-index&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Other configuration details &lt;a href=&quot;https://github.com/mysqlonarm/benchmark-suites/blob/master/mysql-sbench/conf/mdb.cnf/100tx3m_108_cpubound.cnf&quot;&gt;here&lt;/a&gt;[ (io-capacity/max=10K/18K)]
    &lt;ul&gt;
      &lt;li&gt;data-size: 74GB, buffer-pool: 80GB (all-in-memory)&lt;/li&gt;
      &lt;li&gt;redo-log-size: 20 GB&lt;/li&gt;
      &lt;li&gt;storage: sequential read/write IOPS: 33+K/16+K/random read/write IOPS: 28+K/14+K&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;MariaDB Version: 10.10 (#88b2235 in progress)
    &lt;ul&gt;
      &lt;li&gt;OS Version (mostly with default settings)&lt;/li&gt;
      &lt;li&gt;openEuler: 20.03 LTS-SP2&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;evaluation&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Evaluation&lt;/span&gt;&lt;/h2&gt;

&lt;h3 id=&quot;read-only-workload&quot;&gt;&lt;span style=&quot;color:#0F9D58&quot;&gt;read-only workload&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog33/ro.png&quot; height=&quot;400&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observations:&lt;/strong&gt;&lt;br /&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Just like other OS-ARM stacks, the read-only workload continues to scale almost linearly with the openEuler-ARM stack with increasing NUMA nodes.&lt;/li&gt;
  &lt;li&gt;Infact, due to the lower contention of some mutexes (Lock_table_cache), the performance of the read-only workload on the openEuler-ARM stack is better than other stacks.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;

&lt;h3 id=&quot;write-workload&quot;&gt;&lt;span style=&quot;color:#0F9D58&quot;&gt;write workload&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog33/wr.png&quot; height=&quot;400&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observations:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Again, we continue to observe the same pattern as other OS-ARM stacks. Write workload fails to scale on multi-numa nodes.&lt;/li&gt;
  &lt;li&gt;Top-Contention points continue to remain the same.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;openEuler-ARM stack&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;| wait/synch/rwlock/innodb/log_latch                        | 19169202.9823 |  199868944 |
| wait/synch/rwlock/innodb/trx_rseg_latch                   |   785002.5091 |  123518641 |
| wait/synch/mutex/innodb/buf_pool_mutex                    |   638941.9645 |   56602817 |
| wait/synch/cond/mysys/COND_timer                          |   402259.4476 |       1532 |
| wait/synch/cond/aria/SERVICE_THREAD_CONTROL::COND_control |   389933.2353 |         13 |
| wait/synch/rwlock/innodb/fil_space_latch                  |   325025.1544 |    4433912 |
| wait/synch/rwlock/sql/MDL_lock::rwlock                    |   311635.8508 |  236808760 |
| wait/synch/sxlock/innodb/index_tree_rw_lock               |    46259.4764 |  134161482 |
| wait/synch/mutex/sql/LOCK_table_cache                     |    21153.7182 |   77818000 |
| wait/synch/mutex/sql/THD::LOCK_thd_data                   |    11876.1819 |  178734356 |
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;centOS-ARM stack&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;| wait/synch/rwlock/innodb/log_latch                        | 16410690.9523 |  215740728 |
| wait/synch/cond/mysys/COND_timer                          |   834065.5579 |       3066 |
| wait/synch/rwlock/innodb/trx_rseg_latch                   |   833276.8134 |  131173975 |
| wait/synch/cond/aria/SERVICE_THREAD_CONTROL::COND_control |   811668.6162 |         27 |
| wait/synch/rwlock/sql/MDL_lock::rwlock                    |   640257.8943 |  258275876 |
| wait/synch/mutex/innodb/buf_pool_mutex                    |   587507.9794 |   59236236 |
| wait/synch/rwlock/innodb/fil_space_latch                  |   341941.7540 |    4669815 |
| wait/synch/sxlock/innodb/index_tree_rw_lock               |    64058.7856 |  145183127 |
| wait/synch/mutex/sql/LOCK_table_cache                     |    38422.9402 |   84390443 |
| wait/synch/mutex/sql/THD::LOCK_thd_data                   |    11220.5507 |  196408431 |
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;the openEuler-ARM stack continues to perform on par with other stacks and to check that out that we did some comparative analysis against the centos-ARM stack (using ramdisk since both servers don’t have comparable IO systems).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog33/openeuler-centos.png&quot; height=&quot;400&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;hr /&gt;

&lt;h3 id=&quot;evaluation-using-thread-pool&quot;&gt;&lt;span style=&quot;color:#0F9D58&quot;&gt;Evaluation using thread pool&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;OS-Level Scheduler has a significant role to play with thread handling so we also decided to evaluate the effect of the thread pool.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog33/tpool.png&quot; height=&quot;400&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observations:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;As expected, enabling thread pool has +ve effect on performance just like other OS-Hardware stacks.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Conclusion&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;Based on the evaluation we can comfortably say that MariaDB continues to perform on par/better on the openEuler-ARM stack (when compared to proven stacks). Given the said fact it could be worth considering supporting the openEuler-ARM stack.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
&lt;em&gt;If you have more questions/queries do let me know. Will try to answer them.&lt;/em&gt;&lt;/p&gt;</content><author><name>Krunal Bauskar</name></author><summary type="html">MariaDB on the openEuler-ARM stack has shown promising results during the initial evaluation. (If you have missed that blog click here). Taking the assessment further we decided to evaluate the setup in a multi-numa environment since it represents enterprise deployment. The aim is to find out if things scale like other OS-ARM stacks and if the bottleneck continues to remain the same or if something else pops up on the openEuler-ARM stack.</summary></entry><entry><title type="html">Evaluating MariaDB on openEuler-ARM stack</title><link href="https://mysqlonarm.github.io/mdb-on-openeuler/" rel="alternate" type="text/html" title="Evaluating MariaDB on openEuler-ARM stack" /><published>2022-07-06T00:00:00+00:00</published><updated>2022-07-06T00:00:00+00:00</updated><id>https://mysqlonarm.github.io/mdb-on-openeuler</id><content type="html" xml:base="https://mysqlonarm.github.io/mdb-on-openeuler/">&lt;p&gt;openEuler is an opensource linux based operating system with a customized scheduler, io, libraries, etc.. It is optimized for ARM64 architecture and so it could be interesting to evaluate different enterprise software running on the openEuler-ARM stack. As part of this study, let’s explore how MariaDB that already has packages on ARM for different operating systems performs on openEuler.&lt;/p&gt;

&lt;h2 id=&quot;setup&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Setup&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;Given that openEuler is optimized for ARM let’s try to evaluate it against ubuntu-arm stack (proven configuration).&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Machine Configuration (from huaweicloud)
    &lt;ul&gt;
      &lt;li&gt;openEuler-on-arm: 24 vCPU - 48 GB (Kunpeng 920 2.6 Ghz)&lt;/li&gt;
      &lt;li&gt;ubuntu-on-arm: 24 vCPU - 48 GB (Kunpeng 920 2.6 Ghz)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Workload:
    &lt;ul&gt;
      &lt;li&gt;sysbench: point-select, read-only, update-index, update-non-index&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Other configuration details &lt;a href=&quot;https://github.com/mysqlonarm/benchmark-suites/blob/master/mysql-sbench/conf/mdb.cnf/100tx1.5m_cpubound.cnf&quot;&gt;here&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;data-size: 35GB, buffer-pool: 40GB (all-in-memory)/20 GB (part of data in memory)&lt;/li&gt;
      &lt;li&gt;redo-log-size: 10 GB&lt;/li&gt;
      &lt;li&gt;storage: EVS volume with 22K IOPS (read/write mixed).&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;MariaDB Version: 10.8 3 (GA).&lt;/li&gt;
  &lt;li&gt;OS Version (mostly with default settings)
    &lt;ul&gt;
      &lt;li&gt;Ubuntu: 18.04.3 LTS&lt;/li&gt;
      &lt;li&gt;openEuler: 20.03 LTS-SP2&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;benchmarking&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Benchmarking&lt;/span&gt;&lt;/h2&gt;

&lt;h3 id=&quot;read-only-workload&quot;&gt;&lt;span style=&quot;color:#0F9D58&quot;&gt;Read Only Workload&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;Let’s start with read-only workload. sysbench offers 2 read-only workloads: point-select, and read-only.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog32/ro-cpu-bound.png&quot; height=&quot;400&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observations:&lt;/strong&gt;&lt;br /&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;MariaDB on openEuler continues to score for all kinds of scenarios (uniform, zipfian) with improvement crossing 25% in some cases.&lt;/li&gt;
  &lt;li&gt;Performance Profiling pointed out the following facts
    &lt;ul&gt;
      &lt;li&gt;openEuler is better at handling contention (reported lesser contention).&lt;/li&gt;
      &lt;li&gt;memcpy seems to be optimized in openEuler given it doesn’t show up in top-5 perf hot function list.&lt;/li&gt;
      &lt;li&gt;performance schema profiling too reconfirmed a significant reduction in LOCK_table_cache contention.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;

&lt;p&gt;Now let’s test the io-bound use-case for read-only workload&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog32/ro-io-bound.png&quot; height=&quot;400&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observations:&lt;/strong&gt;&lt;br /&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Depending on workload either the performance of openEuler is on-par for lesser contention workload viz. uniform or better for higher contention workload viz. zipfian.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;

&lt;h3 id=&quot;write-workload&quot;&gt;&lt;span style=&quot;color:#0F9D58&quot;&gt;Write Workload&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;Let’s start with write workload. sysbench offers 2 write workloads: update-index, update-non-index&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog32/write-cpu-bound.png&quot; height=&quot;400&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observations:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;With write workload, openeuler has mixed performance. Especially, with uniform workload openeuler continue to lag but start scoring with zipfian use-cases.&lt;/li&gt;
  &lt;li&gt;To understand this lag better we tried multiple studies and eventually found out that flushing of pages is not consistent with openeuler there-by increasing the checkpoint age and in-turn causing the lag.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog32/long.lag.png&quot; height=&quot;400&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;br /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The extended run shows a clear picture. openEuler continues to perform better than ubuntu during an initial run but then starts losing steam due to inconsistent/below par page flushing.&lt;/li&gt;
  &lt;li&gt;Performance profiling reveals a significant increase in contention of rseg-&amp;gt;latch and fil_space_latch with openeuler (both occupying top slot vs ubuntu).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;openeuler&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;| wait/synch/rwlock/innodb/trx_rseg_latch                   | 18733914.8115 |  176880804 |
| wait/synch/rwlock/innodb/fil_space_latch                  |  6036822.3603 |    6801714 |
| wait/synch/cond/mysys/COND_timer                          |  1857132.6029 |       6896 |
| wait/synch/cond/aria/SERVICE_THREAD_CONTROL::COND_control |  1834108.5978 |         61 |
| wait/synch/rwlock/sql/MDL_lock::rwlock                    |   293917.5930 |  344366933 |
| wait/synch/mutex/innodb/flush_list_mutex                  |   181595.7034 |  403167359 |
| wait/synch/sxlock/innodb/index_tree_rw_lock               |   101674.7901 |  194147135 |
| wait/synch/rwlock/innodb/log_latch                        |    58528.4128 |  302800939 |
| wait/synch/mutex/sql/LOCK_table_cache                     |    43197.0327 |  110726733 |
| wait/synch/mutex/innodb/buf_pool_mutex                    |    26246.6964 |   72853477 |
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;ubuntu&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;| wait/synch/cond/mysys/COND_timer                          | 1889638.3321 |       7310 |
| wait/synch/cond/aria/SERVICE_THREAD_CONTROL::COND_control | 1866600.4146 |         62 |
| wait/synch/rwlock/innodb/trx_rseg_latch                   |  880059.8838 |  432965780 |
| wait/synch/mutex/innodb/flush_list_mutex                  |  740073.9749 |  666248407 |
| wait/synch/rwlock/sql/MDL_lock::rwlock                    |  461974.0141 |  659530098 |
| wait/synch/rwlock/innodb/fil_space_latch                  |  411518.1018 |   19912041 |
| wait/synch/rwlock/innodb/log_latch                        |  276165.8275 |  607470128 |
| wait/synch/mutex/sql/LOCK_table_cache                     |  141710.7974 |  188402516 |
| wait/synch/mutex/innodb/buf_pool_mutex                    |  126368.0575 |  177463579 |
| wait/synch/sxlock/innodb/index_tree_rw_lock               |   43622.2066 |  403046978 |
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;hr /&gt;

&lt;p&gt;Now let’s test the io-bound use-case for write workload&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog32/write-io-bound.png&quot; height=&quot;400&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observations:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Performance is quite similar to cpu bound use case.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Conclusion&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;From the study so far, openEuler on ARM looks promising. It has scored well on the read-only front and has shown mixed results for write workload. The issue of contention needs to be looked at to help understand why the said latches are prominent on openEuler and possible IO issues.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
&lt;em&gt;If you have more questions/queries do let me know. Will try to answer them.&lt;/em&gt;&lt;/p&gt;</content><author><name>Krunal Bauskar</name></author><summary type="html">openEuler is an opensource linux based operating system with a customized scheduler, io, libraries, etc.. It is optimized for ARM64 architecture and so it could be interesting to evaluate different enterprise software running on the openEuler-ARM stack. As part of this study, let’s explore how MariaDB that already has packages on ARM for different operating systems performs on openEuler.</summary></entry><entry><title type="html">Stabilize your MariaDB performance using this simple step</title><link href="https://mysqlonarm.github.io/stablize-your-mdb/" rel="alternate" type="text/html" title="Stabilize your MariaDB performance using this simple step" /><published>2022-03-07T00:00:00+00:00</published><updated>2022-03-07T00:00:00+00:00</updated><id>https://mysqlonarm.github.io/stablize-your-mdb</id><content type="html" xml:base="https://mysqlonarm.github.io/stablize-your-mdb/">&lt;p&gt;While benchmarking MariaDB Server, I often observe a spike in performance at the start that eventually drops by 10-15% and then continues to remain constant at the said level. Concern is not the drawn down but the fluctuation that users may observe especially while running longer workload. Fortunately, found a way to resolve this but still investigating why it is so.&lt;/p&gt;

&lt;h2 id=&quot;setup&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Setup&lt;/span&gt;&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Machine Configuration:
    &lt;ul&gt;
      &lt;li&gt;ARM: 64 vCPU (2 NUMA) ARM Kunpeng 920 CPU @ 2.6 Ghz&lt;/li&gt;
      &lt;li&gt;x86: 64 vCPU (2 NUMA) Intel(R) Xeon(R) Gold 6151 CPU @ 3.00GHz&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Workload:
    &lt;ul&gt;
      &lt;li&gt;sysbench update-index uniform&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Other configuration details here (+ thread_handling=pool-of-threads).
    &lt;ul&gt;
      &lt;li&gt;Shared Buffer: 80GB&lt;/li&gt;
      &lt;li&gt;Data: 74 GB&lt;/li&gt;
      &lt;li&gt;Redo-Log: 20 GB&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Storage: NVME SSD
    &lt;ul&gt;
      &lt;li&gt;sequential read/write IOPS: 190+K/125+K&lt;/li&gt;
      &lt;li&gt;random read/write IOPS: 180+K/65+K&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;MariaDB Version: 10.8 3 (work-in-progress. Wanted to use redo-log optimization).&lt;/li&gt;
  &lt;li&gt;Scalability: 1-1024 threads&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;benchmarking&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Benchmarking&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;For benchmarking, we execute sysbench update-index (uniform) workload for 60 mins and monitor the throughput every second.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog31/arm.tps.v1.png&quot; height=&quot;400&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog31/x86.tps.v1.png&quot; height=&quot;400&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;Observations:&lt;/strong&gt;&lt;br /&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;ARM starts with approximately ~ 150K throughput but within a short period of time throughput drops down to 125K and then continues to remain stable at the new level there-by leading to a draw-down of 15% from the recent high.&lt;/li&gt;
  &lt;li&gt;x86 continues to show a lot of jitter in the performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I started doubting the furious flushing, filling of the redo log, etc… None of these indicators explained the draw-down.&lt;br /&gt; &lt;strong&gt;Accidentally, I tried to clear the OS cache and things started to improve.&lt;/strong&gt;
&lt;i&gt;Note: “command used to purge os cache: echo 3 &amp;gt; /proc/sys/vm/drop_caches”&lt;/i&gt;&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog31/arm.tps.v2.png&quot; height=&quot;400&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog31/x86.tps.v2.png&quot; height=&quot;400&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;Observations:&lt;/strong&gt;&lt;br /&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;So clearing the OS cache, at a regular interval (in this case every 100 seconds) has a +ve effect on throughput in ARM case but it fails to have a +ve effect in the x86 case.&lt;/li&gt;
  &lt;li&gt;Let’s reconfirm this observation with a different configuration machines.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog31/arm.tps.v3.png&quot; height=&quot;400&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog31/x86.tps.v3.png&quot; height=&quot;400&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;Observations:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;With these different configuration machines (24 vCPU, 48 GB, 22K IOPS), regularly clearing the OS cache helps in reducing jitter for both ARM and x86.&lt;/li&gt;
  &lt;li&gt;Just to rule possible ambiguity, vm.swapiness is set to 1 on all benchmarked machines.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Conclusion&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;So clearing OS cache seems to have a +ve effect on performance. It helps reduce jitter. But it is not a blanket advice. As we saw above, it could have different effect based on the machine and configuration. It could be something worth trying in your environment to explore if that helps. Also, we are yet to trace why/what cached data clearance is helping reducing jitter.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
&lt;em&gt;If you have more questions/queries do let me know. Will try to answer them.&lt;/em&gt;&lt;/p&gt;</content><author><name>Krunal Bauskar</name></author><summary type="html">While benchmarking MariaDB Server, I often observe a spike in performance at the start that eventually drops by 10-15% and then continues to remain constant at the said level. Concern is not the drawn down but the fluctuation that users may observe especially while running longer workload. Fortunately, found a way to resolve this but still investigating why it is so.</summary></entry><entry><title type="html">Profiling new version of MariaDB</title><link href="https://mysqlonarm.github.io/profiling-new-version-of-mdb/" rel="alternate" type="text/html" title="Profiling new version of MariaDB" /><published>2022-02-24T00:00:00+00:00</published><updated>2022-02-24T00:00:00+00:00</updated><id>https://mysqlonarm.github.io/profiling-new-version-of-mdb</id><content type="html" xml:base="https://mysqlonarm.github.io/profiling-new-version-of-mdb/">&lt;p&gt;MariaDB is continuously evolving and in order to make it more scalable lot of age old, data constructs are being upgraded/revamped to the new age scalable constructs. This series of changes has helped it scale better than most of the open-source databases available. If you have been profiling MariaDB for quite some time now then it is important to ensure that you upgrade/widen your profiling scope to cover these new hot spots.&lt;/p&gt;

&lt;h2 id=&quot;mutex-vs-latch&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Mutex vs Latch&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;There are a lot of use-cases where the flow doesn’t need a mutex (exclusive access) but what it really needs is a latch (multiple readers, single writer) kinds of access. MariaDB has started identifying such use-cases and has ported them to use latches (vs original mutexes). So the traditional tracking mechanism for mutex hotspot needs to be widened.&lt;/p&gt;

&lt;p&gt;Most of the users may be using following settings to track MariaDB performance bottleneck&lt;/p&gt;

&lt;p&gt;performance-schema=1&lt;br /&gt;
performance-schema-instrument=’wait/synch/mutex/innodb/%=ON’&lt;/p&gt;

&lt;p&gt;Since the said construct only captures mutex and doesn’t capture locks/latches profiling, information obtain using performance_schema.events_waits_summary_global_by_event_name will not present a complete picture.&lt;/p&gt;

&lt;p&gt;Let’s understand how the hotspot has changed from 10.5 to 10.6 to 10.8&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;10.5.16&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;| wait/synch/mutex/innodb/log_sys_mutex         | 10993543.1228 |   73893323 |
| wait/synch/mutex/innodb/lock_mutex            |   409445.6364 |   72562319 |
| wait/synch/mutex/innodb/redo_rseg_mutex       |   282889.2468 |   60315800 |
| wait/synch/mutex/sql/LOCK_table_cache         |    87841.0034 |   30053316 |
| wait/synch/mutex/innodb/buf_pool_mutex        |    31164.3056 |   24091341 |
| wait/synch/mutex/innodb/log_flush_order_mutex |    30715.0940 |    6204400 |
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;10.6.8&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;| wait/synch/mutex/innodb/log_sys_mutex         | 8196724.5317 |   94451895 |
| wait/synch/mutex/sql/LOCK_table_cache         |   53218.7635 |   38989014 |
| wait/synch/mutex/innodb/log_flush_order_mutex |   22115.2731 |    5461808 |
| wait/synch/mutex/innodb/buf_pool_mutex        |   19749.9868 |   24328777 |
| wait/synch/mutex/threadpool/group_mutex       |    9871.5299 |   73233384 |
| wait/synch/mutex/sql/THD::LOCK_thd_data       |    6153.8019 |   91115395 |
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;ul&gt;
  &lt;li&gt;log_sys_mutex contention continued.&lt;/li&gt;
  &lt;li&gt;lock_mutex, redo_rseg_mutex disappeared. Does that mean contention has been completely resolved in 10.6?&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;10.8.3&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;| wait/synch/mutex/innodb/buf_pool_mutex                 | 48952.9588 |   20786569 |
| wait/synch/mutex/sql/LOCK_table_cache                  | 34947.7470 |   43650073 |
| wait/synch/mutex/threadpool/group_mutex                | 14281.8536 |   84727091 |
| wait/synch/mutex/innodb/fil_system_mutex               |  7047.8210 |    5240336 |
| wait/synch/mutex/sql/THD::LOCK_thd_data                |  6963.7040 |  103323350 |
| wait/synch/mutex/innodb/flush_list_mutex               |  6182.9708 |    4925339 |
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;ul&gt;
  &lt;li&gt;log_sys_mutex contention also disappeared.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;If you are used to the old way of mutex profiling then you should expand the scope and now start tracking rw-locks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;10.6.8&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Lock_mutex is now ported to latch and so contention is not visible with mutexes tracing.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;| wait/synch/rwlock/innodb/lock_latch                       |    11823.5208 |   92213015 |
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As part of this transition profiling of some important mutexes (converted to latches) has been removed. One such latch is redo_rseg_mutex which is now converted to latch but is not performance profiled. I tried local changes to cover it and found that it still represents significant contention. &lt;a href=&quot;https://jira.mariadb.org/browse/MDEV-27935&quot;&gt;MDEV-27935&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;| wait/synch/rwlock/innodb/trx_rseg_latch_key                 | 342616.7699 |   98834024 |
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;10.8.3&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Same way contention for log_mutex is not visible with 10.8.3 as log-mutex is now ported to log_latch.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;| wait/synch/rwlock/innodb/log_latch                        |  2993779.4089 |  103110276 |
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;hr /&gt;

&lt;p&gt;Let’s see how all these optimization has helped.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog30/version.tps.png&quot; height=&quot;400&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Improvement has significantly improved the performance of MariaDB.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Conclusion&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;More such mutexes are lined up to get ported to latches that will helps scale the MariaDB further. Also, it opens up an opportunities to explore NUMA optimized distributed latch.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
&lt;em&gt;If you have more questions/queries do let me know. Will try to answer them.&lt;/em&gt;&lt;/p&gt;</content><author><name>Krunal Bauskar</name></author><summary type="html">MariaDB is continuously evolving and in order to make it more scalable lot of age old, data constructs are being upgraded/revamped to the new age scalable constructs. This series of changes has helped it scale better than most of the open-source databases available. If you have been profiling MariaDB for quite some time now then it is important to ensure that you upgrade/widen your profiling scope to cover these new hot spots.</summary></entry><entry><title type="html">Benchmarking MariaDB on ARM with TPCC</title><link href="https://mysqlonarm.github.io/tpcc-mariadb-on-arm/" rel="alternate" type="text/html" title="Benchmarking MariaDB on ARM with TPCC" /><published>2022-02-22T00:00:00+00:00</published><updated>2022-02-22T00:00:00+00:00</updated><id>https://mysqlonarm.github.io/tpcc-mariadb-on-arm</id><content type="html" xml:base="https://mysqlonarm.github.io/tpcc-mariadb-on-arm/">&lt;p&gt;Majority of the users use cases are covered with sysbench variants of workload but there are users who have use-cases that could be best represented with TPCC or for that matter they would like to compare 2 databases using TPCC as a base standard. To help fill this gap I decided to evaluate TPCC using MariaDB on ARM.&lt;/p&gt;

&lt;h2 id=&quot;setup&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Setup&lt;/span&gt;&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Machine Configuration:
    &lt;ul&gt;
      &lt;li&gt;ARM: 128 vCPU (4 NUMA) ARM Kunpeng 920 CPU @ 2.6 Ghz&lt;/li&gt;
      &lt;li&gt;x86: 64 vCPU (2 NUMA) Intel(R) Xeon(R) Gold 6151 CPU @ 3.00GHz&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Workload (TPCC using sysbench) : –tables=10 –scale=100
    &lt;ul&gt;
      &lt;li&gt;https://github.com/Percona-Lab/sysbench-tpcc&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Other configuration details here (+ thread_handling=pool-of-threads).
    &lt;ul&gt;
      &lt;li&gt;Shared Buffer: 120GB&lt;/li&gt;
      &lt;li&gt;Data: 95 GB&lt;/li&gt;
      &lt;li&gt;Redo-Log: 30 GB&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Storage: NVME SSD
    &lt;ul&gt;
      &lt;li&gt;sequential read/write IOPS: 190+K/125+K&lt;/li&gt;
      &lt;li&gt;random read/write IOPS: 180+K/65+K&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;MariaDB Version:
    &lt;ul&gt;
      &lt;li&gt;10.8.3 (work-in-progress. Wanted to use redo-log optimization).&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Scalability: 1-1024 threads&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;benchmarking&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Benchmarking&lt;/span&gt;&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;For ARM, we benchmarked TPCC with 4 different (1/2/4) NUMA configurations.&lt;br /&gt;&lt;br /&gt;
 &lt;img src=&quot;/images/blog29/tpcc-arm.png&quot; height=&quot;400&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;

&lt;ul&gt;
  &lt;li&gt;We also compared 2 NUMA ARM and 2 NUMA x86 configurations to check if the ARM is performing on par with what the user would expect or used to. Both configurations have the same number of vCPU with different frequencies (2.6 (arm) vs 3 (x86)).&lt;br /&gt;&lt;br /&gt;
 &lt;img src=&quot;/images/blog29/arm-vs-x86-tpcc.png&quot; height=&quot;400&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;Observations:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;MariaDB on ARM continues to perform in line with the expectation even with TPCC workload.&lt;/li&gt;
  &lt;li&gt;MariaDB on ARM continues to be on par or even beat x86 performance, especially with higher scalability (despite of difference in frequency and ~50% lesser TCO).&lt;/li&gt;
  &lt;li&gt;For higher scalability, with 4 NUMA nodes, throughput continue to drop well below 50% (with respect to 2 NUMA node). Let’s quickly understand what is causing it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;perf profiling with 2 NUMA&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;+   17.66%         12557  mysqld   [kernel.kallsyms]    [k] queued_spin_lock_slowpath
+    8.70%          6544  mysqld   mariadbd             [.] l_find
+    5.56%         78524  mysqld   [kernel.kallsyms]    [k] finish_task_switch
+    3.34%          2887  mysqld   mariadbd             [.] MYSQLparse
+    2.89%          2090  mysqld   mariadbd             [.] rec_get_offsets_func
+    2.55%          1899  mysqld   [kernel.kallsyms]    [k] __wake_up_common_lock
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;| wait/synch/rwlock/innodb/lock_latch                       |  1497129.9573 |   73915212 |
| wait/synch/rwlock/innodb/log_latch                        |   802585.5836 |   79094893 |
| wait/synch/cond/mysys/COND_timer                          |   217272.8043 |        786 |
| wait/synch/cond/threadpool/timer_cond                     |   216049.2886 |        432 |
| wait/synch/cond/aria/SERVICE_THREAD_CONTROL::COND_control |   209020.7979 |          7 |
| wait/synch/sxlock/innodb/index_tree_rw_lock               |   118856.8702 |  118740596 |
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;perf profiling with 4 NUMA&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;+   58.35%         31357  mysqld   [kernel.kallsyms]    [k] queued_spin_lock_slowpath
+    3.40%          1883  mysqld   mariadbd             [.] l_find
+    1.62%        146440  mysqld   [kernel.kallsyms]    [k] finish_task_switch
+    1.46%          1062  mysqld   mariadbd             [.] rec_get_offsets_func
+    1.45%           883  mysqld   mariadbd             [.] buf_page_get_low
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;| wait/synch/sxlock/innodb/index_tree_rw_lock               |  6927204.8781 |   52455490 |
| wait/synch/rwlock/innodb/lock_latch                       |  6760422.3653 |   28812428 |
| wait/synch/rwlock/innodb/log_latch                        |  2650246.1917 |   32740980 |
| wait/synch/rwlock/sql/MDL_lock::rwlock                    |   221800.4496 |   40688789 |
| wait/synch/cond/mysys/COND_timer                          |   188957.6817 |        673 |
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Contention for lock_latch (in previous versions lock_sys.mutex) and log_latch (in previous version log_sys.mutex) has increased (inline with expectation as revealed with sysbench workloads too) but it is suprising to see index_tree_rw_lock contention increasing with 4 NUMA nodes. This contention now occupies top slot and has grown multi-fold from 6th to 1st position. Surely it deemed for the further investigation cum optimization. &lt;i&gt;(I tried some quick approaches like cacheline and spin-mutex. Nothing helped. Need to look at the access pattern).&lt;/i&gt;&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Conclusion&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;TPCC workload scale quite well with MariaDB on ARM. Growing NUMA nodes continue to pose challenges but those could be addressed as part of the wider/generic optimization. Sysbench access workload mostly shows up log_sys and lock contention but with TPCC we started seeing a different contention too.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
&lt;em&gt;If you have more questions/queries do let me know. Will try to answer them.&lt;/em&gt;&lt;/p&gt;</content><author><name>Krunal Bauskar</name></author><summary type="html">Majority of the users use cases are covered with sysbench variants of workload but there are users who have use-cases that could be best represented with TPCC or for that matter they would like to compare 2 databases using TPCC as a base standard. To help fill this gap I decided to evaluate TPCC using MariaDB on ARM.</summary></entry><entry><title type="html">MariaDB threadpool and NUMA scalability</title><link href="https://mysqlonarm.github.io/mdb-tpool-and-numa/" rel="alternate" type="text/html" title="MariaDB threadpool and NUMA scalability" /><published>2021-12-24T00:00:00+00:00</published><updated>2021-12-24T00:00:00+00:00</updated><id>https://mysqlonarm.github.io/mdb-tpool-and-numa</id><content type="html" xml:base="https://mysqlonarm.github.io/mdb-tpool-and-numa/">&lt;p&gt;MariaDB/MySQL default uses one thread per connection. This approach is generally good if the connection is active for a longer time. If the connections are short-lived then the cost of creating a connection could overshadow the cost of running the query. Also, with increasing scalability, OS-scheduling introduces more jitter. In this case, threadpool could act as a good alternative.&lt;/p&gt;

&lt;p&gt;In this article, we will explore the effect of the thread pool with growing NUMA scalability.&lt;/p&gt;

&lt;h2 id=&quot;threadpool&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Threadpool&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;MariaDB support dynamic threads pooling allowing it to scale up/down the number of thread groups as needed. Each thread group has 2 threads: listener and worker. Worker thread count could further increase if the stall is detected. You can read more details &lt;a href=&quot;https://mariadb.com/kb/en/thread-pool-in-mariadb/&quot;&gt;here&lt;/a&gt; but let’s try to understand important parameters that we plan to use/experiment with.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configuration:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;thread_handling=pool-of-threads (to enable thread pool).&lt;br /&gt;
thread_pool_size=X (number of threads group that determines how many statements execute in parallel).&lt;br /&gt;
thread_pool_idle_timeout=1 (number of seconds after which idle thread should exit if there is no work).&lt;br /&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-Monitoring:&quot;&gt;
show status like 'Threadpool%';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Threadpool_idle_threads | 201   |
| Threadpool_threads      | 202   |
+-------------------------+-------+
2 rows in set (0.001 sec)
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;setup&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Setup&lt;/span&gt;&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Machine Configuration:
    &lt;ul&gt;
      &lt;li&gt;ARM: 128 vCPU (4 NUMA/2 Socket) ARM Kunpeng 920 CPU @ 2.6 Ghz&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Workload (using sysbench):
    &lt;ul&gt;
      &lt;li&gt;CPU bound workload: Pattern: uniform, zipfian&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Other configuration details here (+ skip-log-bin).
    &lt;ul&gt;
      &lt;li&gt;Buffer Pool: 80GB&lt;/li&gt;
      &lt;li&gt;Data: 70GB&lt;/li&gt;
      &lt;li&gt;Redo-log: 20 GB&lt;/li&gt;
      &lt;li&gt;Configuration details: &lt;a href=&quot;https://github.com/mysqlonarm/benchmark-suites/tree/master/mysql-cluster-bench/cluster-conf/mdb-cluster-conf&quot;&gt;click here&lt;/a&gt; [+ skip-log-bin]&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Storage: NvME SSD
    &lt;ul&gt;
      &lt;li&gt;sequential read/write IOPS: 190+K/125+K&lt;/li&gt;
      &lt;li&gt;random read/write IOPS: 180+K/65+K&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;MariaDB Version: 10.6.5 (tagged GA)
    &lt;ul&gt;
      &lt;li&gt;Scalability: 512-1024 threads
        &lt;ul&gt;
          &lt;li&gt;28/56/112 threads for server&lt;/li&gt;
          &lt;li&gt;4/8/16 threads for sysbench&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;benchmarking&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Benchmarking&lt;/span&gt;&lt;/h2&gt;

&lt;h3 id=&quot;without-thread-pool-default&quot;&gt;&lt;span style=&quot;color: #0F9D58&quot;&gt;Without Thread Pool (default)&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog28/no-tpool.png&quot; height=&quot;300&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;observations&quot;&gt;&lt;span style=&quot;color: #DB4437&quot;&gt;Observations&lt;/span&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;For read-only workload, with increasing scalability (in form of NUMA) throughput continues to grow linearly.&lt;/li&gt;
  &lt;li&gt;For write workload, with increasing scalability (in form of NUMA) throughput drops. (why? Check &lt;a href=&quot;https://mysqlonarm.github.io/numa-scalability-with-mdb/&quot;&gt;this arcticle&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now let’s enable threadpool and see if this effect could be reversed or limited as threadpool could help eliminate the OS-scheduling jitter.&lt;/p&gt;
&lt;hr /&gt;

&lt;h3 id=&quot;with-thread-pool-thread_handlingpool-of-threads&quot;&gt;&lt;span style=&quot;color: #0F9D58&quot;&gt;With Thread Pool (thread_handling=pool-of-threads)&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog28/ro-tpool.png&quot; height=&quot;300&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;
&lt;br /&gt;
&lt;img src=&quot;/images/blog28/wr-tpool.png&quot; height=&quot;300&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;
&lt;br /&gt;&lt;/p&gt;

&lt;h3 id=&quot;observations-1&quot;&gt;&lt;span style=&quot;color: #DB4437&quot;&gt;Observations&lt;/span&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;read-only workload:&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Enabling threadpool helps improve the performance in a range of 10-20% for the majority of the scenarios except for 4 NUMA point-select use-case.&lt;/li&gt;
      &lt;li&gt;For 4 NUMA point select, there is a regression in performance (30-40%) with thread pool enabled.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;write workload:&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Enabling threadpool helps improve the write performance especially with the high contention use-case. Infact, in some cases, improvement is up to 100% that is 2x performance.&lt;/li&gt;
      &lt;li&gt;For the non-contention use-case, the performance of 4 NUMA threadpool is either on par or marginally less in some cases.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So we have mixed results with the majority of the cases seeing good benefit with the use of thread pool. The main cause of concern is the 4 NUMA point-select that have shown serious regression. Performance Profiling revealed that the issue is related to memory allocation in lock-free-hash due to the limited number of threads with higher throughput (1 million).&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+   62.41%        550335  mysqld   mariadbd             [.] lf_alloc_new&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Threadpool default configuration boots 112 threads (based on a number of cores allotted to the server) threads. Limited threads serving 1024 scalability introduce the NUMA allocation challenge.&lt;/p&gt;

&lt;p&gt;This prompted us to increase the base number of thread groups from default to 1024+&lt;/p&gt;
&lt;hr /&gt;

&lt;h3 id=&quot;with-thread-pool-thread_handlingpool-of-threads-thread_pool_size1500-thread_pool_idle_timeout1&quot;&gt;&lt;span style=&quot;color: #0F9D58&quot;&gt;With Thread Pool (thread_handling=pool-of-threads, thread_pool_size=1500, thread_pool_idle_timeout=1)&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;Setting thread_pool_size &amp;gt; 1024 is pretty clear but then why we need to set thread_pool_idle_timeout. This will ensure that if the thread is not serving any query or workload for more than 1 sec it is released and doesn’t hog the resources. Especially important when we try workload &amp;lt; 1024 threads (like 512 use-cases). (default of thread_pool_idle_timeout=60 sec which is too high for short workloads).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog28/ro-tpool2.png&quot; height=&quot;300&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;
&lt;br /&gt;
&lt;img src=&quot;/images/blog28/wr-tpool2.png&quot; height=&quot;300&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;
&lt;br /&gt;
&lt;em&gt;&lt;span style=&quot;font-size:0.5em;&quot;&gt;Note: legend tpool* refer to the above configuration&lt;/span&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;observations-2&quot;&gt;&lt;span style=&quot;color: #DB4437&quot;&gt;Observations&lt;/span&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;read-only workload:
    &lt;ul&gt;
      &lt;li&gt;So it helped 4 NUMA point-select use-case but made other scenarios worse or comparable to the w/o threadpool
  variant.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;write workload:
    &lt;ul&gt;
      &lt;li&gt;So additional fine-tuning doesn’t help.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That suggests depending on the workload user should tune the thread-pool parameters but enabling threadpool for system-wide usage seems to be a good idea including NUMA scalability.&lt;/p&gt;
&lt;hr /&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Conclusion&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;Enabling thread pool continue to help in most of the scenarios (with a significant gain observed in high contention scenario) but increasing NUMA scalability seems to pose a challenge that needs some more attention to help resolve the LF_HASH allocation issue.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
&lt;em&gt;If you have more questions/queries do let me know. Will try to answer them.&lt;/em&gt;&lt;/p&gt;</content><author><name>Krunal Bauskar</name></author><summary type="html">MariaDB/MySQL default uses one thread per connection. This approach is generally good if the connection is active for a longer time. If the connections are short-lived then the cost of creating a connection could overshadow the cost of running the query. Also, with increasing scalability, OS-scheduling introduces more jitter. In this case, threadpool could act as a good alternative.</summary></entry><entry><title type="html">GCC vs Clang - with MariaDB on ARM</title><link href="https://mysqlonarm.github.io/gcc-vs-clang-mdbonarm/" rel="alternate" type="text/html" title="GCC vs Clang - with MariaDB on ARM" /><published>2021-10-01T00:00:00+00:00</published><updated>2021-10-01T00:00:00+00:00</updated><id>https://mysqlonarm.github.io/gcc-vs-clang-mdbonarm</id><content type="html" xml:base="https://mysqlonarm.github.io/gcc-vs-clang-mdbonarm/">&lt;p&gt;It is a well-known fact that a good compiler can emit an optimal code thereby allowing software to produce better throughput. Clang compiler popularity continues to grow and since I am working mostly on performance issues I am often asked if I have tried MariaDB-on-Arm with clang compiled binaries. Finally, I got some time and decided to try it out.&lt;/p&gt;

&lt;h2 id=&quot;setup&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Setup&lt;/span&gt;&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;ARM instance: 24 vCPU Kunpeng 920 (2.6 Ghz), 48 GB of memory&lt;/li&gt;
  &lt;li&gt;MariaDB-Server: 10.6 trunk (#76972163)&lt;/li&gt;
  &lt;li&gt;Compiler:
    &lt;ul&gt;
      &lt;li&gt;clang-11.1&lt;/li&gt;
      &lt;li&gt;gcc-10.1&lt;/li&gt;
      &lt;li&gt;Was also checking the ARM C/C++ compiler but seems like there is &lt;a href=&quot;https://developer.arm.com/tools-and-software/server-and-hpc/compile/arm-compiler-for-linux/arm-cpp-compiler&quot;&gt;licensed&lt;/a&gt; version only (please let me knows if there is a free-to-use for opensource community license available).&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;OS: Ubuntu 18.04&lt;/li&gt;
  &lt;li&gt;Workload: sysbench point-select, read-only, update-index, update-non-index. CPU bound, pattern=uniform(uni)/zipfian(zip).&lt;/li&gt;
  &lt;li&gt;Scalability: 128, 256 (+ 512) threads&lt;/li&gt;
  &lt;li&gt;optimization flags: default (O2), O3, Ofast&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;benchmarking&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Benchmarking&lt;/span&gt;&lt;/h2&gt;

&lt;h3 id=&quot;read-only-workload&quot;&gt;&lt;span style=&quot;color: #0F9D58&quot;&gt;read-only workload&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;The difference is quite marginal in most cases so a normal graph will not be able to capture it clearly so we will use some kind of heat map.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog27/ro-heatmap.png&quot; height=&quot;300&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;
&lt;em&gt;Notes: green cell represents the highest throughput for the said test case. Like for ps-uni (point-select uniform 128 threads), the highest throughput is seen with clang-o3 and lowest with clang-ofast&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;observations&quot;&gt;&lt;span style=&quot;color: #DB4437&quot;&gt;Observations&lt;/span&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;In most cases, clang O2/O3 has performed better than gcc.&lt;/li&gt;
  &lt;li&gt;Ofast fails to perform for both gcc or clang.&lt;/li&gt;
  &lt;li&gt;default (gcc-O2) tend to perform worse/average for most cases. (For no scenario, it is able to perform best).&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;

&lt;h3 id=&quot;read-write-workload&quot;&gt;&lt;span style=&quot;color: #0F9D58&quot;&gt;read-write workload&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog27/rw-heatmap.png&quot; height=&quot;300&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;observations-1&quot;&gt;&lt;span style=&quot;color: #DB4437&quot;&gt;Observations&lt;/span&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;For read-write workload, the picture is quite different. clang-O2 continues to perform worse. clang-O3 average and clang-Ofast fill the missing lower throughput gaps left by clang-O2. In short, clang continues to perform worse for the read-write workload.&lt;/li&gt;
  &lt;li&gt;On other hand, gcc continue to score well for read-write workload with O3 and Ofast giving the best performance.&lt;/li&gt;
  &lt;li&gt;It is interesting to note that only for update-non-index (zipfian) 256 use-case, clang-Ofast score quite well.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;

&lt;h3 id=&quot;increasing-scalabilitycontention-further&quot;&gt;&lt;span style=&quot;color: #0F9D58&quot;&gt;increasing scalability/contention further&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;Based on overall observation it sounds like with a lot of contention and higher throughput clang continues to perform but with normal contention and lower throughput gcc continues to perform. Let’s try to re-confirm this with some more experiments.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog27/scale-heatmap.png&quot; height=&quot;300&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;observations-2&quot;&gt;&lt;span style=&quot;color: #DB4437&quot;&gt;Observations&lt;/span&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Again, we continue to see mixed results but with increasing scalability it sounds like gcc with -Ofast tends to score.&lt;/li&gt;
  &lt;li&gt;If we try to consider all the 3 heat-maps and try to score them (-1 for red count and +1 for green count) and add it columnwise then clang -O3 and gcc -Ofast seems to be on-par.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s try to run a full benchmark with all thread scalabilities (1-256) with the short-listed modes (gcc-Ofast, clang-O3)
&lt;img src=&quot;/images/blog27/gccofast-clango3.png&quot; height=&quot;400&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;As we could see for read-only workload clang-O3 continue to score marginally but with read-write workload gcc Ofast continue to score with some visible differences.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Conclusion&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;Based on the said study, there is no clear winner. Depending on use-case at times clang score and at times gcc score. Of-course the optimization mode could be O3 with clang and Ofast with gcc. Also, make a note that -Ofast (optimize very aggressively to the point of breaking standard compliance).&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
&lt;em&gt;If you have more questions/queries do let me know. Will try to answer them.&lt;/em&gt;&lt;/p&gt;</content><author><name>Krunal Bauskar</name></author><summary type="html">It is a well-known fact that a good compiler can emit an optimal code thereby allowing software to produce better throughput. Clang compiler popularity continues to grow and since I am working mostly on performance issues I am often asked if I have tried MariaDB-on-Arm with clang compiled binaries. Finally, I got some time and decided to try it out.</summary></entry><entry><title type="html">Adaptive Purge in MariaDB</title><link href="https://mysqlonarm.github.io/adaptive-purge-mariadb/" rel="alternate" type="text/html" title="Adaptive Purge in MariaDB" /><published>2021-09-21T00:00:00+00:00</published><updated>2021-09-21T00:00:00+00:00</updated><id>https://mysqlonarm.github.io/adaptive-purge-mariadb</id><content type="html" xml:base="https://mysqlonarm.github.io/adaptive-purge-mariadb/">&lt;p&gt;If you are using MariaDB for some time now you may have heard about adaptive flushing. “Adaptive” refers to a behavior where-in the algorithm auto-tunes itself based on certain parameters. In the new-generation world, it is called an “AI-based algorithm”. The same concept is now being applied to purge. Purge is a critical and resource-consuming operation so scheduling of purge along with user workload needs to be balanced. This is what exactly adaptive purge would do.&lt;/p&gt;

&lt;h2 id=&quot;a-quick-note-on-purge&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;A quick note on purge&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;MySQL/MariaDB doesn’t immediately delete the data. Data is marked for delete and updated data is placed in the mainline tree. Old data is copied over to UNDO logs. This is needed to support multi-version concurrency control (further details are beyond the scope of this blog so you can always read details &lt;a href=&quot;http://klouddb.io/understanding-innodb-history-length-part-1/&quot;&gt;here&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;These old copies needs to be cleaned/removed/purged once there is no active transactions referring to it. This is done by the purge operations in the background. There are dedicated purge thread(s) meant to do this operation. Configurable using innodb_purge_threads (default=4/max=32).&lt;/p&gt;

&lt;p&gt;If purge is being done aggressively along with the active user workload, the latter will start seeing effects like jitter, reduced performance, etc.. Let’s see how adaptive purge could help solve these issues.&lt;/p&gt;

&lt;h2 id=&quot;benchmarking-purge-with-user-workload&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Benchmarking purge with user workload&lt;/span&gt;&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;MariaDB-Server:
    &lt;ul&gt;
      &lt;li&gt;MariaDB Server 10.6.4 (GA; without adaptive purge)&lt;/li&gt;
      &lt;li&gt;MariaDB Server 10.6.5 (trunk work-in-progress; with adaptive purge folded)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Machine Configuration:
    &lt;ul&gt;
      &lt;li&gt;ARM: 24 vCPU (2 NUMA) ARM Kunpeng 920 CPU @ 2.6 Ghz&lt;/li&gt;
      &lt;li&gt;X86: 24 vCPU (2 NUMA) Intel(R) Xeon(R) Gold 6151 CPU @ 3.00GHz&lt;/li&gt;
      &lt;li&gt;Disk: read/write 350 MB/sec&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Workload :
    &lt;ul&gt;
      &lt;li&gt;sysbench: test: oltp-update-index, scalability: 128, pattern: uniform&lt;/li&gt;
      &lt;li&gt;5 rounds 300 seconds each is executed with a 20-sec gap between each round.&lt;/li&gt;
      &lt;li&gt;In parallel, history length is tracked.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Server configuration: &lt;a href=&quot;https://github.com/mysqlonarm/benchmark-suites/blob/master/mysql-sbench/conf/mdb.cnf/100tx1.5m_cpubound.cnf&quot;&gt;here&lt;/a&gt; (+ innodb_purge_threads=4 (default)).
    &lt;ul&gt;
      &lt;li&gt;Data: 35GB&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;benchmark-without-adaptive-purge&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Benchmark (without adaptive purge):&lt;/span&gt;&lt;/h2&gt;

&lt;h3 id=&quot;with-purge-threads4&quot;&gt;&lt;span style=&quot;color: #0F9D58&quot;&gt;With purge-threads=4&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog26/arm.pt4.png&quot; height=&quot;300&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;
&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog26/x86.pt4.png&quot; height=&quot;300&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;
&lt;br /&gt;&lt;/p&gt;

&lt;h3 id=&quot;with-purge-threads8&quot;&gt;&lt;span style=&quot;color: #0F9D58&quot;&gt;With purge-threads=8&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog26/arm.pt8.png&quot; height=&quot;300&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;
&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog26/x86.pt8.png&quot; height=&quot;300&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;
&lt;br /&gt;&lt;/p&gt;

&lt;h3 id=&quot;observations&quot;&gt;&lt;span style=&quot;color: #DB4437&quot;&gt;Observations&lt;/span&gt;&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;In both cases (arm and x86) user workloads continues to show a lot of jitters with tps occasionally touching 0. history-length is kept in check that suggests an aggressive purge is taking place.&lt;/li&gt;
  &lt;li&gt;Given the workload is user-workload the jitter with tps touching 0 is least expected and that needs to be improved. Investigative analysis showed that aggressive background purge continues to generate a lot of redo that puts pressure on redo log filling it fast and once the threshold is crossed furious flushing starts causing jitter with user workload.&lt;/li&gt;
  &lt;li&gt;Here an important decision needs to be made. If we have limited resources, active workload (like user workload) needs to get priority in using it and passive workload (like purge) could be delayed or throttled to a level so that the mainline performance is not affected.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;

&lt;h2 id=&quot;adaptive-purge-part-of-mariadb-1065&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Adaptive Purge (part of MariaDB-10.6.5)&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;The purge system is multi-threaded with N threads progressing in parallel. If we can limit these threads (based on the redo log fill factor) we can limit the pressure, purge is adding on the redo-log allowing the user/active workload to make effective use of redo-log. On the flip side, there will be an increase in history length and in turn larger undo-logs (that can eventually truncate with mariadb supporting undo log truncate).&lt;/p&gt;

&lt;p&gt;Let’s try to devise an algorithm using this rule and see if that helps improve active workload.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Phase-1:&lt;/strong&gt; &lt;br /&gt;
    &lt;ul&gt;
      &lt;li&gt;adaptive purge continues to monitor the redo-log size. If the redo-log-size &amp;lt; lower watermark nothing is done.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Phase-2:&lt;/strong&gt; &lt;br /&gt;
    &lt;ul&gt;
      &lt;li&gt;Once the redo log size crosses the lower watermark, the adaptive purge will kick in.&lt;/li&gt;
      &lt;li&gt;As part of the logic, the range between lower and higher watermark is divided into N buckets (N = innodb_purge_threads). As and when redo log continues to cross the bucket, purge threads continue to reduce/increase by 1.&lt;/li&gt;
      &lt;li&gt;Say, innodb_purge_threads=4. This will create 4 buckets. When the redo log is within the range of 1st bucket then the purge system will operate with 4 purge threads. When the redo log grows and enters the range of the 2nd bucket, the purge system will operate with 3 purge threads and so on. With increasing redo-log size, purge is throttled and with reducing size aggressive purge is re-enabled.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Phase-3:&lt;/strong&gt; &lt;br /&gt;
    &lt;ul&gt;
      &lt;li&gt;Once the redo-log crosses the higher watermark (by then purge system is already operating with 1 purge thread) an additional delay of 10 ms is introduced to avoid scheduling purge back to back.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog26/bucket-distribution.png&quot; height=&quot;300&quot; class=&quot;centerimg&quot; /&gt;
&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Note: lower and higher watermarks are pre-configured to 20/80 (currently not user-configurable. not sure if really needed). The number of buckets/ranges depends upon the number of purge threads. The ranges are created using arithmetic progression if possible otherwise, an average distribution is used.&lt;/p&gt;
&lt;hr /&gt;

&lt;h2 id=&quot;benchmark-with-adaptive-purge&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Benchmark (with adaptive purge):&lt;/span&gt;&lt;/h2&gt;

&lt;h3 id=&quot;with-purge-threads4-1&quot;&gt;&lt;span style=&quot;color: #0F9D58&quot;&gt;With purge-threads=4&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog26/arm-pt4.apurge.png&quot; height=&quot;300&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;
&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog26/x86.pt4.apurge.png&quot; height=&quot;300&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;
&lt;br /&gt;&lt;/p&gt;

&lt;h3 id=&quot;with-purge-threads8-1&quot;&gt;&lt;span style=&quot;color: #0F9D58&quot;&gt;With purge-threads=8&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog26/arm.pt8.apurge.png&quot; height=&quot;300&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;
&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog26/x86.pt8.apurge.png&quot; height=&quot;300&quot; style=&quot;border:1px solid black&quot; class=&quot;centerimg&quot; /&gt;
&lt;br /&gt;&lt;/p&gt;

&lt;h3 id=&quot;observations-1&quot;&gt;&lt;span style=&quot;color: #DB4437&quot;&gt;Observations&lt;/span&gt;&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;As expected, with reduced pressure on redo log, jitter in user/active workload considerably reduced.
Importantly, overall throughput improved from an average of 10K tps to 55K tps (ARM). This improvement is due to reduced pressure on log_sys mutex (related to redo-log).&lt;/li&gt;
  &lt;li&gt;As a flip-side, history length increases but when there is an idle time it continues to fall steeply allowing it to reach considerable range without affecting the mainline performance.&lt;/li&gt;
  &lt;li&gt;Also, with changing redo-log ranges, the purge system continues to become aggressive (purge threads increasing) or conservative (purge thread decreasing).&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;caveats&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Caveats&lt;/span&gt;&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;If your workload and/or configuration are such that even &lt;em&gt;without purge&lt;/em&gt; the active workload continues to put pressure on redo-log, in such cases furious flushing will take place but this algorithm will not solve the said issue since it is not originating from the purge.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;With the faster disk (say NVMe SSD), we observed that flushing is done quite fast thereby keeping a check on redo-log size and so adaptive purge may not kick in (depending on size of redo-log).&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Conclusion&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;Adaptive purge not only helps reduce jitter in performance but also helps improve overall throughput there-by improving the responsiveness of the server (including latency).&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
&lt;em&gt;If you have more questions/queries do let me know. Will try to answer them.&lt;/em&gt;&lt;/p&gt;</content><author><name>Krunal Bauskar</name></author><summary type="html">If you are using MariaDB for some time now you may have heard about adaptive flushing. “Adaptive” refers to a behavior where-in the algorithm auto-tunes itself based on certain parameters. In the new-generation world, it is called an “AI-based algorithm”. The same concept is now being applied to purge. Purge is a critical and resource-consuming operation so scheduling of purge along with user workload needs to be balanced. This is what exactly adaptive purge would do.</summary></entry><entry><title type="html">Tune your MariaDB IO workload using this simple step</title><link href="https://mysqlonarm.github.io/tune-your-io-workload/" rel="alternate" type="text/html" title="Tune your MariaDB IO workload using this simple step" /><published>2021-07-14T00:00:00+00:00</published><updated>2021-07-14T00:00:00+00:00</updated><id>https://mysqlonarm.github.io/tune-your-io-workload</id><content type="html" xml:base="https://mysqlonarm.github.io/tune-your-io-workload/">&lt;p&gt;Tuning IO workloads is often challenging given it involves optimal usage of available IO bandwidth. MariaDB has multiple options to control this but often users tend to ignore the simpler options and tend to play around with complex or wrong options. In this article, we will take a step-by-step approach and see if we can tune an IO workload.&lt;/p&gt;

&lt;h2 id=&quot;a-quick-note-about-flushing&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;A quick note about flushing&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;Before we get into details let’s first refresh our understanding of the flushing in MariaDB as it exists today (10.6.3 GA).&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;During boot time all the pages are added to the free list.&lt;/li&gt;
  &lt;li&gt;Workload then starts demanding the page. If the page is not present then the page is allocated    from the free list and moved into the LRU list followed by IO from disk to the allocated page.&lt;/li&gt;
  &lt;li&gt;If workload happens to change the page then said page is added to flush list that is sorted based on changes done (lsn).&lt;/li&gt;
  &lt;li&gt;The page-cleaner thread continues to flush the page to disk at regular intervals as per the configured parameters. This ensures that checkpoint age is kept under the threshold to avoid furious flushing. Flushed pages are marked clean (not freed). They continue to hold the data and workload can pin them for further use (without need for IO). This also means flushing doesn’t increase the free-page list length.&lt;/li&gt;
  &lt;li&gt;Whenever workload needs a page it tries to get it from a free list. If the free list is exhausted LRU algorithm kicks in. A page is selected for replacement. If the said page is clean then it could be used immediately (without the need for IO) else the page first needs to be flushed to the disk. This is termed as LRU flush and different from the normal flush (note: normal flush doesn’t generate free page but LRU flush will generate free page).&lt;/li&gt;
  &lt;li&gt;Now the LRU flush algorithm has an option to flush a page whenever needed or flush in batches (like N pages) at times. Also, it is quite possible that out of these N pages, some are already clean so there is no IO overhead to replace such pages (only evict and reload the new page).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With that basic understanding let’s now see if we can tune the IO workload.&lt;/p&gt;

&lt;h2 id=&quot;setup&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Setup&lt;/span&gt;&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Machine Configuration:
    &lt;ul&gt;
      &lt;li&gt;ARM: 64 vCPU (2 NUMA) ARM Kunpeng 920 CPU @ 2.6 Ghz&lt;/li&gt;
      &lt;li&gt;X86: 64 vCPU (2 NUMA) Intel(R) Xeon(R) Gold 6151 CPU @ 3.00GHz&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Workload (using sysbench):
    &lt;ul&gt;
      &lt;li&gt;IO-bound (only 50% of the data in buffer pool)&lt;/li&gt;
      &lt;li&gt;Pattern: uniform&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Other configuration details &lt;a href=&quot;https://github.com/mysqlonarm/benchmark-suites/blob/master/mysql-sbench/conf/mdb.cnf/100tx3m_106_cpubound.cnf&quot;&gt;here&lt;/a&gt; (+ skip-log-bin).
    &lt;ul&gt;
      &lt;li&gt;Buffer Pool: 35GB&lt;/li&gt;
      &lt;li&gt;Data: 70GB&lt;/li&gt;
      &lt;li&gt;Redo-log: 20 GB&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Storage: NvME SSD (for both machines)
    &lt;ul&gt;
      &lt;li&gt;sequential read/write IOPS: 190+K/125+K&lt;/li&gt;
      &lt;li&gt;random read/write IOPS: 180+K/65+K&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;MariaDB Version: 10.6.3 (tagged GA)&lt;/li&gt;
  &lt;li&gt;Scalability: 512 threads&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;iteration-1&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Iteration-1&lt;/span&gt;&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Given IO involved, the most obvious setting that the majority of the user tends to tune is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;innodb_io_capacity&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;innodb_io_capacity_max&lt;/code&gt;. A general recommendation suggests setting these values to something lower like 1K/2K for a high-speed disk to avoid writing a copy of the page multiple times there-by making old copies stale in no time and in turn reducing the SSD endurance.&lt;/li&gt;
  &lt;li&gt;The flip side of these really low values is clearly visible on performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog25/2k.4k.32.png&quot; height=&quot;300&quot; class=&quot;centerimg&quot; /&gt;
&lt;br /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Once the adaptive flushing kicks it is unable to keepup with the rate at which pages are being modified causing performance to drop close to 0 (actual values are around 100-500 range) as dirty pages hit the threshold.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;iteration-2&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Iteration-2&lt;/span&gt;&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Learning from experience we now decided to increase the innodb_io_capacity/innodb_io_capacity_max to 12K/24K. Why 12K? We have to start somewhere and some of the common cloud volumes offer speed in the range of 250-350 MB/sec that is around 16K-22K.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog25/12k.24k.32.png&quot; height=&quot;300&quot; class=&quot;centerimg&quot; /&gt;
&lt;br /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;There is an improvement but still, the performance is not consistent. In fact, for a production user, a system like this is more dangerous due to a wide range of performance fluctuations. The drop in the above case is coming from dirty page hitting threshold (innodb_max_dirty_pages_pct=90).&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;iteration-3&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Iteration-3&lt;/span&gt;&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Given we have a really fast disk let’s allocate the maximum possible capacity. Please make a note that innodb_io_capacity represents IOPS which includes read and write operations and some of them could be sequential too. Given this fact, we set innodb_io_capacity/innodb_io_capacity_max to 120K/120K allowing adaptive flushing to figure out best possible usage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog25/120k.120k.32.png&quot; height=&quot;600&quot; class=&quot;centerimg&quot; /&gt;
&lt;br /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;So despite giving the maximum possible IO bandwidth, jitter in performance continues. In fact, the server did consume considerable IO bandwidth with the average hitting around 2-2.5 GB/sec (for ARM), 1-1.5 GB/sec (for x86).&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;understanding-what-is-going-wrong&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Understanding what is going wrong&lt;/span&gt;&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;By increasing the innodb_io_capacity, we were providing more bandwidth to the flushing algorithm to ensure that the redo log threshold is not hit but we forgot the fact that with IO workload, LRU flushing has a big role to play.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Also, the innodb_io_capacity limit is meant for normal flushing but LRU flushing doesn’t respect the said limit. Instead, there are different parameters to control how many pages LRU flushing will flush. Tunning these limits will ensure that there are enough free pages available beforehand to load a new page.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;One may wonder LRU flushing is even enabled in the above scenarios but we still see the jitter in performance. Let’s understand an important parameter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;innodb_lru_flush_size&lt;/code&gt;. When the LRU flushing algorithm needs to flush it will flush innodb_lru_flush_size pages in a single invocation. The default value of this parameter is 32 pages. Even though the running transaction needs only 1 page, more are freed so that other threads don’t need to wait. Unfortunately, just 31 extra pages for 512 active threads is a big mismatch, and this kind of turns into each thread invoking a cycle of LRU flush. LRU flush cycle involves flushing a page to disk in-turn involvement of doublewrite buffer too and more mutex contention. This increases the latency of the LRU flush with jitter in performance.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;playing-around-with-innodb_lru_flush_size&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Playing around with innodb_lru_flush_size&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;Let’s experiment with different innodb_lru_flush_size starting with 512 (1 multiple), 1024 (2 multiples), 2048 (4 multiples), 4096 (8 multiples), etc… This way LRU flush will ensure that there is at least 1 free page per thread and also help reduce the LRU latency with fewer double-write buffer invocations.&lt;/p&gt;

&lt;p&gt;Also, let’s restore the value of innodb_io_capacity and innodb_io_capacity_max back to 12K/24K as even with these values REDO log was kept in check (without causing a flush storm of furious flushing).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog25/diff.flush.sizes.png&quot; height=&quot;600&quot; class=&quot;centerimg&quot; /&gt;
 &lt;br /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;It is evident from the graph above that the optimal size for innodb_lru_flush_size is 2048 for ARM (increasing it beyond that doesn’t help) and 1024 for x86 (2048 has slightly lesser tps).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Also, it is interesting to note that despite giving the same resources for ARM and x86, ARM IO-bound workload performs way better than x86 with a significant margin (71K (ARM) vs 36K (x86)).&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;does-this-increases-the-overall-io&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Does this increases the overall IO&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog25/12k.24.2048.1024.png&quot; height=&quot;300&quot; class=&quot;centerimg&quot; /&gt;
 &lt;br /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Overall io continues to remain at the around same level (capped at around 2.5 GB/sec for ARM and 1.5 GB/sec for x86).&lt;/li&gt;
  &lt;li&gt;So with the said tuning, we ensured the right things are made available at the right time that helps improve the overall performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;flipside&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Flipside&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog25/latency.png&quot; height=&quot;300&quot; class=&quot;centerimg&quot; /&gt;
 &lt;br /&gt;&lt;/p&gt;

&lt;p&gt;With an increase in innodb_lru_flush_size from the default of 32 to 2048, the latency of the thread that invokes LRU flush would be slightly higher during the transaction that causes LRU flush to invoke. Assuming all threads has equal chances of invoking LRU at regular interval the latency should amortize as we could see from the graph above.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;what-about-innodb_lru_scan_depth&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;What about innodb_lru_scan_depth&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;Definition of innodb_lru_scan_depth as per the documentation.&lt;br /&gt;
&lt;em&gt;Specifies how far down the buffer pool least-recently-used (LRU) list the cleaning thread should look for dirty pages to flush.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But the said condition should be read with innodb_lru_flush_size as it exists in the code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;n-&amp;gt;flushed + n-&amp;gt;evicted&lt;/strong&gt; &lt;em&gt;[total-pages freed by this batch]&lt;/em&gt; &amp;lt; &lt;strong&gt;max&lt;/strong&gt; &lt;em&gt; [batch limit (innodb_lru_flush_size)] &lt;/em&gt; &amp;amp;&amp;amp; &lt;br /&gt;
&lt;strong&gt;UT_LIST_GET_LEN(buf_pool.free)&lt;/strong&gt; &lt;em&gt;[free pages but the value is dynamically changing with consumption active in background]&lt;/em&gt; &amp;lt; &lt;strong&gt;free_limit&lt;/strong&gt; &lt;em&gt;[innodb_lru_scan_depth]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So this means:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;If innodb_lru_flush_size pages are freed then the batch will end OR&lt;/li&gt;
  &lt;li&gt;If free-pages &amp;gt; innodb_lru_scan_depth batch will end.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But tuning innodb_lru_scan_depth is difficult given it is linked to dynamically changing free-list length. Say user set innodb_lru_scan_depth = 1024. So once 1024 pages are freed batch should end but it is quite possible that after freeing 900 pages 200 get pined and the free count drops down to 700 and the batch continues to work further (increasing latency of the invoking thread).&lt;/p&gt;

&lt;p&gt;So it is advisable to keep &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;innodb_lru_scan_depth &amp;gt; innodb_lru_flush_size&lt;/code&gt; so that each batch will free up at least innodb_lru_flush_size and will end. Dynamically changing buf_pool.free limit makes it difficult to set a good value for innodb_lru_scan_depth. Also, I see the documentation or interpretation as a misnomer now. Maybe originally, variable semantics was inline but as per the existing condition, it is better to tune innodb_lru_flush_size.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;&lt;span style=&quot;color:#4885ed&quot;&gt;Conclusion&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;While tuning of an IO workload it is important to keep a watch on all facets of IO viz. normal flushing (meant for redo log), LRU flushing to ensure enough free pages are available, etc.. From the experiment, it is quite evident that setting innodb_lru_flush_size to 2/4 multiples (of scalability) helps in improving performance with less jitter (without increasing IO).&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
&lt;em&gt;If you have more questions/queries do let me know. Will try to answer them.&lt;/em&gt;&lt;/p&gt;</content><author><name>Krunal Bauskar</name></author><summary type="html">Tuning IO workloads is often challenging given it involves optimal usage of available IO bandwidth. MariaDB has multiple options to control this but often users tend to ignore the simpler options and tend to play around with complex or wrong options. In this article, we will take a step-by-step approach and see if we can tune an IO workload.</summary></entry></feed>