{"id":3575,"date":"2026-04-04T17:01:09","date_gmt":"2026-04-04T17:01:09","guid":{"rendered":"https:\/\/kedar.nitty-witty.com\/blog\/?p=3575"},"modified":"2026-04-04T17:01:09","modified_gmt":"2026-04-04T17:01:09","slug":"how-to-fix-write-latency-in-mysql-8-4-upgrade","status":"publish","type":"post","link":"https:\/\/kedar.nitty-witty.com\/blog\/how-to-fix-write-latency-in-mysql-8-4-upgrade","title":{"rendered":"How to fix write latency in MySQL 8.4 Upgrade"},"content":{"rendered":"\n<p>During any <strong>MySQL major version upgrade<\/strong>, especially when moving from 8.0 to 8.4, it\u2019s not just about compatibility checks. Subtle default changes can directly impact <strong>MySQL performance tuning<\/strong>, and if you\u2019re not watching closely, they can show up as unexpected latency or throughput issues.<\/p>\n\n\n\n<p>This is a tale from a MySQL 8.4 upgrade causing write latency and the default change of <code>innodb_change_buffering<\/code>. <\/p>\n\n\n\n<p>InnoDB\u2019s change buffer (<code>innodb_change_buffering<\/code>) used to be an important optimisation for write-heavy workloads. However, in <strong>MySQL 8.4, it is disabled by default<\/strong>. Remember, it is <strong><em>disabled<\/em>, not deprecated<\/strong> (at least as of now).<\/p>\n\n\n\n<p>This blog post walks through a small but practical use case and what it means for real-world systems.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">What the change buffer was for<\/h1>\n\n\n\n<p>InnoDB\u2019s change buffer (formerly insert buffer) was designed for secondary indexes when the required leaf page is not present in the buffer pool.<\/p>\n\n\n\n<p>Instead of performing a random read to fetch that page just to apply an insert, delete-mark, or purge operation, InnoDB records the change in the change buffer and merges it later.<\/p>\n\n\n\n<p>On spinning disks, where random seeks were expensive and sequential I\/O was cheaper, this provided a real performance benefit and played a role in <strong>MySQL write optimization<\/strong>. I have it covered in my previous blog \/ podcast below.<\/p>\n\n\n\n<p>Reference: <a href=\"https:\/\/kedar.nitty-witty.com\/blog\/mysql-change-buffer-what-when-and-faq\" target=\"_blank\" rel=\"noopener\" title=\"\">https:\/\/kedar.nitty-witty.com\/blog\/mysql-change-buffer-what-when-and-faq<\/a><br>Podcast: <a href=\"https:\/\/www.youtube.com\/watch?v=vt78zI_fwfU\" target=\"_blank\" rel=\"noopener\" title=\"\">https:\/\/www.youtube.com\/watch?v=vt78zI_fwfU<\/a><\/p>\n\n\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Why is <code>innodb_change_buffering<\/code> off by default?<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">1. Database hardware has changed<\/h2>\n\n\n\n<p>On most modern systems, storage is SSD\/NVMe or RAID-backed, where random I\/O is significantly faster compared to traditional HDDs.<\/p>\n\n\n\n<p>The original benefit of change buffering was to avoid slow random disk reads for secondary index updates. That bottleneck largely no longer exists.<\/p>\n\n\n\n<p>So effectively:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>If random I\/O is no longer expensive, avoiding it brings less value for most <strong>MySQL workloads<\/strong>.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">2. Additional overhead<\/h2>\n\n\n\n<p>Maintaining the change buffer is not free.<br>&#8211; Extra writes<br>&#8211; Additional metadata tracking<br>&#8211; Background merge operations<\/p>\n\n\n\n<p>All of this adds overhead to write operations and can impact <strong>MySQL query performance<\/strong>, especially under sustained write load.<\/p>\n\n\n\n<p>So question arise:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Does the optimisation still justify the cost?<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>In my understanding, this is not a case of \u201c<strong>Change buffer is useless<\/strong>\u201d but rather \u201c<strong>Change buffer is not beneficial enough<\/strong>\u201d<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>This aligns with MySQL 8.4 LTS philosophy of more practical defaults for <strong>MySQL performance optimization<\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">innodb_change_buffering: all \u2192 none<\/pre>\n\n\n\n<p>Effectively disabling it out of the box.But why disable it and not remove it? I can\u2019t speak on behalf of MySQL, but I\u2019m actually glad it\u2019s still there.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">8.4 Upgrade and Write Latency<\/h2>\n\n\n\n<p>A &#8220;friend&#8221; of mine upgraded from MySQL 8.0 to 8.4 and quickly hit the panic button, as their <strong>write latency<\/strong> almost doubled. From ~2ms to ~4ms. Now, I may joke about it saying &#8220;dude, that&#8217;s just 2ms&#8221; but for latency-sensitive systems, that\u2019s significant for <strong>database performance<\/strong>.<\/p>\n\n\n\n<p>I know, you being a smart reader would have doubted innodb_change_buffering behaviour and so did I.<\/p>\n\n\n\n<p>We reverted:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">innodb_change_buffering = all<\/pre>\n\n\n\n<p>\u2026and the latency dropped back to expected levels. <\/p>\n\n\n\n<p><em><strong>You probably already know this, and maybe I did bait you a bit with the \u201chow to fix\u201d title. But in reality, I just wanted to put this out there for someone who might find it useful.<\/strong><\/em><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Will the change buffering stay?<\/h1>\n\n\n\n<p>We don\u2019t really know.<\/p>\n\n\n\n<p>Maybe it will stay, maybe it won\u2019t. Defaults have already shifted, and future versions may evolve further as MySQL continues to refine&#8230; but this brings us to the important takeaway:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Always test every aspect of your workload during a <strong>MySQL upgrade<\/strong>.<\/p>\n<\/blockquote>\n\n\n\n<p>Especially when even a few milliseconds of latency change can have real impact on <strong>MySQL performance tuning and query optimization<\/strong>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Final note<\/h1>\n\n\n\n<p>I recently covered a <strong>MySQL Major Version Upgrade Checklist<\/strong> in a podcast, focused on practical upgrade strategies, validation steps, and avoiding surprises like this one.<\/p>\n\n\n\n<p>If you\u2019re planning an upgrade, it might be a useful reference.<\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe title=\"MySQL Major Version Upgrade Checklist (Zero Downtime Strategy)\" width=\"500\" height=\"281\" src=\"https:\/\/www.youtube.com\/embed\/LKilfCJI_y4?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n","protected":false},"excerpt":{"rendered":"During any MySQL major version upgrade, especially when moving from 8.0 to 8.4, it\u2019s not just about compatibility checks. Subtle default changes can directly impact MySQL performance tuning, and if&hellip;\n","protected":false},"author":1,"featured_media":3576,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8,6],"tags":[1196,1199,1200,1006,1186,1193,1091,1197,1192],"class_list":{"0":"post-3575","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","7":"category-mysql","8":"category-technical","9":"tag-innodb_change_buffering","10":"tag-mysql-8-0-to-8-4","11":"tag-mysql-8-4-upgrade","12":"tag-mysql-best-practices","13":"tag-mysql-configuration-tuning","14":"tag-mysql-latency","15":"tag-mysql-upgrade","16":"tag-mysql-upgrade-issues","17":"tag-mysql-write-performance"},"aioseo_notices":[],"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/kedar.nitty-witty.com\/blog\/wp-json\/wp\/v2\/posts\/3575","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kedar.nitty-witty.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kedar.nitty-witty.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kedar.nitty-witty.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/kedar.nitty-witty.com\/blog\/wp-json\/wp\/v2\/comments?post=3575"}],"version-history":[{"count":2,"href":"https:\/\/kedar.nitty-witty.com\/blog\/wp-json\/wp\/v2\/posts\/3575\/revisions"}],"predecessor-version":[{"id":3578,"href":"https:\/\/kedar.nitty-witty.com\/blog\/wp-json\/wp\/v2\/posts\/3575\/revisions\/3578"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/kedar.nitty-witty.com\/blog\/wp-json\/wp\/v2\/media\/3576"}],"wp:attachment":[{"href":"https:\/\/kedar.nitty-witty.com\/blog\/wp-json\/wp\/v2\/media?parent=3575"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/kedar.nitty-witty.com\/blog\/wp-json\/wp\/v2\/categories?post=3575"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kedar.nitty-witty.com\/blog\/wp-json\/wp\/v2\/tags?post=3575"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}