Files
nexus/sreweekly/articles/480/07-identifying-cacheable-queries-using-tools-like-pt-query-digest-or-the.html
2026-09-12 17:23:01 +08:00

308 lines
103 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><link rel="preload" as="image" href="https://cms.readyset.io/files/articles/682f58d7e1d9680001d06cce/25836706172d9d9642a5df9d.png" fetchPriority="high"/><link rel="preload" as="image" href="https://lh7-rt.googleusercontent.com/docsz/AD_4nXc-5BsSkZQ9c8-uGPn1um3q2cQHD5X7xrjV9O7QHKESOWcyFIiA-CXducu-6UyV2j-_vbR0IpE0EZifyFw9h5yzMFhw8OMBsl_SNzwxvW-BfSGTu5gXaR4S4xusEDZ_2wi4SD72BQ?key=QXIkV4ZDxUo4YCbsb2npdA"/><link rel="stylesheet" href="/assets/styles-Dkt3QWRo.css" nonce="ktmOMgITNrkoYILTaF4Tiw==" data-precedence="default"/><meta property="csp-nonce" content="ktmOMgITNrkoYILTaF4Tiw=="/><title>Identifying Cacheable Queries: Using tools like pt-query-digest or the MySQL sys schema to pinpoint queries that would benefit from caching | Readyset</title><meta name="theme-color" content="#ffffff" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta name="msapplication-TileColor" content="#161615" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta name="apple-mobile-web-app-capable" content="yes" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta name="mobile-web-app-capable" content="yes" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta name="apple-mobile-web-app-status-bar-style" content="black" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta name="description" content="Introduction It’s common for DBAs to optimize queries to address bottlenecks and reduce resource contention. However, in production workloads, the most resource-intensive queries aren’t always the longest-running ones..." nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta name="application-name" content="Readyset | Drop-in SQL Caching for PostgreSQL and MySQL" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta name="apple-mobile-web-app-title" content="Readyset | Drop-in SQL Caching for PostgreSQL and MySQL" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta name="keywords" content="SQL caching, real-time cache, database cache, PostgreSQL cache, MySQL cache" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta property="og:type" content="article" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta property="og:site_name" content="Readyset | Drop-in SQL Caching for PostgreSQL and MySQL" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta property="og:title" content="Identifying Cacheable Queries: Using tools like pt-query-digest or the MySQL sys schema to pinpoint queries that would benefit from caching | Readyset" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta property="og:description" content="Introduction It’s common for DBAs to optimize queries to address bottlenecks and reduce resource contention. However, in production workloads, the most resource-intensive queries aren’t always the longest-running ones..." nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta property="og:image" content="https://cms.readyset.io/files/articles/682f58d7e1d9680001d06cce/25836706172d9d9642a5df9d.png" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta property="og:image:secure_url" content="https://cms.readyset.io/files/articles/682f58d7e1d9680001d06cce/25836706172d9d9642a5df9d.png" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta property="og:image:type" content="image/png" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta property="og:image:alt" content="Identifying Cacheable Queries: Using tools like pt-query-digest or the MySQL sys schema to pinpoint queries that would benefit from caching | Readyset" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta name="twitter:card" content="summary_large_image" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta name="twitter:site" content="@readysetio" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta name="twitter:creator" content="@readysetio" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta name="twitter:title" content="Identifying Cacheable Queries: Using tools like pt-query-digest or the MySQL sys schema to pinpoint queries that would benefit from caching | Readyset" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta name="twitter:description" content="Introduction It’s common for DBAs to optimize queries to address bottlenecks and reduce resource contention. However, in production workloads, the most resource-intensive queries aren’t always the longest-running ones..." nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta name="twitter:image" content="https://cms.readyset.io/files/articles/682f58d7e1d9680001d06cce/25836706172d9d9642a5df9d.png" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta property="og:url" content="https://readyset.io/blog/identifying-cacheable-queries-using-tools-like-pt-query-digest-or-the-mysql-sys-schema-to-pinpoint-queries-that-would-benefit-from-caching" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta name="twitter:url" content="https://readyset.io/blog/identifying-cacheable-queries-using-tools-like-pt-query-digest-or-the-mysql-sys-schema-to-pinpoint-queries-that-would-benefit-from-caching" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta property="article:author" content="Vinicius Grippa" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta property="article:published_time" content="2025-05-29" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta property="article:section" content="Query Optimization" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><meta property="article:tag" content="MySQL" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><link rel="modulepreload" href="/assets/index-CgozBq9o.js" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><link rel="modulepreload" href="/assets/_slug-feOgb-yl.js" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><link rel="modulepreload" href="/assets/article-card-BslrmY68.js" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><link rel="modulepreload" href="/assets/newsletter-popup-C0xZnHQP.js" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><link rel="modulepreload" href="/assets/rich-markdown-DYApPWH1.js" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><link rel="modulepreload" href="/assets/index-rMShz_NE.js" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><link rel="modulepreload" href="/assets/use-disclosure-DaTsKJMJ.js" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><link rel="icon" href="/favicon.ico" sizes="any" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><link rel="icon" type="image/png" sizes="16x16" href="/manifest/favicon-16x16.png" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><link rel="icon" type="image/png" sizes="32x32" href="/manifest/favicon-32x32.png" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><link rel="apple-touch-icon" sizes="180x180" href="/manifest/apple-touch-icon.png" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><link rel="mask-icon" href="/manifest/safari-pinned-tab.svg" color="#161615" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><link rel="manifest" href="/manifest.webmanifest" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><link rel="prefetch" href="/icons/sprite-stroke.svg" as="image" type="image/svg+xml" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><link rel="prefetch" href="/icons/sprite-bulk.svg" as="image" type="image/svg+xml" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><link rel="canonical" href="https://readyset.io/blog/identifying-cacheable-queries-using-tools-like-pt-query-digest-or-the-mysql-sys-schema-to-pinpoint-queries-that-would-benefit-from-caching" nonce="ktmOMgITNrkoYILTaF4Tiw=="/><script type="application/ld+json" nonce="ktmOMgITNrkoYILTaF4Tiw==">{"@context":"https://schema.org","@type":"Organization","@id":"https://readyset.io/#organization","name":"Readyset","url":"https://readyset.io","logo":"https://readyset.io/manifest/apple-touch-icon.png","description":"Increase the scale of your PostgreSQL and MySQL deployment by up to 100x with Readyset - all without modifying your application code or database. Start using Readyset today for free!","sameAs":["https://x.com/readysetio"]}</script><script type="application/ld+json" nonce="ktmOMgITNrkoYILTaF4Tiw==">{"@context":"https://schema.org","@type":"WebSite","@id":"https://readyset.io/#website","url":"https://readyset.io","name":"Readyset","publisher":{"@id":"https://readyset.io/#organization"}}</script><script type="application/ld+json" nonce="ktmOMgITNrkoYILTaF4Tiw==">{"@context":"https://schema.org","@type":"Article","headline":"Identifying Cacheable Queries: Using tools like pt-query-digest or the MySQL sys schema to pinpoint queries that would benefit from caching","description":"Introduction It’s common for DBAs to optimize queries to address bottlenecks and reduce resource contention. However, in production workloads, the most resource-intensive queries aren’t always the longest-running ones...","url":"https://readyset.io/blog/identifying-cacheable-queries-using-tools-like-pt-query-digest-or-the-mysql-sys-schema-to-pinpoint-queries-that-would-benefit-from-caching","image":"https://cms.readyset.io/files/articles/682f58d7e1d9680001d06cce/25836706172d9d9642a5df9d.png","datePublished":"2025-05-29T00:00:00.000Z","dateModified":"2026-05-04T14:04:59.901Z","author":{"@type":"Person","name":"Vinicius Grippa"},"publisher":{"@id":"https://readyset.io/#organization"}}</script></head><body class="min-h-screen bg-surface-layout-2"><a href="#main-content" class="sr-only focus:not-sr-only focus:absolute focus:left-4 focus:top-4 focus:z-[100] focus:rounded-lg focus:bg-content-layout-1 focus:px-4 focus:py-2 focus:text-sm focus:font-semibold focus:text-surface-layout-1">Skip to content</a><header class="sticky top-0 z-50 bg-surface-layout-2/80 backdrop-blur-lg border-b border-border-layout-soft"><div class="relative max-w-7xl mx-auto px-6 py-4 flex items-center justify-between"><div class="flex items-center gap-8"><a href="/" class="flex items-center"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 145 33" class="fill-current text-content-layout-1 h-6"><path d="M16 31.328c0 .736-.597 1.333-1.333 1.333H4a4 4 0 0 1-4-4V17.994c0-.736.597-1.333 1.333-1.333H16v14.667Z"></path><path d="M21.333 11.328v5.333h-4c-.736 0-1.333.597-1.333 1.333v4h-5.333v-5.333h4c.736 0 1.333-.597 1.333-1.333v-4h5.333Z"></path><path d="M32 15.328c0 .736-.597 1.333-1.333 1.333H16V1.994c0-.736.597-1.333 1.333-1.333H28a4 4 0 0 1 4 4v10.667Z"></path><path fill-rule="evenodd" d="M91.272 5.367h2.864V23.25h-2.864v-1.99c-1.145 1.45-2.665 2.175-4.557 2.175-1.204 0-2.296-.294-3.275-.882a6.299 6.299 0 0 1-2.304-2.45c-.548-1.036-.822-2.193-.822-3.47 0-1.276.274-2.433.822-3.469a6.209 6.209 0 0 1 2.304-2.437c.98-.597 2.071-.896 3.275-.896 1.9 0 3.42.722 4.557 2.164V5.367Zm-3.997 15.308c1.146 0 2.096-.386 2.852-1.157.763-.779 1.145-1.74 1.145-2.885 0-1.152-.382-2.114-1.145-2.884-.756-.78-1.706-1.17-2.852-1.17-1.154 0-2.117.39-2.889 1.17-.764.77-1.145 1.732-1.145 2.884 0 1.145.381 2.106 1.145 2.885.772.771 1.735 1.157 2.89 1.157Zm-24.258-2.898c.05-.364.075-.737.075-1.119 0-1.956-.652-3.581-1.955-4.874-1.295-1.302-2.918-1.953-4.87-1.953-1.278 0-2.444.295-3.498.883a6.442 6.442 0 0 0-2.466 2.438c-.597 1.028-.896 2.172-.896 3.432 0 1.948.68 3.577 2.042 4.887 1.37 1.31 3.084 1.965 5.143 1.965 1.203 0 2.32-.24 3.35-.722 1.037-.48 1.892-1.144 2.564-1.99l-1.93-1.653c-.39.514-.938.94-1.643 1.28-.698.34-1.453.51-2.267.51-1.054 0-1.959-.277-2.714-.833-.747-.563-1.237-1.314-1.47-2.25h10.535Zm-9.19-4.538c.69-.556 1.494-.834 2.416-.834.93 0 1.74.278 2.428.834.697.555 1.154 1.289 1.37 2.2h-7.584c.233-.911.69-1.645 1.37-2.2Zm21.469-3.166h2.863V23.25h-2.864v-1.99c-1.145 1.45-2.664 2.175-4.557 2.175-1.204 0-2.296-.294-3.275-.882a6.299 6.299 0 0 1-2.304-2.45c-.548-1.036-.822-2.193-.822-3.47 0-1.276.274-2.433.822-3.469a6.209 6.209 0 0 1 2.304-2.437c.98-.597 2.071-.896 3.275-.896 1.901 0 3.42.722 4.558 2.164v-1.922Zm-3.998 10.602c1.146 0 2.096-.386 2.852-1.157.764-.779 1.145-1.74 1.145-2.885 0-1.152-.381-2.114-1.145-2.884-.756-.78-1.706-1.17-2.852-1.17-1.154 0-2.117.39-2.889 1.17-.763.77-1.145 1.732-1.145 2.884 0 1.145.382 2.106 1.145 2.885.772.771 1.735 1.157 2.89 1.157Zm35.186-10.602h3.188L102.941 25.6l-1.027 2.356h-3.319c.461-.939 1.313-2.68 1.718-3.537l.697-1.443-5.51-12.902h3.275l3.842 9.532 3.867-9.532Zm8.586 13.363c-2.092 0-3.894-.664-5.405-1.99l1.482-2.015c1.071 1.02 2.362 1.53 3.873 1.53.755 0 1.374-.124 1.855-.373.482-.257.722-.61.722-1.057a.93.93 0 0 0-.161-.535c-.1-.157-.225-.29-.374-.398-.141-.108-.353-.211-.635-.31-.282-.1-.531-.179-.747-.237a25.515 25.515 0 0 0-.884-.224l-.934-.224a16.527 16.527 0 0 1-.909-.286 6.683 6.683 0 0 1-.909-.373 5.835 5.835 0 0 1-.76-.497 2.716 2.716 0 0 1-.647-.672 3.57 3.57 0 0 1-.561-1.952c0-.862.241-1.6.722-2.213a4.153 4.153 0 0 1 1.868-1.343c.764-.29 1.627-.436 2.59-.436 1.901 0 3.549.577 4.944 1.729l-1.507 2.014c-.996-.845-2.125-1.268-3.387-1.268-.672 0-1.228.112-1.668.336-.432.224-.648.551-.648.982 0 .166.029.315.087.448a.93.93 0 0 0 .299.36c.15.108.291.2.423.274.133.075.328.153.586.236l.66.187.772.174c.373.083.68.157.921.224.249.058.552.149.909.273a4.943 4.943 0 0 1 1.669.87c.266.2.477.42.635.66.166.232.299.514.398.845.108.332.162.693.162 1.082 0 .87-.245 1.63-.734 2.276-.49.638-1.142 1.115-1.955 1.43-.806.315-1.723.473-2.752.473Zm20.26-6.778c0 .382-.025.755-.075 1.12h-10.534c.232.936.722 1.687 1.469 2.25.755.556 1.66.833 2.715.833.813 0 1.569-.17 2.266-.51.706-.34 1.254-.766 1.644-1.28l1.93 1.654c-.673.845-1.528 1.508-2.565 1.99-1.03.48-2.146.72-3.35.72-2.059 0-3.773-.654-5.143-1.964-1.361-1.31-2.042-2.94-2.042-4.887 0-1.26.299-2.404.897-3.432a6.436 6.436 0 0 1 2.465-2.438c1.054-.588 2.221-.883 3.499-.883 1.951 0 3.574.651 4.869 1.953 1.303 1.293 1.955 2.918 1.955 4.874Zm-6.849-4.253c-.921 0-1.726.278-2.415.834-.681.555-1.138 1.289-1.37 2.2h7.583c-.216-.911-.672-1.645-1.37-2.2-.689-.556-1.498-.834-2.428-.834Zm15.99.255h-3.674v6.212c0 .597.162 1.049.486 1.355.332.299.801.448 1.407.448.573 0 1.166-.15 1.781-.448v2.674c-.706.356-1.499.535-2.379.535-1.37 0-2.403-.361-3.1-1.082-.698-.722-1.046-1.729-1.046-3.022V12.66h-2.205v-2.587h2.205V5.367h2.851v4.706h3.674v2.587ZM48.869 9.873h-.49c-1.673 0-3.033.712-4.08 2.135v-1.935h-2.887V23.25h2.887v-6.434c0-1.222.36-2.189 1.08-2.9.728-.72 1.729-1.08 3-1.08h.49V9.873Z" clip-rule="evenodd"></path></svg></a><nav class="hidden laptop:flex items-center"><button type="button" class="flex items-center gap-1 text-label-medium transition-colors px-3 py-2 cursor-pointer text-content-layout-2 hover:text-content-layout-1" aria-haspopup="true" aria-expanded="false">Products<svg class="inline justify-self-center stroke-current h-4 w-4 min-w-4 stroke-2 transition-transform" aria-hidden="true" focusable="false"><use href="/icons/sprite-stroke.svg#chevron-down"></use></svg><span style="position:absolute;border:0;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;word-wrap:normal">dropdown</span></button><button type="button" class="flex items-center gap-1 text-label-medium transition-colors px-3 py-2 cursor-pointer text-content-layout-2 hover:text-content-layout-1" aria-haspopup="true" aria-expanded="false">Solutions<svg class="inline justify-self-center stroke-current h-4 w-4 min-w-4 stroke-2 transition-transform" aria-hidden="true" focusable="false"><use href="/icons/sprite-stroke.svg#chevron-down"></use></svg><span style="position:absolute;border:0;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;word-wrap:normal">dropdown</span></button><button type="button" class="flex items-center gap-1 text-label-medium transition-colors px-3 py-2 cursor-pointer text-content-layout-2 hover:text-content-layout-1" aria-haspopup="true" aria-expanded="false">Resources<svg class="inline justify-self-center stroke-current h-4 w-4 min-w-4 stroke-2 transition-transform" aria-hidden="true" focusable="false"><use href="/icons/sprite-stroke.svg#chevron-down"></use></svg><span style="position:absolute;border:0;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;word-wrap:normal">dropdown</span></button></nav></div><div class="hidden laptop:flex items-center"><button type="button" class="relative flex items-center justify-center min-w-max cursor-pointer select-none transition duration-fast ease-base transform focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-primary-soft focus-visible:ring-offset-2 focus-visible:ring-offset-surface-layout-1 active:scale-[0.98] active:origin-center text-button-medium py-3 px-4 rounded-2xl h-10 text-content-primary-solid bg-surface-primary-solid hover:bg-surface-primary-solid-hover active:bg-surface-primary-solid-active [&amp;_#loader]:border-t-content-primary-solid"><div class="flex items-center">Try locally<div class="h-1 w-1"></div><svg aria-hidden="true" class="inline justify-self-center stroke-current h-5 w-5 min-w-5 stroke-[1.5px]" focusable="false"><use href="/icons/sprite-stroke.svg#arrow-right"></use></svg><span style="position:absolute;border:0;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;word-wrap:normal"></span></div></button></div><button type="button" class="laptop:hidden flex items-center justify-center w-10 h-10 rounded-2xl text-content-layout-1 hover:bg-surface-layout-2 transition-colors cursor-pointer" aria-label="Open menu" aria-expanded="false"><svg class="inline justify-self-center stroke-current h-5 w-5 min-w-5 stroke-[1.5px]" aria-hidden="true" focusable="false"><use href="/icons/sprite-stroke.svg#menu"></use></svg><span style="position:absolute;border:0;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;word-wrap:normal">menu</span></button></div></header><main id="main-content"><!--$--><!--$--><div class="relative mx-auto container px-4 tablet:px-6 laptop:px-8 max-w-[1456px] flex flex-col gap-2 mt-0" id="blog-post-hero"><div class="w-full gap-4 flex flex-col"><div class="max-w-3xl mx-auto w-full aspect-[1200/630] rounded-4xl bg-surface-layout-1 overflow-hidden"><img src="https://cms.readyset.io/files/articles/682f58d7e1d9680001d06cce/25836706172d9d9642a5df9d.png" alt="Identifying Cacheable Queries: Using tools like pt-query-digest or the MySQL sys schema to pinpoint queries that would benefit from caching" fetchPriority="high" decoding="async" class="w-full h-full object-cover"/></div></div></div><div class="relative mx-auto container px-4 tablet:px-6 laptop:px-8 max-w-[1456px] flex flex-col gap-2 mt-12" id="blog-post-content"><div class="w-full flex flex-col gap-8"><div class="max-w-3xl mx-auto w-full flex flex-col gap-8"><div class="flex flex-col gap-6"><a class="text-body-small text-content-layout-3 hover:text-content-layout-1 transition-colors w-fit active" href="/blog" data-status="active" aria-current="page">← Back to blog</a><span class="inline-block w-fit px-3 py-1 rounded-full bg-surface-layout-1 text-body-small text-content-layout-3">Query Optimization</span><h1 class="text-display-small">Identifying Cacheable Queries: Using tools like pt-query-digest or the MySQL sys schema to pinpoint queries that would benefit from caching</h1><p class="text-body-large text-content-layout-3">Introduction
It’s common for DBAs to optimize queries to address bottlenecks and reduce resource contention. However, in production workloads, the most resource-intensive queries aren’t always the longest-running ones. High-frequency queries that execute quickly but are called repeatedly can collectively consume significant resources, increasing overall latency and system load. Identifying these queries requires a more comprehensive analysis than simply inspecting the slow query log or process </p><div class="flex items-center gap-4"><a href="/blog/authors/vinicius" class="shrink-0"><img src="https://cms.readyset.io/files/authors/665e269a0177f50001d54d46/26154aff7b87c541fa8a7e2e.bin" alt="Vinicius Grippa" loading="lazy" decoding="async" class="w-10 h-10 rounded-full object-cover"/></a><div class="flex flex-col"><a href="/blog/authors/vinicius" class="hover:underline"><p class="text-body-medium">Vinicius Grippa</p></a><p class="text-body-small text-content-layout-3">2025-05-29<!-- --> · <!-- -->10 min read</p></div></div><div class="flex flex-wrap gap-2"><a href="/blog/tags/mysql" class="px-3 py-1 rounded-full bg-surface-layout-1 text-body-small text-content-layout-3 hover:bg-surface-layout-2 transition-colors">MySQL</a></div></div><div role="separator" class="bg-border-layout-soft h-(--divider-thickness) w-full" style="--divider-thickness:1.5px"></div><div class="prose prose-neutral max-w-none"><!--$--><h3 id="introduction"><a href="#introduction"><strong>Introduction</strong></a></h3>
<p>It’s common for DBAs to optimize queries to address bottlenecks and reduce resource contention. However, in production workloads, the most resource-intensive queries aren’t always the longest-running ones. High-frequency queries that execute quickly but are called repeatedly can collectively consume significant resources, increasing overall latency and system load. Identifying these queries requires a more comprehensive analysis than simply inspecting the slow query log or process list.</p>
<p>A more effective approach involves analyzing query frequency and data access patterns, particularly focusing on queries with a skewed distribution. In many workloads, a small subset of queries accounts for the majority of database load, following the Pareto principle, where a small number of queries contribute to most of the issues. The chart below illustrates this principle: a small portion of queries causes a disproportionately high incidence of workload.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXc-5BsSkZQ9c8-uGPn1um3q2cQHD5X7xrjV9O7QHKESOWcyFIiA-CXducu-6UyV2j-_vbR0IpE0EZifyFw9h5yzMFhw8OMBsl_SNzwxvW-BfSGTu5gXaR4S4xusEDZ_2wi4SD72BQ?key=QXIkV4ZDxUo4YCbsb2npdA" alt=""/></p>
<p>This means that by focusing on a small percentage of the queries, we can address the majority of performance problems. Working smarter, not necessarily harder, is often the most efficient path to better results.</p>
<p>In this post, we’ll explore how to use tools like <strong>pt-query-diges</strong>t and the MySQL <strong>sys</strong> schema to identify high-frequency, cacheable queries and implement targeted optimizations using Readyset.</p>
<h3 id="capturing-workload-data"><a href="#capturing-workload-data"><strong>Capturing Workload Data</strong></a></h3>
<p>Before analyzing cacheable queries, it’s essential to capture accurate workload data. MySQL provides two main options for this: the <strong>Performance Schema (P_S)</strong> and the <strong>Slow Query Log</strong>.</p>
<p>The <strong>Performance Schema</strong> is a built-in mechanism that enables detailed profiling of query execution. It organizes events hierarchically, from transactions down to individual waits. This structure allows you to trace the lifecycle of a query and identify bottlenecks at each stage. Unlike the <strong>Slow Query Log</strong>, which is disabled by default, the <strong>Performance Schema</strong> is typically enabled with basic instrumentation.</p>
<p>For example, here’s a sample output using a <strong>Performance Schema</strong> query:</p>
<pre><code>mysql &gt; SELECT      DIGEST,      DIGEST_TEXT,      COUNT_STAR AS executions,      SUM_TIMER_WAIT / 1000000000000 AS total_latency_sec,      SUM_LOCK_TIME / 1000000000000 AS total_lock_time_sec,      SUM_ROWS_SENT AS rows_sent,      SUM_ROWS_EXAMINED AS rows_examined,      FIRST_SEEN,      LAST_SEEN  FROM      performance_schema.events_statements_summary_by_digest  ORDER BY      total_latency_sec DESC  LIMIT 1\G
*************************** 1. row ***************************
             DIGEST: c4abbf6653c63ebc5ce9c4b49f7bfd8711851dd9d968ed69244319850c548813
        DIGEST_TEXT: SELECT DISTINCTROW `e` . `emp_no` , `e` . `first_name` , `e` . `last_name` , MAX ( `s` . `salary` ) AS `highest_salary` FROM `employees` `e` JOIN `salaries` `s` ON `e` . `emp_no` = `s` . `emp_no` GROUP BY `e` . `emp_no` , `e` . `first_name` , `e` . `last_name` HAVING `highest_salary` &gt; ? ORDER BY `highest_salary` DESC LIMIT ?
         executions: 61
  total_latency_sec: 6649.4135
total_lock_time_sec: 0.0004
          rows_sent: 610
      rows_examined: 191788941
         FIRST_SEEN: 2025-05-18 22:16:32.324267
          LAST_SEEN: 2025-05-18 22:44:34.113610
1 row in set (0.00 sec)
</code></pre>
<p>This kind of output gives a digest of the query text and key metrics like execution count and latency.</p>
<p>To make <strong>Performance Schema</strong> more easier to work with, MySQL includes the <strong>sys</strong> schema—a collection of views, procedures, and functions designed to convert raw <strong>P_S</strong> data into actionable insights. The example below shows how many of the sys schema views already suggest their purpose just by name:</p>
<pre><code>mysql(sys) &gt; show tables like &#x27;%statement%&#x27;;
+-----------------------------------------------+
| Tables_in_sys (%statement%) |
+-----------------------------------------------+
| host_summary_by_statement_latency |
| host_summary_by_statement_type |
| statement_analysis |
| statements_with_errors_or_warnings |
| statements_with_full_table_scans |
| statements_with_runtimes_in_95th_percentile |
| statements_with_sorting |
| statements_with_temp_tables |
| user_summary_by_statement_latency |
| user_summary_by_statement_type |
| x$host_summary_by_statement_latency |
| x$host_summary_by_statement_type |
| x$statement_analysis |
| x$statements_with_errors_or_warnings |
| x$statements_with_full_table_scans |
| x$statements_with_runtimes_in_95th_percentile |
| x$statements_with_sorting |
| x$statements_with_temp_tables |
| x$user_summary_by_statement_latency |
| x$user_summary_by_statement_type |
+-----------------------------------------------+
20 rows in set (0.00 sec)
</code></pre>
<p>You’ll see views like <strong>statements_with_full_table_scans</strong> and x$<strong>statements_with_runtimes_in_95th_percentile</strong>, which simplify query analysis.</p>
<p>The last method worth mentioning is the <strong>Slow Query Log</strong>. This traditional mechanism captures queries that exceed a threshold defined by the <strong>long_query_time</strong> parameter. By default, it’s turned off and set to 10 seconds. Here’s a sample log entry:</p>
<pre><code># Time: 2025-05-18T22:44:34.113510Z
# User@Host: sysbench[sysbench] @ localhost [127.0.0.1] Id: 20284
# Schema: employees Last_errno: 0 Killed: 0
# Query_time: 103.518173 Lock_time: 0.000007 Rows_sent: 10 Rows_examined: 3144081 Rows_affected: 0 Bytes_sent: 581
SET timestamp=1747608170;
/* Employees who received a salary above $120,000 */
SELECT DISTINCT e.emp_no, e.first_name, e.last_name, MAX(s.salary) AS highest_salary
FROM employees e
JOIN salaries s ON e.emp_no = s.emp_no
GROUP BY e.emp_no, e.first_name, e.last_name
HAVING highest_salary &gt; 120000
ORDER BY highest_salary DESC
LIMIT 10;
</code></pre>
<p>Both the <strong>Performance Schema</strong> and the <strong>Slow Query Log</strong> have their trade-offs. Increasing the <strong>performance_schema_max_digest_length</strong> from the default 1024 bytes to 2048 can capture more query detail but raises memory usage. Lowering <strong>long_query_time</strong> to values like 0.3 seconds provides broader coverage but may increase disk I/O.</p>
<p>The key is to balance visibility with impact. It’s not just about capturing more data—it’s about capturing relevant data without overwhelming the server.</p>
<h2 id="analyzing-the-slow-query-log-with-pt-query-digest"><a href="#analyzing-the-slow-query-log-with-pt-query-digest"><strong>Analyzing the Slow Query Log with pt-query-digest</strong></a></h2>
<p><strong>pt-query-digest</strong> is a powerful open-source tool from Percona that analyzes <strong>slow query logs</strong> and provides detailed breakdowns of query patterns, execution time, and frequency.</p>
<h3 id="installing-pt-query-digest"><a href="#installing-pt-query-digest"><strong>Installing pt-query-digest</strong></a></h3>
<p>To install pt-query-digest, run the following commands:</p>
<pre><code>wget percona.com/get/pt-query-digest
chmod +x pt-query-digest
</code></pre>
<h3 id="generating-a-report-with-pt-query-digest"><a href="#generating-a-report-with-pt-query-digest"><strong>Generating a Report with pt-query-digest</strong></a></h3>
<p>With the slow_query_log enabled and the long_query_time appropriately set, we can proceed with generating the report. The following command processes the entire slow query log and outputs the analysis to a file:</p>
<pre><code>./pt-query-digest --limit=100% /path/to/slow_query.log &gt; slow_report.out
</code></pre>
<p>A sample output might look like this:</p>
<pre><code># 3.7s user time, 290ms system time, 50.75M rss, 120.27M vsz
# Current date: Sun May 18 22:44:07 2025
# Hostname: support-01.tp.int.percona.com
# Files: /home/vinicius.grippa/sandboxes/rsandbox_8_0_32/master/data/support-01-slow.log
# Profile
# Rank Query ID Response time Calls R/Call
# ==== =================================== =============== ===== ========
# 1 0x94106BB6AC6179081ECBC3290DE9F4FD 5251.4310 40.7% 49 107.1721
# 2 0xC1C1F6C4D653F65959863343736F3440 1560.0465 12.1% 37 42.1634
# 3 0x318A4A6E8512F906A936EE933A8F6903 1424.9221 11.0% 83 17.1677
# 4 0xDBFF9C58A264436A87E953178115BB7D 1402.4261 10.9% 63 22.2607
# 5 0x51E61AF5EDF9E75EC3841241A40D4F5A 1371.5009 10.6% 1449 0.9465
</code></pre>
<p>These results show that two queries account for over 50% of the workload, making them strong candidates for optimization or caching. The report will also give you the queries and the individual analysis of each query. This is the sample of the first query:</p>
<pre><code># Query 1: 0.03 QPS, 3.29x concurrency, ID 0x94106BB6AC6179081ECBC3290DE9F4FD at byte 173343733
# This item is included in the report because it matches --limit.
# Scores: V/M = 2.28
# Time range: 2025-05-18T22:16:32 to 2025-05-18T22:43:08
# Attribute pct total min max avg 95% stddev median
# ============ === ======= ======= ======= ======= ======= ======= =======
# Count 1 49
# Exec time 40 5251s 79s 135s 107s 130s 16s 107s
# Lock time 0 356us 5us 15us 7us 9us 1us 6us
# Rows sent 0 490 10 10 10 10 0 10
# Rows examine 11 146.92M 3.00M 3.00M 3.00M 3.00M 0 3.00M
# Rows affecte 0 0 0 0 0 0 0 0
# Bytes sent 0 27.80k 581 581 581 581 0 581
# Query size 0 14.69k 307 307 307 307 0 307
# String:
# Databases employees
# Hosts localhost
# Last errno 0
# Users sysbench
# Query_time distribution
# 1us
# 10us
# 100us
# 1ms
# 10ms
# 100ms
# 1s
# 10s+ ################################################################
# Tables
# SHOW TABLE STATUS FROM `employees` LIKE &#x27;employees&#x27;\G
# SHOW CREATE TABLE `employees`.`employees`\G
# SHOW TABLE STATUS FROM `employees` LIKE &#x27;salaries&#x27;\G
# SHOW CREATE TABLE `employees`.`salaries`\G
# EXPLAIN /*!50100 PARTITIONS*/
/* Employees who received a salary above $120,000 */
SELECT DISTINCT e.emp_no, e.first_name, e.last_name, MAX(s.salary) AS highest_salary
FROM employees e
JOIN salaries s ON e.emp_no = s.emp_no
GROUP BY e.emp_no, e.first_name, e.last_name
HAVING highest_salary &gt; 120000
ORDER BY highest_salary DESC
LIMIT 10\G
</code></pre>
<h2 id="using-the-sys-schema-for-query-performance"><a href="#using-the-sys-schema-for-query-performance"><strong>Using the sys Schema for Query Performance</strong></a></h2>
<p>The sys schema extends the Performance Schema and exposes aggregated insights. Some particularly useful views include:</p>
<ul>
<li><strong>x$statements_with_runtimes_in_95th_percentile</strong>: Identifies queries in the 95th percentile for runtime.</li>
<li><strong>x$statements_with_full_table_scans</strong>: Highlights queries performing full table scans.</li>
</ul>
<h4 id="identifying-long-running-queries-95th-percentile"><a href="#identifying-long-running-queries-95th-percentile"><strong>Identifying Long Running Queries (95th Percentile)</strong></a></h4>
<p>To identify the most resource-intensive queries by average runtime, we will use a mix of the <strong>sys</strong> schema and <strong>P_S</strong> :</p>
<pre><code>SELECT schema_name,
format_pico_time(total_latency) AS tot_lat,
exec_count,
format_pico_time(total_latency / exec_count) AS latency_per_call,
query_sample_text
FROM sys.x$statements_with_runtimes_in_95th_percentile AS t1
JOIN performance_schema.events_statements_summary_by_digest AS t2
ON t2.digest = t1.digest
WHERE schema_name NOT IN (&#x27;performance_schema&#x27;, &#x27;sys&#x27;)
ORDER BY (total_latency / exec_count) DESC
LIMIT 2\G
</code></pre>
<p>Sample output:</p>
<pre><code>*************************** 1. row ***************************
schema_name: employees
tot_lat: 1.85 h
exec_count: 61
latency_per_call: 1.82 min
query_sample_text: SELECT DISTINCT e.emp_no, e.first_name, e.last_name, MAX(s.salary) AS highest_salary
FROM employees e
JOIN salaries s ON e.emp_no = s.emp_no
GROUP BY e.emp_no, e.first_name, e.last_name
HAVING highest_salary &gt; 120000
ORDER BY highest_salary DESC
LIMIT 10
*************************** 2. row ***************************
schema_name: employees
tot_lat: 26.00 min
exec_count: 37
latency_per_call: 42.16 s
query_sample_text: SELECT e.emp_no, e.first_name, e.last_name, s1.salary AS initial_salary, s2.salary AS recent_salary
FROM employees e
JOIN salaries s1 ON e.emp_no = s1.emp_no
JOIN salaries s2 ON e.emp_no = s2.emp_no
WHERE s1.to_date &lt; s2.from_date
AND s1.salary &gt; s2.salary
AND s2.to_date = &#x27;9999-01-01&#x27;
</code></pre>
<h4 id="identifying-full-table-scans"><a href="#identifying-full-table-scans"><strong>Identifying Full Table Scans</strong></a></h4>
<p>You can also find queries that trigger full table scans:</p>
<pre><code>SELECT query,
db,
exec_count,
format_pico_time(total_latency) AS total_latency,
rows_examined
FROM sys.x$statements_with_full_table_scans
ORDER BY total_latency DESC
LIMIT 2;
</code></pre>
<p>Sample output:</p>
<pre><code>| SELECT DISTINCT e.emp_no, e.first_name, e.last_name, MAX(s.salary) AS highest_salary
FROM employees e
JOIN salaries s ON e.emp_no = s.emp_no
GROUP BY e.emp_no, e.first_name, e.last_name
HAVING highest_salary &gt; 120000
ORDER BY highest_salary DESC LIMIT 10
| SELECT e.emp_no, e.first_name, e.last_name, s1.salary AS initial_salary, s2.salary AS recent_salary
FROM employees e
JOIN salaries s1 ON e.emp_no = s1.emp_no
JOIN salaries s2 ON e.emp_no = s2.emp_no
WHERE s1.to_date &lt; s2.from_date
AND s1.salary &gt; s2.salary
AND s2.to_date = &#x27;9999-01-01&#x27;
</code></pre>
<h2 id="working-on-these-queries"><a href="#working-on-these-queries"><strong>Working on These Queries</strong></a></h2>
<p>As shown above, both <strong>pt-query-digest</strong> and the <strong>sys</strong> schema help identify the same set of top-consuming queries when measured by latency. Assuming these queries are already optimized and indexed properly, the next step is considering other alternatives such as increasing resources or caching solutions.</p>
<p>In a previous blog post, I explored and tested performance improvement strategies by comparing vertical and horizontal scaling approaches, including the use of Readyset. You can read it<a href="https://readyset.io/blog/vertical-scaling-of-mysql-vs-horizontal-scaling-with-readyset-a-cost-and-performance-analysis-in-the-cloud?ref=blog.readyset.io" rel="noopener noreferrer" target="_blank"> here</a>.</p>
<p>To summarize, Readyset acts as a transparent caching layer. In this example, even partial support, just one of the two top queries (as seen in the “Readyset supported” column, can lead to meaningful reductions in database load.</p>
<pre><code>mysql&gt; SHOW PROXIED QUERIES;
+--------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------+-------+
| query id | proxied query | Readyset supported | count |
+--------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------+-------+
| q_cc3975f2c1fa2371 | SELECT `e`.`emp_no`, `e`.`first_name`, `e`.`last_name`, `s1`.`salary` AS `initial_salary`, `s2`.`salary` AS `recent_salary` FROM `employees` AS `e` JOIN `salaries` AS `s1` ON (`e`.`emp_no` = `s1`.`emp_no`) JOIN `salaries` AS `s2` ON (`e`.`emp_no` = `s2`.`emp_no`) WHERE ((`s1`.`to_date` &lt; `s2`.`from_date`) AND ((`s1`.`salary` &gt; `s2`.`salary`) AND (`s2`.`to_date` = $1))) | yes | 0 |
| q_f2b54bb91c39d8de | SELECT DISTINCT `e`.`emp_no`, `e`.`first_name`, `e`.`last_name`, max(`s`.`salary`) AS `highest_salary` FROM `employees` AS `e` JOIN `salaries` AS `s` ON (`e`.`emp_no` = `s`.`emp_no`) GROUP BY `e`.`emp_no`, `e`.`first_name`, `e`.`last_name` HAVING (`highest_salary` &gt; 120000) ORDER BY `highest_salary` DESC | unsupported | 0 |
+--------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------+-------+
2 rows in set (0.00 sec)
</code></pre>
<p>Once the query is cached, query execution time drops significantly:</p>
<p>mysql&gt; CREATE CACHE FROM q_cc3975f2c1fa2371;</p>
<p>Query OK, 0 rows affected (21.09 sec)</p>
<p><strong># Before caching:</strong></p>
<pre><code>mysql&gt; SELECT e.emp_no, e.first_name, e.last_name, s1.salary AS initial_salary, s2.salary AS recent_salary FROM employees e JOIN salaries s1 ON e.emp_no = s1.emp_no JOIN salaries s2 ON e.emp_no = s2.emp_no WHERE s1.to_date &lt; s2.from_date AND s1.salary &gt; s2.salary AND s2.to_date = &#x27;9999-01-01&#x27;;
06721feb72235f72c572b98646d651d4 -
6205 rows in set (1 min 0.94 sec)
</code></pre>
<p><strong># After caching:</strong></p>
<pre><code>mysql&gt; SELECT e.emp_no, e.first_name, e.last_name, s1.salary AS initial_salary, s2.salary AS recent_salary FROM employees e JOIN salaries s1 ON e.emp_no = s1.emp_no JOIN salaries s2 ON e.emp_no = s2.emp_no WHERE s1.to_date &lt; s2.from_date AND s1.salary &gt; s2.salary AND s2.to_date = &#x27;9999-01-01&#x27;;
06721feb72235f72c572b98646d651d4 -
6205 rows in set (0.02 sec)
</code></pre>
<p>I won’t go into the installation or usage details of Readyset in this post, but you can find the complete guide in the<a href="https://readyset.io/docs/get-started?ref=blog.readyset.io" rel="noopener noreferrer" target="_blank"> official documentation</a>.</p>
<h3 id="conclusion"><a href="#conclusion"><strong>Conclusion</strong></a></h3>
<p>Query optimization isn’t just about chasing slow queries, it’s about understanding your workload and focusing your efforts where they have the greatest impact. In real-world systems, a handful of high-frequency queries often dominate resource usage. By applying the Pareto principle to query analysis, DBAs can prioritize work that yields the highest return in performance.</p>
<p>Tools like <strong>pt-query-digest</strong>, the <strong>Performance Schema</strong>, and the <strong>sys</strong> schema provide different but complementary lenses through which to view query behavior. They help uncover both the obvious and the subtle inefficiencies in your database workload. Once you’ve identified your top contributors, caching with Readyset can take optimization further by reducing execution time to milliseconds, even under concurrent load, without touching application code.</p>
<p>The key takeaway is that performance tuning doesn’t always mean rewriting queries or scaling hardware. Sometimes, it means stepping back, analyzing patterns, and applying the right combination of observability and modern caching strategies. Whether you’re troubleshooting a specific incident or planning broader system improvements, having the right insights and tools enables you to work strategically, delivering faster systems, better user experiences, and more stable infrastructure.</p>
<p><a href="https://readyset.io/book-a-demo?ref=blog.readyset.io" rel="noopener noreferrer" target="_blank">Boost MySQL/PostgreSQL Performance With Readyset</a></p>
<h3 id="references"><a href="#references"><strong>References</strong></a></h3>
<ul>
<li><a href="https://lefred.be/content/mysql-8-0-if-i-should-optimize-only-one-query-on-my-application-which-one-should-it-be/?ref=blog.readyset.io" rel="noopener noreferrer" target="_blank"><strong>If I Should Optimize Only One Query on My Application, Which One Should It Be?</strong></a> – lefred.be (by Frederic Descamps)</li>
<li><a href="https://lefred.be/content/mysql-8-0-digest-query-samples-in-performance%5Fschema/?ref=blog.readyset.io" rel="noopener noreferrer" target="_blank"><strong>Digest Query Samples in Performance Schema</strong></a> – lefred.be (by Frederic Descamps)</li>
<li><a href="https://dev.mysql.com/doc/refman/8.4/en/server-system-variables.html?ref=blog.readyset.io#sysvar%5Fslow%5Fquery%5Flog" rel="noopener noreferrer" target="_blank"><strong>MySQL 8.4 Reference Manual: slow_query_log Variable</strong></a> – dev.mysql.com</li>
<li><a href="https://readyset.io/blog/vertical-scaling-of-mysql-vs-horizontal-scaling-with-readyset-a-cost-and-performance-analysis-in-the-cloud?ref=blog.readyset.io" rel="noopener noreferrer" target="_blank"><strong>Vertical Scaling of MySQL vs. Horizontal Scaling with Readyset: A Cost and Performance Analysis in the Cloud</strong></a> – Readyset Blog</li>
<li><a href="https://dev.mysql.com/doc/refman/8.4/en/performance-schema-query-profiling.html?ref=blog.readyset.io" rel="noopener noreferrer" target="_blank"><strong>Performance Schema Query Profiling</strong></a> – dev.mysql.com</li>
<li><a href="https://docs.percona.com/percona-toolkit/pt-query-digest.html?ref=blog.readyset.io" rel="noopener noreferrer" target="_blank"><strong>pt-query-digest Documentation</strong></a> – Percona Toolkit Docs</li>
</ul><!--/$--></div><div role="separator" class="bg-border-layout-soft h-(--divider-thickness) w-full" style="--divider-thickness:1.5px"></div><div class="flex flex-col laptop:flex-row items-start laptop:items-center justify-between gap-4 rounded-4xl bg-surface-layout-1 p-8"><div class="flex flex-col gap-1"><p class="text-headline-5">Want to see Readyset in action?</p><p class="text-body-small text-content-layout-3">Book a demo and see how Readyset can accelerate your database.</p></div><a href="/book-a-demo"><button type="button" class="relative flex items-center justify-center min-w-max cursor-pointer select-none transition duration-fast ease-base transform focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-primary-soft focus-visible:ring-offset-2 focus-visible:ring-offset-surface-layout-1 active:scale-[0.98] active:origin-center text-button-medium py-3 px-4 rounded-2xl h-10 text-content-primary-solid bg-surface-primary-solid hover:bg-surface-primary-solid-hover active:bg-surface-primary-solid-active [&amp;_#loader]:border-t-content-primary-solid"><div class="flex items-center">Book a demo<div class="h-1 w-1"></div><svg aria-hidden="true" class="inline justify-self-center stroke-current h-5 w-5 min-w-5 stroke-[1.5px]" focusable="false"><use href="/icons/sprite-stroke.svg#arrow-right"></use></svg><span style="position:absolute;border:0;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;word-wrap:normal"></span></div></button></a></div></div></div></div><div class="relative mx-auto container px-4 tablet:px-6 laptop:px-8 max-w-[1456px] flex flex-col gap-2 mt-40" id="related-articles"><div class="w-full flex flex-col gap-8"><div class="flex flex-col items-center gap-2 max-w-2xl mx-auto"><p class="text-headline-2 text-center">Related articles</p><p class="text-body-large text-content-layout-3 text-center">Continue reading more about <!-- -->query optimization<!-- -->.</p></div><div class="grid grid-cols-1 laptop:grid-cols-3 gap-6"><a href="/blog/where-a-subquery-can-live" class="group flex flex-col rounded-4xl bg-surface-layout-1 overflow-hidden transition-shadow hover:shadow-lg"><div class="aspect-[1200/630] w-full overflow-hidden"><img src="https://cms.readyset.io/files/uploads/ebe912ee-67a8-4862-9863-2252624a31cc.png" alt="How Readyset Rewrites Your SQL: Subqueries in HAVING, ORDER BY, JOIN ON" loading="lazy" decoding="async" class="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"/></div><div class="flex flex-col gap-3 p-6 flex-1"><span class="inline-block w-fit px-3 py-1 rounded-full bg-surface-layout-2 text-body-small text-content-layout-3">Query Optimization</span><h3 class="text-headline-5 line-clamp-2">How Readyset Rewrites Your SQL: Subqueries in HAVING, ORDER BY, JOIN ON</h3><p class="text-body-small text-content-layout-3 line-clamp-3 flex-1">Readyset used to reject subqueries in HAVING, ORDER BY, and JOIN ON outright, sending the whole query back to the database, uncached. This post extends the rewrite pipeline to decorrelate all four positions, with the soundness argument for the tricky one: LEFT OUTER JOIN, where the obvious move silently returns wrong answers.</p><div class="flex items-center justify-between pt-2"><div class="flex items-center gap-2"><img src="/images/blog-placeholder.svg" alt="Vassili Zarouba" loading="lazy" decoding="async" class="w-5 h-5 rounded-full object-cover"/><span class="text-body-small text-content-layout-2">Vassili Zarouba</span></div><div class="flex items-center gap-2 text-body-small text-content-layout-3"><span>2026-08-20</span><span>·</span><span>13 min read</span></div></div></div></a><a href="/blog/readyset-now-supports-sql-window-functions" class="group flex flex-col rounded-4xl bg-surface-layout-1 overflow-hidden transition-shadow hover:shadow-lg"><div class="aspect-[1200/630] w-full overflow-hidden"><img src="https://cms.readyset.io/files/articles/68ae1cea9faa2f0001b09690/74179b8a29fd23ebb2c6b977.png" alt="Readyset Now Supports SQL Window Functions." loading="lazy" decoding="async" class="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"/></div><div class="flex flex-col gap-3 p-6 flex-1"><span class="inline-block w-fit px-3 py-1 rounded-full bg-surface-layout-2 text-body-small text-content-layout-3">Query Optimization</span><h3 class="text-headline-5 line-clamp-2">Readyset Now Supports SQL Window Functions.</h3><p class="text-body-small text-content-layout-3 line-clamp-3 flex-1">We&#x27;re excited to announce that Readyset now supports SQL Window Functions. This powerful addition means you can now cache more of your complex queries without modifying a single line of application code, bringing the performance benefits of Readyset to an even broader range of use cases.
What Are Window Functions?
Window Functions are one of SQL&#x27;s most powerful features for analytical queries. They let you perform calculations across rows that are related to the current row, without needing c</p><div class="flex items-center justify-between pt-2"><div class="flex items-center gap-2"><img src="https://cms.readyset.io/files/authors/657385d2b4c219000125f4cd/c0376b666550a188861d4a2d.bin" alt="Readyset" loading="lazy" decoding="async" class="w-5 h-5 rounded-full object-cover"/><span class="text-body-small text-content-layout-2">Readyset</span></div><div class="flex items-center gap-2 text-body-small text-content-layout-3"><span>2025-08-26</span><span>·</span><span>2 min read</span></div></div></div></a><a href="/blog/optimizing-sql-pagination-in-mysql" class="group flex flex-col rounded-4xl bg-surface-layout-1 overflow-hidden transition-shadow hover:shadow-lg"><div class="aspect-[1200/630] w-full overflow-hidden"><img src="/images/blog-placeholder.svg" alt="Optimizing SQL Pagination in MySQL" loading="lazy" decoding="async" class="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"/></div><div class="flex flex-col gap-3 p-6 flex-1"><span class="inline-block w-fit px-3 py-1 rounded-full bg-surface-layout-2 text-body-small text-content-layout-3">Query Optimization</span><h3 class="text-headline-5 line-clamp-2">Optimizing SQL Pagination in MySQL</h3><p class="text-body-small text-content-layout-3 line-clamp-3 flex-1">In our blog post on Optimizing SQL Pagination in Postgres, we explained how to optimize SQL pagination for Postgres. As a counterpart, we’ve decided to write one for MySQL pagination. Like PostgreSQL, MySQL offers several ways to handle pagination, but some are far more efficient than others.
Let&#x27;s break down the SQL Pagination options, talk about why LIMIT and OFFSET can be a problem, and look at smarter ways to paginate large datasets efficiently.
The Classic SQL Pagination Approach: LIMIT </p><div class="flex items-center justify-between pt-2"><div class="flex items-center gap-2"><img src="https://cms.readyset.io/files/authors/665e269a0177f50001d54d46/26154aff7b87c541fa8a7e2e.bin" alt="Vinicius Grippa" loading="lazy" decoding="async" class="w-5 h-5 rounded-full object-cover"/><span class="text-body-small text-content-layout-2">Vinicius Grippa</span></div><div class="flex items-center gap-2 text-body-small text-content-layout-3"><span>2025-03-17</span><span>·</span><span>6 min read</span></div></div></div></a></div></div></div><div class="relative mx-auto container px-4 tablet:px-6 laptop:px-8 max-w-[1456px] flex flex-col gap-2 mt-20 mb-12" id="hero"><div class="w-full gap-4 flex flex-col"><div class="rounded-[80px] bg-surface-layout-1 w-full"><div class="flex flex-col justify-center items-center gap-8 w-full max-w-3xl mx-auto py-44"><h2 class="text-display-medium text-center">Still scaling the hard way?</h2><p class="text-body-large text-content-layout-3 text-center">Modern applications demand instant performance, even under unpredictable load. Readyset helps you eliminate slow queries, stabilize latency, and scale confidently.</p><div class="flex justify-center items-center gap-4"><a href="/book-a-demo"><button type="button" class="relative flex items-center justify-center min-w-max cursor-pointer select-none transition duration-fast ease-base transform focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-primary-soft focus-visible:ring-offset-2 focus-visible:ring-offset-surface-layout-1 active:scale-[0.98] active:origin-center text-button-medium py-3 px-4 rounded-3xl h-12 text-content-primary-soft border border-border-primary-soft bg-transparent hover:bg-surface-primary-soft-hover active:bg-surface-primary-soft-active [&amp;_#loader]:border-t-content-primary-soft"><div class="flex items-center">Contact Sales<div class="h-1 w-1"></div><svg aria-hidden="true" class="inline justify-self-center stroke-current h-5 w-5 min-w-5 stroke-[1.5px]" focusable="false"><use href="/icons/sprite-stroke.svg#arrow-right"></use></svg><span style="position:absolute;border:0;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;word-wrap:normal"></span></div></button></a><a href="/book-a-demo"><button type="button" class="relative flex items-center justify-center min-w-max cursor-pointer select-none transition duration-fast ease-base transform focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-primary-soft focus-visible:ring-offset-2 focus-visible:ring-offset-surface-layout-1 active:scale-[0.98] active:origin-center text-button-medium py-3 px-4 rounded-3xl h-12 text-content-primary-solid bg-surface-primary-solid hover:bg-surface-primary-solid-hover active:bg-surface-primary-solid-active [&amp;_#loader]:border-t-content-primary-solid"><div class="flex items-center">Start for free<div class="h-1 w-1"></div><svg aria-hidden="true" class="inline justify-self-center stroke-current h-5 w-5 min-w-5 stroke-[1.5px]" focusable="false"><use href="/icons/sprite-stroke.svg#arrow-right"></use></svg><span style="position:absolute;border:0;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;word-wrap:normal"></span></div></button></a></div></div><div role="separator" class="bg-border-layout-soft h-(--divider-thickness) w-full" style="--divider-thickness:1.5px"></div><div class="flex justify-between items-center w-full py-12 px-12"><div class="flex flex-col gap-2"><p class="text-headline-2 text-content-layout-1 text-left">Revolutionize your database performance with Readyset</p><p class="text-body-large text-content-layout-2">Serve requests at sub-millisecond latencies with the modern database scaling and query caching system for MySQL and PostgreSQL.</p></div><div class="hidden laptop:block"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 145 33" class="fill-current text-content-layout-1 h-6"><path d="M16 31.328c0 .736-.597 1.333-1.333 1.333H4a4 4 0 0 1-4-4V17.994c0-.736.597-1.333 1.333-1.333H16v14.667Z"></path><path d="M21.333 11.328v5.333h-4c-.736 0-1.333.597-1.333 1.333v4h-5.333v-5.333h4c.736 0 1.333-.597 1.333-1.333v-4h5.333Z"></path><path d="M32 15.328c0 .736-.597 1.333-1.333 1.333H16V1.994c0-.736.597-1.333 1.333-1.333H28a4 4 0 0 1 4 4v10.667Z"></path><path fill-rule="evenodd" d="M91.272 5.367h2.864V23.25h-2.864v-1.99c-1.145 1.45-2.665 2.175-4.557 2.175-1.204 0-2.296-.294-3.275-.882a6.299 6.299 0 0 1-2.304-2.45c-.548-1.036-.822-2.193-.822-3.47 0-1.276.274-2.433.822-3.469a6.209 6.209 0 0 1 2.304-2.437c.98-.597 2.071-.896 3.275-.896 1.9 0 3.42.722 4.557 2.164V5.367Zm-3.997 15.308c1.146 0 2.096-.386 2.852-1.157.763-.779 1.145-1.74 1.145-2.885 0-1.152-.382-2.114-1.145-2.884-.756-.78-1.706-1.17-2.852-1.17-1.154 0-2.117.39-2.889 1.17-.764.77-1.145 1.732-1.145 2.884 0 1.145.381 2.106 1.145 2.885.772.771 1.735 1.157 2.89 1.157Zm-24.258-2.898c.05-.364.075-.737.075-1.119 0-1.956-.652-3.581-1.955-4.874-1.295-1.302-2.918-1.953-4.87-1.953-1.278 0-2.444.295-3.498.883a6.442 6.442 0 0 0-2.466 2.438c-.597 1.028-.896 2.172-.896 3.432 0 1.948.68 3.577 2.042 4.887 1.37 1.31 3.084 1.965 5.143 1.965 1.203 0 2.32-.24 3.35-.722 1.037-.48 1.892-1.144 2.564-1.99l-1.93-1.653c-.39.514-.938.94-1.643 1.28-.698.34-1.453.51-2.267.51-1.054 0-1.959-.277-2.714-.833-.747-.563-1.237-1.314-1.47-2.25h10.535Zm-9.19-4.538c.69-.556 1.494-.834 2.416-.834.93 0 1.74.278 2.428.834.697.555 1.154 1.289 1.37 2.2h-7.584c.233-.911.69-1.645 1.37-2.2Zm21.469-3.166h2.863V23.25h-2.864v-1.99c-1.145 1.45-2.664 2.175-4.557 2.175-1.204 0-2.296-.294-3.275-.882a6.299 6.299 0 0 1-2.304-2.45c-.548-1.036-.822-2.193-.822-3.47 0-1.276.274-2.433.822-3.469a6.209 6.209 0 0 1 2.304-2.437c.98-.597 2.071-.896 3.275-.896 1.901 0 3.42.722 4.558 2.164v-1.922Zm-3.998 10.602c1.146 0 2.096-.386 2.852-1.157.764-.779 1.145-1.74 1.145-2.885 0-1.152-.381-2.114-1.145-2.884-.756-.78-1.706-1.17-2.852-1.17-1.154 0-2.117.39-2.889 1.17-.763.77-1.145 1.732-1.145 2.884 0 1.145.382 2.106 1.145 2.885.772.771 1.735 1.157 2.89 1.157Zm35.186-10.602h3.188L102.941 25.6l-1.027 2.356h-3.319c.461-.939 1.313-2.68 1.718-3.537l.697-1.443-5.51-12.902h3.275l3.842 9.532 3.867-9.532Zm8.586 13.363c-2.092 0-3.894-.664-5.405-1.99l1.482-2.015c1.071 1.02 2.362 1.53 3.873 1.53.755 0 1.374-.124 1.855-.373.482-.257.722-.61.722-1.057a.93.93 0 0 0-.161-.535c-.1-.157-.225-.29-.374-.398-.141-.108-.353-.211-.635-.31-.282-.1-.531-.179-.747-.237a25.515 25.515 0 0 0-.884-.224l-.934-.224a16.527 16.527 0 0 1-.909-.286 6.683 6.683 0 0 1-.909-.373 5.835 5.835 0 0 1-.76-.497 2.716 2.716 0 0 1-.647-.672 3.57 3.57 0 0 1-.561-1.952c0-.862.241-1.6.722-2.213a4.153 4.153 0 0 1 1.868-1.343c.764-.29 1.627-.436 2.59-.436 1.901 0 3.549.577 4.944 1.729l-1.507 2.014c-.996-.845-2.125-1.268-3.387-1.268-.672 0-1.228.112-1.668.336-.432.224-.648.551-.648.982 0 .166.029.315.087.448a.93.93 0 0 0 .299.36c.15.108.291.2.423.274.133.075.328.153.586.236l.66.187.772.174c.373.083.68.157.921.224.249.058.552.149.909.273a4.943 4.943 0 0 1 1.669.87c.266.2.477.42.635.66.166.232.299.514.398.845.108.332.162.693.162 1.082 0 .87-.245 1.63-.734 2.276-.49.638-1.142 1.115-1.955 1.43-.806.315-1.723.473-2.752.473Zm20.26-6.778c0 .382-.025.755-.075 1.12h-10.534c.232.936.722 1.687 1.469 2.25.755.556 1.66.833 2.715.833.813 0 1.569-.17 2.266-.51.706-.34 1.254-.766 1.644-1.28l1.93 1.654c-.673.845-1.528 1.508-2.565 1.99-1.03.48-2.146.72-3.35.72-2.059 0-3.773-.654-5.143-1.964-1.361-1.31-2.042-2.94-2.042-4.887 0-1.26.299-2.404.897-3.432a6.436 6.436 0 0 1 2.465-2.438c1.054-.588 2.221-.883 3.499-.883 1.951 0 3.574.651 4.869 1.953 1.303 1.293 1.955 2.918 1.955 4.874Zm-6.849-4.253c-.921 0-1.726.278-2.415.834-.681.555-1.138 1.289-1.37 2.2h7.583c-.216-.911-.672-1.645-1.37-2.2-.689-.556-1.498-.834-2.428-.834Zm15.99.255h-3.674v6.212c0 .597.162 1.049.486 1.355.332.299.801.448 1.407.448.573 0 1.166-.15 1.781-.448v2.674c-.706.356-1.499.535-2.379.535-1.37 0-2.403-.361-3.1-1.082-.698-.722-1.046-1.729-1.046-3.022V12.66h-2.205v-2.587h2.205V5.367h2.851v4.706h3.674v2.587ZM48.869 9.873h-.49c-1.673 0-3.033.712-4.08 2.135v-1.935h-2.887V23.25h2.887v-6.434c0-1.222.36-2.189 1.08-2.9.728-.72 1.729-1.08 3-1.08h.49V9.873Z" clip-rule="evenodd"></path></svg></div></div><div role="separator" class="bg-border-layout-soft h-(--divider-thickness) w-full" style="--divider-thickness:1.5px"></div><div class="grid grid-cols-1 laptop:grid-cols-[1fr_1fr_2fr] w-full py-12 px-12 gap-8"><div class="flex flex-col gap-4"><p class="text-headline-5 text-content-layout-1 text-left">Product</p><div class="flex flex-col gap-2"><a href="/products/readyset-cloud" class="text-content-layout-2 hover:text-content-layout-1 transition-colors">Readyset Cloud</a><a href="/products/readyset-private" class="text-content-layout-2 hover:text-content-layout-1 transition-colors">Readyset Private</a><a href="/products/readyset-community" class="text-content-layout-2 hover:text-content-layout-1 transition-colors">Readyset Community</a><a href="/tools/querypilot" class="text-content-layout-2 hover:text-content-layout-1 transition-colors">QueryPilot</a><a href="/tools/rdst" class="text-content-layout-2 hover:text-content-layout-1 transition-colors">rdst CLI</a><a href="/pricing" class="text-content-layout-2 hover:text-content-layout-1 transition-colors">Pricing</a></div></div><div class="flex flex-col gap-8"><div class="flex flex-col gap-4"><p class="text-headline-5 text-content-layout-1 text-left">Resources</p><div class="flex flex-col gap-2"><a class="text-content-layout-2 hover:text-content-layout-1 transition-colors active" href="/blog" data-status="active" aria-current="page">Blog</a><a href="/case-studies" class="text-content-layout-2 hover:text-content-layout-1 transition-colors">Case Studies</a><a href="/resources/company" class="text-content-layout-2 hover:text-content-layout-1 transition-colors">Company</a><a href="https://docs.readyset.io" target="_blank" rel="noopener noreferrer" class="text-content-layout-2 hover:text-content-layout-1 transition-colors">Documentation</a></div></div><div class="flex flex-col gap-4"><p class="text-headline-5 text-content-layout-1 text-left">Legal</p><div class="flex flex-col gap-2"><a href="/privacy-policy" class="text-content-layout-2 hover:text-content-layout-1 transition-colors">Privacy Policy</a><a href="/term-of-use" class="text-content-layout-2 hover:text-content-layout-1 transition-colors">Terms of Service</a></div></div></div><div class="flex flex-col gap-4"><p class="text-headline-5 text-content-layout-1 text-left">Join our newsletter</p><p class="text-body-small text-content-layout-3 text-left">Stay updated with the latest news, insights, and developments from Readyset — straight to your inbox.</p><form class="flex flex-col gap-2 w-full"><div class="flex gap-2 w-full"><div class="relative w-full text-content-layout-1"><input type="email" class="flex h-10 w-full rounded-lg border-(length:--border-base) border-border-layout-1 bg-surface-layout-2 px-3 py-2 text-body-medium placeholder:text-content-layout-3 focus-visible:outline-none focus-visible:shadow-focus disabled:cursor-not-allowed disabled:opacity-50" aria-label="Email address" placeholder="your@email.address" autoComplete="email" value=""/></div><button type="submit" class="relative flex items-center justify-center min-w-max cursor-pointer select-none transition duration-fast ease-base transform focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-primary-soft focus-visible:ring-offset-2 focus-visible:ring-offset-surface-layout-1 active:scale-[0.98] active:origin-center text-button-medium py-3 px-4 rounded-2xl h-10 text-content-primary-solid bg-surface-primary-solid hover:bg-surface-primary-solid-hover active:bg-surface-primary-solid-active [&amp;_#loader]:border-t-content-primary-solid"><div class="flex items-center">Subscribe</div></button></div></form></div></div><div role="separator" class="bg-border-layout-soft h-(--divider-thickness) w-full" style="--divider-thickness:1.5px"></div><div class="flex flex-col laptop:flex-row laptop:justify-between laptop:items-center gap-4 w-full py-12 px-12"><div class="flex items-center gap-6"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 145 33" class="fill-current text-content-layout-1 h-6"><path d="M16 31.328c0 .736-.597 1.333-1.333 1.333H4a4 4 0 0 1-4-4V17.994c0-.736.597-1.333 1.333-1.333H16v14.667Z"></path><path d="M21.333 11.328v5.333h-4c-.736 0-1.333.597-1.333 1.333v4h-5.333v-5.333h4c.736 0 1.333-.597 1.333-1.333v-4h5.333Z"></path><path d="M32 15.328c0 .736-.597 1.333-1.333 1.333H16V1.994c0-.736.597-1.333 1.333-1.333H28a4 4 0 0 1 4 4v10.667Z"></path><path fill-rule="evenodd" d="M91.272 5.367h2.864V23.25h-2.864v-1.99c-1.145 1.45-2.665 2.175-4.557 2.175-1.204 0-2.296-.294-3.275-.882a6.299 6.299 0 0 1-2.304-2.45c-.548-1.036-.822-2.193-.822-3.47 0-1.276.274-2.433.822-3.469a6.209 6.209 0 0 1 2.304-2.437c.98-.597 2.071-.896 3.275-.896 1.9 0 3.42.722 4.557 2.164V5.367Zm-3.997 15.308c1.146 0 2.096-.386 2.852-1.157.763-.779 1.145-1.74 1.145-2.885 0-1.152-.382-2.114-1.145-2.884-.756-.78-1.706-1.17-2.852-1.17-1.154 0-2.117.39-2.889 1.17-.764.77-1.145 1.732-1.145 2.884 0 1.145.381 2.106 1.145 2.885.772.771 1.735 1.157 2.89 1.157Zm-24.258-2.898c.05-.364.075-.737.075-1.119 0-1.956-.652-3.581-1.955-4.874-1.295-1.302-2.918-1.953-4.87-1.953-1.278 0-2.444.295-3.498.883a6.442 6.442 0 0 0-2.466 2.438c-.597 1.028-.896 2.172-.896 3.432 0 1.948.68 3.577 2.042 4.887 1.37 1.31 3.084 1.965 5.143 1.965 1.203 0 2.32-.24 3.35-.722 1.037-.48 1.892-1.144 2.564-1.99l-1.93-1.653c-.39.514-.938.94-1.643 1.28-.698.34-1.453.51-2.267.51-1.054 0-1.959-.277-2.714-.833-.747-.563-1.237-1.314-1.47-2.25h10.535Zm-9.19-4.538c.69-.556 1.494-.834 2.416-.834.93 0 1.74.278 2.428.834.697.555 1.154 1.289 1.37 2.2h-7.584c.233-.911.69-1.645 1.37-2.2Zm21.469-3.166h2.863V23.25h-2.864v-1.99c-1.145 1.45-2.664 2.175-4.557 2.175-1.204 0-2.296-.294-3.275-.882a6.299 6.299 0 0 1-2.304-2.45c-.548-1.036-.822-2.193-.822-3.47 0-1.276.274-2.433.822-3.469a6.209 6.209 0 0 1 2.304-2.437c.98-.597 2.071-.896 3.275-.896 1.901 0 3.42.722 4.558 2.164v-1.922Zm-3.998 10.602c1.146 0 2.096-.386 2.852-1.157.764-.779 1.145-1.74 1.145-2.885 0-1.152-.381-2.114-1.145-2.884-.756-.78-1.706-1.17-2.852-1.17-1.154 0-2.117.39-2.889 1.17-.763.77-1.145 1.732-1.145 2.884 0 1.145.382 2.106 1.145 2.885.772.771 1.735 1.157 2.89 1.157Zm35.186-10.602h3.188L102.941 25.6l-1.027 2.356h-3.319c.461-.939 1.313-2.68 1.718-3.537l.697-1.443-5.51-12.902h3.275l3.842 9.532 3.867-9.532Zm8.586 13.363c-2.092 0-3.894-.664-5.405-1.99l1.482-2.015c1.071 1.02 2.362 1.53 3.873 1.53.755 0 1.374-.124 1.855-.373.482-.257.722-.61.722-1.057a.93.93 0 0 0-.161-.535c-.1-.157-.225-.29-.374-.398-.141-.108-.353-.211-.635-.31-.282-.1-.531-.179-.747-.237a25.515 25.515 0 0 0-.884-.224l-.934-.224a16.527 16.527 0 0 1-.909-.286 6.683 6.683 0 0 1-.909-.373 5.835 5.835 0 0 1-.76-.497 2.716 2.716 0 0 1-.647-.672 3.57 3.57 0 0 1-.561-1.952c0-.862.241-1.6.722-2.213a4.153 4.153 0 0 1 1.868-1.343c.764-.29 1.627-.436 2.59-.436 1.901 0 3.549.577 4.944 1.729l-1.507 2.014c-.996-.845-2.125-1.268-3.387-1.268-.672 0-1.228.112-1.668.336-.432.224-.648.551-.648.982 0 .166.029.315.087.448a.93.93 0 0 0 .299.36c.15.108.291.2.423.274.133.075.328.153.586.236l.66.187.772.174c.373.083.68.157.921.224.249.058.552.149.909.273a4.943 4.943 0 0 1 1.669.87c.266.2.477.42.635.66.166.232.299.514.398.845.108.332.162.693.162 1.082 0 .87-.245 1.63-.734 2.276-.49.638-1.142 1.115-1.955 1.43-.806.315-1.723.473-2.752.473Zm20.26-6.778c0 .382-.025.755-.075 1.12h-10.534c.232.936.722 1.687 1.469 2.25.755.556 1.66.833 2.715.833.813 0 1.569-.17 2.266-.51.706-.34 1.254-.766 1.644-1.28l1.93 1.654c-.673.845-1.528 1.508-2.565 1.99-1.03.48-2.146.72-3.35.72-2.059 0-3.773-.654-5.143-1.964-1.361-1.31-2.042-2.94-2.042-4.887 0-1.26.299-2.404.897-3.432a6.436 6.436 0 0 1 2.465-2.438c1.054-.588 2.221-.883 3.499-.883 1.951 0 3.574.651 4.869 1.953 1.303 1.293 1.955 2.918 1.955 4.874Zm-6.849-4.253c-.921 0-1.726.278-2.415.834-.681.555-1.138 1.289-1.37 2.2h7.583c-.216-.911-.672-1.645-1.37-2.2-.689-.556-1.498-.834-2.428-.834Zm15.99.255h-3.674v6.212c0 .597.162 1.049.486 1.355.332.299.801.448 1.407.448.573 0 1.166-.15 1.781-.448v2.674c-.706.356-1.499.535-2.379.535-1.37 0-2.403-.361-3.1-1.082-.698-.722-1.046-1.729-1.046-3.022V12.66h-2.205v-2.587h2.205V5.367h2.851v4.706h3.674v2.587ZM48.869 9.873h-.49c-1.673 0-3.033.712-4.08 2.135v-1.935h-2.887V23.25h2.887v-6.434c0-1.222.36-2.189 1.08-2.9.728-.72 1.729-1.08 3-1.08h.49V9.873Z" clip-rule="evenodd"></path></svg><ul class="flex items-center gap-3"><li><a href="https://github.com/readysettech/readyset" aria-label="Readyset on GitHub" target="_blank" rel="noopener noreferrer" class="text-content-layout-3 hover:text-content-layout-1 transition-colors block"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" class="size-5"><path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0 1 12 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.31.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.02 10.02 0 0 0 22 12.017C22 6.484 17.522 2 12 2Z"></path></svg></a></li><li><a href="https://www.linkedin.com/company/readysettech" aria-label="Readyset on LinkedIn" target="_blank" rel="noopener noreferrer" class="text-content-layout-3 hover:text-content-layout-1 transition-colors block"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" class="size-5"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.026-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286ZM5.337 7.433a2.062 2.062 0 0 1-2.063-2.065 2.063 2.063 0 1 1 2.063 2.065Zm1.782 13.019H3.555V9H7.12v11.452ZM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.226.792 24 1.771 24h20.451C23.2 24 24 23.226 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003Z"></path></svg></a></li><li><a href="https://x.com/readysetio" aria-label="Readyset on X" target="_blank" rel="noopener noreferrer" class="text-content-layout-3 hover:text-content-layout-1 transition-colors block"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" class="size-5"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231 5.45-6.231Zm-1.161 17.52h1.833L7.084 4.126H5.117L17.083 19.77Z"></path></svg></a></li><li><a href="https://www.youtube.com/@readyset" aria-label="Readyset on YouTube" target="_blank" rel="noopener noreferrer" class="text-content-layout-3 hover:text-content-layout-1 transition-colors block"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" class="size-5"><path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814ZM9.545 15.568V8.432L15.818 12l-6.273 3.568Z"></path></svg></a></li><li><a href="https://go.readyset.io/community" aria-label="Readyset community Slack" target="_blank" rel="noopener noreferrer" class="text-content-layout-3 hover:text-content-layout-1 transition-colors block"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" class="size-5"><path d="M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52v2.52ZM6.313 15.165a2.527 2.527 0 0 1 2.521-2.52 2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522v-6.313ZM8.834 5.042a2.528 2.528 0 0 1-2.521-2.52A2.528 2.528 0 0 1 8.834 0a2.528 2.528 0 0 1 2.521 2.522v2.52H8.834ZM8.834 6.313a2.528 2.528 0 0 1 2.521 2.521 2.528 2.528 0 0 1-2.521 2.521H2.522A2.528 2.528 0 0 1 0 8.834a2.528 2.528 0 0 1 2.522-2.521h6.312ZM18.956 8.834a2.528 2.528 0 0 1 2.522-2.521A2.528 2.528 0 0 1 24 8.834a2.528 2.528 0 0 1-2.522 2.521h-2.522V8.834ZM17.688 8.834a2.528 2.528 0 0 1-2.523 2.521 2.527 2.527 0 0 1-2.52-2.521V2.522A2.527 2.527 0 0 1 15.165 0a2.528 2.528 0 0 1 2.523 2.522v6.312ZM15.165 18.956a2.528 2.528 0 0 1 2.523 2.522A2.528 2.528 0 0 1 15.165 24a2.527 2.527 0 0 1-2.52-2.522v-2.522h2.52ZM15.165 17.688a2.527 2.527 0 0 1-2.52-2.523 2.526 2.526 0 0 1 2.52-2.52h6.313A2.527 2.527 0 0 1 24 15.165a2.528 2.528 0 0 1-2.522 2.523h-6.313Z"></path></svg></a></li></ul></div><div class="flex flex-col laptop:flex-row laptop:items-center gap-2 laptop:gap-6"><button type="button" class="text-content-layout-2 hover:text-content-layout-1 transition-colors text-left cursor-pointer">Cookie preferences</button><p class="text-body-large text-content-layout-2">© <!-- -->2026<!-- --> Readyset. All rights reserved.</p></div></div></div></div></div><!--/$--><!--/$--></main><script nonce="ktmOMgITNrkoYILTaF4Tiw==" class="$tsr" id="$tsr-stream-barrier">(self.$R=self.$R||{})["tsr"]=[];self.$_TSR={h(){this.hydrated=!0,this.c()},e(){this.streamEnded=!0,this.c()},c(){this.hydrated&&this.streamEnded&&(delete self.$_TSR,delete self.$R.tsr)},p(e){this.initialized?e():this.buffer.push(e)},buffer:[]};$_TSR.router=($R=>$R[0]={manifest:$R[1]={routes:$R[2]={__root__:$R[3]={preloads:$R[4]=["/assets/index-CgozBq9o.js"],scripts:$R[5]=[$R[6]={attrs:$R[7]={type:"module",async:!0,src:"/assets/index-CgozBq9o.js"}}]},"/blog/$slug":$R[8]={preloads:$R[9]=["/assets/_slug-feOgb-yl.js","/assets/article-card-BslrmY68.js","/assets/newsletter-popup-C0xZnHQP.js","/assets/rich-markdown-DYApPWH1.js","/assets/index-rMShz_NE.js","/assets/use-disclosure-DaTsKJMJ.js"]}}},matches:$R[10]=[$R[11]={i:"__root__",u:1789054059440,s:"success",l:$R[12]={siteSettings:$R[13]={top_banner:$R[14]={enabled:!1,message:"",href:"",modifier:"info"},newsletter_popup:$R[15]={homepage:$R[16]={enabled:!0,title:"",description:""},blog:$R[17]={enabled:!0,title:"",description:""},blog_post:$R[18]={enabled:!0,title:"",description:""}},footer_tagline:"Revolutionize your database performance with Readyset",footer_subtagline:"Serve requests at sub-millisecond latencies with the modern database scaling and query caching system for MySQL and PostgreSQL.",footer_cta:$R[19]={title:"Still scaling the hard way?",description:"Modern applications demand instant performance, even under unpredictable load. Readyset helps you eliminate slow queries, stabilize latency, and scale confidently.",actions:$R[20]=[$R[21]={label:"Contact Sales",variant:"primary",modifier:"outline",full_width:!1,href:"/book-a-demo"},$R[22]={label:"Start for free",variant:"primary",modifier:"solid",full_width:!1,href:"/book-a-demo"}]},nav_links:$R[23]=[$R[24]={label:"Products",href:"/products/readyset-cloud",children:$R[25]=[$R[26]={label:"Readyset Community",href:"/products/readyset-community",description:"Run Readyset on your own infrastructure with full control and flexibility.",icon:"oval"},$R[27]={label:"Readyset Private",href:"/products/readyset-private",description:"Self-hosted enterprise deployment with licensed support.",icon:"secured-network"},$R[28]={label:"Readyset Cloud",href:"/products/readyset-cloud",description:"Fully managed caching for Postgres and MySQL, no infrastructure required.",icon:"cloud-server"}]},$R[29]={label:"Solutions",href:"/solutions/instant-performance",children:$R[30]=[$R[31]={label:"Instant Performance",href:"/solutions/instant-performance",description:"Sub-millisecond reads at scale.",icon:""},$R[32]={label:"Database Cost Efficiency",href:"/solutions/database-cost-efficiency",description:"Scale reads without scaling spend.",icon:""},$R[33]={label:"Drop-in Deployment",href:"/solutions/drop-in-deployment",description:"No rewrites, no schema changes.",icon:""},$R[34]={label:"Real-Time Cache Freshness",href:"/solutions/real-time-cache-freshness",description:"Cached reads that stay correct as data changes.",icon:""},$R[35]={label:"Automatic Query Caching",href:"/solutions/automatic-query-caching",description:"Auto-identified, auto-maintained caches.",icon:""},$R[36]={label:"Agent Workloads",href:"/solutions/agent-workloads",description:"Protect databases from agent-driven queries.",icon:""}]},$R[37]={label:"Resources",href:"/blog",children:$R[38]=[$R[39]={label:"Blog",href:"/blog",description:"",icon:""},$R[40]={label:"Case Studies",href:"/case-studies",description:"",icon:""},$R[41]={label:"Pricing",href:"/pricing",description:"",icon:""},$R[42]={label:"Company",href:"/resources/company",description:"",icon:""}]}],footer_links:$R[43]=[$R[44]={column_label:"Product",items:$R[45]=[$R[46]={label:"Readyset Cloud",href:"/products/readyset-cloud",external:!1},$R[47]={label:"Readyset Private",href:"/products/readyset-private",external:!1},$R[48]={label:"Readyset Community",href:"/products/readyset-community",external:!1},$R[49]={label:"QueryPilot",href:"/tools/querypilot",external:!1},$R[50]={label:"rdst CLI",href:"/tools/rdst",external:!1},$R[51]={label:"Pricing",href:"/pricing",external:!1}]},$R[52]={column_label:"Resources",items:$R[53]=[$R[54]={label:"Blog",href:"/blog",external:!1},$R[55]={label:"Case Studies",href:"/case-studies",external:!1},$R[56]={label:"Company",href:"/resources/company",external:!1},$R[57]={label:"Documentation",href:"https://docs.readyset.io",external:!0}]},$R[58]={column_label:"Legal",items:$R[59]=[$R[60]={label:"Privacy Policy",href:"/privacy-policy",external:!1},$R[61]={label:"Terms of Service",href:"/term-of-use",external:!1}]}],social_links:$R[62]=[$R[63]={platform:"github",href:"https://github.com/readysettech/readyset",label:"Readyset on GitHub"},$R[64]={platform:"linkedin",href:"https://www.linkedin.com/company/readysettech",label:"Readyset on LinkedIn"},$R[65]={platform:"twitter",href:"https://x.com/readysetio",label:"Readyset on X"},$R[66]={platform:"youtube",href:"https://www.youtube.com/@readyset",label:"Readyset on YouTube"},$R[67]={platform:"slack",href:"https://go.readyset.io/community",label:"Readyset community Slack"}],logo:$R[68]={full:"",icon:"",alt:"Readyset"},slug:"default"}},ssr:!0},$R[69]={i:"blog$slugblogidentifying-cacheable-queries-using-tools-like-pt-query-digest-or-the-mysql-sys-schema-to-pinpoint-queries-that-would-benefit-from-caching",u:1789054059443,s:"success",l:$R[70]={article:$R[71]={id:"ba826c1e-2a33-4cad-896b-316d27af6e68",slug:"identifying-cacheable-queries-using-tools-like-pt-query-digest-or-the-mysql-sys-schema-to-pinpoint-queries-that-would-benefit-from-caching",title:"Identifying Cacheable Queries: Using tools like pt-query-digest or the MySQL sys schema to pinpoint queries that would benefit from caching",excerpt:"Introduction\n\nIt’s common for DBAs to optimize queries to address bottlenecks and reduce resource contention. However, in production workloads, the most resource-intensive queries aren’t always the longest-running ones. High-frequency queries that execute quickly but are called repeatedly can collectively consume significant resources, increasing overall latency and system load. Identifying these queries requires a more comprehensive analysis than simply inspecting the slow query log or process ",updatedAt:1777903499901,category:$R[72]={id:"b9bea2e8-3bdf-4942-9509-f28ac4619289",slug:"query-optimization",name:"Query Optimization"},tags:$R[73]=[$R[74]={id:"4a1e000d-6031-48bc-9d1d-82cfda81e691",slug:"mysql",name:"MySQL"}],image:"https://cms.readyset.io/files/articles/682f58d7e1d9680001d06cce/25836706172d9d9642a5df9d.png",hasImage:!0,date:"2025-05-29",readTime:"10 min read",authors:$R[75]=[$R[76]={id:"9bd412fb-8a70-4202-a716-e3a466e50d69",slug:"vinicius",name:"Vinicius Grippa",avatar:"https://cms.readyset.io/files/authors/665e269a0177f50001d54d46/26154aff7b87c541fa8a7e2e.bin",bio:""}],author:$R[76],content:"### **Introduction**\n\nIt’s common for DBAs to optimize queries to address bottlenecks and reduce resource contention. However, in production workloads, the most resource-intensive queries aren’t always the longest-running ones. High-frequency queries that execute quickly but are called repeatedly can collectively consume significant resources, increasing overall latency and system load. Identifying these queries requires a more comprehensive analysis than simply inspecting the slow query log or process list.\n\nA more effective approach involves analyzing query frequency and data access patterns, particularly focusing on queries with a skewed distribution. In many workloads, a small subset of queries accounts for the majority of database load, following the Pareto principle, where a small number of queries contribute to most of the issues. The chart below illustrates this principle: a small portion of queries causes a disproportionately high incidence of workload.\n\n![](https://lh7-rt.googleusercontent.com/docsz/AD_4nXc-5BsSkZQ9c8-uGPn1um3q2cQHD5X7xrjV9O7QHKESOWcyFIiA-CXducu-6UyV2j-_vbR0IpE0EZifyFw9h5yzMFhw8OMBsl_SNzwxvW-BfSGTu5gXaR4S4xusEDZ_2wi4SD72BQ?key=QXIkV4ZDxUo4YCbsb2npdA)\n\nThis means that by focusing on a small percentage of the queries, we can address the majority of performance problems. Working smarter, not necessarily harder, is often the most efficient path to better results.\n\nIn this post, we’ll explore how to use tools like **pt-query-diges**t and the MySQL **sys** schema to identify high-frequency, cacheable queries and implement targeted optimizations using Readyset.\n\n### **Capturing Workload Data** \n\nBefore analyzing cacheable queries, it’s essential to capture accurate workload data. MySQL provides two main options for this: the **Performance Schema (P\\_S)** and the **Slow Query Log**.\n\nThe **Performance Schema** is a built-in mechanism that enables detailed profiling of query execution. It organizes events hierarchically, from transactions down to individual waits. This structure allows you to trace the lifecycle of a query and identify bottlenecks at each stage. Unlike the **Slow Query Log**, which is disabled by default, the **Performance Schema** is typically enabled with basic instrumentation.\n\nFor example, here’s a sample output using a **Performance Schema** query:\n\n```\nmysql > SELECT      DIGEST,      DIGEST_TEXT,      COUNT_STAR AS executions,      SUM_TIMER_WAIT / 1000000000000 AS total_latency_sec,      SUM_LOCK_TIME / 1000000000000 AS total_lock_time_sec,      SUM_ROWS_SENT AS rows_sent,      SUM_ROWS_EXAMINED AS rows_examined,      FIRST_SEEN,      LAST_SEEN  FROM      performance_schema.events_statements_summary_by_digest  ORDER BY      total_latency_sec DESC  LIMIT 1\\G\n*************************** 1. row ***************************\n             DIGEST: c4abbf6653c63ebc5ce9c4b49f7bfd8711851dd9d968ed69244319850c548813\n        DIGEST_TEXT: SELECT DISTINCTROW `e` . `emp_no` , `e` . `first_name` , `e` . `last_name` , MAX ( `s` . `salary` ) AS `highest_salary` FROM `employees` `e` JOIN `salaries` `s` ON `e` . `emp_no` = `s` . `emp_no` GROUP BY `e` . `emp_no` , `e` . `first_name` , `e` . `last_name` HAVING `highest_salary` > ? ORDER BY `highest_salary` DESC LIMIT ?\n         executions: 61\n  total_latency_sec: 6649.4135\ntotal_lock_time_sec: 0.0004\n          rows_sent: 610\n      rows_examined: 191788941\n         FIRST_SEEN: 2025-05-18 22:16:32.324267\n          LAST_SEEN: 2025-05-18 22:44:34.113610\n1 row in set (0.00 sec)\n```\n\nThis kind of output gives a digest of the query text and key metrics like execution count and latency.\n\nTo make **Performance Schema** more easier to work with, MySQL includes the **sys** schema—a collection of views, procedures, and functions designed to convert raw **P\\_S** data into actionable insights. The example below shows how many of the sys schema views already suggest their purpose just by name:\n\n```\nmysql(sys) > show tables like '%statement%';\n+-----------------------------------------------+\n| Tables_in_sys (%statement%) |\n+-----------------------------------------------+\n| host_summary_by_statement_latency |\n| host_summary_by_statement_type |\n| statement_analysis |\n| statements_with_errors_or_warnings |\n| statements_with_full_table_scans |\n| statements_with_runtimes_in_95th_percentile |\n| statements_with_sorting |\n| statements_with_temp_tables |\n| user_summary_by_statement_latency |\n| user_summary_by_statement_type |\n| x$host_summary_by_statement_latency |\n| x$host_summary_by_statement_type |\n| x$statement_analysis |\n| x$statements_with_errors_or_warnings |\n| x$statements_with_full_table_scans |\n| x$statements_with_runtimes_in_95th_percentile |\n| x$statements_with_sorting |\n| x$statements_with_temp_tables |\n| x$user_summary_by_statement_latency |\n| x$user_summary_by_statement_type |\n+-----------------------------------------------+\n20 rows in set (0.00 sec)\n\n```\n\nYou’ll see views like **statements\\_with\\_full\\_table\\_scans** and x$**statements\\_with\\_runtimes\\_in\\_95th\\_percentile**, which simplify query analysis.\n\nThe last method worth mentioning is the **Slow Query Log**. This traditional mechanism captures queries that exceed a threshold defined by the **long\\_query\\_time** parameter. By default, it’s turned off and set to 10 seconds. Here’s a sample log entry:\n\n```\n# Time: 2025-05-18T22:44:34.113510Z\n# User@Host: sysbench[sysbench] @ localhost [127.0.0.1] Id: 20284\n# Schema: employees Last_errno: 0 Killed: 0\n# Query_time: 103.518173 Lock_time: 0.000007 Rows_sent: 10 Rows_examined: 3144081 Rows_affected: 0 Bytes_sent: 581\nSET timestamp=1747608170;\n/* Employees who received a salary above $120,000 */\nSELECT DISTINCT e.emp_no, e.first_name, e.last_name, MAX(s.salary) AS highest_salary\nFROM employees e\nJOIN salaries s ON e.emp_no = s.emp_no\nGROUP BY e.emp_no, e.first_name, e.last_name\nHAVING highest_salary > 120000\nORDER BY highest_salary DESC\nLIMIT 10;\n\n```\n\nBoth the **Performance Schema** and the **Slow Query Log** have their trade-offs. Increasing the **performance\\_schema\\_max\\_digest\\_length** from the default 1024 bytes to 2048 can capture more query detail but raises memory usage. Lowering **long\\_query\\_time** to values like 0.3 seconds provides broader coverage but may increase disk I/O.\n\nThe key is to balance visibility with impact. It’s not just about capturing more data—it’s about capturing relevant data without overwhelming the server.\n\n## **Analyzing the Slow Query Log with pt-query-digest**\n\n**pt-query-digest** is a powerful open-source tool from Percona that analyzes **slow query logs** and provides detailed breakdowns of query patterns, execution time, and frequency.\n\n### **Installing pt-query-digest**\n\nTo install pt-query-digest, run the following commands:\n\n```\nwget percona.com/get/pt-query-digest\nchmod +x pt-query-digest\n```\n\n### **Generating a Report with pt-query-digest**\n\nWith the slow\\_query\\_log enabled and the long\\_query\\_time appropriately set, we can proceed with generating the report. The following command processes the entire slow query log and outputs the analysis to a file:\n\n```\n./pt-query-digest --limit=100% /path/to/slow_query.log > slow_report.out\n```\n\nA sample output might look like this:\n\n```\n# 3.7s user time, 290ms system time, 50.75M rss, 120.27M vsz\n# Current date: Sun May 18 22:44:07 2025\n# Hostname: support-01.tp.int.percona.com\n# Files: /home/vinicius.grippa/sandboxes/rsandbox_8_0_32/master/data/support-01-slow.log\n\n# Profile\n# Rank Query ID Response time Calls R/Call\n# ==== =================================== =============== ===== ========\n# 1 0x94106BB6AC6179081ECBC3290DE9F4FD 5251.4310 40.7% 49 107.1721 \n# 2 0xC1C1F6C4D653F65959863343736F3440 1560.0465 12.1% 37 42.1634 \n# 3 0x318A4A6E8512F906A936EE933A8F6903 1424.9221 11.0% 83 17.1677 \n# 4 0xDBFF9C58A264436A87E953178115BB7D 1402.4261 10.9% 63 22.2607 \n# 5 0x51E61AF5EDF9E75EC3841241A40D4F5A 1371.5009 10.6% 1449 0.9465\n\n```\n\nThese results show that two queries account for over 50% of the workload, making them strong candidates for optimization or caching. The report will also give you the queries and the individual analysis of each query. This is the sample of the first query:\n\n```\n# Query 1: 0.03 QPS, 3.29x concurrency, ID 0x94106BB6AC6179081ECBC3290DE9F4FD at byte 173343733\n# This item is included in the report because it matches --limit.\n# Scores: V/M = 2.28\n# Time range: 2025-05-18T22:16:32 to 2025-05-18T22:43:08\n# Attribute pct total min max avg 95% stddev median\n# ============ === ======= ======= ======= ======= ======= ======= =======\n# Count 1 49\n# Exec time 40 5251s 79s 135s 107s 130s 16s 107s\n# Lock time 0 356us 5us 15us 7us 9us 1us 6us\n# Rows sent 0 490 10 10 10 10 0 10\n# Rows examine 11 146.92M 3.00M 3.00M 3.00M 3.00M 0 3.00M\n# Rows affecte 0 0 0 0 0 0 0 0\n# Bytes sent 0 27.80k 581 581 581 581 0 581\n# Query size 0 14.69k 307 307 307 307 0 307\n# String:\n# Databases employees\n# Hosts localhost\n# Last errno 0\n# Users sysbench\n# Query_time distribution\n# 1us\n# 10us\n# 100us\n# 1ms\n# 10ms\n# 100ms\n# 1s\n# 10s+ ################################################################\n# Tables\n# SHOW TABLE STATUS FROM `employees` LIKE 'employees'\\G\n# SHOW CREATE TABLE `employees`.`employees`\\G\n# SHOW TABLE STATUS FROM `employees` LIKE 'salaries'\\G\n# SHOW CREATE TABLE `employees`.`salaries`\\G\n# EXPLAIN /*!50100 PARTITIONS*/\n/* Employees who received a salary above $120,000 */\nSELECT DISTINCT e.emp_no, e.first_name, e.last_name, MAX(s.salary) AS highest_salary\nFROM employees e\nJOIN salaries s ON e.emp_no = s.emp_no\nGROUP BY e.emp_no, e.first_name, e.last_name\nHAVING highest_salary > 120000\nORDER BY highest_salary DESC\nLIMIT 10\\G\n\n```\n\n## **Using the sys Schema for Query Performance**\n\nThe sys schema extends the Performance Schema and exposes aggregated insights. Some particularly useful views include:\n\n- **x$statements\\_with\\_runtimes\\_in\\_95th\\_percentile**: Identifies queries in the 95th percentile for runtime.\n- **x$statements\\_with\\_full\\_table\\_scans**: Highlights queries performing full table scans.\n\n#### **Identifying Long Running Queries (95th Percentile)**\n\nTo identify the most resource-intensive queries by average runtime, we will use a mix of the **sys** schema and **P\\_S** :\n\n```\nSELECT schema_name,\n format_pico_time(total_latency) AS tot_lat,\n exec_count,\n format_pico_time(total_latency / exec_count) AS latency_per_call,\n query_sample_text\nFROM sys.x$statements_with_runtimes_in_95th_percentile AS t1\nJOIN performance_schema.events_statements_summary_by_digest AS t2\n ON t2.digest = t1.digest\nWHERE schema_name NOT IN ('performance_schema', 'sys')\nORDER BY (total_latency / exec_count) DESC\nLIMIT 2\\G\n\n```\n\nSample output:\n\n```\n*************************** 1. row ***************************\n schema_name: employees\n tot_lat: 1.85 h\n exec_count: 61\n latency_per_call: 1.82 min\nquery_sample_text: SELECT DISTINCT e.emp_no, e.first_name, e.last_name, MAX(s.salary) AS highest_salary\n FROM employees e\n JOIN salaries s ON e.emp_no = s.emp_no\n GROUP BY e.emp_no, e.first_name, e.last_name\n HAVING highest_salary > 120000\n ORDER BY highest_salary DESC\n LIMIT 10\n\n*************************** 2. row ***************************\n schema_name: employees\n tot_lat: 26.00 min\n exec_count: 37\n latency_per_call: 42.16 s\nquery_sample_text: SELECT e.emp_no, e.first_name, e.last_name, s1.salary AS initial_salary, s2.salary AS recent_salary\n FROM employees e\n JOIN salaries s1 ON e.emp_no = s1.emp_no\n JOIN salaries s2 ON e.emp_no = s2.emp_no\n WHERE s1.to_date \x3C s2.from_date\n AND s1.salary > s2.salary\n AND s2.to_date = '9999-01-01'\n\n```\n\n#### **Identifying Full Table Scans**\n\nYou can also find queries that trigger full table scans:\n\n```\nSELECT query,\n db,\n exec_count,\n format_pico_time(total_latency) AS total_latency,\n rows_examined\nFROM sys.x$statements_with_full_table_scans\nORDER BY total_latency DESC\nLIMIT 2;\n\n```\n\nSample output:\n\n```\n| SELECT DISTINCT e.emp_no, e.first_name, e.last_name, MAX(s.salary) AS highest_salary\n FROM employees e\n JOIN salaries s ON e.emp_no = s.emp_no\n GROUP BY e.emp_no, e.first_name, e.last_name\n HAVING highest_salary > 120000\n ORDER BY highest_salary DESC LIMIT 10\n\n| SELECT e.emp_no, e.first_name, e.last_name, s1.salary AS initial_salary, s2.salary AS recent_salary\n FROM employees e\n JOIN salaries s1 ON e.emp_no = s1.emp_no\n JOIN salaries s2 ON e.emp_no = s2.emp_no\n WHERE s1.to_date \x3C s2.from_date\n AND s1.salary > s2.salary\n AND s2.to_date = '9999-01-01'\n\n```\n\n## **Working on These Queries**\n\nAs shown above, both **pt-query-digest** and the **sys** schema help identify the same set of top-consuming queries when measured by latency. Assuming these queries are already optimized and indexed properly, the next step is considering other alternatives such as increasing resources or caching solutions.\n\nIn a previous blog post, I explored and tested performance improvement strategies by comparing vertical and horizontal scaling approaches, including the use of Readyset. You can read it[ here](https://readyset.io/blog/vertical-scaling-of-mysql-vs-horizontal-scaling-with-readyset-a-cost-and-performance-analysis-in-the-cloud?ref=blog.readyset.io).\n\nTo summarize, Readyset acts as a transparent caching layer. In this example, even partial support, just one of the two top queries (as seen in the “Readyset supported” column, can lead to meaningful reductions in database load.\n\n```\nmysql> SHOW PROXIED QUERIES;\n+--------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------+-------+\n| query id | proxied query | Readyset supported | count |\n+--------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------+-------+\n| q_cc3975f2c1fa2371 | SELECT `e`.`emp_no`, `e`.`first_name`, `e`.`last_name`, `s1`.`salary` AS `initial_salary`, `s2`.`salary` AS `recent_salary` FROM `employees` AS `e` JOIN `salaries` AS `s1` ON (`e`.`emp_no` = `s1`.`emp_no`) JOIN `salaries` AS `s2` ON (`e`.`emp_no` = `s2`.`emp_no`) WHERE ((`s1`.`to_date` \x3C `s2`.`from_date`) AND ((`s1`.`salary` > `s2`.`salary`) AND (`s2`.`to_date` = $1))) | yes | 0 |\n| q_f2b54bb91c39d8de | SELECT DISTINCT `e`.`emp_no`, `e`.`first_name`, `e`.`last_name`, max(`s`.`salary`) AS `highest_salary` FROM `employees` AS `e` JOIN `salaries` AS `s` ON (`e`.`emp_no` = `s`.`emp_no`) GROUP BY `e`.`emp_no`, `e`.`first_name`, `e`.`last_name` HAVING (`highest_salary` > 120000) ORDER BY `highest_salary` DESC | unsupported | 0 |\n+--------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------+-------+\n2 rows in set (0.00 sec)\n```\n\nOnce the query is cached, query execution time drops significantly:\n\nmysql> CREATE CACHE FROM q\\_cc3975f2c1fa2371;\n\nQuery OK, 0 rows affected (21.09 sec)\n\n**\\# Before caching:**\n\n```\nmysql> SELECT e.emp_no, e.first_name, e.last_name, s1.salary AS initial_salary, s2.salary AS recent_salary FROM employees e JOIN salaries s1 ON e.emp_no = s1.emp_no JOIN salaries s2 ON e.emp_no = s2.emp_no WHERE s1.to_date \x3C s2.from_date AND s1.salary > s2.salary AND s2.to_date = '9999-01-01';\n06721feb72235f72c572b98646d651d4 -\n6205 rows in set (1 min 0.94 sec)\n\n\n```\n\n**\\# After caching:**\n\n```\nmysql> SELECT e.emp_no, e.first_name, e.last_name, s1.salary AS initial_salary, s2.salary AS recent_salary FROM employees e JOIN salaries s1 ON e.emp_no = s1.emp_no JOIN salaries s2 ON e.emp_no = s2.emp_no WHERE s1.to_date \x3C s2.from_date AND s1.salary > s2.salary AND s2.to_date = '9999-01-01';\n06721feb72235f72c572b98646d651d4 -\n6205 rows in set (0.02 sec)\n\n```\n\nI won’t go into the installation or usage details of Readyset in this post, but you can find the complete guide in the[ official documentation](https://readyset.io/docs/get-started?ref=blog.readyset.io).\n\n### **Conclusion**\n\nQuery optimization isn’t just about chasing slow queries, it’s about understanding your workload and focusing your efforts where they have the greatest impact. In real-world systems, a handful of high-frequency queries often dominate resource usage. By applying the Pareto principle to query analysis, DBAs can prioritize work that yields the highest return in performance.\n\nTools like **pt-query-digest**, the **Performance Schema**, and the **sys** schema provide different but complementary lenses through which to view query behavior. They help uncover both the obvious and the subtle inefficiencies in your database workload. Once you’ve identified your top contributors, caching with Readyset can take optimization further by reducing execution time to milliseconds, even under concurrent load, without touching application code.\n\nThe key takeaway is that performance tuning doesn’t always mean rewriting queries or scaling hardware. Sometimes, it means stepping back, analyzing patterns, and applying the right combination of observability and modern caching strategies. Whether you’re troubleshooting a specific incident or planning broader system improvements, having the right insights and tools enables you to work strategically, delivering faster systems, better user experiences, and more stable infrastructure.\n\n[Boost MySQL/PostgreSQL Performance With Readyset](https://readyset.io/book-a-demo?ref=blog.readyset.io)\n\n### **References**\n\n- [**If I Should Optimize Only One Query on My Application, Which One Should It Be?**](https://lefred.be/content/mysql-8-0-if-i-should-optimize-only-one-query-on-my-application-which-one-should-it-be/?ref=blog.readyset.io) – lefred.be (by Frederic Descamps)\n- [**Digest Query Samples in Performance Schema**](https://lefred.be/content/mysql-8-0-digest-query-samples-in-performance%5Fschema/?ref=blog.readyset.io) – lefred.be (by Frederic Descamps)\n- [**MySQL 8.4 Reference Manual: slow\\_query\\_log Variable**](https://dev.mysql.com/doc/refman/8.4/en/server-system-variables.html?ref=blog.readyset.io#sysvar%5Fslow%5Fquery%5Flog) – dev.mysql.com\n- [**Vertical Scaling of MySQL vs. Horizontal Scaling with Readyset: A Cost and Performance Analysis in the Cloud**](https://readyset.io/blog/vertical-scaling-of-mysql-vs-horizontal-scaling-with-readyset-a-cost-and-performance-analysis-in-the-cloud?ref=blog.readyset.io) – Readyset Blog\n- [**Performance Schema Query Profiling**](https://dev.mysql.com/doc/refman/8.4/en/performance-schema-query-profiling.html?ref=blog.readyset.io) – dev.mysql.com\n- [**pt-query-digest Documentation**](https://docs.percona.com/percona-toolkit/pt-query-digest.html?ref=blog.readyset.io) – Percona Toolkit Docs",seo:null},author:$R[76],related:$R[77]=[$R[78]={id:"54975d16-4819-4f80-9bc7-6600d7b6b63a",slug:"where-a-subquery-can-live",title:"How Readyset Rewrites Your SQL: Subqueries in HAVING, ORDER BY, JOIN ON",excerpt:"Readyset used to reject subqueries in HAVING, ORDER BY, and JOIN ON outright, sending the whole query back to the database, uncached. This post extends the rewrite pipeline to decorrelate all four positions, with the soundness argument for the tricky one: LEFT OUTER JOIN, where the obvious move silently returns wrong answers.",updatedAt:1787234199877,category:$R[72],tags:$R[79]=[],image:"https://cms.readyset.io/files/uploads/ebe912ee-67a8-4862-9863-2252624a31cc.png",hasImage:!0,date:"2026-08-20",readTime:"13 min read",authors:$R[80]=[$R[81]={id:"6fe9598c-ba80-4bdb-80c6-552c1f17784f",slug:"vassili",name:"Vassili Zarouba",avatar:"/images/blog-placeholder.svg",bio:""}],author:$R[81],content:"",seo:null,featured:!1,mostRead:!1},$R[82]={id:"00de6412-9ac8-4043-994f-e7bb9935cb90",slug:"readyset-now-supports-sql-window-functions",title:"Readyset Now Supports SQL Window Functions.",excerpt:"We're excited to announce that Readyset now supports SQL Window Functions. This powerful addition means you can now cache more of your complex queries without modifying a single line of application code, bringing the performance benefits of Readyset to an even broader range of use cases.\n\n\nWhat Are Window Functions?\n\nWindow Functions are one of SQL's most powerful features for analytical queries. They let you perform calculations across rows that are related to the current row, without needing c",updatedAt:1777903413815,category:$R[72],tags:$R[83]=[],image:"https://cms.readyset.io/files/articles/68ae1cea9faa2f0001b09690/74179b8a29fd23ebb2c6b977.png",hasImage:!0,date:"2025-08-26",readTime:"2 min read",authors:$R[84]=[$R[85]={id:"552f3435-f728-4436-a6a1-23dba36ed422",slug:"neda",name:"Readyset",avatar:"https://cms.readyset.io/files/authors/657385d2b4c219000125f4cd/c0376b666550a188861d4a2d.bin",bio:""}],author:$R[85],content:"",seo:null},$R[86]={id:"d275c20d-8233-4983-8b78-2fd118772462",slug:"optimizing-sql-pagination-in-mysql",title:"Optimizing SQL Pagination in MySQL",excerpt:"In our blog post on Optimizing SQL Pagination in Postgres, we explained how to optimize SQL pagination for Postgres. As a counterpart, we’ve decided to write one for MySQL pagination. Like PostgreSQL, MySQL offers several ways to handle pagination, but some are far more efficient than others.\n\nLet's break down the SQL Pagination options, talk about why LIMIT and OFFSET can be a problem, and look at smarter ways to paginate large datasets efficiently.\n\n\nThe Classic SQL Pagination Approach: LIMIT ",updatedAt:1777903547389,category:$R[72],tags:$R[87]=[$R[74]],image:"/images/blog-placeholder.svg",hasImage:!1,date:"2025-03-17",readTime:"6 min read",authors:$R[88]=[$R[76]],author:$R[76],content:"",seo:null}],preview:!1},ssr:!0}],lastMatchId:"blog$slugblogidentifying-cacheable-queries-using-tools-like-pt-query-digest-or-the-mysql-sys-schema-to-pinpoint-queries-that-would-benefit-from-caching",dehydratedData:$R[89]={queryStream:$R[90]=($R[91]=(e) => new ReadableStream({ start: (r) => {
e.on({ next: (a) => {
try {
r.enqueue(a);
} catch (t) {
}
}, throw: (a) => {
r.error(a);
}, return: () => {
try {
r.close();
} catch (a) {
}
} });
} }))($R[92]=($R[93]=() => {
let e = [], r = [], t = true, n2 = false, a = 0, s = (l, g, S) => {
for (S = 0; S < a; S++) r[S] && r[S][g](l);
}, i = (l, g, S, d) => {
for (g = 0, S = e.length; g < S; g++) d = e[g], !t && g === S - 1 ? l[n2 ? "return" : "throw"](d) : l.next(d);
}, u = (l, g) => (t && (g = a++, r[g] = l), i(l), () => {
t && (r[g] = r[a], r[a--] = void 0);
});
return { __SEROVAL_STREAM__: true, on: (l) => u(l), next: (l) => {
t && (e.push(l), s(l, "next"));
}, throw: (l) => {
t && (e.push(l), s(l, "throw"), t = false, n2 = false, r.length = 0);
}, return: (l) => {
t && (e.push(l), s(l, "return"), t = false, n2 = true, r.length = 0);
} };
})())}})($R["tsr"]);document.currentScript.remove()</script><script type="module" async="" src="/assets/index-CgozBq9o.js" nonce="ktmOMgITNrkoYILTaF4Tiw=="></script><script nonce='ktmOMgITNrkoYILTaF4Tiw=='>($R=>$R[92].return(void 0))($R["tsr"]);$_TSR.e();document.currentScript.remove()</script><script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" nonce="ktmOMgITNrkoYILTaF4Tiw==" data-cf-beacon='{"version":"2024.11.0","token":"d483087ab45f45f6a431c22e81c6c7f9","r":1,"spa":2}' crossorigin="anonymous"></script>
</body></html>