{"id":1024,"date":"2019-06-20T06:00:51","date_gmt":"2019-06-20T13:00:51","guid":{"rendered":"http:\/\/3.209.169.194\/blogs\/erin\/?p=1024"},"modified":"2019-06-19T19:21:14","modified_gmt":"2019-06-20T02:21:14","slug":"troubleshooting-a-change-in-query-performance","status":"publish","type":"post","link":"https:\/\/www.sqlskills.com\/blogs\/erin\/troubleshooting-a-change-in-query-performance\/","title":{"rendered":"Troubleshooting a Change in Query Performance"},"content":{"rendered":"<p>This is tale of troubleshooting&#8230;<\/p>\n<p>When you unexpectedly or intermittently encounter a change in query performance, it can be extremely frustrating and troublesome for a DBA or developer. If you\u2019re not using Query Store, a third-party application, or your own method to capture query data, then you probably don\u2019t have information about what query performance looked like when things were good\u2026you just know how it\u2019s running now. I was working with a customer of Tim\u2019s last week that had been chasing this exact problem for, in their words, years. They had recently upgraded to SQL Server 2016, and the problem was still occurring.<\/p>\n<h2>First Steps When There&#8217;s a Change in Query Performance<\/h2>\n<p>If there is a change in query performance, we typically look at differences in the execution statistics, as well as differences in the plan in terms of the shape and operations. The execution statistics confirm that performance is worse; duration is higher, CPU and\/or IO is higher. With this client, after we verified the execution data, we used Query Store to compare the plans, and they were different. When I see different plans I immediately check the compiled values (input parameters) for each plan. Typically, I expect the different plans are a result of different values \u2013 because the query is <em>sensitive<\/em> to different input parameters. Interestingly enough, in the case of this customer, the different plans had the <strong>exact same<\/strong> parameters. My next step? Statistics.<\/p>\n<p>The customer had told me that the only way they could consistently resolve the issue was to rebuild indexes for a few of the tables that were involved in the query. Rebuilding indexes causes an update to statistics with a FULLSCAN. I asked if they had ever tried just updating statistics with FULLSCAN to fix the issue, and they reported that it caused problems when they did. I tabled that issue, as I had a theory about what was happening and wanted to prove that out before digging into a secondary issue.<\/p>\n<h2>Collecting Query-Related Data<\/h2>\n<p>I set up an Extended Events session to capture the auto_stats event (which fires when statistics automatically update) and filtered on the objects in question.<\/p>\n<p>I also walked them through the statistics information within SQL Server. This was extremely fun. I love statistics, and I love explaining the output of DBCC SHOW_STATISTICS to people. When I\u2019m teaching, I can typically see the light bulbs come on for people as I talk through the stats header, the density vector, and the histogram (especially the histogram!). On the call, I could hear them say \u201coh!\u201d and \u201cthat makes so much more sense!\u201d as I was talking\u2026so fun. But I digress. So, we looked at the statistics for the tables in question, and we could see that because they had rebuilt indexes earlier in the day to resolve the issue, the sample was 100% (from the FULLSCAN). Query performance was good at this point, so we had our baseline.<\/p>\n<p>Then I showed them how to view the stats header information for all statistics for the tables in question using the <a href=\"https:\/\/www.sqlskills.com\/blogs\/erin\/new-statistics-dmf-in-sql-server-2008r2-sp2\/\">sys.dm_db_stats_properties DMV<\/a> and requested that they query the DMV if they had a change in query performance.\u00a0 I explained that they should look to see if statistics had updated, and if so, take note of the sample size.<\/p>\n<h3>Sidebar<\/h3>\n<p>My hypothesis was that stats were getting automatically updated because Auto Update Statistics was enabled for the database, and enough data had changed to fire the update (realize they are on 2016 with 130 compatibility mode, which means the threshold is lower than it used to be\u202620% + 500 rows). After statistics updated, all queries that used those statistics were recompiled, and because the sample was the default, it wasn\u2019t an accurate representation of the data in the table, and so a different plan was being generated, even though the same input parameters were used.<\/p>\n<h3>Back to the story<\/h3>\n<p>I took a few moments and explained my hypothesis, and then we looked at the data in one of the tables. We looked at the distribution of data in each of the leading columns in the indexes, and in one of them, there was extreme skew.\u00a0 If query performance degraded, and statistics had updated with the default sample, I requested that they run UPDATE STATISTICS with FULLSCAN to see if that resolved the issue.<\/p>\n<h2>Proof!<\/h2>\n<p>Sure enough, within the next 24 hours query performance changed and statistics for one of the indexes had been updated automatically with the default sample. I correlated this finding with the\u00a0 the Extended Events output as an extra validation step.\u00a0 When they updated statistics with the FULLSCAN, the &#8220;good&#8221; query plan was used again, query performance improved, and CPU dropped immediately.<\/p>\n<h2>A Better Solution<\/h2>\n<p>Now that we knew the issue, we had to decide how to address it. I recommended that they manually update statistics again with the FULLSCAN, but this time with the <a href=\"https:\/\/support.microsoft.com\/en-us\/help\/4039284\/enhancement-new-keyword-is-added-to-create-and-update-statistics-state\">PERSIST_SAMPLE_PERCENT<\/a> option set to ON. With this option enabled, statistics will retain the sample rate used with the manual update of statistics, <em>even if it&#8217;s an automatic update<\/em>. In this scenario, we wanted to ensure that statistics would always be updated with a FULLSCAN.<\/p>\n<p>The customer was slightly concerned about the implications of the FULLSCAN update running during the day, because the tables were large and the update would generate overhead. This is absolutely a valid concern for tables that are extremely large.\u00a0 To address this, we included the NORECOMPUTE option in the UPDATE STATISTICS command for the statistic in question, ensuring that the statistic would <em>not<\/em> automatically update <em>even if the number of modifications crossed the threshold<\/em>.<\/p>\n<p>I explained to the customer that they would definitely need to run a SQL Agent job on a regular basis to update statistics for those which used the NORECOMPUTE option so that the optimizer had updated information about the distribution of data. Using NORECOMPUTE is rare, I find, but it definitely has its use cases. It\u2019s essential to have a job to manually update those stats if you use that option for any of your statistics.<\/p>\n<h2>The Take Away<\/h2>\n<p>I haven\u2019t heard from the customer since we determined the problem and created a solution, and I take that as a good sign. It\u2019s ironic, because so often when I\u2019m talking about changes in query performance, I mention that updating statistics is not the solution. It can just <em>look<\/em> like the solution because it causes a recompile and for a parameter-sensitive query, it often \u201cfixes\u201d the issue. But it hides the root problem in that scenario, and updating statistics (via the index rebuild) was hiding the root problem here as well. It wasn\u2019t that the query was parameter sensitive, it\u2019s that the optimizer needed a histogram from a FULLSCAN of the column, due to the skew in the data.<\/p>\n<p>Most of the time, the default sample for a stats update is good enough. But when it isn\u2019t, then you either need a FULLSCAN, or if that doesn\u2019t do it, you need to look at <a href=\"https:\/\/www.sqlskills.com\/blogs\/kimberly\/sqlskills-procs-analyze-data-skew-create-filtered-statistics\/\">filtered statistics across ranges of the data<\/a> to give the optimizer better information.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is tale of troubleshooting&#8230; When you unexpectedly or intermittently encounter a change in query performance, it can be extremely frustrating and troublesome for a DBA or developer. If you\u2019re not using Query Store, a third-party application, or your own method to capture query data, then you probably don\u2019t have information about what query performance [&hellip;]<\/p>\n","protected":false},"author":7,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[15,17],"tags":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v21.9.1 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Troubleshooting a Change in Query Performance - Erin Stellato<\/title>\n<meta name=\"description\" content=\"Any time there is an unexpected change in query performance, the instinct is to fix it quickly; but it&#039;s essential to know how to track down the root cause,\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.sqlskills.com\/blogs\/erin\/troubleshooting-a-change-in-query-performance\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Troubleshooting a Change in Query Performance - Erin Stellato\" \/>\n<meta property=\"og:description\" content=\"Any time there is an unexpected change in query performance, the instinct is to fix it quickly; but it&#039;s essential to know how to track down the root cause,\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.sqlskills.com\/blogs\/erin\/troubleshooting-a-change-in-query-performance\/\" \/>\n<meta property=\"og:site_name\" content=\"Erin Stellato\" \/>\n<meta property=\"article:published_time\" content=\"2019-06-20T13:00:51+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2019-06-20T02:21:14+00:00\" \/>\n<meta name=\"author\" content=\"Erin Stellato\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Erin Stellato\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"6 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/erin\/troubleshooting-a-change-in-query-performance\/\",\"url\":\"https:\/\/www.sqlskills.com\/blogs\/erin\/troubleshooting-a-change-in-query-performance\/\",\"name\":\"Troubleshooting a Change in Query Performance - Erin Stellato\",\"isPartOf\":{\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/erin\/#website\"},\"datePublished\":\"2019-06-20T13:00:51+00:00\",\"dateModified\":\"2019-06-20T02:21:14+00:00\",\"author\":{\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/erin\/#\/schema\/person\/76170223ffffa1df03fd9be5b66cb158\"},\"description\":\"Any time there is an unexpected change in query performance, the instinct is to fix it quickly; but it's essential to know how to track down the root cause,\",\"breadcrumb\":{\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/erin\/troubleshooting-a-change-in-query-performance\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.sqlskills.com\/blogs\/erin\/troubleshooting-a-change-in-query-performance\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/erin\/troubleshooting-a-change-in-query-performance\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.sqlskills.com\/blogs\/erin\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Troubleshooting a Change in Query Performance\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/erin\/#website\",\"url\":\"https:\/\/www.sqlskills.com\/blogs\/erin\/\",\"name\":\"Erin Stellato\",\"description\":\"The SQL Sequel\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.sqlskills.com\/blogs\/erin\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/erin\/#\/schema\/person\/76170223ffffa1df03fd9be5b66cb158\",\"name\":\"Erin Stellato\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/erin\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/0c8b485bd54ea26b57e99f79b525f409?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/0c8b485bd54ea26b57e99f79b525f409?s=96&d=mm&r=g\",\"caption\":\"Erin Stellato\"},\"sameAs\":[\"http:\/\/3.209.169.194\/blogs\/erin\"],\"url\":\"https:\/\/www.sqlskills.com\/blogs\/erin\/author\/erin\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Troubleshooting a Change in Query Performance - Erin Stellato","description":"Any time there is an unexpected change in query performance, the instinct is to fix it quickly; but it's essential to know how to track down the root cause,","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.sqlskills.com\/blogs\/erin\/troubleshooting-a-change-in-query-performance\/","og_locale":"en_US","og_type":"article","og_title":"Troubleshooting a Change in Query Performance - Erin Stellato","og_description":"Any time there is an unexpected change in query performance, the instinct is to fix it quickly; but it's essential to know how to track down the root cause,","og_url":"https:\/\/www.sqlskills.com\/blogs\/erin\/troubleshooting-a-change-in-query-performance\/","og_site_name":"Erin Stellato","article_published_time":"2019-06-20T13:00:51+00:00","article_modified_time":"2019-06-20T02:21:14+00:00","author":"Erin Stellato","twitter_misc":{"Written by":"Erin Stellato","Est. reading time":"6 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.sqlskills.com\/blogs\/erin\/troubleshooting-a-change-in-query-performance\/","url":"https:\/\/www.sqlskills.com\/blogs\/erin\/troubleshooting-a-change-in-query-performance\/","name":"Troubleshooting a Change in Query Performance - Erin Stellato","isPartOf":{"@id":"https:\/\/www.sqlskills.com\/blogs\/erin\/#website"},"datePublished":"2019-06-20T13:00:51+00:00","dateModified":"2019-06-20T02:21:14+00:00","author":{"@id":"https:\/\/www.sqlskills.com\/blogs\/erin\/#\/schema\/person\/76170223ffffa1df03fd9be5b66cb158"},"description":"Any time there is an unexpected change in query performance, the instinct is to fix it quickly; but it's essential to know how to track down the root cause,","breadcrumb":{"@id":"https:\/\/www.sqlskills.com\/blogs\/erin\/troubleshooting-a-change-in-query-performance\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.sqlskills.com\/blogs\/erin\/troubleshooting-a-change-in-query-performance\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.sqlskills.com\/blogs\/erin\/troubleshooting-a-change-in-query-performance\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.sqlskills.com\/blogs\/erin\/"},{"@type":"ListItem","position":2,"name":"Troubleshooting a Change in Query Performance"}]},{"@type":"WebSite","@id":"https:\/\/www.sqlskills.com\/blogs\/erin\/#website","url":"https:\/\/www.sqlskills.com\/blogs\/erin\/","name":"Erin Stellato","description":"The SQL Sequel","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.sqlskills.com\/blogs\/erin\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/www.sqlskills.com\/blogs\/erin\/#\/schema\/person\/76170223ffffa1df03fd9be5b66cb158","name":"Erin Stellato","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.sqlskills.com\/blogs\/erin\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/0c8b485bd54ea26b57e99f79b525f409?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/0c8b485bd54ea26b57e99f79b525f409?s=96&d=mm&r=g","caption":"Erin Stellato"},"sameAs":["http:\/\/3.209.169.194\/blogs\/erin"],"url":"https:\/\/www.sqlskills.com\/blogs\/erin\/author\/erin\/"}]}},"_links":{"self":[{"href":"https:\/\/www.sqlskills.com\/blogs\/erin\/wp-json\/wp\/v2\/posts\/1024"}],"collection":[{"href":"https:\/\/www.sqlskills.com\/blogs\/erin\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sqlskills.com\/blogs\/erin\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/erin\/wp-json\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/erin\/wp-json\/wp\/v2\/comments?post=1024"}],"version-history":[{"count":0,"href":"https:\/\/www.sqlskills.com\/blogs\/erin\/wp-json\/wp\/v2\/posts\/1024\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.sqlskills.com\/blogs\/erin\/wp-json\/wp\/v2\/media?parent=1024"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/erin\/wp-json\/wp\/v2\/categories?post=1024"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/erin\/wp-json\/wp\/v2\/tags?post=1024"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}