{"id":838,"date":"2021-02-08T14:48:39","date_gmt":"2021-02-08T22:48:39","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/azure-sql\/?p=838"},"modified":"2021-02-08T14:48:39","modified_gmt":"2021-02-08T22:48:39","slug":"the-positive-impact-of-intelligent-query-processing","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/azure-sql\/the-positive-impact-of-intelligent-query-processing\/","title":{"rendered":"The Positive Impact of Intelligent Query Processing"},"content":{"rendered":"<h2><img decoding=\"async\" class=\"aligncenter wp-image-852 size-full\" src=\"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2021\/02\/pexels-joagbriel-1753922-scaled.jpg\" alt=\"Image pexels joagbriel 1753922\" width=\"2500\" height=\"1875\" srcset=\"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2021\/02\/pexels-joagbriel-1753922-scaled.jpg 2500w, https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2021\/02\/pexels-joagbriel-1753922-300x225.jpg 300w, https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2021\/02\/pexels-joagbriel-1753922-1024x768.jpg 1024w, https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2021\/02\/pexels-joagbriel-1753922-768x576.jpg 768w, https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2021\/02\/pexels-joagbriel-1753922-1536x1152.jpg 1536w, https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2021\/02\/pexels-joagbriel-1753922-2048x1536.jpg 2048w\" sizes=\"(max-width: 2500px) 100vw, 2500px\" \/><\/h2>\n<h2>What happens when you fall off a cliff&#8230;<\/h2>\n<blockquote><p>It\u2019s 2am.\u00a0 Your lead developer\u2019s phone buzzes\u2026again.\u00a0 She groggily gets out of bed and fires up her laptop.\u00a0 It appears your web platform has become unresponsive. Requests to the backend database are all timing out.\u00a0 Customers across the globe are angry.<\/p>\n<p>It appears that the database is over-consuming memory.\u00a0 A workload that, just yesterday, operated smoothly is now consuming too many resources, limiting the database bandwidth, and timing out.\u00a0 Your lead developer opens up a tool that allows her to see\u00a0<a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/relational-databases\/performance\/monitoring-performance-by-using-the-query-store?view=sql-server-ver15#Regressed\" target=\"_blank\" rel=\"noopener noreferrer\">regressed queries<\/a>.\u00a0 Two of them have experienced changes in the way they execute in the past 4 hours, and those two queries are now consuming massively more memory than they once were.\u00a0 This memory consumption is bogging down the system \u2013 not only are the queries much slower, but they are also consuming far more memory and CPU than they had been.\u00a0 This means that the other queries can\u2019t get through.\u00a0 The system is at a complete standstill, even though the database is working overtime.\u00a0 Fortunately, she is able to\u00a0<a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/relational-databases\/performance\/monitoring-performance-by-using-the-query-store?view=sql-server-ver15#force-a-plan-for-a-query-apply-forcing-policy\" target=\"_blank\" rel=\"noopener noreferrer\">request that the future executions<\/a>\u00a0of these two queries go back to the strategy of 6 hours ago, at least until she and the team can do more investigation tomorrow. She trudges back to bed, hoping to get a few more hours of shuteye before her morning commute.<\/p><\/blockquote>\n<p>The scenario above describes what I call \u201cFalling off a cliff\u201d.\u00a0 Your database is doing fine.\u00a0 Great even.\u00a0 Then something changes, and performance, response time, everything, changes in a blink of an eye.\u00a0 All the time, databases are changing \u2013 data is being inserted, loaded, archived, updated\u2026etc.\u00a0 Most often, the number of rows in an active database is steadily increasing.\u00a0 Sometimes, the number of rows passes a threshold \u2013 and the way the queries are executed changes.\u00a0 In the general case, this is good.\u00a0 You want your database to adapt to changing data loads and query loads.\u00a0 Unfortunately for the developer in the story above, the change was not smooth, and did not yield better overall workload performance, in fact, things got very bad very quickly.<\/p>\n<p>In this blog, I will attempt to explain, at a high level, why Query Processing (QP) matters, how it works, what can go wrong when QP makes a mistake, and what exciting features are present in the Azure SQL and SQL Server QP that will improve end-user experience, and ultimately, allow you to use resources smarter.\u00a0 Your developers will be able to get work done faster and with fewer middle of the night emergencies.<\/p>\n<h2 id=\"toc-hId-1938388598\" class=\"lia-align-left\">Why Good QP Matters<\/h2>\n<p class=\"lia-align-left\">Typically, QP is silent and unseen.\u00a0 Not thinking about QP means things are going well.\u00a0 It\u2019s only when you see a sudden degradation in performance, or when queries stop completing, that you must think about QP.\u00a0 While every database requires some planning and setup, once that is complete you want business to move forward without investing substantial time fixing issues or making tweaks.\u00a0 To this goal, having a good quality QP is essential.<\/p>\n<p class=\"lia-align-left\">A good QP:<\/p>\n<ol class=\"lia-align-left\">\n<li>Expands the capability of the database<\/li>\n<li>Uses resources effectively<\/li>\n<li>Reduces manual intervention<\/li>\n<li>Adapts to changing conditions<\/li>\n<\/ol>\n<p class=\"lia-align-left\"><strong>Expands the capability of the database:\u00a0<\/strong>When a query doesn\u2019t undergo a good optimization process, it may never complete (or will take too long to complete to be reasonable), but, with good optimization, the same query can complete quickly.\u00a0 This means that a good optimizer allows queries that may be impossible, or require extensive re-writing, or even need multiple execution steps in an un-optimized environment.<\/p>\n<p class=\"lia-align-left\"><strong>Uses resources effectively:\u00a0<\/strong>When an optimizer is able to grant the correct amount of resources to a query, parallelization can occur.\u00a0 If a query is given too many resources, it could block other queries from executing simultaneously.\u00a0 If given too few, it could execute too slowly.<\/p>\n<p class=\"lia-align-left\"><strong>Reduces manual intervention:\u00a0<\/strong>Oftentimes, when a query runs too slowly (or doesn\u2019t complete), someone need to go do some work, or tweaks, etc. to fix the problem.\u00a0 Not only is manual intervention tedious, it can be error prone and may not adapt well to changing conditions inside the database.\u00a0 Further, sometimes these tweaks can slow down other operations.<\/p>\n<p class=\"lia-align-left\"><strong>Adapts<\/strong><strong>\u00a0to changing conditions:<\/strong>\u00a0As table sizes change, or as plan parameters change, the query plan will often automatically adjust to these new conditions.\u00a0 These changes reflect the most optimal plan for the query given the database as it is now.\u00a0 If QP only used the very first plan it created for a query, it would be blind to the changes in data or plan parameters, and thus may executions would be far from optimal.<\/p>\n<p><figure id=\"attachment_839\" aria-labelledby=\"figcaption_attachment_839\" class=\"wp-caption aligncenter\" ><a href=\"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2021\/01\/planSpacewithPlanLabels.png\"><img decoding=\"async\" class=\" wp-image-839\" src=\"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2021\/01\/planSpacewithPlanLabels-300x211.png\" alt=\"Plan space diagram showing different plans\" width=\"415\" height=\"292\" srcset=\"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2021\/01\/planSpacewithPlanLabels-300x211.png 300w, https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2021\/01\/planSpacewithPlanLabels-768x540.png 768w, https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2021\/01\/planSpacewithPlanLabels.png 861w\" sizes=\"(max-width: 415px) 100vw, 415px\" \/><\/a><figcaption id=\"figcaption_attachment_839\" class=\"wp-caption-text\">Figure 1: Plan space diagram showing different plans<\/figcaption><\/figure><\/p>\n<p>&nbsp;<\/p>\n<p class=\"lia-align-left\">You can see in Figure 1 a diagram called a \u201cplan space\u201d diagram.\u00a0 As the number of rows selected by a query from various tables increases, the plan changes.\u00a0 The diagram in figure 1 is just a cartoon, with the intent of showing what the\u00a0<em>Plan Space<\/em>\u00a0for a given query might be, as the number of rows being processed increases.\u00a0 In this diagram, each colored area represents a different plan for the same query, as the number of rows being processed changes. \u00a0The plan space for this imaginary query and tables contains 7 plans, each of which is ideal based on the number of rows being processed.<\/p>\n<p class=\"lia-align-left\">The general trend with plan spaces is that there are more plans when the number of rows is smaller, and fewer plans when the number of rows gets very large. \u00a0The reason for this trend is that QP has a limited set of tools \u2013 some work better for large data sets, some for smaller.\u00a0 When the number of rows is small, tools can be combined and balanced to create different plans.\u00a0 However, when the data size becomes large enough, the QP chooses all of the best operators for large data.\u00a0 For example, nested loops join is a join technique that is well-suited to smaller data sets.\u00a0 Hash join is preferred for processing larger numbers of rows.\u00a0 It is easy to keep making the data size larger, but once the QP is choosing hash join (and other operational equivalents for large data), the options for further improvement are limited.<\/p>\n<h2 id=\"toc-hId-130934135\" class=\"lia-align-left\">What a good QP does<\/h2>\n<p class=\"lia-align-left\">But how does a good QP even find this \u201cplan space\u201d and execute queries?\u00a0 First, the QP needs to determine\u00a0<em>how<\/em>\u00a0it is going to execute a query, then it needs to\u00a0<em>execute efficiently<\/em>.<\/p>\n<p class=\"lia-align-left\">Upon receipt of a query, the QP first determines\u00a0<em>how<\/em>\u00a0the query will be executed.\u00a0 The result of this step is a query plan.\u00a0 \u00a0A few of the steps involved are:<\/p>\n<p class=\"lia-align-left\"><strong>Reorganizing the query:\u00a0<\/strong>\u00a0Some operators can be moved around to occur at different times during execution for faster results, but with no difference in the result set (semantically equivalent).<\/p>\n<p class=\"lia-align-left\"><strong>Choosing specific execution strategies:\u00a0<\/strong>With knowledge about the various operators and by using statistics about the data, the optimizer can choose, for example, the way a join will be executed, considering memory usage and the estimated size of the data.<\/p>\n<p class=\"lia-align-left\">To do these things, may underlying pieces play a part, the details of which I will skip here.\u00a0 A few examples would be: selecting good indexes to operate on and estimating the data size for each step of execution.\u00a0\u00a0 In Azure SQL and SQL Server, you can optimize a query without executing it.\u00a0 This way you can see the query plan without waiting for execution to complete.\u00a0 Once the QP creates a plan for a query, that plan can be executed multiple times.\u00a0 Since these plans adjust based on the estimated size of the data, it makes sense that there are a number of different plans that could be selected for the same query.<\/p>\n<p class=\"lia-align-left\">Query execution involves manipulating the rows from the source tables through the query and into the result set.\u00a0 Query execution accesses the indexes, performs the joins, filters the rows according to the query plan, and ultimately provides as output the qualifying rows from the query.<\/p>\n<h2 id=\"toc-hId--1676520328\" class=\"lia-align-left\">What can go wrong?<\/h2>\n<p class=\"lia-align-left\">Sometimes, however, query processing makes reasonable, understandable choices that simply don\u2019t work once the query starts to execute.\u00a0 This is typically due to lack of complete information, or due to the necessary assumptions that the must be made: finding a plan works well in the general case, even if it doesn\u2019t work well in a specific case.<\/p>\n<p class=\"lia-align-left\">At the highest level, either query processing has chosen one or more operators incorrectly based on the actual size of the data (plan generation relies on estimates only), or it has requested the wrong amount of memory for the plan to execute with.\u00a0 Both problems come from the generalizations and assumptions that are necessary for planning a query without having executed it<\/p>\n<p><figure id=\"attachment_840\" aria-labelledby=\"figcaption_attachment_840\" class=\"wp-caption aligncenter\" ><a href=\"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2021\/01\/planSpacewithTransitions.png\"><img decoding=\"async\" class=\" wp-image-840\" src=\"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2021\/01\/planSpacewithTransitions-300x172.png\" alt=\"Plan space diagram showing plan transitions\" width=\"399\" height=\"229\" srcset=\"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2021\/01\/planSpacewithTransitions-300x172.png 300w, https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2021\/01\/planSpacewithTransitions-768x440.png 768w, https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2021\/01\/planSpacewithTransitions.png 1012w\" sizes=\"(max-width: 399px) 100vw, 399px\" \/><\/a><figcaption id=\"figcaption_attachment_840\" class=\"wp-caption-text\">Figure 2: Plan space diagram showing transitions between plans<\/figcaption><\/figure><\/p>\n<p class=\"lia-align-left\">Another thing that can go wrong is when plans change gracelessly.\u00a0 As you can see if Figure 2, we should take note of the transition points between plans.\u00a0 In some cases, users experience a performance change at those points.\u00a0 I often refer to it as going off a cliff.\u00a0 You are chugging along with a decent plan, the data size changes, and boom! Now your query takes a lot longer, or a lot more memory to complete.<\/p>\n<h2 id=\"toc-hId-810992505\" class=\"lia-align-left\">Intelligent QP to the Rescue<\/h2>\n<p class=\"lia-align-left\">Azure SQL and SQL Server have released a number of improvements that improve upon this standard workflow in an attempt to lessen or correct common errors.<\/p>\n<p class=\"lia-align-left\">Briefly,\u00a0<a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/relational-databases\/performance\/intelligent-query-processing?view=sql-server-ver15\" target=\"_blank\" rel=\"noopener noreferrer\">Intelligent QP<\/a>\u00a0can:<\/p>\n<ol class=\"lia-align-left\">\n<li>Remember past query execution\u2019s memory consumption and allocate the proper amount in future executions using memory grant feedback<em>\u00a0(<\/em><a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/relational-databases\/performance\/intelligent-query-processing?view=sql-server-ver15#batch-mode-memory-grant-feedback\" target=\"_blank\" rel=\"noopener noreferrer\"><em>1<\/em><\/a><em>,\u00a0<\/em><a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/relational-databases\/performance\/intelligent-query-processing?view=sql-server-ver15#row-mode-memory-grant-feedback\" target=\"_blank\" rel=\"noopener noreferrer\"><em>2<\/em><\/a><em>).<\/em><\/li>\n<li>Adjust on the fly when a join operator turns out to be the wrong one for the job using\u00a0<a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/relational-databases\/performance\/intelligent-query-processing?view=sql-server-ver15#batch-mode-adaptive-joins\" target=\"_blank\" rel=\"noopener noreferrer\"><em>Adaptive Join.<\/em><\/a><\/li>\n<li>Postpone decision making during optimization while it gathers more information by using both\u00a0<a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/relational-databases\/performance\/intelligent-query-processing?view=sql-server-ver15#interleaved-execution-for-mstvfs\" target=\"_blank\" rel=\"noopener noreferrer\"><em>Interleaved execution<\/em><\/a>\u00a0and\u00a0<a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/relational-databases\/performance\/intelligent-query-processing?view=sql-server-ver15#table-variable-deferred-compilation\" target=\"_blank\" rel=\"noopener noreferrer\"><em>Table variable deferred computation<\/em><\/a><em>.<\/em><\/li>\n<li>Invest\u00a0<em>less\u00a0<\/em>time and\u00a0<em>fewer\u00a0<\/em>resources optimizing when the exact answer is not needed with\u00a0<a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/relational-databases\/performance\/intelligent-query-processing?view=sql-server-ver15#approximate-query-processing\" target=\"_blank\" rel=\"noopener noreferrer\"><em>Approximate QP<\/em><\/a><em>.<\/em><\/li>\n<li>Use\u00a0<a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/relational-databases\/user-defined-functions\/scalar-udf-inlining?view=sql-server-ver15\" target=\"_blank\" rel=\"noopener noreferrer\"><em>Scalar UDF inlining<\/em><\/a>\u00a0replace function calls with the body of the function, thus allowing for more optimization.<\/li>\n<li>Execute things in batches, even when data is not compressed, using\u00a0<a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/relational-databases\/performance\/intelligent-query-processing?view=sql-server-ver15#batch-mode-on-rowstore\" target=\"_blank\" rel=\"noopener noreferrer\"><em>Batch mode on row store<\/em><\/a><em>.<\/em><\/li>\n<\/ol>\n<p class=\"lia-align-left\">Another way of looking at these elements of Intelligent QP is that they are designed to:<\/p>\n<ul class=\"lia-align-left\">\n<li><strong>Learn<\/strong> (1) \u2013 Modify behavior based on past performance.<\/li>\n<li><strong>Adapt<\/strong>\u00a0(2, 3) \u2013 Instead of generating a plan and executing it with no changes, these improvements allow for on-the-fly adjustments so that queries execute more smoothly.<\/li>\n<li><strong>Enhance<\/strong>\u00a0<strong>Efficiency<\/strong> (4, 5, 6) \u2013 New capabilities allowing the QP to execute in the most efficient way possible.\u00a0 These new techniques improve the efficiency of plan execution.<\/li>\n<\/ul>\n<p class=\"lia-align-left\">These features of intelligent QP have one goal in mind:\u00a0\u00a0<strong>Better plans, fewer surprises<\/strong>.\u00a0 By learning, adjusting, and adapting to the changing context of the database, intelligent QP means that:<\/p>\n<ul class=\"lia-align-left\">\n<li>DBAs don\u2019t have to spend so much time on manual adjustment and workarounds for poor query performance.<\/li>\n<li>Queries, on average, execute faster.\u00a0 Faster queries save time and machine hours.<\/li>\n<li>A small team can be successful with Azure SQL\u2019s QP without hiring a full time DBA.<\/li>\n<li>Fewer customer incidents when plans go south unexpectedly.<\/li>\n<\/ul>\n<p class=\"lia-align-left\">In this way, Intelligent QP reduces the likelihood of sudden, abrupt plan regressions.\u00a0 With it, you can walk smoothly along the heights without worrying about falling off a cliff.<\/p>\n<hr \/>\n<p>Photo by\u00a0<strong><a href=\"https:\/\/www.pexels.com\/@joagbriel?utm_content=attributionCopyText&amp;utm_medium=referral&amp;utm_source=pexels\">@joagbriel<\/a><\/strong>\u00a0from\u00a0<strong><a href=\"https:\/\/www.pexels.com\/photo\/man-standing-on-cliff-1753922\/?utm_content=attributionCopyText&amp;utm_medium=referral&amp;utm_source=pexels\">Pexels<\/a><\/strong><\/p>\n","protected":false},"excerpt":{"rendered":"<p>An overview of Azure SQL and SQL Server&#8217;s Intelligent Query Processing and how it positively impacts developers.<\/p>\n","protected":false},"author":51469,"featured_media":852,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[488,487],"class_list":["post-838","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-azure-sql","tag-engine","tag-query-processing"],"acf":[],"blog_post_summary":"<p>An overview of Azure SQL and SQL Server&#8217;s Intelligent Query Processing and how it positively impacts developers.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/posts\/838","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/users\/51469"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/comments?post=838"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/posts\/838\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/media\/852"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/media?parent=838"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/categories?post=838"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/tags?post=838"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}