290 91 7MB
English Pages 545 Year 2009
SQL Server 2008 Query Performance Tuning Distilled
Grant Fritchey and Sajal Dam
SQL Server 2008 Query Performance Tuning Distilled Copyright © 2009 by Grant Fritchey and Sajal Dam All rights reserved. No part of this work may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording, or by any information storage or retrieval system, without the prior written permission of the copyright owner and the publisher. ISBN-13 (pbk): 978-1-4302-1902-6 ISBN-13 (electronic): 978-1-4302-1903-3 Printed and bound in the United States of America 9 8 7 6 5 4 3 2 1 Trademarked names may appear in this book. Rather than use a trademark symbol with every occurrence of a trademarked name, we use the names only in an editorial fashion and to the benefit of the trademark owner, with no intention of infringement of the trademark. Lead Editor: Jonathan Gennick Development Editor: Douglas Pundick Technical Reviewer: Joseph Sack Editorial Board: Clay Andres, Steve Anglin, Mark Beckner, Ewan Buckingham, Tony Campbell, Gary Cornell, Jonathan Gennick, Michelle Lowman, Matthew Moodie, Jeffrey Pepper, Frank Pohlmann, Ben Renow-Clarke, Dominic Shakeshaft, Matt Wade, Tom Welsh Project Manager: Richard Dal Porto Copy Editor: Kim Wimpsett Associate Production Director: Kari Brooks-Copony Production Editor: Kelly Winquist Compositor: Patrick Cunningham Proofreader: April Eddy Indexer: John Collin Artist: April Milne Cover Designer: Kurt Krames Manufacturing Director: Tom Debolski Distributed to the book trade worldwide by Springer-Verlag New York, Inc., 233 Spring Street, 6th Floor, New York, NY 10013. Phone 1-800-SPRINGER, fax 201-348-4505, e-mail kn`ano)ju«ÌiÀÊ£]ʺ-+Ê+ÕiÀÞÊ*iÀvÀ>ViÊ/Õ}]»ÊÌÀ`ÕViÃÊÌ
iÊÌiÀ>ÌÛiÊ«ÀViÃÃÊvÊ performance tuning. You’ll get a first glimpse at establishing a performance baseline, identifying bottlenecks, resolving the problems, and quantifying the improvements.
Ê
UÊ
>«ÌiÀÊÓ]ʺ-ÞÃÌiÊ*iÀvÀ>ViÊ>ÞÃÃ]»ÊÃÌ>ÀÌÃÊÞÕÊvvÊÜÌ
ÊÌÀ}ÊÌ
iÊ7`ÜÃÊ system on which SQL Server runs. Performance Monitor and user-defined functions are shown as a mechanism for collecting data.
Ê
UÊ
>«ÌiÀÊÎ]ʺ-+Ê+ÕiÀÞÊ*iÀvÀ>ViÊ>ÞÃÃ]»Ê`iviÃÊÌ
iÊLiÃÌÊÜ>ÞÃÊÌÊʺÕ`iÀÊÌ
iÊ hood” and see what kind of queries are being run on your system. It provides a detailed look at the Profiler and trace tools. Several of the most useful dynamic management views and functions used to monitor queries are first identified in this chapter.
Ê
UÊ
>«ÌiÀÊ{]ʺ`iÝÊ>ÞÃÃ]»ÊiÝ«>ÃÊ`iÝiÃÊ>`Ê`iÝÊ>ÀV
ÌiVÌÕÀi°ÊÌÊ`iviÃÊÌ
iÊ differences between clustered and nonclustered indexes. It shows which types of indexes work best with different types of querying. Basic index maintenance is also introduced.
Ê
UÊ
>«ÌiÀÊx]ʺ >Ì>L>ÃiÊ }iÊ/Õ}Ê`ÛÃÀ]»ÊVÛiÀÃÊÌ
iÊVÀÃvÌÊÌÊ >Ì>L>ÃiÊ Engine Tuning Advisor. The chapter goes over in detail how to use the Database Engine Tuning Advisor; you’re introduced to the various mechanisms for calling the tool and shown how it works under real loads.
Ê
UÊ
>«ÌiÀÊÈ]ʺ >ÀÊÕ«Ê>ÞÃÃ]»ÊÌ>iÃÊÊÌ
iÊV>ÃÃVÊ«iÀvÀ>ViÊ«ÀLi]Ê the key lookup, which is also known as the bookmark lookup. This chapter explores various solutions to the bookmark lookup operation.
Ê
UÊ
>«ÌiÀÊÇ]ʺ-Ì>ÌÃÌVÃÊ>ÞÃÃ]»ÊÌÀ`ÕViÃÊÌ
iÊVVi«ÌÊvÊÃÌ>ÌÃÌVðÊ/
iÊ«ÌâiÀÊÕÃiÃÊ statistics to make decisions regarding the execution of the query. Maintaining statistics, understanding how they’re stored, learning how they work, and learning how they affect your queries are all topics within this chapter.
Ê
UÊ
>«ÌiÀÊn]ʺÀ>}iÌ>ÌÊ>ÞÃÃ]»ÊÃ
ÜÃÊ
ÜÊ`iÝiÃÊvÀ>}iÌÊÛiÀÊÌi°Ê9Õ½Ê learn how to identify when an index is fragmented. You’ll see what happens to your queries as indexes fragment. You’ll learn mechanisms to eliminate index fragmentation.
INTRODUCTION
Ê
UÊ
>«ÌiÀÊ]ʺ ÝiVÕÌÊ*>Ê >V
iÊ>ÞÃÃ]»Ê«ÀiÃiÌÃÊÌ
iÊiV
>ÃÃÊÌ
>ÌÊ-+Ê-iÀÛiÀÊ uses to store execution plans. Plan reuse is an important concept within SQL Server. You’ll learn how to identify whether plans are being reused. You’ll get various mechanisms for looking at the cache. This chapter also introduces new dynamic management views that allow more access to the cache than ever before.
Ê
UÊ
>«ÌiÀÊ£ä]ʺ-ÌÀi`Ê*ÀVi`ÕÀiÊ,iV«>Ì]»Ê`ë>ÞÃÊ
ÜÊ>`ÊÜ
iÊ-+Ê-iÀÛiÀÊ will recompile plans that were stored in cache. You’ll learn how plan recompiles can hurt or help the performance of your system. You’ll pick up mechanisms for forcing a recompile and for preventing one.
Ê
UÊ
>«ÌiÀÊ££]ʺ+ÕiÀÞÊ iÃ}Ê>ÞÃÃ]»ÊÀiÛi>ÃÊ
ÜÊÌÊÜÀÌiʵÕiÀiÃÊÌ
>ÌÊ«iÀvÀÊÜiÊ within your system. Common mistakes are explored, and solutions are provided. You’ll learn several best practices to avoid common bottlenecks.
Ê
UÊ
>«ÌiÀÊ£Ó]ʺ V}Ê>ÞÃÃ]»ÊÌi>V
iÃÊÌ
iÊLiÃÌÊÜ>ÞÃÊÌÊÀiV}âiÊÜ
iÊÛ>ÀÕÃÊ sessions on your server are in contention for resources. You’ll learn how to monitor for blocking along with methods and techniques to avoid blocked sessions.
Ê
UÊ
>«ÌiÀÊ£Î]ʺ i>`VÊ>ÞÃÃ]»ÊÃ
ÜÃÊ
ÜÊ`i>`VÃÊVVÕÀÊÊÞÕÀÊÃÞÃÌi°Ê9Õ½Ê get methods for identifying sessions involved with deadlocks. The chapter also presents best practices for avoiding deadlocks or fixing your code if deadlocks are already occurring.
Ê
UÊ
>«ÌiÀÊ£{]ʺ ÕÀÃÀÊ ÃÌÊ>ÞÃÃ]»Ê`>}À>ÃÊÌ
iÊ
iÀiÌÊVÃÌÃÊÌ
>ÌÊVÕÀÃÀÃÊ«ÀiÃiÌÊ to set-oriented T-SQL code. However, when cursors are unavoidable, you need to understand how they work, what they do, and how best to tune them within your environment if eliminating them outright is not an option.
Ê
UÊ
>«ÌiÀÊ£x]ʺ >Ì>L>ÃiÊ7À>`Ê"«Ìâ>Ì]»Ê`iÃÌÀ>ÌiÃÊ
ÜÊÌÊÌ>iÊÌ
iÊvÀmation presented in all the previous chapters and put it to work on a real database workload. You’ll identify the worst-performing procedures and put them through various tuning methods to arrive at better performance.
Ê
UÊ
>«ÌiÀÊ£È]ʺ-+Ê-iÀÛiÀÊ"«Ìâ>ÌÊ
iVÃÌ]»ÊÃÕ>ÀâiÃÊ>ÊÌ
iÊ«ÀiVi`}ÊV
>«ters into a set of checklists and best practices. The goal of the chapter is to enable you to have a place for quickly reviewing all you have learned from the rest of the book.
Downloading the Code You can download the code examples used in this book from the Source Code section of the Apress website (dppl6++sss*]lnaoo*_ki). Most of the code is straight T-SQL stored in *omh files, which can be opened and used in any SQL Server T-SQL editing tool. There is one PowerShell script that will have to be run through a PowerShell command line.
Contacting the Author You can contact the author, Grant Fritchey, at cn]jpa``ÌVÊ-+ʵÕiÀiÃ
Ê
UÊ >Þâ}Ê>ʵÕiÀÞÊiÝiVÕÌÊ«>
Ê
UÊ Û>Õ>Ì}ÊÌ
iÊivviVÌÛiiÃÃÊvÊÌ
iÊVÕÀÀiÌÊ`iÝiÃ
Ê
UÊ Û`}ÊL>ÀÊÕ«Ã
Ê
UÊ Û>Õ>Ì}ÊÌ
iÊivviVÌÛiiÃÃÊvÊÌ
iÊVÕÀÀiÌÊÃÌ>ÌÃÌVÃ
Ê
UÊ >Þâ}Ê>`ÊÀiÃÛ}ÊvÀ>}iÌ>Ì
Ê
UÊ "«Ìâ}ÊiÝiVÕÌÊ«>ÊV>V
}
Ê
UÊ >Þâ}Ê>`Ê>Û`}ÊÃÌÀi`Ê«ÀVi`ÕÀiÊÀiV«>Ì
Ê
UÊ â}ÊLV}Ê>`Ê`i>`VÃ
Ê
UÊ >Þâ}ÊÌ
iÊivviVÌÛiiÃÃÊvÊVÕÀÃÀÊÕÃi
Ê
UÊ ««Þ}Ê«iÀvÀ>ViÌÕ}Ê«ÀViÃÃiÃ]ÊÌÃ]Ê>`Ê«Ìâ>ÌÊÌiV
µÕiÃÊÌÊ optimize SQL workload
Before jumping straight in to these topics, let’s first examine why we go about performance tuning the way we do. In this chapter, I discuss the basic concepts of performance 1
2
C HAPTER 1
SQ L QU ER Y P ER FOR MA NC E TU NING
tuning for a SQL Server database system. I detail the main performance bottlenecks and show just how important it is to design a database-friendly application, which is the consumer of the data, as well as how to optimize the database. Specifically, I cover the following topics: Ê
UÊ /
iÊ«iÀvÀ>ViÌÕ}Ê«ÀViÃÃ
Ê
UÊ *iÀvÀ>ViÊÛðʫÀVi
Ê
UÊ /
iÊ«iÀvÀ>ViÊL>Ãii
Ê
UÊ 7
iÀiÊÌÊvVÕÃÊivvÀÌÃÊÊÌÕ}
Ê
UÊ /
iÊÌ«Ê££Ê-+Ê-iÀÛiÀÊ«iÀvÀ>ViÊiÀÃ
The Performance-Tuning Process The performance-tuning process consists of identifying performance bottlenecks, troubleshooting their causes, applying different resolutions, and then quantifying performance improvements. It is necessary to be a little creative, since most of the time there is no one silver bullet to improve performance. The challenge is to narrow down the list of possible causes and evaluate the effects of different resolutions. You can even undo modifications as you iterate through the tuning process.
The Core Process During the tuning process, you must examine various hardware and software factors that can affect the performance of a SQL Server–based application. You should be asking yourself the following general questions during the performance analysis: Ê
UÊ ÃÊ>ÞÊÌ
iÀÊÀiÃÕÀViÌiÃÛiÊ>««V>ÌÊÀÕ}ÊÊÌ
iÊÃ>iÊÃiÀÛiÀ¶
Ê
UÊ ÃÊÌ
iÊ
>À`Ü>ÀiÊÃÕLÃÞÃÌiÊV>«>LiÊvÊÜÌ
ÃÌ>`}ÊÌ
iÊ>ÝÕÊÜÀ>`¶
Ê
UÊ ÃÊ-+Ê-iÀÛiÀÊVv}ÕÀi`Ê«À«iÀÞ¶
Ê
UÊ ÃÊÌ
iÊ`>Ì>L>ÃiÊViVÌÊLiÌÜiiÊ-+Ê-iÀÛiÀÊ>`ÊÌ
iÊ`>Ì>L>ÃiÊ>««V>ÌÊivvVi̶
Ê
UÊ iÃÊÌ
iÊ`>Ì>L>ÃiÊ`iÃ}ÊÃÕ««ÀÌÊÌ
iÊv>ÃÌiÃÌÊ`>Ì>ÊÀiÌÀiÛ>Ê>`Ê`vV>ÌÊvÀÊ>Ê Õ«`>Ì>LiÊ`>Ì>L>Ãi®¶
Ê
UÊ ÃÊÌ
iÊÕÃiÀÊÜÀ>`]ÊVÃÃÌ}ÊvÊ-+ʵÕiÀiÃ]Ê«Ìâi`ÊÌÊÀi`ÕViÊÌ
iÊ>`ÊÊ -+Ê-iÀÛiÀ¶
Ê
UÊ 7
>ÌÊ«ÀViÃÃiÃÊ>ÀiÊV>ÕÃ}ÊÌ
iÊÃÞÃÌiÊÌÊÃÜÊ`ÜÊ>ÃÊÀiviVÌi`ÊÊÌ
iÊi>ÃÕÀiiÌÊ vÊÛ>ÀÕÃÊÜ>ÌÊÃÌ>Ìiö
Ê
UÊ iÃÊÌ
iÊÜÀ>`ÊÃÕ««ÀÌÊÌ
iÊ>ÝÕÊVVÕÀÀiVÞ¶
If any of these factors is not configured properly, then the overall system performance may suffer. Let’s briefly examine these factors. Having another resource-intensive application on the same server can limit the resources >Û>>LiÊÌÊ-+Ê-iÀÛiÀ°Ê ÛiÊ>Ê>««V>ÌÊÀÕ}Ê>ÃÊ>ÊÃiÀÛViÊV>ÊVÃÕiÊ>Ê}`Ê«>ÀÌÊvÊ the system resources and limit the resources available to SQL Server. For example, running 7`ÜÃÊ/>ÃÊ>>}iÀÊVÌÕÕÃÞÊÊÌ
iÊÃiÀÛiÀÊÃÊÌÊÀiVi`i`°Ê7`ÜÃÊ/>ÃÊ
CHAPTER 1
SQL QUERY PERFORMANCE TUNING
>>}iÀÊÃÊ>ÃÊ>Ê>««V>Ì]Êp]ogicn*ata, which runs at a higher priority than the SQL Server process. Priority is the weight given to a resource that pushes the processor to give it greater preference when executing. To determine the priority of a process, follow these steps: 1. >ÕV
Ê7`ÜÃÊ/>ÃÊ>>}iÀ° 2. Select View
Select Columns.
3. -iiVÌÊÌ
iÊ >ÃiÊ*ÀÀÌÞÊV
iVÊLÝ° 4. Click the OK button. These steps will add the >]oaLneknepu column to the list of processes. Subsequently, ÞÕÊÜÊLiÊ>LiÊÌÊ`iÌiÀiÊÌ
>ÌÊÌ
iÊ-+Ê-iÀÛiÀÊ«ÀViÃÃÊomhoanrn*ata) by default runs at À>Ê«ÀÀÌÞ]ÊÜ
iÀi>ÃÊÌ
iÊ7`ÜÃÊ/>ÃÊ>>}iÀÊ«ÀViÃÃÊp]ogicn*ata) runs at High priority. Therefore, to allow SQL Server to maximize the use of available resources, you should look for all the nonessential applications/services running on the SQL Server machine and ensure that they are not acting as resource hogs. Improperly configuring the hardware can prevent SQL Server from gaining the maximum benefit from the available resources. The main hardware resources to be considered are processor, memory, disk, and network. For example, in a server with more than 4GB of memory, an improper memory configuration will prevent SQL Server from using the memory beyond 4GB. Furthermore, if the capacity of a particular resource is small, then it can soon become a performance bottleneck for SQL Server. Chapter 2 covers these hardware bottlenecks in detail. You should also look at the configuration of SQL Server, since proper configuration is essential for an optimized application. There is a long list of SQL Server configurations that define the generic behavior of a SQL Server installation. These configurations can be viewed and modified using a system stored procedure, ol[_kjbecqna°Ê>ÞÊvÊÌ
iÃiÊVv}ÕÀ>ÌÃÊ V>ÊLiÊ>>}i`ÊÌiÀ>VÌÛiÞÊÌ
ÀÕ}
Ê-+Ê-iÀÛiÀÊ ÌiÀ«ÀÃiÊ>>}iÀ° Since the SQL Server configurations are applicable for the complete SQL Server installation, a standard configuration is usually preferred. The good news is that, generally, you need not modify these configurations; the default settings work best for most situations. In fact, the general recommendation is to keep the SQL Server configurations at the default values. I discuss the configuration parameters in detail throughout the book. *ÀÊViVÌÛÌÞÊLiÌÜii SQL Server and the database application can hurt application performance. One of the questions you should ask yourself is, How good is the database coniV̶ÊÀÊiÝ>«i]ÊÌ
iʵÕiÀÞÊiÝiVÕÌi`ÊLÞÊÌ
iÊ>««V>ÌÊ>ÞÊLiÊ
}
ÞÊ«Ìâi`]ÊLÕÌÊÌ
iÊ database connection used to submit this query may add considerable overhead to the query performance. Based on the distribution of the application and the database, different network protocols should be used to reduce the network overhead. Additionally, the data access layer used to manage the database connectivity above the network connection may not be efficient. The data access layer technology or the way the data access layer is used by the application may not be optimal. The design of the database should also be analyzed while troubleshooting performance. This helps you understand not only the entity-relationship model of the database but also why a query may be written in a certain way. Although it may not always be possible to modify a database design because of wider implications on the database application, a good understanding of the database design helps you focus in the right direction and understand the impact of a resolution. This is especially true of the primary and foreign keys and the clustered indexes used in the tables.
3
4
C HAPTER 1
SQ L QU ER Y P ER FOR MA NC E TU NING
The application may be slow because of poorly built queries, the queries might not be able to use the indexes, or perhaps even the indexes themselves are incorrect or missing. If any of the queries are not optimized sufficiently, they can seriously impact other queries’ performance. I cover index optimization in depth in Chapters 3, 4, 5, and 6. The next question at this stage should be, Is a query slow because of its resource intensiveness or because of concurrency ÃÃÕiÃÊÜÌ
ÊÌ
iÀʵÕiÀiöÊ9ÕÊV>Êv`Ê`i«Ì
ÊvÀ>ÌÊÊLV}Ê>>ÞÃÃÊÊ
>«ÌiÀÊ£Ó° 7
iÊ«ÀViÃÃiÃÊÀÕÊÊ>ÊÃiÀÛiÀ]ÊiÛiÊiÊÜÌ
ÊÕÌ«iÊ«ÀViÃÃÀÃ]Ê>ÌÊÌiÃÊiÊ«ÀViÃÃÊ will be waiting on another to complete. You can get a fundamental understanding of the root cause of slowdowns by identifying what is waiting and what is causing it to wait. You can realize this through operating system counters that you access through dynamic management ÛiÜÃÊÜÌ
Ê-+Ê-iÀÛiÀ°ÊÊVÛiÀÊÌ
ÃÊvÀ>ÌÊÊ
>«ÌiÀÊÓÊ>`ÊÊ
>«ÌiÀÊ£Ó° The challenge is to find out which factor is causing the performance bottleneck. For example, with slow-running SQL queries and high pressure on the hardware resources, you may find that both poor database design and a nonoptimized workload are to blame. In such a case, you must diagnose the symptoms further and correlate the findings with possible causes. Because performance tuning can be time consuming and tiresome, you should ideally take a preventive approach by designing the system for optimum performance from the outset. To strengthen the preventive approach, every lesson that you learn during the optimization of poor performance should be considered an optimization guideline when implementing new database applications. There are also proven best practices that you should consider while implementing database applications. I present these best practices in detail throughout Ì
iÊL]Ê>`Ê
>«ÌiÀÊ£nÊÃÊ`i`V>Ìi`ÊÌÊÕÌ}Ê>ÞÊvÊÌ
iÊ«Ìâ>ÌÊLiÃÌÊ«À>VÌVið *i>ÃiÊiÃÕÀiÊÌ
>ÌÊÞÕÊÌ>iÊÌ
iÊ«iÀvÀ>ViÊ«Ìâ>ÌÊÌiV
µÕiÃÊÌÊVÃ`iÀ>ÌÊ at the early stages of your database application development. Doing so will help you roll out your database projects without big surprises later. Unfortunately, we rarely live up to this ideal and often find database applications needing performance tuning. Therefore, it is important to understand not only how to improve the performance of a SQL Server–based application but also how to diagnose the causes of poor performance.
Iterating the Process *iÀvÀ>ViÊÌÕ}ÊÃÊ>Êiterative process, where you identify major bottlenecks, attempt to resolve them, measure the impact of your changes, and return to the first step until pervÀ>ViÊÃÊ>VVi«Ì>Li°Ê7
iÊ>««Þ}ÊÞÕÀÊÃÕÌÃ]ÊÞÕÊÃ
Õ`ÊvÜÊÌ
iÊ}`iÊÀÕiÊvÊ making only one change at a time. Any change usually affects other parts of the system, so you must reevaluate the effect of each change on the performance of the overall system. As an example, adding an index may fix the performance of a specific query, but it could cause other queries to run more slowly, as explained in Chapter 4. Consequently, it is preferable to conduct a performance analysis in a test environment to shield users from your diagnosis attempts and intermediate optimization steps. In such a case, evaluating one change at a time also helps in prioritizing the implementation order of the changes on the production server, based on their relative contributions. You can keep on chipping away at performance bottlenecks and improving the system performance gradually. Initially, you will be able to resolve big performance bottlenecks and achieve significant performance improvements, but as you proceed through the iterations, your returns will gradually diminish. Therefore, to use your time efficiently, it is worthwhile to quanÌvÞÊÌ
iÊ«iÀvÀ>ViÊLiVÌÛiÃÊvÀÃÌÊvÀÊiÝ>«i]Ê>ÊnäÊ«iÀViÌÊÀi`ÕVÌÊÊÌ
iÊÌiÊÌ>iÊvÀÊ a certain query, with no adverse effect anywhere else on the server) and then work toward them.
CHAPTER 1
SQL QUERY PERFORMANCE TUNING
The performance of a SQL Server application is highly dependent on the amount and `ÃÌÀLÕÌÊvÊÕÃiÀÊ>VÌÛÌÞÊÀÊÜÀ>`®Ê>`Ê`>Ì>°Ê Ì
ÊÌ
iÊ>ÕÌÊ>`Ê`ÃÌÀLÕÌÊvÊ workload and data change over time, and differing data can cause SQL Server to execute SQL queries differently. The performance resolution applicable for a certain workload and data may lose its effect over a period of time. Therefore, to ensure an optimum system performance Ê>ÊVÌÕ}ÊL>ÃÃ]ÊÞÕÊÜÊii`ÊÌÊ>>ÞâiÊ«iÀvÀ>ViÊ>ÌÊÀi}Õ>ÀÊÌiÀÛ>ðÊ*iÀvÀ>ViÊ ÌÕ}ÊÃÊ>ÊiÛiÀi`}Ê«ÀViÃÃ]Ê>ÃÊÃ
ÜÊÊ}ÕÀiÊ££°
Figure 1-1. Performance-tuning process
5
6
C HAPTER 1
SQ L QU ER Y P ER FOR MA NC E TU NING
You can see that the steps to optimize the costliest query make for a complex process, which also requires multiple iterations to troubleshoot the performance issues within the µÕiÀÞÊ>`Ê>««ÞÊiÊV
>}iÊ>ÌÊ>ÊÌi°Ê}ÕÀiÊ£ÓÊÃ
ÜÃÊÌ
iÊÃÌi«ÃÊÛÛi`ÊÊÌ
iÊoptimization of the costliest query.
Figure 1-2. Optimization of the costliest query
CHAPTER 1
SQL QUERY PERFORMANCE TUNING
As you can see from this process, there is quite a lot to do to ensure that you correctly tune the performance of a given query. It is important to use a solid process like this in performance tuning to focus on the main identified issues. Having said this, it also helps to keep a broader perspective about the problem as a whole, since sometimes you may believe that you are trying to solve the correct performance bottleneck when in reality something else is causing the problem.
Performance vs. Price One of the points I touched on earlier is that to gain increasingly small performance increments, you need to spend increasingly large amounts of time and money. Therefore, to ensure the best return on your investment, you should be very objective while optimizing performance. Always consider the following two aspects: Ê
UÊ 7
>ÌÊÃÊÌ
iÊ>VVi«Ì>LiÊ«iÀvÀ>ViÊvÀÊÞÕÀÊ>««V>̶
Ê
UÊ ÃÊÌ
iÊÛiÃÌiÌÊÜÀÌ
ÊÌ
iÊ«iÀvÀ>ViÊ}>¶
Performance Targets To derive maximum efficiency, you must realistically estimate your performance requirements. You can follow many best practices to improve performance—for example, you can have your database files on the most efficient disk subsystem. However, before applying a best practice, you should consider how much you may gain from it and whether the gain will be worth the investment. Sometimes it is really difficult to estimate the performance gain without actually making the enhancement. That makes properly identifying the source of your performance bottleiVÃÊiÛiÊÀiÊ«ÀÌ>Ì°ÊÀiÊÞÕÊ *1]ÊiÀÞ]ÊÀÊ`ÃÊLÕ`¶ÊÃÊÌ
iÊV>ÕÃiÊV`i]Ê`>Ì>Ê ÃÌÀÕVÌÕÀi]ÊÀÊ`iÝ}]ÊÀÊ>ÀiÊÞÕÊëÞÊ>ÌÊÌ
iÊÌÊvÊÞÕÀÊ
>À`Ü>Ài¶Ê iÊÃÕÀiÊÞÕÊV>Ê>iÊ these possibly costly decisions from a known point rather than guessing. A practical approach can be to increase a resource in increments and analyze the application’s scalability with the added resource. A scalable application will proportionately benefit from an incremental increase of the resource, if the resource was truly causing the scalability bottleneck. If the ÀiÃÕÌÃÊ>««i>ÀÊÌÊLiÊÃ>ÌÃv>VÌÀÞ]ÊÌ
iÊÞÕÊV>ÊVÌÊÌÊÌ
iÊvÕÊi
>ViiÌ°Ê Ý«iÀiViÊ also plays a very important role here.
“Good Enough” Tuning Instead of tuning a system to the theoretical maximum performance, the goal should be to tune until the system performance is “good enough.” This is a commonly adopted performancetuning approach. The cost investment after such a point usually increases exponentially in V«>ÀÃÊÌÊÌ
iÊ«iÀvÀ>ViÊ}>°Ê/
iÊnä\ÓäÊÀÕiÊÜÀÃÊÛiÀÞÊÜi\ÊLÞÊÛiÃÌ}ÊÓäÊ«iÀViÌÊ vÊÞÕÀÊÀiÃÕÀViÃ]ÊÞÕÊ>ÞÊ}iÌÊnäÊ«iÀViÌÊvÊÌ
iÊ«ÃÃLiÊ«iÀvÀ>ViÊi
>ViiÌ]ÊLÕÌÊvÀÊ Ì
iÊÀi>}ÊÓäÊ«iÀViÌÊ«ÃÃLiÊ«iÀvÀ>ViÊ}>]ÊÞÕÊ>ÞÊ
>ÛiÊÌÊÛiÃÌÊ>Ê>``Ì>ÊnäÊ percent of resources. It is therefore important to be realistic when setting your performance objectives. A business benefits not by considering pure performance but by considering price pervÀ>Vi°ÊÜiÛiÀ]ÊvÊÌ
iÊÌ>À}iÌÊÃÊÌÊv`ÊÌ
iÊÃV>>LÌÞÊÌÊvÊÞÕÀÊ>««V>ÌÊvÀÊÛ>ÀÕÃÊ
7
8
C HAPTER 1
SQ L QU ER Y P ER FOR MA NC E TU NING
reasons, including marketing the product against its competitors), then it may be worthwhile ÛiÃÌ}Ê>ÃÊÕV
Ê>ÃÊÞÕÊV>°Ê ÛiÊÊÃÕV
ÊV>ÃiÃ]ÊÕÃ}Ê>ÊÌ
À`«>ÀÌÞÊÃÌÀiÃÃÊÌiÃÌÊ>LÊ>ÞÊLi a better investment decision.
Performance Baseline One of the main objectives of performance analysis is to understand the underlying level of system use or pressure on different hardware and software subsystems. This knowledge helps you in the following ways: Ê
UÊ ÜÃÊÞÕÊÌÊ>>ÞâiÊÀiÃÕÀViÊLÌÌiiVð
Ê
UÊ >LiÃÊÞÕÊÌÊÌÀÕLiÃ
ÌÊLÞÊV«>À}ÊÃÞÃÌiÊÕÌâ>ÌÊ«>ÌÌiÀÃÊÜÌ
Ê>Ê preestablished baseline.
Ê
UÊ ÃÃÃÌÃÊÞÕÊÊ>}Ê>VVÕÀ>ÌiÊiÃÌ>ÌiÃÊÊV>«>VÌÞÊ«>}Ê>`ÊÃV
i`Õ}Ê hardware upgrades.
Ê
UÊ `ÃÊÞÕÊÊ`iÌvÞ}ÊÜÕÌâ>ÌÊ«iÀ`ÃÊÜ
iÊÌ
iÊ`>Ì>L>ÃiÊ>`ÃÌÀ>ÌÛiÊ activities can be executed.
Ê
UÊ i«ÃÊÞÕÊiÃÌ>ÌiÊÌ
iÊ>ÌÕÀiÊvÊ«ÃÃLiÊ
>À`Ü>ÀiÊ`ÜÃâ}ÊÀÊÃiÀÛiÀÊVÃ`>Ì°Ê7
ÞÊÜÕ`Ê>ÊV«>ÞÊ`ÜÃâi¶Ê7i]ÊÊÌ
iÊ«>ÃÌ]ÊÃiÊV«>iÃÊi>Ãi`ÊÛiÀÞÊ high-end systems expecting strong growth, but because of poor growth, they are now vÀVi`ÊÌÊ`ÜÃâiÊÌ
iÀÊÃÞÃÌiÊÃiÌիðÊ`ÊVÃ`>Ì¶Ê «>iÃÊ>ÞÊÃitimes buy too many servers or realize that the maintenance and licensing costs are too high. This would make using fewer servers very attractive.
Therefore, to better understand your application’s resource requirements, you should create a baseline for your application’s hardware and software usage. A baseline serves as a statistic of your system’s current usage pattern and as a reference with which to compare future statistics. Baseline analysis helps you understand your application’s behavior during a stable period, how hardware resources are used during such periods, and what the software V
>À>VÌiÀÃÌVÃÊ>Ài°Ê7Ì
Ê>ÊL>ÃiiÊÊ«>Vi]ÊÞÕÊV>Ê`ÊÌ
iÊvÜ}\ Ê
UÊ i>ÃÕÀiÊVÕÀÀiÌÊ«iÀvÀ>ViÊ>`ÊiÝ«ÀiÃÃÊÞÕÀÊ>««V>̽ÃÊ«iÀvÀ>ViÊ}>ð
Ê
UÊ «>ÀiÊÌ
iÀÊ
>À`Ü>ÀiÊÀÊÃvÌÜ>ÀiÊVL>ÌÃÊ>}>ÃÌÊÌ
iÊL>Ãii°
Ê
UÊ i>ÃÕÀiÊ
ÜÊÌ
iÊÜÀ>`Ê>`ÉÀÊ`>Ì>ÊV
>}iÃÊÛiÀÊÌi°
Ê
UÊ Û>Õ>ÌiÊÌ
iÊ«i>Ê>`Ê«i>ÊÕÃ>}iÊ«>ÌÌiÀÊvÊÌ
iÊ>««V>Ì°Ê/
ÃÊvÀ>ÌÊ can be used to effectively distribute database administration activities, such as full database backup and database defragmentation during nonpeak hours.
You can use the -ÞÃÌiÊÌÀÊÌÊ>ÃÊÀiviÀÀi`ÊÌÊ>ÃÊ*iÀvÀ>ViÊÌÀ®ÊÌÊVÀi>ÌiÊ a baseline for SQL Server’s hardware and software resource utilization. Similarly, you may baseline the SQL Server workload using the -+Ê*ÀviÀÊÌ]ÊÜ
V
ÊV>Ê
i«ÊÞÕÊÕ`iÀÃÌ>`Ê the average resource utilization and execution time of SQL queries when conditions are stable. You can also get snapshots of this information by using dynamic management views and dynamic management functions. You will learn in detail how to use these tools and queries in Chapters 2 and 3.
CHAPTER 1
SQL QUERY PERFORMANCE TUNING
One other option is to take advantage of one of the many tools that can generate an artificial load on a given server or database. Numerous third-party tools are available. VÀÃvÌÊvviÀÃÊ-+"Ê>Û>>LiÊ>ÌÊdppl6++sss*ie_nkokbp*_ki+`ksjhk]`o+`ap]eho* ]olt;b]iehue`95]4^,,1^)40a0)0b.0)4`21)_^1/00.`5a-5"`eolh]uh]jc9aj), which measures Ì
iÊÉ"ÊV>«>VÌÞÊvÊÞÕÀÊÃÞÃÌi°ÊVÀÃvÌÊ>ÃÊ
>ÃÊ-+"-]Ê>ÊÌÊvÀÊ}iiÀ>Ì}Ê-+Ê -iÀÛiÀqëiVvVÊV>ÃÊ>`ÊÃÕ>Ìi`Ê>`ÃÊ>Û>>LiÊ>ÌÊdppl6++`ksjhk]`*ie_nkokbp*_ki+ `ksjhk]`+/+4+,+/4,0_^-_)]5--)0`-.)41.1)a134,-53a,^1+OMHEKOeiT42*ata®°Ê>ÞÊÌ
À`«>ÀÌÞÊ tools are available that can also help with this. If your system is not yet in production, using one of these tools to simulate a load to test the system is a very good idea.
Where to Focus Efforts 7
iÊÞÕÊÌÕi a particular system, pay special attention to the >««V>ÌÊ>ÞiÀÊÌ
iÊ`>Ì>base queries and stored procedures executed by Visual Basic/ADO or otherwise that are used to access the database). You will usually find that you can positively affect performance in the application layer far more than if you spend an equal amount of time figuring out how to tune the hardware, operating system, or SQL Server configuration. Although a proper configuration of hardware, operating system, and SQL Server is essential for the best performance of a database application, these fields have standardized so much that you usually need to spend only a limited amount of time configuring them properly for performance. Application design issues such as query design and indexing strategies, on the other hand, are application dependent. Consequently, there is usually more to optimize in the application layer than in the hardware, operating system, or SQL Server configuration. Thus, for a unit of time spent in each area, work in the application layer usually yields the maximum performance benefit, as ÕÃÌÀ>Ìi`ÊÊ}ÕÀiʣΰ
Figure 1-3. Time spent vs. performance gain
9
10
C HAPTER 1
SQ L QU ER Y P ER FOR MA NC E TU NING
In my experience, you can obtain the greatest improvement in database application performance by looking first at the area of application design, including logical/physical database design, query design, and index design. Sure, if you concentrate on hardware configuration and upgrades, you may obtain a satisfactory performance gain. However, a bad SQL query sent by the application can consume all the hardware resources available, no matter how much you have. Therefore, a poor application design can make the hardware upgrade requirements very high, even beyond your limits. In the presence of a heavy SQL workload, concentrating on hardware configurations and upgrades usually produces a poor return on investment. You should analyze the stress created by an application on a SQL Server database at two levels: Ê
UÊ High level: Analyze how much stress the database application is creating on individual hardware resources and what the overall behavior of the SQL Server installation is. The best measures for this are the various wait states. This information can help you in two ways. First, it helps you identify the area to concentrate on within a SQL Server application where there is poor performance. Second, it helps you identify any lack of proper configuration at the higher levels. You can then decide which hardware resource may LiÊÕ«}À>`i`ÊvÊÞÕÊ>ÀiÊÌÊ>LiÊÌÊÌÕiÊÌ
iÊ>««V>ÌÊÕÃ}ÊÌ
iÊ*iÀvÀ>ViÊÌÀÊ tool, as explained in Chapter 2.
Ê
UÊ Low level: Identify the exact culprits within the application—in other words, the SQL queries that are creating most of the pressure visible at the overall higher level. This V>ÊLiÊ`iÊÕÃ}ÊÌ
iÊ-+Ê*ÀviÀÊÌÊ>` various dynamic management views, as explained in Chapter 3.
SQL Server Performance Killers Let’s now consider the major problem areas that can degrade SQL Server performance. By being aware of the main performance killers in SQL Server in advance, you will be able to focus your tuning efforts on the likely causes. Once you have optimized the hardware, operating system, and SQL Server settings, the >Ê«iÀvÀ>ViÊiÀÃÊÊ-+Ê-iÀÛiÀÊ>ÀiÊ>ÃÊvÜÃ]ÊÊ>ÊÀÕ}
ÊÀ`iÀÊÜÌ
ÊÌ
iÊÜÀÃÌÊ>««i>Àing first): Ê
UÊ *ÀÊ`iÝ}
Ê
UÊ >VVÕÀ>ÌiÊÃÌ>ÌÃÌVÃ
Ê
UÊ ÝViÃÃÛiÊLV}Ê>`Ê`i>`VÃ
Ê
UÊ
Ê
UÊ *ÀʵÕiÀÞÊ`iÃ}
Ê
UÊ *ÀÊ`>Ì>L>ÃiÊ`iÃ}
Ê
UÊ ÝViÃÃÛiÊvÀ>}iÌ>Ì
Ê
UÊ
Ê
UÊ *ÀÊiÝiVÕÌÊ«>Ã]ÊÕÃÕ>ÞÊV>ÕÃi`ÊLÞÊ«>À>iÌiÀÊÃvv}
ÃiÌL>Ãi`Ê«iÀ>ÌÃ]ÊÕÃÕ>ÞÊ/-+ÊVÕÀÃÀÃ
ÀiÕÃ>LiÊiÝiVÕÌÊ«>Ã
CHAPTER 1
Ê
UÊ ÀiµÕiÌÊÀiV«>ÌÊvÊiÝiVÕÌÊ«>Ã
Ê
UÊ «À«iÀÊÕÃiÊvÊVÕÀÃÀÃ
Ê
UÊ «À«iÀÊVv}ÕÀ>ÌÊvÊÌ
iÊ`>Ì>L>ÃiÊ}
Ê
UÊ ÝViÃÃÛiÊÕÃiÊÀÊ«À«iÀÊVv}ÕÀ>ÌÊvÊpail`^
SQL QUERY PERFORMANCE TUNING
Let’s take a quick look at each of these, before considering them in more depth in later chapters.
Poor Indexing *ÀÊ`iÝ}ÊÃ usually one of the biggest performance killers in SQL Server. In the absence of proper indexing for a query, SQL Server has to retrieve and process much more data while iÝiVÕÌ}ÊÌ
iʵÕiÀÞ°Ê/
ÃÊV>ÕÃiÃÊ
}
Ê>ÕÌÃÊvÊÃÌÀiÃÃÊÊÌ
iÊ`Ã]ÊiÀÞ]Ê>`Ê *1]Ê increasing the query execution time significantly. Increased query execution time then leads to excessive blocking and deadlocks in SQL Server. You will learn how to determine the indexing strategies and resolve indexing problems in Chapters 4, 5, and 6. Generally, indexes are considered to be the responsibility of the database administrator ®°ÊÜiÛiÀ]ÊÌ
iÊ ÊV>ÌÊ`iviÊ
ÜÊÌÊÕÃiÊÌ
iÊ`iÝiÃ]ÊÃViÊÌ
iÊÕÃiÊvÊ`iÝiÃÊÃÊ determined by the database queries and stored procedures written by the developers. Therefore, defining the indexes must be a shared responsibility since the developers usually have more knowledge of the data to be retrieved and the DBAs have a better understanding of how indexes work. Indexes created without the knowledge of the queries serve little purpose.
Note Because indexes created without the knowledge of the queries serve little purpose, database developers need to understand indexes at least as well as they know T-SQL.
Inaccurate Statistics SQL Server relies heavily on cost-based optimization, so accurate data-distribution statisÌVÃÊ>ÀiÊiÝÌÀiiÞÊ«ÀÌ>ÌÊvÀÊÌ
iÊivviVÌÛiÊÕÃiÊvÊ`iÝiðÊ7Ì
ÕÌÊ>VVÕÀ>ÌiÊÃÌ>ÌÃÌVÃ]Ê-+Ê Server’s built-in query optimizer cannot accurately estimate the number of rows affected by a query. Because the amount of data to be retrieved from a table is highly important in deciding how to optimize the query execution, the query optimizer is much less effective if the data distribution statistics are not maintained accurately. You will look at how to analyze statistics in Chapter 7.
Excessive Blocking and Deadlocks BecauseÊ-+Ê-iÀÛiÀÊÃÊvÕÞÊ>ÌVÌÞ]ÊVÃÃÌiVÞ]ÊÃ>Ì]Ê>`Ê`ÕÀ>LÌÞÊ ®ÊV«>Ì]Ê the database engine ensures that modifications made by concurrent transactions are properly isolated from one another. By default, a transaction sees the data either in the state before another concurrent transaction modified the data or after the other transaction completed—it does not see an intermediate state.
11
12
C HAPTER 1
SQ L QU ER Y P ER FOR MA NC E TU NING
Because of this isolation, when multiple transactions try to access a common resource concurrently in a noncompatible way, blocking occurs in the database. A deadlock occurs when two resources attempt to escalate or expand locked resources and conflict with one another. The query engine determines which process is the least costly to roll back and chooses it as the deadlock victim. This requires that the database request be resubmitted for successful execution. The execution time of a query is adversely affected by the amount of blocking and deadlock it faces. For scalable performance of a multiuser database application, properly controlling the isolation levels and transaction scopes of the queries to minimize blocking and deadlock is critical; otherwise, the execution time of the queries will increase significantly, even though the hardware resources may be highly underutilized. I cover this problem in depth in
>«ÌiÀÃÊ£ÓÊ>`ʣΰ
Non-Set-Based Operations Transact-SQL is a set-based scripting language, which means it operates on sets of data. This forces you to think in terms of columns rather than in terms of rows. Non-set-based thinking leads to excessive use of cursors and loops rather than exploring more efficient joins and subqueries. The T-SQL language offers rich mechanisms for manipulating sets of data. For performance to shine, you need to take advantage of these mechanisms rather than trying to vÀViÊ>ÊÀÜLÞÀÜÊ>««À>V
ÊÌÊÞÕÀÊV`i]ÊÜ
V
ÊÜÊÊ«iÀvÀ>Vi°Ê Ý>«iÃÊvÊ
ÜÊÌÊ`Ê Ì
ÃÊ>ÀiÊ>Û>>LiÊÌ
ÀÕ}
ÕÌÊÌ
iÊLÆÊ>Ã]ÊÊ>``ÀiÃÃÊ/-+ÊLiÃÌÊ«À>VÌViÃÊÊ
>«ÌiÀÊ££Ê>`Ê VÕÀÃÀÃÊÊ
>«ÌiÀÊ£{°
Poor Query Design The effectiveness of indexes depends entirely on the way you write SQL queries. Retrieving excessively large numbers of rows from a table, or specifying a filter criterion that returns a larger result set from a table than is required, renders the indexes ineffective. To improve performance, you must ensure that the SQL queries are written to make the best use of new or existing indexes. Failing to write cost-effective SQL queries may prevent SQL Server from choosing proper indexes, which increases query execution time and database blocking.
>«ÌiÀÊ££ÊVÛiÀÃÊ
ÜÊÌÊÜÀÌiÊivviVÌÛiʵÕiÀið Query design covers not only single queries but also sets of queries often used to implement database functionalities such as a queue management among queue readers and writers.
ÛiÊÜ
iÊÌ
iÊ«iÀvÀ>ViÊvÊ`Û`Õ>ʵÕiÀiÃÊÕÃi`ÊÊÌ
iÊ`iÃ}ÊÃÊvi]ÊÌ
iÊÛiÀ>Ê«iÀformance of the database can be very poor. Resolving this kind of bottleneck requires a broad understanding of different characteristics of SQL Server, which can affect the performance of database functionalities. You will see how to design effective database functionality using SQL queries throughout the book.
Poor Database Design A database should be adequately normalized to increase the performance of data retrieval and reduce blocking. For example, if you have an undernormalized database with customer and order information in the same table, then the customer information will be repeated in all the order rows of the customer. This repetition of information in every row will increase the I/Os required to fetch all the orders placed by a customer. At the same time, a data writer working
CHAPTER 1
SQL QUERY PERFORMANCE TUNING
on a customer’s order will reserve all the rows that include the customer information and thus block all other data writers/data readers trying to access the customer profile. Overnormalization of a database is as bad as undernormalization. Overnormalization increases the number and complexity of joins required to retrieve data. An overnormalized database contains a large number of tables with a very small number of columns. Having too many joins in a query may also be because database entities have not been partitioned very distinctly or the query is serving a very complex set of requirements that could perhaps be better served by creating a new view or stored procedure. >Ì>L>ÃiÊ`iÃ}ÊÃÊ>Ê>À}iÊÃÕLiVÌ°ÊÊÜÊ«ÀÛ`iÊ>ÊviÜÊ«ÌiÀÃÊÊ
>«ÌiÀÊ£nÊ>`ÊÌ
ÀÕ}
out the rest of the book. Because of the size of the topic, I won’t be able to treat it in the complete manner it requires. However, if you want to read a book on database design, with an emphasis on introducing the subject, I recommend reading Data Modeling for Everyone by -
>ÀÊiÊ«ÀiÃÃ]ÊÓääή°
Excessive Fragmentation 7
iÊ>>Þâ}Ê`>Ì> retrieval operations, you can usually assume that the data is organized in an orderly way, as indicated by the index used by the data retrieval operation. However, if the pages containing the data are fragmented in a nonorderly fashion or if they contain a small amount of data because of frequent page splits, then the number of read operations required by the data retrieval operation will be much higher than might otherwise be required. The increase in the number of read operations caused by fragmentation hurts query performance. Ê
>«ÌiÀÊn]ÊÞÕÊÜÊi>ÀÊ
ÜÊÌÊ>>ÞâiÊ>`ÊÀiÛiÊvÀ>}iÌ>Ì°
Nonreusable Execution Plans To execute a query in an efficient way, SQL Server’s query optimizer spends a fair amount of
*1ÊVÞViÃÊVÀi>Ì}Ê>ÊVÃÌivviVÌÛiÊiÝiVÕÌÊ«>°Ê/
iÊ}`ÊiÜÃÊÃÊÌ
>ÌÊÌ
iÊ«>ÊÃÊV>V
i`ÊÊ memory, so you can reuse it once created. However, if the plan is designed so that you cannot plug variable values into it, SQL Server creates a new execution plan every time the same query is resubmitted with different variable values. So, for better performance, it is extremely important to submit SQL queries in forms that help SQL Server cache and reuse the execution plans. I will also address topics such as plan freezing, forcing query plans, and the problems associated with parameter sniffing. You will see in detail how to improve the reusability of execution plans in Chapter 9.
Poor Execution Plans The same mechanisms that allow SQL Server to establish an efficient stored procedure and reuse that procedure again and again instead of recompiling can, in some cases, work against you. A bad execution plan can be a real performance killer. Bad plans are frequently caused by a process called parameter sniffing, which comes from the mechanisms that the query optimizer uses to determine the best plan based on statistics. It’s important to understand how statistics and parameters combine to create execution plans and what you can do to control them. I cover statistics in depth in Chapter 7 and execution plan analysis in Chapter 9.
13
14
C HAPTER 1
SQ L QU ER Y P ER FOR MA NC E TU NING
Frequent Recompilation of Execution Plans One of the standard ways of ensuring a reusable execution plan, independent of variable values used in a query, is to use a stored procedure. Using a stored procedure to execute a set of SQL queries allows SQL Server to create a parameterized execution plan. A parameterized execution plan is independent of the parameter values supplied during the execution of the stored procedure, and it is consequently highly reusable. However, the execution plan of the stored procedure can be reused only if SQL Server does not have to recompile the execution plan every time the stored procedure is run. Frequent recompilation vÊ>ÊÃÌÀi`Ê«ÀVi`ÕÀiÊVÀi>ÃiÃÊ«ÀiÃÃÕÀiÊÊÌ
iÊ *1Ê>`ÊÌ
iʵÕiÀÞÊiÝiVÕÌÊÌi°Ê-+Ê-iÀÛiÀÊ has partially addressed this problem with the addition of statement-level recompilation, but it’s not a complete fix to the issue. I will discuss in detail the various causes and resolutions of ÃÌÀi`Ê«ÀVi`ÕÀi]Ê>`ÊÃÌ>ÌiiÌ]ÊÀiV«>ÌÊÊ
>«ÌiÀÊ£ä°
Improper Use of Cursors By preferring aÊVÕÀÃÀL>Ãi`ÊÀÜ>Ì>Ìi®ÊÀiÃÕÌÊÃiÌpÀÊ>ÃÊivvÊ`iÊ
>ÃÊÃÊ>«ÌÞÊÌiÀi`Ê Ì]Ê,ÜÊ ÞÊ}â}Ê,ÜÊ, ,ÆÊ«ÀÕVi`ʺÀiiL>À»®pÃÌi>`ÊvÊ>ÊÀi}Õ>ÀÊÃiÌL>Ãi`Ê-+Ê query, you add a large amount of overhead on SQL Server. Use set-based queries whenever possible, but if you are forced to deal with cursors, be sure to use efficient cursor types such as v>ÃÌvÀÜ>À`ÊÞ°Ê ÝViÃÃÛiÊÕÃiÊvÊivvViÌÊVÕÀÃÀÃÊVÀi>ÃiÃÊÃÌÀiÃÃÊÊ-+Ê-iÀÛiÀÊÀiÃÕÀViÃ]Ê slowing down system performance. I discuss how to work with cursors, if you must, properly Ê
>«ÌiÀÊ£{°
Improper Configuration of the Database Log By failing to follow the general recommendations in configuring a database log, you can >`ÛiÀÃiÞÊ>vviVÌÊÌ
iÊ«iÀvÀ>ViÊvÊ>Ê"iÊ/À>Ã>VÌÊ*ÀViÃÃ}Ê"/*®qL>Ãi`Ê-+Ê Server database. For optimal performance, SQL Server heavily relies on accessing the database logs effectively. Chapter 2 covers how to configure the database log properly.
Excessive Use or Improper Configuration of tempdb There is only one pail`^ for any SQL Server instance. Since temporary storage such as operations involving user objects such as temporary tables and table variables, system objects such as cursors or hash tables for joins, and operations such as sorts and row versioning all use the pail`^ database, pail`^ can become quite a bottleneck. All these options and others that you can use lead to space, I/O, and contention issues within pail`^. I cover some configuration options to help with this in Chapter 2 and other options in other chapters appropriate to the issues addressed by that chapter.
CHAPTER 1
SQL QUERY PERFORMANCE TUNING
Summary In this introductory chapter, you have seen that SQL Server performance tuning is an iterative process, consisting of identifying performance bottlenecks, troubleshooting their cause, applying different resolutions, quantifying performance improvements, and then going back to the start until your required performance level is reached. To assist in this process, you should create a system baseline to compare with your modifications. Throughout the performance-tuning process, you need to be very objective about the amount of tuning you want to perform—you can always make a query run a little bit faster, LÕÌÊÃÊÌ
iÊivvÀÌÊÜÀÌ
ÊÌ
iÊVÃ̶Ê>Þ]ÊÃViÊ«iÀvÀ>ViÊ`i«i`ÃÊÊÌ
iÊ«>ÌÌiÀÊvÊÕÃiÀÊ activity and data, you must reevaluate the database server performance on a regular basis. To derive the optimal performance from a SQL Server database system, it is extremely important that you understand the stresses on the server created by the database application. In the next two chapters, I discuss how to analyze these stresses, both at a higher system level and at a lower SQL Server activities level. Then I show how to combine the two. In the rest of the book, you will examine in depth the biggest SQL Server performance killers, as mentioned earlier in the chapter. You will learn how these individual factors can affect performance if used incorrectly and how to resolve or avoid these traps.
15
CHAPT ER
2
System Performance Analysis I
n the first chapter, I stressed the importance of having a performance baseline that you can use to measure performance changes. In fact, this is one of the first things you should do when starting the performance-tuning process, since without a baseline you will not be able to quantify improvements. In this chapter, you will learn how to use the Performance Monitor tool to accomplish this and how to use the different performance counters that are required to create a baseline. Specifically, I cover the following topics: Ê
UÊ /
iÊL>ÃVÃÊvÊÌ
iÊ*iÀvÀ>ViÊÌÀÊÌ
Ê
UÊ ÜÊÌÊ>>ÞâiÊ
>À`Ü>ÀiÊÀiÃÕÀViÊLÌÌiiVÃÊÕÃ}Ê*iÀvÀ>ViÊÌÀ
Ê
UÊ ÜÊÌÊÀiÌÀiÛiÊ*iÀvÀ>ViÊÌÀÊ`>Ì>ÊÜÌ
Ê-+Ê-iÀÛiÀÊÕÃ}Ê`Þ>VÊ management views
Ê
UÊ ÜÊÌÊÀiÃÛiÊ
>À`Ü>ÀiÊÀiÃÕÀViÊLÌÌiiVÃ
Ê
UÊ ÜÊÌÊ>>ÞâiÊÌ
iÊÛiÀ>Ê«iÀvÀ>ViÊvÊ-+Ê-iÀÛiÀ
Ê
UÊ ÜÊÌÊVÀi>ÌiÊ>ÊL>ÃiiÊvÀÊÌ
iÊÃÞÃÌi
Performance Monitor Tool Windows Server 2008 provides a tool called Performance Monitor, although all the documentation for it refers to System Monitor°Ê/
ÃÊÌÊÃÊ«>ÀÌÊvÊ>Ê>À}iÀÊÃÕÌiÊvÊ`>Ì>ÊViVÌÊÌÃÊ called the Reliability and Performance Monitor. Performance Monitor collects detailed infor>ÌÊ>LÕÌÊÌ
iÊÕÌâ>ÌÊvÊ«iÀ>Ì}ÊÃÞÃÌiÊÀiÃÕÀViðÊÌÊ>ÜÃÊÞÕÊÌÊÌÀ>VÊi>ÀÞÊiÛiÀÞÊ >ëiVÌÊvÊÃÞÃÌiÊ«iÀvÀ>Vi]ÊVÕ`}ÊiÀÞ]Ê`Ã]Ê«ÀViÃÃÀ]Ê>`ÊÌ
iÊiÌÜÀ°ÊÊ>``Ì]Ê-+Ê-iÀÛiÀÊÓäänÊ«ÀÛ`iÃÊiÝÌiÃÃÊÌÊÌ
iÊ*iÀvÀ>ViÊÌÀÊÌÊÌÊÌÀ>VÊ>ÊÛ>ÀiÌÞÊ vÊvÕVÌ>Ê>Ài>ÃÊÜÌ
Ê-+Ê-iÀÛiÀ° *iÀvÀ>ViÊÌÀÊÌÀ>VÃÊÀiÃÕÀViÊLi
>ÛÀÊLÞÊV>«ÌÕÀ}Ê«iÀvÀ>ViÊ`>Ì>Ê}iiÀ>Ìi`Ê by hardware and software components of the system such as a processor, a process, a thread, >`ÊÃÊ°Ê/
iÊ«iÀvÀ>ViÊ`>Ì>Ê}iiÀ>Ìi`ÊLÞÊ>ÊÃÞÃÌiÊV«iÌÊÃÊÀi«ÀiÃiÌi`ÊLÞÊ>Êperformance object. A performance object provides counters that represent specific aspects of a component, such as !Lnk_aooknPeia for a Lnk_aookn object.
17
18
C HAPTER 2
SYS TEM P ER FOR MA NC E A NA L YS IS
/
iÀiÊV>ÊLiÊÕÌ«iÊÃÌ>ViÃÊvÊ>ÊÃÞÃÌiÊV«iÌ°ÊÀÊÃÌ>Vi]ÊÌ
iÊLnk_aookn object in a computer with two processors will have two instances represented as instances 0 and 1. Performance objects with multiple instances may also have an instance called [Pkp]h to Ài«ÀiÃiÌÊÌ
iÊÌÌ>ÊÛ>ÕiÊvÀÊ>ÊÌ
iÊÃÌ>ViðÊÀÊiÝ>«i]ÊÌ
iÊ«ÀViÃÃÀÊÕÃ>}iÊvÊ>ÊV«ÕÌiÀÊ with four processors can be determined using the following performance object, counter, and ÃÌ>ViÊ>ÃÊÃ
ÜÊÊ}ÕÀiÊÓ£®\ Ê
UÊ Performance object: Lnk_aookn
Ê
UÊ Counter: !Lnk_aooknPeia
Ê
UÊ Instance: [Pkp]h
Figure 2-1. Adding a Performance Monitor counter -ÞÃÌiÊLi
>ÛÀÊV>ÊLiÊiÌ
iÀÊÌÀ>Vi`ÊÊÀi>ÊÌiÊÊÌ
iÊvÀÊvÊ}À>«
ÃÊÀÊV>«ÌÕÀi`Ê>ÃÊ>Ê log (called a counter log®ÊvÀÊvviÊ>>ÞÃð /ÊÀÕÊÌ
iÊ*iÀvÀ>ViÊÌÀÊÌ]ÊiÝiVÕÌiÊlanbikj from a command prompt, which ÜÊ«iÊÌ
iÊ,i>LÌÞÊ>`Ê*iÀvÀ>ViÊÌÀÊÃÕÌi°Ê9ÕÊV>Ê>ÃÊÀ}
ÌVVÊÌ
iÊ «ÕÌiÀÊ VÊÊÌ
iÊ`iÃÌ«ÊÀÊ-Ì>ÀÌÊiÕ]ÊiÝ«>`Ê >}ÃÌVÃ]Ê>`ÊÌ
iÊiÝ«>`ÊÌ
iÊ,i>LÌÞÊ>`Ê Performance Monitor. Both will allow you to open the Performance Monitor utility. 9ÕÊÜÊi>ÀÊ
ÜÊÌÊÃiÌÊÕ«ÊÌ
iÊ`Û`Õ>ÊVÕÌiÀÃÊÊÌ
iʺ Ài>Ì}Ê>Ê >Ãii»ÊÃiVÌÊ >ÌiÀÊÊÌ
ÃÊV
>«ÌiÀ°ÊÀÃÌÊÞÕÊÜÊiÝ>iÊÜ
V
ÊVÕÌiÀÃÊÞÕÊÃ
Õ`ÊV
ÃiÊÊÀ`iÀÊÌÊ`iÌvÞÊÃÞÃÌiÊLÌÌiiVÃÊ>`Ê>ÃÊ
ÜÊÞÕÊV>ÊÀiÃÛiÊÃiÊvÊÌ
iÃiÊLÌÌiiVð
CHAPTER 2
S Y S T E M P E R F O R M A N C E A N A LY S I S
Dynamic Management Views /Ê}iÌÊ> immediate snapshot of a large amount of data that was formerly available only Ê*iÀvÀ>ViÊÌÀ]Ê-+Ê-iÀÛiÀÊÜÊvviÀÃÊÌ
iÊÃ>iÊ`>Ì>ÊÌiÀ>ÞÊÌ
ÀÕ}
Ê>ÊÃiÌ of `Þ>VÊ>>}iiÌÊÛiÜÃÊ 6îÊ>`Ê`Þ>VÊ>>}iiÌÊvÕVÌÃÊ Ã®°Ê/
iÃiÊ>ÀiÊ iÝÌÀiiÞÊÕÃivÕÊiV
>ÃÃÊvÀÊV>«ÌÕÀ}Ê>ÊÃ>«Ã
ÌÊvÊÌ
iÊVÕÀÀiÌÊ«iÀvÀ>ViÊvÊÞÕÀÊ ÃÞÃÌi°Ê½ÊÌÀ`ÕViÊÃiÛiÀ>ÊvÊÌ
iÃiÊÌ
ÀÕ}
ÕÌÊÌ
iÊL]ÊLÕÌʽÊvVÕÃÊÊ>ÊviÜÊÌ
>ÌÊ>ÀiÊÌ
iÊ most important for monitoring performance and for establishing a baseline. /
iÊouo*`i[ko[lanbkni]j_a[_kqjpano view displaysÊÌ
iÊ-+Ê-iÀÛiÀÊVÕÌiÀÃÊÜÌ
Ê>Ê µÕiÀÞ]Ê>Ü}ÊÞÕÊÌÊ>««ÞÊÌ
iÊvÕÊÃÌÀi}Ì
ÊvÊ/-+ÊÌÊÌ
iÊ`>Ì>Êi`>ÌiÞ°ÊÀÊiÝ>«i]Ê this simple query will return the current value for Hkcejo+oa_: OAHA?P_jpn[r]hqa BNKIouo*`i[ko[lanbkni]j_a[_kqjpano SDANAK>FA?P[J=IA9#IOOMH CB.,,46Cajan]hOp]peope_o# =J@_kqjpan[j]ia9#Hkcejo+oa_# /
ÃÊÀiÌÕÀÃÊÌ
iÊÛ>ÕiÊvÊ£xÊvÀÊÞÊÃiÀÛiÀ°ÊÀÊÞÕÀÊÃiÀÛiÀ]ÊÞÕ½Êii`ÊÌÊÃÕLÃÌÌÕÌiÊÌ
iÊ appropriate server name in the K>FA?P[J=IA comparison. /
iÀiÊ>ÀiÊ>Ê>À}iÊÕLiÀÊvÊ 6ÃÊ>`Ê ÃÊÌ
>ÌÊV>ÊLiÊÕÃi`ÊÌÊ}>Ì
iÀÊvÀ>ÌÊ about the server. Rather than cover them all, I’ll introduce one more that you will find yourself accessing on a regular basis, ouo*`i[ko[s]ep[op]po°Ê/
ÃÊ 6ÊÃ
ÜÃÊ>Ê>}}Ài}>Ìi`ÊÛiÜÊvÊ Ì
iÊÌ
Ài>`ÃÊÜÌ
Ê-+Ê-iÀÛiÀÊÌ
>ÌÊ>ÀiÊÜ>Ì}ÊÊÛ>ÀÕÃÊÀiÃÕÀViÃ]ÊViVÌi`ÊÃViÊÌ
iÊ>ÃÌÊ ÌiÊ-+Ê-iÀÛiÀÊÜ>ÃÊÃÌ>ÀÌi`ÊÀÊÌ
iÊVÕÌiÀÃÊÜiÀiÊÀiÃiÌ°Ê`iÌvÞ}ÊÌ
iÊÌÞ«iÃÊvÊÜ>ÌÃÊÌ
>ÌÊ>ÀiÊ occurring within your system is one of the easiest mechanisms to begin identifying the source vÊÞÕÀÊLÌÌiiVðÊ9ÕÊV>ÊÃÀÌÊÌ
iÊ`>Ì>ÊÊÛ>ÀÕÃÊÜ>ÞÃ]ÊLÕÌÊvÀÊÌ
iÊvÀÃÌÊiÝ>«i]ʽÊÊ>ÌÊ the waits that have the longest current count using this simple query: OAHA?PPKL$-,% & BNKIouo*`i[ko[s]ep[op]po KN@AN>Us]ep[peia[io@AO? }ÕÀiÊÓÓÊ`ë>ÞÃÊÌ
iÊÕÌ«ÕÌ°
Figure 2-2. Output from ouo*`i[ko[s]ep[op]po You can see not only the cumulative time that particular waits have occurred but also >ÊVÕÌÊvÊ
ÜÊvÌiÊÌ
iÞÊ
>ÛiÊVVÕÀÀi`Ê>`ÊÌ
iÊ>ÝÕÊÌiÊÌ
>ÌÊÃiÌ
}Ê
>`ÊÌÊ Ü>Ì°ÊÀÊ
iÀi]ÊÞÕÊV>Ê`iÌvÞÊÌ
iÊÜ>ÌÊÌÞ«iÊ>`ÊLi}ÊÌÀÕLiÃ
Ì}°Ê"iÊvÊÌ
iÊÃÌÊ
19
20
C HAPTER 2
SYS TEM P ER FOR MA NC E A NA L YS IS
VÊÌÞ«iÃÊvÊÜ>ÌÃÊÃÊÉ"°ÊvÊÞÕÊÃiiÊ=OUJ?D[EK[?KILHAPEKJ, EK[?KILHAPEKJ, HKCICN, SNEPAHKC, or L=CAEKH=P?D in your top tenÊÜ>ÌÊÌÞ«iÃ]ÊÞÕÊ>ÞÊLiÊiÝ«iÀiV}ÊÉ"ÊVÌiÌ]Ê >`ÊÞÕÊÜÊÜÊÜ
iÀiÊÌÊÃÌ>ÀÌÊÜÀ}°ÊÀÊ>ÊÀiÊ`iÌ>i`Ê>>ÞÃÃÊvÊÜ>ÌÊÌÞ«iÃÊ>`Ê
ÜÊ ÌÊÕÃiÊÌ
iÊ>ÃÊ>ÊÌÀ}ÊÌÊÜÌ
Ê-+Ê-iÀÛiÀ]ÊÀi>`ÊÌ
iÊVÀÃvÌÊÜ
ÌiÊ«>«iÀʺ-+Ê -iÀÛiÀÊÓääxÊ7>ÌÃÊ>`Ê+ÕiÕiûÊdppl6++sss*ie_nkokbp*_ki+pa_djap+lnk`pa_djkh+omh+ ^aopln]_pe_a+lanbkni]j_a[pqjejc[s]epo[mqaqao*iolt®°ÊÌ
Õ}
ÊÌÊÜ>ÃÊÜÀÌÌiÊvÀÊ-+Ê -iÀÛiÀÊÓääx]ÊÌÊÃÊiµÕ>ÞÊ>««V>LiÊÌÊ-+Ê-iÀÛiÀÊÓään°
Hardware Resource Bottlenecks /Þ«V>Þ]Ê-+Ê-iÀÛiÀÊ`>Ì>L>Ãi performance is affected by stress on the following hardware resources: Ê
UÊ iÀÞ
Ê
UÊ ÃÊÉ"
Ê
UÊ *ÀViÃÃÀ
Ê
UÊ
iÌÜÀ
-ÌÀiÃÃÊLiÞ`ÊÌ
iÊV>«>VÌÞÊvÊ>Ê
>À`Ü>ÀiÊÀiÃÕÀViÊvÀÃÊ>ÊLÌÌiiV°Ê/Ê>``ÀiÃÃÊÌ
iÊ ÛiÀ>Ê«iÀvÀ>ViÊvÊ>ÊÃÞÃÌi]ÊÞÕÊii`ÊÌÊ`iÌvÞÊÌ
iÃiÊLÌÌiiVÃ]ÊLiV>ÕÃiÊÌ
iÞÊvÀÊÌ
iÊ limit on overall system performance.
Identifying Bottlenecks /
iÀiÊÃÊÕÃÕ>ÞÊ>ÊÀi>ÌÃ
«ÊLiÌÜiiÊÀiÃÕÀViÊLÌÌiiVðÊÀÊiÝ>«i]Ê>Ê«ÀViÃÃÀÊ LÌÌiiVÊ>ÞÊLiÊ>ÊÃÞ«ÌÊvÊiÝViÃÃÛiÊ«>}}ÊiÀÞÊLÌÌiiV®ÊÀÊ>ÊÃÜÊ`ÃÊ`ÃÊ LÌÌiiV®°ÊvÊ>ÊÃÞÃÌiÊÃÊÜÊÊiÀÞ]ÊV>ÕÃ}ÊiÝViÃÃÛiÊ«>}}]Ê>`Ê
>ÃÊ>ÊÃÜÊ`Ã]ÊÌ
iÊ iÊvÊÌ
iÊi`ÊÀiÃÕÌÃÊÜÊLiÊ>Ê«ÀViÃÃÀÊÜÌ
Ê
}
ÊÕÌâ>ÌÊÃViÊÌ
iÊ«ÀViÃÃÀÊ
>ÃÊÌÊëi`Ê >ÊÃ}vV>ÌÊÕLiÀÊvÊ *1ÊVÞViÃÊÌÊÃÜ>«Ê«>}iÃÊÊ>`ÊÕÌÊvÊÌ
iÊiÀÞÊ>`ÊÌÊ>>}iÊ Ì
iÊÀiÃÕÌ>ÌÊ
}
ÊÕLiÀÊvÊÉ"ÊÀiµÕiÃÌðÊ,i«>V}ÊÌ
iÊ«ÀViÃÃÀÊÜÌ
Ê>Êv>ÃÌiÀÊiÊ>ÞÊ
i«Ê >ÊÌÌi]ÊLÕÌÊÌÊÜÕ`ÊÌÊLiÊÌ
iÊLiÃÌÊÛiÀ>ÊÃÕÌ°ÊÊ>ÊV>ÃiÊiÊÌ
Ã]ÊVÀi>Ã}ÊiÀÞÊÃÊ>Ê ÀiÊ>««À«À>ÌiÊÃÕÌ]ÊLiV>ÕÃiÊÌÊÜÊ`iVÀi>ÃiÊ«ÀiÃÃÕÀiÊÊÌ
iÊ`ÃÊ>`Ê«ÀViÃÃÀÊ>ÃÊÜi°Ê
ÛiÊÕ«}À>`}ÊÌ
iÊ`ÃÊÜÊ«ÀL>LÞÊLiÊ>ÊLiÌÌiÀÊÃÕÌÊÌ
>ÊÕ«}À>`}ÊÌ
iÊ«ÀViÃÃÀ°
Note The most common performance problem is usually I/O, either from memory or from the disk.
"iÊvÊÌ
iÊLiÃÌÊÜ>ÞÃÊvÊV>Ì}Ê>ÊLÌÌiiVÊÃÊÌÊ`iÌvÞÊÀiÃÕÀViÃÊÌ
>ÌÊ>ÀiÊÜ>Ì}ÊvÀÊ some other resource to complete its operation. You can use Performance Monitor counters ÀÊ 6ÃÊÃÕV
Ê>ÃÊouo*`i[ko[s]ep[op]poÊÌÊ}>Ì
iÀÊÌ
>ÌÊvÀ>Ì°Ê/
iÊÀiëÃiÊÌiÊvÊ>Ê request served by a resource includes the time the request had to wait in the resource queue, >ÃÊÜiÊ>ÃÊÌ
iÊÌiÊÌ>iÊÌÊiÝiVÕÌiÊÌ
iÊÀiµÕiÃÌ]ÊÃÊi`ÊÕÃiÀÊÀiëÃiÊÌiÊÃÊ`ÀiVÌÞÊ«À«Àtional to the amount of queuing in a system.
CHAPTER 2
S Y S T E M P E R F O R M A N C E A N A LY S I S
ÀÊiÝ>«i]ÊVÃ`iÀÊÌ
>ÌÊÌ
iÊ`ÃÊÃÕLÃÞÃÌiÊ
>ÃÊ>Ê`ÃʵÕiÕiÊi}Ì
ÊvÊ£ä°Ê-ViÊÌ
iÊ `ÃÊÃÕLÃÞÃÌiÊ>Ài>`ÞÊ
>ÃÊ«i`}Ê`ÃÊÀiµÕiÃÌÃÊÊÌ]Ê>ÊiÜÊ`ÃÊÀiµÕiÃÌÊ
>ÃÊÌÊÜ>ÌÊÕÌÊÌ
iÊ «ÀiÛÕÃÊ`ÃÊÀiµÕiÃÌÃÊV«iÌi°ÊvÊÌ
iÊÌiÊÌ>iÊLÞÊ>Ê>ÛiÀ>}iÊ`ÃÊÌÀ>ÃviÀÊÃÊiÊÃiV`]Ê Ì
iÊÌ
iÊiÜÊ`ÃÊÀiµÕiÃÌÊ
>ÃÊÌÊÜ>ÌÊvÀÊ>LÕÌÊÌiÊÃiV`ÃÊLivÀiÊ}iÌÌ}ÊÌ
iÊ>ÌÌiÌÊvÊÌ
iÊ `ÃÊÃÕLÃÞÃÌi°Ê/
iÀivÀi]ÊÌ
iÊÌÌ>ÊÀiëÃiÊÌiÊvÊÌ
iÊ`ÃÊÀiµÕiÃÌÊÜÊLiÊÌiÊÃiV`ÃÊÜ>ÌÊ Ìi]Ê«ÕÃÊiÊÃiV`Ê`ÃÊÌÀ>ÃviÀÊÌi° iÊ>Ü>ÀiÊÌ
>ÌÊÌ
iÊ>LÃiViÊvÊ>ʵÕiÕiÊ`iÃÊÌÊi>ÊÌ
>ÌÊÌ
iÀiÊÃÊÊLÌÌiiV°Ê7
iÊ µÕiÕiÊi}Ì
ÃÊÃÌ>ÀÌÊ}ÀÜ}]Ê
ÜiÛiÀ]ÊÌÊÃÊ>ÊÃÕÀiÊÃ}ÊÌ
>ÌÊÌ
iÊÃÞÃÌiÊÃÊÌÊ>LiÊÌÊii«ÊÕ«Ê with the demand. ÌÊ>ÊÀiÃÕÀViÃÊ
>ÛiÊëiVvVÊVÕÌiÀÃÊÌ
>ÌÊÃ
ÜʵÕiÕ}ÊiÛiÃ]ÊLÕÌÊÃÌÊÀiÃÕÀViÃÊ
>ÛiÊÃiÊVÕÌiÀÃÊÌ
>ÌÊÀi«ÀiÃiÌÊ>ÊÛiÀVÌÌ>ÊvÊÌ
>ÌÊÀiÃÕÀVi°ÊÀÊiÝ>«i]ÊiÀÞÊ has no such counter, but a large number of hard page faults represents the overcommittal of «
ÞÃV>ÊiÀÞÊ
>À`Ê«>}iÊv>ÕÌÃÊ>ÀiÊiÝ«>i`Ê>ÌiÀÊÊÌ
iÊV
>«ÌiÀÊÊÌ
iÊÃiVÌʺ*>}iÃÉÃiVÊ >`Ê*>}iÊ>ÕÌÃÉÃiVÊ ÕÌiÀû®°Ê"Ì
iÀÊÀiÃÕÀViÃ]ÊÃÕV
Ê>ÃÊÌ
iÊ«ÀViÃÃÀÊ>`Ê`Ã]Ê
>ÛiÊëiVvVÊ VÕÌiÀÃÊÌÊ`V>ÌiÊÌ
iÊiÛiÊvʵÕiÕ}°ÊÀÊiÝ>«i]ÊÌ
i counter L]caHebaAtla_p]j_u `V>ÌiÃÊ
ÜÊ}Ê>Ê«>}iÊÜÊÃÌ>ÞÊÊÌ
iÊLÕvviÀÊ«ÊÜÌ
ÕÌÊLi}ÊÀiviÀiVi`°Ê/
ÃÊÃÊ>Ê `V>ÌÀÊvÊ
ÜÊÜiÊ-+Ê-iÀÛiÀÊÃÊ>LiÊÌÊ>>}iÊÌÃÊiÀÞ]ÊÃViÊ>Ê}iÀÊviÊi>ÃÊÌ
>ÌÊ >Ê«iViÊvÊ`>Ì>ÊÊÌ
iÊLÕvviÀÊÜÊLiÊÌ
iÀi]Ê>Û>>Li]ÊÜ>Ì}ÊvÀÊÌ
iÊiÝÌÊÀiviÀiVi°ÊÜiÛiÀ]Ê >ÊÃ
ÀÌiÀÊviÊi>ÃÊÌ
>ÌÊ-+Ê-iÀÛiÀÊÃÊÛ}Ê«>}iÃÊÊ>`ÊÕÌÊvÊÌ
iÊLÕvviÀʵÕVÞ]Ê«ÃÃLÞÊ ÃÕ}}iÃÌ}Ê>ÊiÀÞÊLÌÌiiV° You will see which counters to use inÊ>>Þâ}Êi>V
ÊÌÞ«iÊvÊLÌÌiiVÊÃ
ÀÌÞ°
Bottleneck Resolution "ViÊÞÕÊ
>ÛiÊ`iÌvi`ÊLÌÌiiVÃ]ÊÞÕÊV>ÊÀiÃÛiÊÌ
iÊÊÌÜÊÜ>ÞÃ\ Ê
UÊ 9ÕÊV>ÊVÀi>ÃiÊÀiÃÕÀViÊÌ
ÀÕ}
«ÕÌ°
Ê
UÊ 9ÕÊV>Ê`iVÀi>ÃiÊÌ
iÊ>ÀÀÛ>ÊÀ>ÌiÊvÊÀiµÕiÃÌÃÊÌÊÌ
iÊÀiÃÕÀVi°
VÀi>Ã}ÊÌ
iÊÌ
ÀÕ}
«ÕÌÊÕÃÕ>ÞÊÀiµÕÀiÃÊiÝÌÀ>ÊÀiÃÕÀViÃÊÃÕV
Ê>ÃÊiÀÞ]Ê`ÃÃ]Ê«ÀViÃÃÀÃ]ÊÀÊiÌÜÀÊ>`>«ÌiÀðÊ9ÕÊV>Ê`iVÀi>ÃiÊÌ
iÊ>ÀÀÛ>ÊÀ>ÌiÊLÞÊLi}ÊÀiÊÃiiVÌÛiÊ>LÕÌÊ Ì
iÊÀiµÕiÃÌÃÊÌÊ>ÊÀiÃÕÀVi°ÊÀÊiÝ>«i]ÊÜ
iÊÞÕÊ
>ÛiÊ>Ê`ÃÊÃÕLÃÞÃÌiÊLÌÌiiV]ÊÞÕÊV>Ê iÌ
iÀÊVÀi>ÃiÊÌ
iÊÌ
ÀÕ}
«ÕÌÊvÊÌ
iÊ`ÃÊÃÕLÃÞÃÌiÊÀÊ`iVÀi>ÃiÊÌ
iÊ>ÕÌÊvÊÉ"ÊÀiµÕiÃÌð VÀi>Ã}ÊÌ
iÊÌ
ÀÕ}
«ÕÌÊi>ÃÊ>``}ÊÀiÊ`ÃÃÊÀÊÕ«}À>`}ÊÌÊv>ÃÌiÀÊ`ÃÃ°Ê iVÀi>Ã}ÊÌ
iÊ>ÀÀÛ>ÊÀ>ÌiÊi>ÃÊ`iÌvÞ}ÊÌ
iÊV>ÕÃiÊvÊ
}
ÊÉ"ÊÀiµÕiÃÌÃÊÌÊÌ
iÊ`ÃÊÃÕLÃÞÃÌiÊ>`Ê >««Þ}ÊÀiÃÕÌÃÊÌÊ`iVÀi>ÃiÊÌ
iÀÊÕLiÀ°Ê9ÕÊ>ÞÊLiÊ>LiÊÌÊ`iVÀi>ÃiÊÌ
iÊÉ"ÊÀiµÕiÃÌÃ]Ê vÀÊiÝ>«i]ÊLÞÊ>``}Ê>««À«À>ÌiÊ`iÝiÃÊÊ>ÊÌ>LiÊÌÊÌÊÌ
iÊ>ÕÌÊvÊ`>Ì>Ê>VViÃÃi`ÊÀÊ by partitioning a tableÊLiÌÜiiÊÕÌ«iÊ`Ãð
Memory Bottleneck Analysis Memory can be aÊ«ÀLi>ÌVÊLÌÌiiVÊLiV>ÕÃiÊ>ÊLÌÌiiVÊÊiÀÞÊÜÊ>viÃÌÊÊ Ì
iÀÊÀiÃÕÀViÃ]ÊÌ°Ê/
ÃÊÃÊ«>ÀÌVÕ>ÀÞÊÌÀÕiÊvÀÊ>ÊÃÞÃÌiÊÀÕ}Ê-+Ê-iÀÛiÀ°Ê7
iÊ-+Ê -iÀÛiÀÊÀÕÃÊÕÌÊvÊV>V
iÊÀÊiÀÞ®]Ê>Ê«ÀViÃÃÊÜÌ
Ê-+Ê-iÀÛiÀÊV>i`Êlazy writer®Ê
>Ã ÌÊÜÀÊiÝÌiÃÛiÞÊÌÊ>Ì>ÊiÕ}
ÊvÀiiÊÌiÀ>ÊiÀÞÊ«>}iÃÊÜÌ
Ê-+Ê-iÀÛiÀ°Ê/
ÃÊ VÃÕiÃÊiÝÌÀ>Ê *1ÊVÞViÃÊ>`Ê«iÀvÀÃÊ>``Ì>Ê«
ÞÃV>Ê`ÃÊÉ"ÊÌÊÜÀÌiÊiÀÞÊ«>}iÃÊ L>VÊÌÊ`ð
21
22
C HAPTER 2
SYS TEM P ER FOR MA NC E A NA L YS IS
SQL Server Memory Management -+Ê-iÀÛiÀÊ>>}ià memory for databases, including memory requirements for data and µÕiÀÞÊiÝiVÕÌÊ«>Ã]ÊÊ>Ê>À}iÊ«ÊvÊiÀÞÊV>i`ÊÌ
iÊmemory pool°Ê/
iÊiÀÞÊ«Ê consists of a collection of 8KB buffers to manage data pages and plan cache pages, free pages, >`ÊÃÊvÀÌ
°Ê/
iÊiÀÞÊ«ÊÃÊÕÃÕ>ÞÊÌ
iÊ>À}iÃÌÊ«ÀÌÊvÊ-+Ê-iÀÛiÀÊiÀÞ°Ê-+Ê -iÀÛiÀÊ>>}iÃÊiÀÞÊLÞÊ}ÀÜ}ÊÀÊÃ
À}ÊÌÃÊiÀÞÊ«ÊÃâiÊ`Þ>V>Þ° You canÊVv}ÕÀiÊ-+Ê-iÀÛiÀÊvÀÊ`Þ>VÊiÀÞÊ>>}iiÌÊÊ-+Ê-iÀÛiÀÊ>>}iiÌÊ-ÌÕ`Ê---®°ÊÊÌÊÌ
iÊiÀÞÊv`iÀÊvÊÌ
iÊ-iÀÛiÀÊ*À«iÀÌiÃÊ`>}ÊLÝ]Ê>ÃÊÃ
ÜÊÊ }ÕÀiÊÓΰ
Figure 2-3. SQL Server memory configuration /
iÊ`Þ>VÊiÀÞÊÀ>}iÊÃÊVÌÀi`ÊÌ
ÀÕ}
ÊÌÜÊVv}ÕÀ>ÌÊ«À«iÀÌiÃ\Ê Iejeiqi$I>% and I]teiqi$I>%.
CHAPTER 2
S Y S T E M P E R F O R M A N C E A N A LY S I S
Ê
UÊ Iejeiqi$I>%]Ê>ÃÊÜÊ>ÃÊiejoanraniaiknu]ÊÜÀÃÊ>ÃÊ>ÊvÀÊÛ>ÕiÊvÀÊÌ
iÊiÀÞÊ «°Ê"ViÊÌ
iÊiÀÞÊ«ÊÀi>V
iÃÊÌ
iÊÃ>iÊÃâiÊ>ÃÊÌ
iÊvÀÊÛ>Õi]Ê-+Ê-iÀÛiÀÊV>Ê VÌÕiÊVÌÌ}Ê«>}iÃÊÊÌ
iÊiÀÞÊ«]ÊLÕÌÊÌÊV>ÌÊLiÊÃ
ÀÕÊÌÊiÃÃÊÌ
>Ê Ì
iÊvÀÊÛ>Õi°Ê ÌiÊÌ
>ÌÊ-+Ê-iÀÛiÀÊ`iÃÊÌÊÃÌ>ÀÌÊÜÌ
ÊÌ
iÊiejoanraniaiknu configuration value but commits memory dynamically, as needed.
Ê
UÊ I]teiqi$I>%]Ê>ÃÊÜÊ>Ã i]toanraniaiknu, serves as a ceiling value to limit the >ÝÕÊ}ÀÜÌ
ÊvÊÌ
iÊiÀÞÊ«°Ê/
iÃiÊVv}ÕÀ>ÌÊÃiÌÌ}ÃÊÌ>iÊivviVÌÊidiately and do not require a restart.
VÀÃvÌÊÀiVi`ÃÊÌ
>ÌÊÞÕÊÕÃiÊ`Þ>VÊiÀÞÊVv}ÕÀ>ÌÊvÀÊ-+Ê-iÀÛiÀ]Ê where iejoanraniaiknu will be 0 and i]toanraniaiknuÊÜÊLiÊÌ
iÊ>ÝÕÊ«
ÞÃV>Ê memory of the system, assuming a single instance on the machine. You should not run other iÀÞÌiÃÛiÊ>««V>ÌÃÊÊÌ
iÊÃ>iÊÃiÀÛiÀÊ>ÃÊ-+Ê-iÀÛiÀ]ÊLÕÌÊvÊÞÕÊÕÃÌ]ÊÊÀiVmend you first get estimates on how much memory is needed by other applications and then Vv}ÕÀiÊ-+Ê-iÀÛiÀÊÜÌ
Ê>Êi]toanraniaiknu value set to prevent the other applications vÀÊÃÌ>ÀÛ}Ê-+Ê-iÀÛiÀÊvÊiÀÞ°Ê"Ê>ÊÃÞÃÌiÊÜ
iÀiÊ-+Ê-iÀÛiÀÊÃÊÀÕ}ÊÊÌÃÊÜ]ÊÊ «ÀiviÀÊÌÊÃiÌÊÌ
iÊÕÊÃiÀÛiÀÊiÀÞÊiµÕ>ÊÌÊÌ
iÊ>ÝÊÛ>ÕiÊ>`ÊëÞÊ`ë>ÌV
ÊÜÌ
Ê `Þ>VÊ>>}iiÌ°Ê"Ê>ÊÃiÀÛiÀÊÜÌ
ÊÕÌ«iÊ-+Ê-iÀÛiÀÊÃÌ>ViÃ]ÊÞÕ½Êii`ÊÌÊ>`ÕÃÌÊ Ì
iÃiÊiÀÞÊÃiÌÌ}ÃÊÌÊiÃÕÀiÊi>V
ÊÃÌ>ViÊ
>ÃÊ>Ê>`iµÕ>ÌiÊÛ>Õi°ÊÕÃÌÊ>iÊÃÕÀiÊÞÕ½ÛiÊ ivÌÊiÕ}
ÊiÀÞÊvÀÊÌ
iÊ«iÀ>Ì}ÊÃÞÃÌiÊ>`ÊiÝÌiÀ>Ê«ÀViÃÃiÃ]Ê>ÃÊÜiÊ>ÃÊLÕvviÀ pool memory (which used to be called IaiPkHa]ra®° iÀÞÊÜÌ
Ê-+Ê-iÀÛiÀÊV>ÊLiÊÀÕ}
ÞÊ`Û`i`ÊÌÊLÕvviÀÊ«ÊiÀÞ]ÊÜ
V
ÊÀi«ÀiÃiÌÃÊ`>Ì>Ê«>}iÃÊ>`ÊvÀiiÊ«>}iÃ]Ê>`ÊLÕvviÀÊiÀÞ]ÊÜ
V
ÊVÃÃÌÃÊvÊÌ
Ài>`Ã]Ê Ã]Ê i`ÊÃiÀÛiÀÃ]Ê>`ÊÌ
iÀðÊÃÌÊvÊÌ
iÊiÀÞÊÕÃi`ÊLÞÊ-+Ê-iÀÛiÀÊ}iÃÊÌÊÌ
iÊLÕvviÀÊ«°
Note SQL Server does consume more memory than simply that specified by the i]t[oanran[iaiknu setting.
You can also manage the configuration values for iejoanraniaiknu and i]toanran iaiknu by using the ol[_kjbecqnaÊÃÞÃÌiÊÃÌÀi`Ê«ÀVi`ÕÀi°Ê/ÊÃiiÊÌ
iÊVv}ÕÀ>ÌÊÛ>ÕiÃÊvÀÊ Ì
iÃiÊ«>À>iÌiÀÃ]ÊiÝiVÕÌiÊÌ
iÊol[_kjbecqna stored procedure as follows: ata_ol[_kjbecqna#iejoanraniaiknu$I>%# ata_ol[_kjbecqna#i]toanraniaiknu$I>%# }ÕÀiÊÓ{ÊÃ
ÜÃÊÌ
iÊÀiÃÕÌÊvÊÀÕ}ÊÌ
iÃiÊV>`ð
Figure 2-4. SQL Server memory configuration properties
23
24
C HAPTER 2
SYS TEM P ER FOR MA NC E A NA L YS IS
ÌiÊÌ
>ÌÊÌ
iÊ`iv>ÕÌÊÛ>ÕiÊvÀÊÌ
iÊiejoanraniaiknu setting is 0MB, and for the i]t oanraniaiknuÊÃiÌÌ}ÊÌÊÃÊÓ£{Ç{nÎÈ{Ç °ÊÃ]Êi]toanraniaiknu cannot be set to less Ì
>Ê{ ° You can also modify these configuration values using the ol[_kjbecqna stored procedure. ÀÊiÝ>«i]ÊÌÊÃiÌÊi]toanraniaiknu to 200MB and iejoanraniaiknuÊÌÊ£ää ]ÊiÝiVÕÌiÊ the following set of statements (oap[iaiknu*omhÊÊÌ
iÊ`Ü>`®\ QOAi]opan ATA?ol[_kjbecqna#odks]`r]j_a`klpekj#(#-# NA?KJBECQNA ata_ol[_kjbecqna#iejoanraniaiknu$I>%#(-,, ata_ol[_kjbecqna#i]toanraniaiknu$I>%#(.,, NA?KJBECQNASEPDKRANNE@A /
iÊiejoanraniaiknu and i]toanraniaiknu configurations are classified as advanced options. By default, the ol[_kjbecqna stored procedure does not affect/display the advanced options. Setting odks]`r]j_a`klpekj to - as shown previously enables the ol[_kjbecqna stored procedure to affect/display the advanced options. /
iÊNA?KJBECQNA statement updates the memory configuration values set by ol[_kjbecqna. Since ad hoc updates to the system catalog containing the memory configuration values are not recommended, the KRANNE@A flag is used with the NA?KJBECQNA statement to force the memory configuration. If you do the memory configuration through Management Studio, >>}iiÌÊ-ÌÕ`Ê>ÕÌ>ÌV>ÞÊiÝiVÕÌiÃÊÌ
iÊNA?KJBECQNASEPDKRANNE@A statement after the configuration setting. ÊÃiÊÀ>ÀiÊVÀVÕÃÌ>ViÃ]ÊÞÕÊ>ÞÊii`ÊÌÊ>ÜÊvÀÊ-+Ê-iÀÛiÀÊÃ
>À}Ê>ÊÃÞÃÌi½ÃÊ iÀÞ°Ê/Êi>LÀ>Ìi]ÊVÃ`iÀÊ>ÊV«ÕÌiÀÊÜÌ
Ê-+Ê-iÀÛiÀÊ>`Ê ÝV
>}iÊ-iÀÛiÀÊÀÕ}Ê ÊÌ°Ê Ì
ÊÃiÀÛiÀÃÊ>ÀiÊ
i>ÛÞÊÕÃiÀÃÊvÊiÀÞÊ>`ÊÌ
ÕÃÊii«Ê«ÕÃ
}Êi>V
ÊÌ
iÀÊvÀÊiÀÞ°Ê /
iÊ`Þ>ViÀÞÊLi
>ÛÀÊvÊ-+Ê-iÀÛiÀÊ>ÜÃÊÌÊÌÊÀii>ÃiÊiÀÞÊÌÊ ÝV
>}iÊ-iÀÛiÀÊ >ÌÊiÊÃÌ>ViÊ>`Ê}À>LÊÌÊL>VÊ>ÃÊ ÝV
>}iÊ-iÀÛiÀÊÀii>ÃiÃÊÌ°Ê9ÕÊV>Ê>Û`ÊÌ
ÃÊ`Þ>V iÀÞÊ>>}iiÌÊÛiÀ
i>`ÊLÞÊVv}ÕÀ}Ê-+Ê-iÀÛiÀÊvÀÊ>ÊvÝi`ÊiÀÞÊÃâi°ÊÜiÛiÀ]Ê «i>ÃiÊii«ÊÊ`ÊÌ
>ÌÊÃViÊ-+Ê-iÀÛiÀÊÃÊ>ÊiÝÌÀiiÞÊÀiÃÕÀViÌiÃÛiÊ«ÀViÃÃ]ÊÌÊÃÊ
}
ÞÊÀiVi`i`ÊÌ
>ÌÊÞÕÊ
>ÛiÊ>Ê`i`V>Ìi`Ê-+Ê-iÀÛiÀÊ«À`ÕVÌÊ>V
i° ÜÊÌ
>ÌÊÞÕÊÕ`iÀÃÌ>`Ê-+Ê-iÀÛiÀÊiÀÞÊ>>}iiÌ]Êi̽ÃÊVÃ`iÀÊÌ
iÊperfor>ViÊVÕÌiÀÃÊÞÕÊV>ÊÕÃiÊÌÊ>>ÞâiÊÃÌÀiÃÃÊÊiÀÞ]Ê>ÃÊÃ
ÜÊÊ/>LiÊÓ£° Table 2-1. Performance Monitor Counters to Analyze Memory Pressure
Object(Instance[,InstanceN])
Counter
Description
Values
Iaiknu
=r]eh]^ha>upao
Àii physical memory
System dependent
L]cao+oa_
Rate of hard page faults
Average Û>ÕiÊÊxä
L]caB]qhpo+oa_
Rate of total page faults
«>ÀiÊÜÌ
Ê its baseline value for trend analysis
L]caoEjlqp+oa_
Rate of input page faults
L]caoKqplqp+oa_
Rate of output page faults
CHAPTER 2
S Y S T E M P E R F O R M A N C E A N A LY S I S
Object(Instance[,InstanceN])
Counter
Description
Values
OMHOanran6>qbbanI]j]can
>qbban_]_da depn]pek
Percentage of requests served out of buffer cache
Average value q 90%
L]caHeba Atla_p]j_u
/i page spends in buffer
Average Û>ÕiÊÊÎää
?da_glkejp L]cao+oa_
PagesÊÜÀÌÌiÊÌÊ`ÃÊLÞÊ V
iV«Ì
Average Û>ÕiÊÊÎä
H]vusnepao+oa_
ÀÌÞ aged pages flushed from buffer
Average value < 20
IaiknuCn]jpo Laj`ejc
ÕLiÀ of processes waiting for memory grant
Average value = 0
P]ncapOanran Iaiknu$G>%
>ÝÕ physical iÀÞÊ-+Ê-iÀÛiÀÊV>Ê VÃÕiÊÊÌ
iÊLÝ
ÃiÊÌÊÃâiÊ of physical memory
Pkp]hOanran Iaiknu$G>%
Physical memory curÀiÌÞÊ>ÃÃ}i`ÊÌÊ-+Ê Server
P]ncapOanran Iaiknu$G>%
Lner]pa>upao
-âi]Ê bytes, of memory that this process has allocated that cannot be shared with other processes
OMHOanran6IaiknuI]j]can
Lnk_aoo
ÃiÊÌÊ
½ÊÜÊÜ>ÊÞÕÊÌ
ÀÕ}
ÊÌ
iÃiÊVÕÌiÀÃÊÌÊ}iÌÊ>ÊLiÌÌiÀÊ`i> of what you can use them for.
Available Bytes /
iÊ=r]eh]^ha>upaoÊVÕÌiÀÊÀi«ÀiÃiÌÃÊvÀiiÊ«
ÞÃV>ÊiÀÞÊÊÌ
iÊÃÞÃÌi°ÊÀÊ}`Ê«iÀvÀ>Vi]ÊÌ
ÃÊVÕÌiÀÊÛ>ÕiÊÃ
Õ`ÊÌÊLiÊÌÊÜ°ÊvÊ-+Ê-iÀÛiÀÊÃÊVv}ÕÀi`ÊvÀÊ`Þ>VÊ memory usage, then this value will be controlled by calls to a Windows API that determines Ü
iÊ>`Ê
ÜÊÕV
ÊiÀÞÊÌÊÀii>Ãi°Ê ÝÌi`i`Ê«iÀ`ÃÊvÊÌiÊÜÌ
ÊÌ
ÃÊÛ>ÕiÊÛiÀÞÊÜÊ>`Ê -+Ê-iÀÛiÀÊiÀÞÊÌÊV
>}}Ê`V>ÌiÃÊÌ
>ÌÊÌ
iÊÃiÀÛiÀÊÃÊÕ`iÀÊÃiÛiÀiÊiÀÞÊÃÌÀiÃð
Pages/sec and Page Faults/sec Counters /ÊÕ`iÀÃÌ>`ÊÌ
iÊ«ÀÌ>ViÊvÊÌ
iÊL]cao+oa_ and L]caB]qhpo+oa_ counters, you first need to learn about page faults. A page fault occurs when a process requires code or data that is not in its working setÊÌÃÊë>ViÊÊ«
ÞÃV>ÊiÀÞ®°ÊÌÊ>ÞÊi>`ÊÌÊ>ÊÃvÌÊ«>}iÊv>ÕÌÊÀÊ>Ê
>À`Ê«>}iÊ fault. If the faulted page is found elsewhere in physical memory, then it is called a soft page fault. A hard page faultÊVVÕÀÃÊÜ
iÊ>Ê«ÀViÃÃÊÀiµÕÀiÃÊV`iÊÀÊ`>Ì>ÊÌ
>ÌÊÃÊÌÊÊÌÃÊÜÀ}Ê ÃiÌÊÀÊiÃiÜ
iÀiÊÊ«
ÞÃV>ÊiÀÞÊ>`ÊÕÃÌÊLiÊÀiÌÀiÛi`ÊvÀÊ`ð /
iÊëii`ÊvÊ>Ê`ÃÊ>VViÃÃÊÃÊÊÌ
iÊÀ`iÀÊvÊÃiV`Ã]ÊÜ
iÀi>ÃÊ>ÊiÀÞÊ>VViÃÃÊÃÊÊ Ì
iÊÀ`iÀÊvÊ>ÃiV`ðÊ/
ÃÊ
Õ}iÊ`vviÀiViÊÊÌ
iÊëii`ÊLiÌÜiiÊ>Ê`ÃÊ>VViÃÃÊ>`Ê>ÊiÀÞÊ>VViÃÃÊ>iÃÊÌ
iÊivviVÌÊvÊ
>À`Ê«>}iÊv>ÕÌÃÊÃ}vV>ÌÊV«>Ài`ÊÌÊÌ
>ÌÊvÊÃvÌÊ«>}iÊv>ÕÌð /
iÊL]cao+oa_ÊVÕÌiÀÊÀi«ÀiÃiÌÃÊÌ
iÊÕLiÀÊvÊ«>}iÃÊÀi>`ÊvÀÊÀÊÜÀÌÌiÊÌÊ`ÃÊ«iÀÊ ÃiV`ÊÌÊÀiÃÛiÊ
>À`Ê«>}iÊv>ÕÌðÊ/
iÊL]caB]qhpo+oa_ performance counter indicates the total page faults per second—soft page faults plus hard page faults—handled by the system.
25
26
C HAPTER 2
SYS TEM P ER FOR MA NC E A NA L YS IS
>À`Ê«>}iÊv>ÕÌÃ]Ê`V>Ìi`ÊLÞÊL]cao+oa_, should not be consistently high. If this counter ÃÊVÃÃÌiÌÞÊÛiÀÞÊ
}
]ÊÌ
iÊ-+Ê-iÀÛiÀÊÃÊ«ÀL>LÞÊÃÌ>ÀÛ}ÊÌ
iÀÊ>««V>ÌðÊ/
iÀiÊ>ÀiÊÊ hard and fast numbers for what indicates a problem, because these numbers will vary widely LiÌÜiiÊÃÞÃÌiÃÊL>Ãi`ÊÊÌ
iÊ>ÕÌÊ>`ÊÌÞ«iÊvÊiÀÞÊ>ÃÊÜiÊ>ÃÊÌ
iÊëii`ÊvÊ`ÃÊ>VViÃÃÊ on the system. If the L]cao+oa_ counter isÊÛiÀÞÊ
}
]ÊÌ
iÊÞÕÊV>ÊLÀi>ÊÌÊÕ«ÊÌÊL]caoEjlqp+oa_ and L]caoKqplqp+oa_: Ê
UÊ L]caoEjlqp+oa_: An application will wait only on an input page, not on an output page.
Ê
UÊ L]caoKqplqp+oa_: Page output will stress the system, but an application usually does not see this stress. Pages output are usually represented by the application’s dirty «>}iÃÊÌ
>ÌÊii`ÊÌÊLiÊL>Vi`ÊÕÌÊÌÊÌ
iÊ`ðÊL]caoKqplqp+oa_ is an issue only when `ÃÊ>`ÊLiViÊ>ÊÃÃÕi°
Ã]ÊV
iVÊLnk_aoo6L]caB]qhpo+oa_ÊÌÊv`ÊÕÌÊÜ
V
Ê«ÀViÃÃÊÃÊV>ÕÃ}ÊiÝViÃÃÛiÊ paging in case of high L]cao+oa_°Ê/
iÊLnk_aoo object is the system component that provides performance data for the processes running on the system, which are individually represented by their corresponding instance name. ÀÊiÝ>«i]ÊÌ
iÊ-+Ê-iÀÛiÀÊ«ÀViÃÃÊÃÊÀi«ÀiÃiÌi`ÊLÞÊÌ
iÊomhoanrn instance of the Lnk_aooÊLiVÌ°Ê}
ÊÕLiÀÃÊvÀÊÌ
ÃÊVÕÌiÀÊÕÃÕ>ÞÊ`ÊÌÊi>ÊÕV
ÊÕiÃÃÊL]cao+oa_ is high. L]caB]qhpo+oa_ can range all over the spectrum with normal application behavior, with Û>ÕiÃÊvÀÊäÊÌÊ£]äääÊ«iÀÊÃiV`ÊLi}Ê>VVi«Ì>Li°Ê/
ÃÊiÌÀiÊ`>Ì>ÊÃiÌÊi>ÃÊ>ÊL>ÃiiÊÃÊ iÃÃiÌ>ÊÌÊ`iÌiÀiÊÌ
iÊiÝ«iVÌi`ÊÀ>ÊLi
>ÛÀ°
Buffer Cache Hit Ratio /
iÊbuffer cache is the pool of buffer pages into which data pages are read, and it is often the L}}iÃÌÊ«>ÀÌÊvÊÌ
iÊ-+Ê-iÀÛiÀÊiÀÞÊ«°Ê/
ÃÊVÕÌiÀÊÛ>ÕiÊÃ
Õ`ÊLiÊ>ÃÊ
}
Ê>ÃÊ«ÃÃLi]Ê iëiV>ÞÊvÀÊ"/*ÊÃÞÃÌiÃÊÌ
>ÌÊÃ
Õ`Ê
>ÛiÊv>ÀÞÊÀi}iÌi`Ê`>Ì>Ê>VViÃÃ]ÊÕiÊ>ÊÜ>Ài
ÕÃiÊÀÊÀi«ÀÌ}ÊÃÞÃÌi°ÊÌÊÃÊiÝÌÀiiÞÊVÊÌÊv`ÊÌ
ÃÊVÕÌiÀÊÛ>ÕiÊ>ÃÊÊ«iÀViÌÊ or more for most production servers. A low >qbban_]_dadepn]pek value indicates that few requests could be served out of the buffer cache, with the rest of the requests being served vÀÊ`ð 7
iÊÌ
ÃÊ
>««iÃ]ÊiÌ
iÀÊ-+Ê-iÀÛiÀÊÃÊÃÌÊÜ>À}ÊÕ«ÊÀÊÌ
iÊiÀÞÊÀiµÕÀiiÌÊvÊ Ì
iÊLÕvviÀÊV>V
iÊÃÊÀiÊÌ
>ÊÌ
iÊ>ÝÕÊiÀÞÊ>Û>>LiÊvÀÊÌÃÊ}ÀÜÌ
°ÊvÊÌ
ÃÊÃÊVÃÃtently low, you should consider getting more memory for the system.
Page Life Expectancy L]caHebaAtla_p]j_u indicates how long a page will stay in the buffer pool without being refiÀiVi`°ÊiiÀ>Þ]Ê>ÊÜÊÕLiÀÊvÀÊÌ
ÃÊVÕÌiÀÊi>ÃÊÌ
>ÌÊ«>}iÃÊ>ÀiÊLi}ÊÀiÛi`ÊvÀÊ the buffer, lowering the efficiency of the cache and indicating the possibility of memory presÃÕÀi°Ê"ÊÀi«ÀÌ}ÊÃÞÃÌiÃ]Ê>ÃÊ««Ãi`ÊÌÊ"/*ÊÃÞÃÌiÃ]ÊÌ
ÃÊÕLiÀÊ>ÞÊÀi>Ê>ÌÊ>ÊÜiÀÊ Û>ÕiÊÃViÊÀiÊ`>Ì>ÊÃÊ>VViÃÃi`ÊvÀÊÀi«ÀÌ}ÊÃÞÃÌiðÊÊÀi>Ã>LiÊÛ>ÕiÊÌÊiÝ«iVÌÊÌÊÃiiÊ
iÀiÊÃÊÎääÊÃiV`ÃÊÀÊÀi°
CHAPTER 2
S Y S T E M P E R F O R M A N C E A N A LY S I S
Checkpoint Pages/sec /
iÊ?da_glkejpL]cao+oa_ÊVÕÌiÀÊÀi«ÀiÃiÌÃÊÌ
iÊÕLiÀÊvÊ«>}iÃÊÌ
>ÌÊ>ÀiÊÛi`ÊÌÊ`ÃÊLÞÊ >ÊV
iV«ÌÊ«iÀ>Ì°Ê/
iÃiÊÕLiÀÃÊÃ
Õ`ÊLiÊÀi>ÌÛiÞÊÜ]ÊvÀÊiÝ>«i]ÊiÃÃÊÌ
>ÊÎäÊ«iÀÊ ÃiV`ÊvÀÊÃÌÊÃÞÃÌiðÊÊ
}
iÀÊÕLiÀÊi>ÃÊÀiÊ«>}iÃÊ>ÀiÊLi}Ê>Ài`Ê>ÃÊ`ÀÌÞÊÊÌ
iÊ V>V
i°ÊÊ`ÀÌÞÊ«>}iÊÃÊiÊÌ
>ÌÊÃÊ`vi`ÊÜ
iÊÊÌ
iÊLÕvviÀ°Ê7
iÊ̽ÃÊ`vi`]Ê̽ÃÊ>Ài`Ê >ÃÊ`ÀÌÞÊ>`ÊÜÊ}iÌÊÜÀÌÌiÊL>VÊÌÊÌ
iÊ`ÃÊ`ÕÀ}ÊÌ
iÊiÝÌÊV
iV«Ì°Ê}
iÀÊÛ>ÕiÃÊÊÌ
ÃÊ counter indicate a larger number of writes occurring within the system, possibly indicative of É"Ê«ÀLið
Lazy writes/sec /
iÊH]vusnepao+oa_ counter records the number of buffers written each second by the buffer >>}iÀ½ÃÊ>âÞÊÜÀÌiÊ«ÀViÃðÊ/
ÃÊ«ÀViÃÃÊÃÊÜ
iÀiÊÌ
iÊ`ÀÌÞ]Ê>}i`ÊLÕvviÀÃÊ>ÀiÊÀiÛi`ÊvÀÊ the buffer by a system process that frees the memory up for other uses. A dirty, aged buffer ÃÊiÊÌ
>ÌÊ
>ÃÊV
>}iÃÊ>`Êii`ÃÊÌÊLiÊÜÀÌÌiÊÌÊÌ
iÊ`ðÊ}
iÀÊÛ>ÕiÃÊÊÌ
ÃÊVÕÌiÀÊ «ÃÃLÞÊ`V>ÌiÊÉ"ÊÃÃÕiÃÊÀÊiÛiÊiÀÞÊ«ÀLiðÊ/
iÊH]vusnepao+oa_ values should consistently be less than 20 for the average system.
Memory Grants Pending /
iÊIaiknuCn]jpoLaj`ejc counter represents the number of processes pending for a memÀÞÊ}À>ÌÊÜÌ
Ê-+Ê-iÀÛiÀÊiÀÞ°ÊvÊÌ
ÃÊVÕÌiÀÊÛ>ÕiÊÃÊ
}
]ÊÌ
iÊ-+Ê-iÀÛiÀÊÃÊÃ
ÀÌÊvÊ iÀÞ°Ê1`iÀÊÀ>ÊV`ÌÃ]ÊÌ
ÃÊVÕÌiÀÊÛ>ÕiÊÃ
Õ`ÊVÃÃÌiÌÞÊLiÊäÊvÀÊÃÌÊ«Àduction servers. Another way to retrieve this value, on the fly, is to run queries against theÊ 6Êouo*`i[ ata_[mqanu[iaiknu[cn]jpo. A jqhh value in the column cn]jp[peia indicates that the process ÃÊÃÌÊÜ>Ì}ÊvÀÊ>ÊiÀÞÊ}À>Ì°Ê/
ÃÊÃÊiÊiÌ
`ÊÞÕÊV>ÊÕÃiÊÌÊÌÀÕLiÃ
ÌʵÕiÀÞÊ ÌiÕÌÃÊLÞÊ`iÌvÞ}ÊÌ
>ÌÊ>ʵÕiÀÞÊÀʵÕiÀiîÊÃÊÜ>Ì}ÊÊiÀÞÊÊÀ`iÀÊÌÊiÝiVÕÌi°
Target Server Memory (KB) and Total Server Memory (KB) P]ncapOanranIaiknu$G>% indicatesÊÌ
iÊÌÌ>Ê>ÕÌÊvÊ`Þ>VÊiÀÞÊ-+Ê-iÀÛiÀÊÃÊ willing to consume. Pkp]hOanranIaiknu$G>% indicates the amount of memory currently >ÃÃ}i`ÊÌÊ-+Ê-iÀÛiÀ°Ê/
iÊPkp]hOanranIaiknu$G>% counter value can be very high if the ÃÞÃÌiÊÃÊ`i`V>Ìi`ÊÌÊ-+Ê-iÀÛiÀ°ÊvÊPkp]hOanranIaiknu$G>% is much less than P]ncap OanranIaiknu$G>%]ÊÌ
iÊiÌ
iÀÊÌ
iÊ-+Ê-iÀÛiÀÊiÀÞÊÀiµÕÀiiÌÊÃÊÜ]ÊÌ
iÊi]toanran iaiknuÊVv}ÕÀ>ÌÊ«>À>iÌiÀÊvÊ-+Ê-iÀÛiÀÊÃÊÃiÌÊ>ÌÊÌÊÜÊ>ÊÛ>Õi]ÊÀÊÌ
iÊÃÞÃÌiÊÃÊÊ warm-up phase°Ê/
iÊÜ>ÀÕ«Ê«
>ÃiÊÃÊÌ
iÊ«iÀ`Ê>vÌiÀÊ-+Ê-iÀÛiÀÊÃÊÃÌ>ÀÌi`ÊÜ
iÊÌ
iÊ`>Ì>L>ÃiÊÃiÀÛiÀÊÃÊÊÌ
iÊ«ÀViÃÃÊvÊiÝ«>`}ÊÌÃÊiÀÞÊ>V>ÌÊ`Þ>V>ÞÊ>ÃÊÀiÊ`>Ì>Ê sets are accessed, bringing more data pages into memory. 9ÕÊV>ÊVvÀÊ>ÊÜÊiÀÞÊÀiµÕÀiiÌÊvÀÊ-+Ê-iÀÛiÀÊLÞÊÌ
iÊ«ÀiÃiViÊvÊ>Ê>À}iÊ ÕLiÀÊvÊvÀiiÊ«>}iÃ]ÊÕÃÕ>ÞÊx]äääÊÀÊÀi°
Memory Bottleneck Resolutions When there is high stress on memory, indicated by a large number of hard page faults, you can ÀiÃÛiÊiÀÞÊLÌÌiiVÊÕÃ}ÊÌ
iÊvÜV
>ÀÌÊÃ
ÜÊÊ}ÕÀiÊÓx°
27
28
C HAPTER 2
SYS TEM P ER FOR MA NC E A NA L YS IS
Figure 2-5. Memory bottleneck resolution chart ÊviÜÊvÊÌ
iÊVÊÀiÃÕÌÃÊvÀÊiÀÞÊLÌÌiiVÃÊ>ÀiÊ>ÃÊvÜÃ\ Ê
UÊ "«Ìâ}Ê>««V>ÌÊÜÀ>`
Ê
UÊ V>Ì}ÊÀiÊiÀÞÊÌÊ-+Ê-iÀÛiÀ
Ê
UÊ VÀi>Ã}ÊÃÞÃÌiÊiÀÞ
Ê
UÊ
>}}ÊvÀÊ>ÊÎÓLÌÊÌÊ>ÊÈ{LÌÊ«ÀViÃÃÀ
Ê
UÊ >L}ÊÎ ÊvÊ«ÀViÃÃÊë>Vi
Ê
UÊ 1Ã}ÊiÀÞÊLiÞ`Ê{ ÊÜÌ
ÊÎÓLÌÊ-+Ê-iÀÛiÀ i̽ÃÊÌ>iÊ>ÊÊ>ÌÊi>V
ÊvÊÌ
iÃiÊÊÌÕÀ°
CHAPTER 2
S Y S T E M P E R F O R M A N C E A N A LY S I S
Optimizing Application Workload "«Ìâ}Ê>««V>ÌÊÜÀ>`ÊÃÊÌ
iÊÃÌÊivviVÌÛiÊÀiÃÕÌÊÃÌÊvÊÌ
iÊÌi]ÊLÕÌÊLiV>ÕÃiÊ vÊÌ
iÊV«iÝÌÞÊ>`ÊV
>i}iÃÊÛÛi`ÊÊÌ
ÃÊ«ÀViÃÃ]ÊÌÊÃÊÕÃÕ>ÞÊVÃ`iÀi`Ê>ÃÌ°Ê/Ê`iÌvÞÊÌ
iÊiÀÞÌiÃÛiʵÕiÀiÃ]ÊV>«ÌÕÀiÊ>ÊÌ
iÊ-+ʵÕiÀiÃÊÕÃ}Ê-+Ê*ÀviÀÊÜ
V
ÊÞÕÊ ÜÊi>ÀÊ
ÜÊÌÊÕÃiÊÊ
>«ÌiÀÊή]Ê>`ÊÌ
iÊ}ÀÕ«ÊÌ
iÊÌÀ>ViÊÕÌ«ÕÌÊÊÌ
iÊNa]`oÊVÕ°Ê/
iÊ queries with the highest number of logical reads contribute to most of the memory stress. You ÜÊÃiiÊ
ÜÊÌÊ«ÌâiÊÌ
ÃiʵÕiÀiÃÊÊÀiÊ`iÌ>ÊÌ
ÀÕ}
ÕÌÊÌ
ÃÊL°
Allocating More Memory to SQL Server As you learned in theʺ-+Ê-iÀÛiÀÊiÀÞÊ>>}iiÌ»ÊÃiVÌ]ÊÌ
i i]toanraniaiknu Vv}ÕÀ>ÌÊV>ÊÌÊÌ
iÊ>ÝÕÊÃâiÊvÊÌ
iÊ-+Ê-iÀÛiÀÊiÀÞÊ«°ÊvÊÌ
iÊiÀÞÊ ÀiµÕÀiiÌÊvÊ-+Ê-iÀÛiÀÊÃÊÀiÊÌ
>ÊÌ
iÊi]toanraniaiknu value, which you can tell through the number of hard page faults, then increasing the value will allow the memory pool ÌÊ}ÀÜ°Ê/ÊLiivÌÊvÀÊVÀi>Ã}ÊÌ
iÊi]toanraniaiknu value, ensure that enough physical memory is available in the system.
Increasing System Memory /
iÊiÀÞÊÀiµÕÀiiÌÊvÊ-+Ê-iÀÛiÀÊ`i«i`ÃÊÊÌ
iÊÌÌ>Ê>ÕÌÊvÊ`>Ì>Ê«ÀViÃÃi`ÊLÞÊ -+Ê>VÌÛÌiðÊÌÊÃÊÌÊ`ÀiVÌÞÊVÀÀi>Ìi`ÊÌÊÌ
iÊÃâiÊvÊÌ
iÊ`>Ì>L>ÃiÊÀÊÌ
iÊÕLiÀÊvÊV}Ê-+ʵÕiÀiðÊÀÊiÝ>«i]ÊvÊ>ÊiÀÞÌiÃÛiʵÕiÀÞÊ«iÀvÀÃÊ>ÊVÀÃÃÊÊLiÌÜiiÊÌÜÊ small tables without any filter criteria to narrow down the result set, it can cause high stress on the system memory. "iÊvÊÌ
iÊi>ÃiÃÌÊ>`ÊV
i>«iÃÌÊÀiÃÕÌÃÊÃÊÌÊëÞÊVÀi>ÃiÊÃÞÃÌiÊiÀÞ°ÊÜiÛiÀ]Ê it is still important to find out what is consuming the physical memory, because if the appliV>ÌÊÜÀ>`ÊÃÊiÝÌÀiiÞÊiÀÞÊÌiÃÛi]ÊÞÕÊÜÊÃÊLiÊÌi`ÊLÞÊÌ
iÊ>ÝÕÊ >ÕÌÊvÊiÀÞÊ>ÊÃÞÃÌiÊV>Ê>VViÃðÊ/Ê`iÌvÞÊÜ
V
ʵÕiÀiÃÊ>ÀiÊÕÃ}ÊÀiÊiÀÞ]Ê you can query the ouo*`i[ata_[mqanu[iaiknu[cn]jpoÊ 6°ÊÕÃÌÊLiÊV>ÀivÕÊÜ
iÊÀÕ}ʵÕiÀiÃÊ>}>ÃÌÊÌ
ÃÊ 6ÊÕÃ}Ê>ÊFKEJ or an KN@AN>U statement because, if your system is already under memory stress, these actions can lead to your query needing its own memory grant. -iÌ
}ÊÌÊii«ÊÊ`ÊÃÊÌ
iÊiÀÞÊii`i`ÊLÞÊ-+Ê-iÀÛiÀÊÕÌÃ`iÊÌ
iÊLÕvviÀÊ«°Ê /
ÃÊÃÊÀiviÀÀi`ÊÌÊ>ÃÊnonbuffer memory and used to be called IaiPkHa]ra°Ê"iÊvÊÌ
iÊÜÀÃÌÊ iÀÞÊ«ÀLiÃÊvÀʵÕiÀiÃÊVVÕÀÃÊÜ
iÊÌ
iÊÃâiÊvÊÌ
iʵÕiÀÞÊÌÃiv]ÊÌÊÌ
iÊ`>Ì>ÊÀiÌÕÀi`Ê LÕÌÊÌ
iÊ/-+ÊV`i]ÊiÝVii`ÃÊn ÊÊÃâi°Ê/
ÃÊiÀÞÊÃÊ>V>Ìi`Ê>ÌÊÃÌ>ÀÌÕ«ÊÕÃ}ÊÌ
iÊÌc ÃÜÌV
°ÊvÊÞÕÀʵÕiÀiÃÊ>ÀiÊ>À}iÊ>}>]Ê}Ài>ÌiÀÊÌ
>Ên ÊÊÃâi®]ÊÌ
iÞÊÜÊii`ÊÌÊÌ>iÊ>`Û>Ì>}iÊvÊÌ
ÃÊiÀÞÊ>V>Ì°Ê9ÕÊÜÊii`ÊÌÊ>iÊÃÕÀiÊÞÕÊ
>ÛiÊiÕ}
ÊiÀÞÊ>Û>>LiÊ ÊÞÕÀÊÃiÀÛiÀÊvÀÊÌ
ðÊ1Ã}ÊiÝÌi`i`ÊÃÌÀi`Ê«ÀVi`ÕÀiÃ]Ê`ÃÌÀLÕÌi`ʵÕiÀiÃ]Ê>`Ê>ÕÌ>tion objects also places a strain on this memory space.
Changing from a 32-bit to a 64-bit Processor Switching theʫ
ÞÃV>ÊÃiÀÛiÀÊvÀÊ>ÊÎÓLÌÊ«ÀViÃÃÀÊÌÊ>ÊÈ{LÌÊ«ÀViÃÃÀÊ>`ÊÌ
iÊ>ÌÌi`>ÌÊ 7`ÜÃÊ-iÀÛiÀÊÃvÌÜ>ÀiÊÕ«}À>`i®ÊÀ>`V>ÞÊV
>}iÃÊÌ
iÊiÀÞÊ>>}iiÌÊV>«>LÌiÃÊvÊ -+Ê-iÀÛiÀ°Ê/
iÊÌ>ÌÃÊÊ-+Ê-iÀÛiÀÊvÀÊiÀÞÊ}ÊvÀÊÎ ÊÌÊVÕÌ}ÊÕ«ÊÌÊÈ{ Ê of additional data cache available throughÊ``ÀiÃÃÊ7`Ü}Ê ÝÌiÃÃÊQ7 R®ÊÌÊ>ÊÌÊvÊ Õ«ÊÌÊn/ Ê`i«i`}ÊÊÌ
iÊÛiÀÃÊvÊÌ
iÊ«iÀ>Ì}ÊÃÞÃÌiÊ>`ÊÌ
iÊëiVvVÊ«ÀViÃÃÀÊÌÞ«i°Ê
29
30
C HAPTER 2
SYS TEM P ER FOR MA NC E A NA L YS IS
Ì>ÌÃÊiÝ«iÀiVi`ÊÜÌ
ÊÌ
iÊ7 Ê>ÀiÊÀiÛi`ÊÃViÊÌ
iÊiÀÞÊÕÃi`ÊÊÈ{LÌÊÃÞÃÌiÃÊ V>ÊLiÊÕÃi`ÊvÀÊLÌ
Ê`>Ì>Ê>`ÊÌ
iÊ«ÀVi`ÕÀiÊV>V
i]ÊÕiÊ7 ÊiÀÞ°Ê ÊÌ
iÀÊÃÜÌV
iÃÊÀÊ iÝÌÀ>ÊiV
>ÃÃÊ>ÀiÊii`i`ÊvÀÊ-+Ê-iÀÛiÀÊÌÊÌ>iÊ>`Û>Ì>}iÊvÊÌ
ÃÊiÀÞÊë>Vi°
Enabling 3GB of Process Space -Ì>`>À`ÊÎÓLÌÊ>``ÀiÃÃiÃÊV>Ê>«Ê>Ê>ÝÕÊvÊ{ ÊvÊiÀÞ°Ê/
iÊÃÌ>`>À`Ê>``ÀiÃÃÊ Ã«>ViÃÊvÊÎÓLÌÊ7`ÜÃÊ«iÀ>Ì}ÊÃÞÃÌiÃÊ«ÀViÃÃiÃÊ>ÀiÊÌ
iÀivÀiÊÌi`ÊÌÊ{ °Ê"ÕÌÊvÊ Ì
ÃÊ{ Ê«ÀViÃÃÊë>Vi]ÊLÞÊ`iv>ÕÌÊÌ
iÊÕ««iÀÊÓ ÊÃÊÀiÃiÀÛi`ÊvÀÊÌ
iÊ«iÀ>Ì}ÊÃÞÃÌi]Ê>`ÊÌ
iÊ ÜiÀÊÓ ÊÃÊ>`iÊ>Û>>LiÊÌÊÌ
iÊ>««V>Ì°ÊvÊÞÕ specify a +/C> switch in the ^kkp*eje file vÊÌ
iÊÎÓLÌÊ"-]ÊÌ
iÊ«iÀ>Ì}ÊÃÞÃÌiÊÀiÃiÀÛiÃÊÞÊ£ ÊvÊÌ
iÊ>``ÀiÃÃÊë>Vi]Ê>`ÊÌ
iÊ>««V>ÌÊV>Ê>VViÃÃÊÕ«ÊÌÊÎ °Ê/
ÃÊÃÊ>Ã called 4-gig tuningÊ{/®°Ê ÊiÜÊ*ÃÊ>ÀiÊÀiµÕÀi`Ê for this purpose. /
iÀivÀi]ÊÊ>Ê>V
iÊÜÌ
Ê{ ÊvÊ«
ÞÃV>ÊiÀÞÊ>`ÊÌ
iÊ`iv>ÕÌÊ7`ÜÃÊVv}ÕÀ>Ì]ÊÞÕÊÜÊv`Ê>Û>>LiÊiÀÞÊvÊ>LÕÌÊÓ ÊÀÊÀi°Ê/ÊiÌÊ-+Ê-iÀÛiÀÊÕÃiÊÕ«ÊÌÊÎ Ê of the available memory, you can add the +/C> switch in the ^kkp*eje file as follows: W^kkphk]`anY peiakqp9/, `ab]qhp9iqhpe$,%`eog$,%n`eog$,%l]npepekj$-%XSEJJP Wklan]pejcouopaioY iqhpe$,%`eog$,%n`eog$,%l]npepekj$-%XSEJJP9 Ie_nkokbpSej`ksoOanran.,,4=`r]j_a`Oanran +b]op`apa_p+/C> /
iÊ+/C>ÊÃÜÌV
ÊÃ
Õ`ÊÌÊLiÊÕÃi`ÊvÀÊÃÞÃÌiÃÊÜÌ
ÊÀiÊÌ
>Ê£È ÊvÊ«
ÞÃV>ÊiÀÞ]Ê >ÃÊiÝ«>i`ÊÊÌ
iÊvÜ}ÊÃiVÌ]ÊÀÊvÀÊÃÞÃÌiÃÊÌ
>ÌÊÀiµÕÀiÊ>Ê
}
iÀÊ>ÕÌÊvÊiÀiÊ memory. -+Ê-iÀÛiÀÊÓäänÊÊÈ{LÌÊÃÞÃÌiÃÊV>ÊÃÕ««ÀÌÊÕ«ÊÌÊn/ ÊÊ>ÊÝÈ{Ê«>ÌvÀÊ>`ÊÕ«ÊÌÊ Ç/ ÊÊÌ
iÊÈ{°Ê/
iÀiÊÃÊ>ÊvÕÀÌ
iÀÊÌÊÊ7`ÜÃÊÓääÎÊvÊ£/ °ÊÃÊÀiÊiÀÞÊÃÊ>Û>>LiÊ vÀÊÌ
iÊ"-]ÊÌ
iÊÌÃÊ«Ãi`ÊLÞÊ-+Ê-iÀÛiÀÊ>ÀiÊÀi>V
i`°Ê/
ÃÊÃÊÜÌ
ÕÌÊ
>Û}ÊÌÊÕÃiÊ>ÞÊ Ì
iÀÊÃÜÌV
iÃÊÀÊiÝÌi`i`ÊiÀÞÊÃV
iið
Using Memory Beyond 4GB Within SQL Server All processors basedÊÊÌ
iÊÎÓÊ>ÀV
ÌiVÌÕÀi]ÊLi}}ÊÜÌ
ÊÌ
iÊÌiÊ*iÌÕÊ*À]ÊÃÕ««ÀÌÊ a new ÎÈLÌÊ«
ÞÃV>Ê>``ÀiÃÃ}Ê`iÊV>i`Ê*
ÞÃV>Ê``ÀiÃÃÊ ÝÌiÃÊ* ®°Ê* Ê>ÜÃÊ Õ«ÊÌÊÈ{ ÊvÊ«
ÞÃV>ÊiÀÞ]Ê`i«i`}ÊÕ«ÊÌ
iÊ«iÀ>Ì}ÊÃÞÃÌi°Ê/
iÊ* Ê`iÊiÀiÊ ÀiµÕÀiÃÊ>ÊÌiÊÀV
ÌiVÌÕÀiÊ«ÀViÃÃÀÊ*iÌÕÊ*ÀÊÀÊiÜiÀ®Ê>`ÊÀiÊÌ
>Ê{ ÊvÊ,° 7`ÜÃÊ-iÀÛiÀÊ«iÀ>Ì}ÊÃÞÃÌiÃÊÌ
>ÌÊ>ÀiÊÎÓLÌÊ«iiÌÊAddress Windowing
ÝÌiÃÃÊÌÊ>VViÃÃÊÕ«ÊÌÊÈ{ ÊvÊ«
ÞÃV>ÊiÀÞÊÜÌ
ÊÌ
iÊ* Ê>``ÀiÃÃ}Ê`i°Ê/
ÃÊ memory is available for the buffer cache, for data storage only, but cannot be used by operaÌÃÊ>`ÊÌ>ÃÃÊÌÊVÀi>ÃiÊÌ
iÊÕLiÀÊv]ÊvÀÊiÝ>«i]Ê`>Ì>L>ÃiÃÊ>Û>>LiÊÊÌ
iÊÃÞÃÌi°
CHAPTER 2
S Y S T E M P E R F O R M A N C E A N A LY S I S
VViÃÃ}ÊiÀÞÊLiÞ`Ê{ ÊÊ>ÊÎÓLÌÊ7`ÜÃÊ-iÀÛiÀÊ"-ÊÀiµÕÀiÃÊVv}ÕÀ>ÌÊ>ÌÊ ÌÜÊiÛiÃ\ÊÌ
iÊ«iÀ>Ì}ÊÃÞÃÌiÊiÛiÊ>`ÊÌ
iÊ>««V>ÌÊiÛi°Ê/Êi>LiÊÌ
iÊ«iÀ>Ì}ÊÃÞÃÌiÊ ÌÊ>VViÃÃÊÀiÊÌ
>Ê{ ÊvÊ«
ÞÃV>ÊiÀÞ]Ê>``Ê>Ê+L=A switch in the ^kkp*eje file as follows: W^kkphk]`anY peiakqp9/, `ab]qhp9iqhpe$,%`eog$,%n`eog$,%l]npepekj$-%XSEJJP Wklan]pejcouopaioY iqhpe$,%`eog$,%n`eog$,%l]npepekj$-%XSEJJP9 Ie_nkokbpSej`ksoOanran.,,4=`r]j_a`Oanran +b]op`apa_p+L=A "ViÊÞÕÊ
>ÛiÊ`vi`Ê^kkp*eje, you can use ol[_kjbecqnaÊÌÊi>LiÊ-+Ê-iÀÛiÀÊÓäänÊÌÊ >VViÃÃÊÀiÊÌ
>Ê{ ÊvÊ«
ÞÃV>ÊiÀÞ°Ê/
iÊvÜ}ÊiÝ>«iÊoap[1CecIaiknu*omh in the `Ü>`®ÊÃ
ÜÃÊ
ÜÊÌÊi>LiÊx \ ol[_kjbecqna#odks]`r]j_a`klpekjo#(NA?KJBECQNA CK ol[_kjbecqna#]saaj]^ha`#(NA?KJBECQNA CK ol[_kjbecqna#i]toanraniaiknu#(1-., NA?KJBECQNA CK /
iÊ«iÀ>Ì}ÊÃÞÃÌiÊÕÃÌÊLiÊÀiÃÌ>ÀÌi`ÊvÀÊÌ
iÊ+L=AÊÃÜÌV
ÊÌÊÌ>iÊivviVÌ°Ê/
iÊ>ÃÃV>Ìi`Ê ÀiÃÌ>ÀÌÊvÊ-+Ê-iÀÛiÀÊ«ÕÌÃÊÌ
iÊ7 ÊVv}ÕÀ>ÌÊÌÊivviVÌ° ÃÌ>ViÃÊvÊ-+Ê-iÀÛiÀÊÓäänÊ`ÊÌÊ`Þ>V>ÞÊ>>}iÊÌ
iÊÃâiÊvÊÌ
iÊ>``ÀiÃÃÊë>ViÊ when AWE memory is enabled; therefore, setting the i]toanraniaiknu configuration «>À>iÌiÀÊvÊ-+Ê-iÀÛiÀÊÃÊ>`>ÌÀÞÊÜ
iÊÕÃ}Ê7 ÊiÀÞ°Ê"Ê>Ê`i`V>Ìi`Ê-+Ê-iÀÛiÀÊ machine, i]toanraniaiknu may be set to $pkp]hlduoe_]hiaiknuÌ.,,I>% so that enough «
ÞÃV>ÊiÀÞÊÃÊi«ÌÊ>Ã`iÊvÀÊÌ
iÊ«iÀ>Ì}ÊÃÞÃÌiÊ>`ÊÌ
iÀÊiÃÃiÌ>ÊÌÃÉ>««V>Ìð 7
iÊÀÕ}ÊÕÌ«iÊÃÌ>ViÃÊvÊ-+Ê-iÀÛiÀÊÓäänÊÊÌ
iÊÃ>iÊV«ÕÌiÀ]ÊÞÕÊÕÃÌÊ ensure the following with each instance using AWE memory: Ê
UÊ >V
ÊÃÌ>ViÊ
>ÃÊ>Êi]toanraniaiknu setting.
Ê
UÊ 7
iÊÃiÌÌ}Êi]toanraniaiknu]ÊÞÕÊii`ÊÌÊÌ>iÊÌÊ>VVÕÌÊÌ
iÊ>ÕÌÊvÊ nonbuffer pool memory (formerly called IaiPkHa]ra®ÊÌ
>ÌÊÞÕÊ>ÞÊii`ÊvÀÊ-+Ê Server to operate.
Ê
UÊ /
iÊÃÕÊvÊÌ
iÊi]toanraniaiknu values for all the instances should be less than the amount of physical memory in the computer.
Ã`iÀ}ÊÌ
iÊ«ÀiVi`}Êv>VÌÀÃ]ÊÊ>Ê-+Ê-iÀÛiÀÊÓäänÊVÕÃÌiÀÊiÛÀiÌÊÜÌ
ÊÌÜÊ nodes and AWE/PAE enabled, the i]toanraniaiknu value on each node should be less than half the system physical memory.
31
32
C HAPTER 2
SYS TEM P ER FOR MA NC E A NA L YS IS
Note If you use the +/C> feature along with AWE/PAE, then an instance of SQL Server will be limited to a maximum of 16GB of extended memory. This limitation is because of the internal design of the Windows Server operating system. Limiting the system space within the process space to 1GB using the +/C> switch allows the Windows Server operating system to manage physical memory up to 16GB. Therefore, to access memory beyond 16GB, you should not use the +/C> switch.
Disk Bottleneck Analysis -+Ê-iÀÛiÀÊÃÊ>Ê
i>ÛÞÊÕÃiÀÊvÊÌ
iÊ
>À`Ê`Ã]Ê>`ÊÃViÊ`ÃÊëii`ÃÊ>ÀiÊV«>À>ÌÛiÞÊÕV
Ê ÃÜiÀÊÌ
>ÊiÀÞÊ>`Ê«ÀViÃÃÀÊëii`Ã]Ê>ÊVÌiÌÊÊ`ÃÊÀiÃÕÀViÃÊV>ÊÃ}vV>ÌÞÊ `i}À>`iÊ-+Ê-iÀÛiÀÊ«iÀvÀ>Vi°Ê>ÞÃÃÊ>`ÊÀiÃÕÌÊvÊ>ÞÊ`ÃÊÀiÃÕÀViÊLÌÌiiVÊV>Ê «ÀÛiÊ-+Ê-iÀÛiÀÊ«iÀvÀ>ViÊÃ}vV>ÌÞ°
Disk Counters /Ê>>ÞâiÊ`ÃÊ«iÀvÀ>Vi]ÊÞÕÊV>ÊÕÃiÊÌ
iÊVÕÌiÀÃÊÃ
ÜÊÊ/>LiÊÓÓ° Table 2-2. Performance Monitor Counters to Analyze I/O Pressure
Object(Instance[,InstanceN]) Counter
Description
Value
Lduoe_]h@eog$@]p])`eog( !@eogPeia Hkc)`eog%
Percentage of time `ÃÊÜ>ÃÊLÕÃÞ
Average Û>ÕiÊÊnx¯
?qnnajp@eogMqaqaHajcpd
ÕLiÀ of ÕÌÃÌ>`}Ê`ÃÊ requests at the time performance data is collected
Average value < 2 «iÀÊ`Ã
=rc*@eogMqaqaHajcpd
Average number vʵÕiÕi`Ê`ÃÊ requests during the sample interval
Average value < 2 «iÀÊ`Ã
@eogPn]jobano+oa_
Rate of read/write «iÀ>ÌÃÊÊ`Ã
>ÝÕÊ value < 100 «iÀÊ`Ã
@eog>upao+oa_
Amount of data transfer to/from «iÀÊ`ÃÊ«iÀÊÃiV`
>ÝÕÊ value < 10MB per second
=rc*@eogOa_+Na]`
Average time in ms ÌÊÀi>`ÊvÀÊ`Ã
Average value < 10 ms
=rc*@eogOa_+Snepa
Average time in ms ÌÊÜÀÌiÊÌÊ`Ã
Average value < 10 ms
CHAPTER 2
S Y S T E M P E R F O R M A N C E A N A LY S I S
/
iÊLduoe_]h@eogÊVÕÌiÀÃÊÀi«ÀiÃiÌÊÌ
iÊ>VÌÛÌiÃÊÊ>Ê«
ÞÃV>Ê`ðÊHkce_]h@eog counÌiÀÃÊÀi«ÀiÃiÌÊ}V>ÊÃÕLÕÌÃÊÀÊ«>ÀÌÌîÊVÀi>Ìi`ÊÊ>Ê«
ÞÃV>Ê`ðÊvÊÞÕÊVÀi>ÌiÊÌÜÊ partitions, say ?6 and @6ÊÊ>Ê«
ÞÃV>Ê`Ã]ÊÌ
iÊÞÕÊV>ÊÌÀÊÌ
iÊ`ÃÊ>VÌÛÌiÃÊvÊÌ
iÊ `Û`Õ>Ê}V>Ê`ÃÃÊÕÃ}Ê}V>Ê`ÃÊVÕÌiÀðÊÜiÛiÀ]ÊLiV>ÕÃiÊ>Ê`ÃÊLÌÌiiVÊÕÌ>ÌiÞÊVVÕÀÃÊÊÌ
iʫ
ÞÃV>Ê`Ã]ÊÌÊÊÌ
iÊ}V>Ê`Ã]ÊÌÊÃÊÕÃÕ>ÞÊ«ÀiviÀ>LiÊÌÊÕÃiÊÌ
iÊ Lduoe_]h@eog counters. ÌiÊÌ
>ÌÊvÀÊ>Ê
>À`Ü>ÀiÊÀi`Õ`>ÌÊ>ÀÀ>ÞÊvÊ`i«i`iÌÊ`ÃÃÊ, ®ÊÃÕLÃÞÃÌiÊÃiiÊ Ì
iʺ1Ã}Ê>Ê, ÊÀÀ>Þ»ÊÃiVÌÊvÀÊÀiÊÊ, ®]ÊÌ
iÊVÕÌiÀÃÊÌÀi>ÌÊÌ
iÊ>ÀÀ>ÞÊ>ÃÊ>ÊÃ}iÊ «
ÞÃV>Ê`ðÊÀÊiÝ>«i]ÊiÛiÊvÊÞÕÊ
>ÛiÊÌiÊ`ÃÃÊÊ>Ê, ÊVv}ÕÀ>Ì]ÊÌ
iÞÊÜÊ>ÊLiÊ Ài«ÀiÃiÌi`Ê>ÃÊiÊ«
ÞÃV>Ê`ÃÊÌÊÌ
iÊ«iÀ>Ì}ÊÃÞÃÌi]Ê>`ÊÃÕLÃiµÕiÌÞÊÞÕÊÜÊ
>ÛiÊÞÊ one set of Lduoe_]h@eogÊVÕÌiÀÃÊvÀÊÌ
>ÌÊ, ÊÃÕLÃÞÃÌi°Ê/
iÊÃ>iÊ«ÌÊ>««iÃÊÌÊstorage >Ài>ÊiÌÜÀÊ- ®Ê`ÃÃÊÃiiÊÌ
iʺ1Ã}Ê>Ê- Ê-ÞÃÌi»ÊÃiVÌÊvÀÊëiVvVî°
% Disk Time /
iÊ!@eogPeia counter monitorsÊÌ
iÊ«iÀViÌ>}iÊvÊÌiÊÌ
iÊ`ÃÊÃÊLÕÃÞÊÜÌ
ÊÀi>`ÉÜÀÌiÊ >VÌÛÌiðÊÌÊÃ
Õ`ÊÌÊLiÊVÌÕÕÃÞÊ
}
°ÊvÊÌ
ÃÊVÕÌiÀÊÛ>ÕiÊÃÊVÃÃÌiÌÞÊÀiÊÌ
>ÊnxÊ «iÀViÌ]ÊÌ
iÊÞÕÊÕÃÌÊÌ>iÊÃÌi«ÃÊÌÊLÀ}ÊÌÊ`Ü°Ê9ÕÊVÕ`ÊÕ«}À>`iÊÌ
iÊ`ÃÊÃÕLÃÞÃÌi]ÊLÕÌÊ >ÊÀiÊivviVÌÛiÊÃÕÌÊÃÊÌÊ>Û`Ê}}ÊÌÊÌ
iÊ`>Ì>Ê`ÃÊ>ÃÊÕV
Ê>ÃÊ«ÃÃLi°Ê ÛiÊ>ÊVÃÃÌiÌÊ`ÃÊÕÃ>}iÊvÊxÊ«iÀViÌÊ>ÞÊ>`ÛiÀÃiÞÊ>vviVÌÊ«iÀvÀ>Vi° ÀÊ«iÀvÀ>Vi]ÊÌÊÃÊ>Ü>ÞÃÊLiivV>ÊÌÊV>V
iÊÌ
iÊ`ÃÊVÌiÌÃÊÊiÀÞÊÃViÊ`ÃÊ access is in the order of milliseconds, whereas memory access is in the order of nanoseconds. -+Ê-iÀÛiÀÊ>`«ÌÃÊÌ
ÃÊÃÌÀ>Ìi}ÞÊLÞÊV>V
}ÊÌ
iÊ`>Ì>Ê«>}iÃÊÊÌÃÊLÕvviÀÊV>V
i°Ê ÕÌÊvÊ-+Ê-iÀÛiÀÊ
>ÃÊÌÊ}ÊÌÊ`ÃÊvÌi]Ê>ÃÊ`V>Ìi`ÊLÞÊ>Ê
}
Ê!@eogPeia value, then the slow access time of Ì
iÊ`ÃÊV«>Ài`ÊÌÊÌ
>ÌÊvÊÌ
iÊiÀÞÊÜÊ
ÕÀÌÊÌ
iÊ«iÀvÀ>ViÊvÊÌ
iÊ`>Ì>L>Ãi°
Current Disk Queue Length ?qnnajp@eogMqaqaHajcpd isÊÌ
iÊÕLiÀÊvÊÀiµÕiÃÌÃÊÕÌÃÌ>`}ÊÊÌ
iÊ`ÃÊÃÕLÃÞÃÌiÊ>ÌÊ the time the performance data is collected. It includes requests in service at the time of the Ã>«Ã
Ì°ÊÊ`ÃÊÃÕLÃÞÃÌiÊÜÊ
>ÛiÊÞÊiÊ`ÃʵÕiÕi°Ê9ÕÊÃ
Õ`ÊÕÃiÊÌ
ÃÊVÕÌiÀÊÛ>ÕiÊ to support the conclusion made from the !@eogPeiaÊVÕÌiÀ°ÊÊVÃÃÌiÌÊ`ÃʵÕiÕiÊi}Ì
Ê vÊÌÜÊ«iÀÊ`ÃÊ`V>ÌiÃÊÌ
iÊ«ÃÃLÌÞÊvÊ>ÊLÌÌiiVÊÊÌ
iÊ`ð ÀÊiÝ>«i]ÊÊ>Ê, ÊVv}ÕÀ>ÌÊÜÌ
Ê£äÊ`ÃÃ]Ê>ÊVÃÃÌiÌÊ`ÃʵÕiÕiÊi}Ì
ÊvÊ ÓÊ«iÀÊ`Ã]ÊÀÊÓäÊrÊ£äÊ`ÃÃÊ ÊÓÊ«iÀÊ`Ãî]ÊvÀÊÌ
iÊV«iÌiÊ`ÃÊÃÕLÃÞÃÌiÊ`V>ÌiÃÊ>Ê`ÃÊ LÌÌiiV°Ê, ÊVÌÀiÀÃÊÕÃÕ>ÞÊ`ÃÌÀLÕÌiÊ`ÃÊÉ"ÃÊÕvÀÞÊ>VÀÃÃÊ>Ê`ÃÃÊÊÌ
iÊ`ÃÊ ÃÕLÃÞÃÌi]ÊLÕÌÊLiV>ÕÃiÊvÊÌ
iÊLÛÕÃÊV
>ViÃÊvÊÕvÀÊ`ÃÊÉ"]ÊVÃ`iÀÊÌ
iÊvÜ}Ê range of values for the ?qnnajp@eogMqaqaHajcpd counter: Ê
UÊ iÃÃÊÌ
>ÊÓʳÊÊvÊë`iîÊÃÊ>Êexcellent value to see. In the worst case, the two ÀiµÕiÃÌÃÊÊÌ
iʵÕiÕiÊ>ÞÊLiÊ«i`}ÊÊÌ
iÊÃ>iÊ`ð
Ê
UÊ iÃÃÊÌ
>ÊÓÊ ÊÊvÊë`iîÊÃÊ>Êfair value to see. In the best case, the queued requests >ÞÊLiÊÕvÀÞÊ`ÃÌÀLÕÌi`Ê>VÀÃÃÊ>ÊÌ
iÊ`ÃÃÊÊÌ
iÊ`ÃÊÃÕLÃÞÃÌiÊÜÌ
ÊÌÜÊ ÀiµÕiÃÌÃÊ«i`}Ê«iÀÊ`ð
33
34
C HAPTER 2
SYS TEM P ER FOR MA NC E A NA L YS IS
vÊÞÕÊ`ÊÌÊÃÕëiVÌÊ`Þ>VÊÛ>À>ÌÊÊ`ÃÊ>`Ã]ÊÌ
iÊÞÕÊ>ÞÊÕÃiÊÌ
iÊ=rc*@eog MqaqaHajcpd counter since this counter represents the average of the instantaneous values provided by the ?qnnajp@eogMqaqaHajcpdÊVÕÌiÀ°ÊvÊÞÕÊÌ
ÊÞÕÊ>ÞÊ
>ÛiÊ>Ê`ÃÊÃÃÕiÊ L>Ãi`ÊÊÌ
iʵÕiÕiÊi}Ì
]ÊÞÕÊÃ
Õ`Ê>ÃÊV
iVÊÌ
iÊ=ran]ca@eogOa_kj`oLanPn]joban valueÊvÀÊÌ
iÊÃ>iÊ`ðÊ>Þ]ÊV
iVÊÌ
iÊÜ>ÌÊÃÌ>ÌiÃÊvÊÞÕÀÊ«ÀViÃÃiÃÊÌÊÃiiÊÜ
iÌ
iÀÊÞÕÊ>ÀiÊ iÝ«iÀiV}ÊÉ"ÊÜ>Ìð
Disk Transfers/sec @eogPn]jobano+oa_ monitorsÊÌ
iÊÀ>ÌiÊvÊÀi>`Ê>`ÊÜÀÌiÊ«iÀ>ÌÃÊÊÌ
iÊ`ðÊÊÌÞ«V>Ê`à Ì`>ÞÊV>Ê`Ê>LÕÌÊ£näÊ`ÃÊÌÀ>ÃviÀÃÊ«iÀÊÃiV`ÊvÀÊÃiµÕiÌ>ÊÉ"Ê>`Ê£ääÊ`ÃÊÌÀ>ÃviÀÃÊ«iÀÊ ÃiV`ÊvÀÊÀ>`ÊÉ"°ÊÊÌ
iÊV>ÃiÊvÊÀ>`ÊÉ"]Ê@eogPn]jobano+oa_ is lower because more `ÃÊ>ÀÊ>`Ê
i>`ÊÛiiÌÃÊ>ÀiÊÛÛi`°Ê"/*ÊÜÀ>`Ã]ÊÜ
V
Ê>ÀiÊÜÀ>`ÃÊvÀÊ`}Ê mainly singleton operations, small operations, and random access, are typically constrained LÞÊ`ÃÊÌÀ>ÃviÀÃÊ«iÀÊÃiV`°Ê-]ÊÊÌ
iÊV>ÃiÊvÊ>Ê"/*ÊÜÀ>`]ÊÞÕÊ>ÀiÊÀiÊVÃÌÀ>i`ÊLÞÊ Ì
iÊv>VÌÊÌ
>ÌÊ>Ê`ÃÊV>Ê`ÊÞÊ£ääÊ`ÃÊÌÀ>ÃviÀÃÊ«iÀÊÃiV`ÊÌ
>ÊLÞÊÌÃÊÌ
ÀÕ}
«ÕÌÊëiVvV>tion of 10MB per second. iV>ÕÃiÊvÊÌ
iÊ
iÀiÌÊÃÜiÃÃÊvÊ>Ê`Ã]ÊÌÊÃÊÀiVi`i`ÊÌ
>ÌÊÞÕÊii«Ê`ÃÊÌÀ>ÃviÀÃÊ «iÀÊÃiV`Ê>ÃÊÜÊ>ÃÊ«ÃÃLi°Ê9ÕÊÜÊÃiiÊ
ÜÊÌÊ`ÊÌ
ÃÊiÝÌ°
Disk Bytes/sec /
iÊ@eog>upao+oa_ counter monitors the rate at which bytes are transferred to or from the `ÃÊ`ÕÀ}ÊÀi>`ÊÀÊÜÀÌiÊ«iÀ>ÌðÊÊÌÞ«V>Ê`ÃÊV>ÊÌÀ>ÃviÀÊ>LÕÌÊ£ä Ê«iÀÊÃiV`°Ê iiÀ>Þ]Ê"/*Ê>««V>ÌÃÊ>ÀiÊÌÊVÃÌÀ>i`ÊLÞÊÌ
iÊ`ÃÊÌÀ>ÃviÀÊV>«>VÌÞÊvÊÌ
iÊ`ÃÊ ÃÕLÃÞÃÌiÊÃViÊÌ
iÊ"/*Ê>««V>ÌÃÊ>VViÃÃÊÃ>Ê>ÕÌÃÊvÊ`>Ì>ÊÊ`Û`Õ>Ê`>Ì>L>ÃiÊ ÀiµÕiÃÌðÊvÊÌ
iÊ>ÕÌÊvÊ`>Ì>ÊÌÀ>ÃviÀÊiÝVii`ÃÊÌ
iÊV>«>VÌÞÊvÊÌ
iÊ`ÃÊÃÕLÃÞÃÌi]ÊÌ
iÊ >ÊL>V}ÊÃÌ>ÀÌÃÊ`iÛi«}ÊÊÌ
iÊ`ÃÊÃÕLÃÞÃÌi]Ê>ÃÊÀiviVÌi`ÊLÞÊÌ
iÊ@eogMqaqaHajcpd counters.
Avg. Disk Sec/Read and Avg. Disk Sec/Write =rc*@eogOa_+Na]` and =rc*@eogOa_+SnepaÊÌÀ>VÊÌ
i average amount of timeÊÌÊÌ>iÃÊÊ ÃiV`ÃÊÌÊÀi>`ÊvÀÊÀÊÜÀÌiÊÌÊ>Ê`ðÊ>Û}Ê>ÊÕ`iÀÃÌ>`}ÊvÊÕÃÌÊ
ÜÊÜiÊÌ
iÊ `ÃÃÊ>ÀiÊ
>`}ÊÌ
iÊÜÀÌiÃÊ>`ÊÀi>`ÃÊÌ
>ÌÊÌ
iÞÊÀiViÛiÊV>ÊLiÊ>ÊÛiÀÞÊÃÌÀ}Ê`V>ÌÀÊvÊ Ü
iÀiÊ«ÀLiÃÊi°ÊvÊ̽ÃÊÌ>}ÊÀiÊÌ
>Ê>LÕÌÊ£äÊÃÊÌÊÛiÊÌ
iÊ`>Ì>ÊvÀÊÀÊÌÊÞÕÀÊ `Ã]ÊÞÕÊ>ÞÊii`ÊÌÊÌ>iÊ>ÊÊ>ÌÊÌ
iÊ
>À`Ü>ÀiÊ>`ÊVv}ÕÀ>ÌÊÌÊLiÊÃÕÀiÊiÛiÀÞÌ
}Ê ÃÊÜÀ}ÊVÀÀiVÌÞ°Ê9Õ½Êii`ÊÌÊ}iÌÊiÛiÊLiÌÌiÀÊÀiëÃiÊÌiÃÊvÀÊÌ
iÊÌÀ>Ã>VÌÊ}ÊÌÊ perform well.
CHAPTER 2
S Y S T E M P E R F O R M A N C E A N A LY S I S
Disk Bottleneck Resolutions A few of the commonÊ`ÃÊLÌÌiiVÊÀiÃÕÌÃÊ>ÀiÊ>ÃÊvÜÃ\ Ê
UÊ "«Ìâ}Ê>««V>ÌÊÜÀ>`
Ê
UÊ 1Ã}Ê>Êv>ÃÌiÀÊ`ÃÊ`ÀÛi
Ê
UÊ 1Ã}Ê>Ê, Ê>ÀÀ>Þ
Ê
UÊ 1Ã}Ê>Ê- ÊÃÞÃÌi
Ê
UÊ }}Ê`ÃÃÊ«À«iÀÞ
Ê
UÊ 1Ã}Ê>ÊL>ÌÌiÀÞL>Vi`ÊVÌÀiÀÊV>V
i
Ê
UÊ ``}ÊÃÞÃÌiÊiÀÞ
Ê
UÊ Ài>Ì}ÊÕÌ«iÊviÃÊ>`Êvi}ÀÕ«Ã
Ê
UÊ *>V}ÊÌ
iÊÌ>LiÊ>`ÊÌ
iÊ`iÝÊvÀÊÌ
>ÌÊÌ>LiÊÊ`vviÀiÌÊ`ÃÃ
Ê
UÊ ->Û}ÊÌ
iÊ}ÊviÊÌÊ>ÊÃi«>À>ÌiÊ«
ÞÃV>Ê`ÀÛi
Ê
UÊ 1Ã}Ê«>ÀÌÌi`ÊÌ>Lià ½ÊÜÊÜ>ÊÞÕÊÌ
ÀÕ}
Êi>V
ÊvÊÌ
iÃiÊÀiÃÕÌÃÊÊÌÕÀ°
Optimizing Application Workload I cannot stressÊiÕ}
Ê
ÜÊ«ÀÌ>ÌÊÌÊÃÊÌÊ«ÌâiÊ>Ê>««V>̽ÃÊÜÀ>`ÊÊÀiÃÛ}Ê >Ê«iÀvÀ>ViÊÃÃÕi°Ê/
iʵÕiÀiÃÊÜÌ
ÊÌ
iÊ
}
iÃÌÊÕLiÀÊvÊÀi>`ÃÊÜÊLiÊÌ
iÊiÃÊÌ
>ÌÊV>ÕÃiÊ >Ê}Ài>ÌÊ`i>ÊvÊ`ÃÊÉ"°Ê½ÊVÛiÀÊÌ
iÊÃÌÀ>Ìi}iÃÊvÀÊ«Ìâ}ÊÌ
ÃiʵÕiÀiÃÊÊÀiÊ`iÌ>Ê Ì
ÀÕ}
ÕÌÊÌ
iÊÀiÃÌÊvÊÌ
ÃÊL°
Using a Faster Disk Drive "iÊvÊÌ
iÊi>ÃiÃÌÊÀiÃÕÌÃ]Ê>`ÊiÊÌ
>ÌÊÞÕÊÜÊ>`«ÌÊÃÌÊvÊÌ
iÊÌi]ÊÃÊÌÊÕÃiÊ`ÃÊ `ÀÛiÃÊÜÌ
Êv>ÃÌiÀÊ`ÃÊÌÀ>ÃviÀÃÊ«iÀÊÃiV`°ÊÜiÛiÀ]ÊÞÕÊÃ
Õ`ÊÌÊÕÃÌÊÕ«}À>`iÊ`ÃÊ`ÀÛiÃÊ ÜÌ
ÕÌÊvÕÀÌ
iÀÊÛiÃÌ}>ÌÆÊÞÕÊii`ÊÌÊv`ÊÕÌÊÜ
>ÌÊÃÊV>ÕÃ}ÊÌ
iÊÃÌÀiÃÃÊÊÌ
iÊ`ð
Using a RAID Array "iÊÜ>ÞÊvÊLÌ>}Ê`ÃÊÉ"Ê«>À>iÃÊÃÊÌÊVÀi>ÌiÊ>ÊÃ}iÊ«ÊvÊ`ÀÛiÃÊÌÊÃiÀÛiÊ>Ê-+Ê -iÀÛiÀÊ`>Ì>L>ÃiÊviÃ]ÊiÝVÕ`}ÊÌÀ>Ã>VÌÊ}ÊviðÊ/
iÊ«ÊV>ÊLiÊ>ÊÃ}iÊ, Ê>ÀÀ>Þ]Ê Ü
V
ÊÃÊÀi«ÀiÃiÌi`ÊÊ7`ÜÃÊ-iÀÛiÀÊÓäänÊ>ÃÊ>ÊÃ}iÊ«
ÞÃV>Ê`ÃÊ`ÀÛi°Ê/
iÊivviVÌÛiiÃÃÊ vÊ>Ê`ÀÛiÊ«Ê`i«i`ÃÊÊÌ
iÊVv}ÕÀ>ÌÊvÊÌ
iÊ, Ê`Ãð "ÕÌÊvÊ>Ê>Û>>LiÊ, ÊVv}ÕÀ>ÌÃ]ÊÌ
iÊÃÌÊVÞÊÕÃi`Ê, ÊVv}ÕÀ>ÌÃÊ >ÀiÊÌ
iÊvÜ}ÊÃiiÊ}ÕÀiÊÓÈ®\ Ê
UÊ RAID 0: Striping with no fault tolerance
Ê
UÊ RAID 1: Mirroring
Ê
UÊ RAID 5: Striping with parity
Ê
UÊ RAID 1+0: Striping with mirroring
35
36
C HAPTER 2
SYS TEM P ER FOR MA NC E A NA L YS IS
Figure 2-6. RAID configurations
RAID 0 Since thisÊ, ÊVv}ÕÀ>ÌÊ
>ÃÊÊv>ÕÌÊÌiÀ>Vi]ÊÞÕÊV>ÊÕÃiÊÌÊÞÊÊÃÌÕ>ÌÃÊÜ
iÀiÊ Ì
iÊÀi>LÌÞÊvÊ`>Ì>ÊÃÊÌÊ>ÊVViÀ°Ê/
iÊv>ÕÀiÊvÊ>ÞÊ`ÃÊÊÌ
iÊ>ÀÀ>ÞÊÜÊV>ÕÃiÊV«iÌiÊ `>Ì>ÊÃÃÊÊÌ
iÊ`ÃÊÃÕLÃÞÃÌi°Ê/
iÀivÀi]ÊÞÕÊV>ÌÊÕÃiÊÌÊvÀÊ>ÞÊ`>Ì>ÊviÊÀÊÌÀ>Ã>VÌÊ}Ê viÊÌ
>ÌÊVÃÌÌÕÌiÃÊ>Ê`>Ì>L>Ãi]ÊiÝVi«ÌÊvÀÊÌ
iÊÃÞÃÌiÊÌi«À>ÀÞÊ`>Ì>L>ÃiÊV>i`Êpail`^. /
iÊÕLiÀÊvÊÉ"ÃÊ«iÀÊ`ÃÊÊ, ÊäÊÃÊÀi«ÀiÃiÌi`ÊLÞÊÌ
iÊvÜ}ÊiµÕ>Ì\
É"ÃÊ«iÀÊ`ÃÊrÊ,i>`ÃʳÊ7ÀÌiîÊÉÊ ÕLiÀÊvÊ`ÃÃÊÊÌ
iÊ>ÀÀ>Þ In this equation, Na]`oÊÃÊÌ
iÊÕLiÀÊvÊÀi>`ÊÀiµÕiÃÌÃÊÌÊÌ
iÊ`ÃÊÃÕLÃÞÃÌi]Ê>`ÊSnepao is Ì
iÊÕLiÀÊvÊÜÀÌiÊÀiµÕiÃÌÃÊÌÊÌ
iÊ`ÃÊÃÕLÃÞÃÌi°
RAID 1 , ʣʫÀÛ`iÃÊ
}
Êv>ÕÌÊÌiÀ>ViÊvÀÊVÀÌV>Ê`>Ì>ÊLÞÊÀÀÀ}ÊÌ
iÊ`>Ì>Ê`ÃÊÌÊ>ÊÃi«>À>ÌiÊ `ðÊÌÊV>ÊLiÊÕÃi`ÊÜ
iÀiÊÌ
iÊV«iÌiÊ`>Ì>ÊV>ÊLiÊ>VV`>Ìi`ÊÊiÊ`ÃÊÞ°Ê >Ì>L>ÃiÊ ÌÀ>Ã>VÌÊ}ÊviÃÊvÀÊÕÃiÀÊ`>Ì>L>ÃiÃ]Ê«iÀ>Ì}ÊÃÞÃÌiÊviÃ]Ê>`Ê-+Ê-iÀÛiÀÊÃÞÃÌiÊ`>Ì>L>ÃiÃÊ (i]opan and io`^®Ê>ÀiÊÕÃÕ>ÞÊÃ>ÊiÕ}
ÊÌÊÕÃiÊ, Ê£° /
iÊÕLiÀÊvÊÉ"ÃÊ«iÀÊ`ÃÊÊ, Ê£ÊÃÊÀi«ÀiÃiÌi`ÊLÞÊÌ
iÊvÜ}ÊiµÕ>Ì\
É"ÃÊ«iÀÊ`ÃÊr,i>`ÃʳÊÓÊ Ê7ÀÌiîÊÉÊÓ
RAID5 /
à configuration is a good option in many cases. It provides reasonable fault tolerance by ivviVÌÛiÞÊÕÃ}ÊÞÊiÊiÝÌÀ>Ê`ÃÊÌÊÃ>ÛiÊÌ
iÊV«ÕÌi`Ê«>ÀÌÞÊvÊÌ
iÊ`>Ì>ÊÊÌ
iÀÊ`ÃÃ]Ê>ÃÊ Ã
ÜÊÊ}ÕÀiÊÓÈ°Ê7
iÊÌ
iÀiÊÃÊ>Ê`ÃÊv>ÕÀiÊÊ, ÊxÊVv}ÕÀ>Ì]ÊÉ"Ê«iÀvÀ>ViÊ becomes terrible, although the system does remain usable while operating with the failed drive.
CHAPTER 2
S Y S T E M P E R F O R M A N C E A N A LY S I S
ÞÊ`>Ì>ÊÜ
iÀiÊÜÀÌiÃÊ>iÊÕ«ÊÀiÊÌ
>Ê£äÊ«iÀViÌÊvÊÌ
iÊÌÌ>Ê`ÃÊÀiµÕiÃÌÃÊÃÊÌÊ>Ê }`ÊV>``>ÌiÊvÀÊ, Êx°Ê/
ÕÃ]ÊÕÃiÊ, ÊxÊÊÀi>`ÞÊÛÕiÃÊÀÊÛÕiÃÊÜÌ
Ê>ÊÜÊ«iÀViÌ>}iÊvÊ`ÃÊÜÀÌið /
iÊÕLiÀÊvÊÉ"ÃÊ«iÀÊ`ÃÊÊ, ÊxÊÃÊÀi«ÀiÃiÌi`ÊLÞÊÌ
iÊvÜ}ÊiµÕ>Ì\
É"ÃÊ«iÀÊ`ÃÊrÊ,i>`ÃʳÊ{Ê Ê7ÀÌiîÊÉÊ ÕLiÀÊvÊ`ÃÃÊÊÌ
iÊ>ÀÀ>Þ ÃÊÃ
ÜÊÊÌ
iÊ«ÀiVi`}ÊiµÕ>Ì]ÊÌ
iÊÜÀÌiÊ«iÀ>ÌÃÊÊÌ
iÊ, ÊxÊ`ÃÊÃÕLÃÞÃÌiÊ >ÀiÊ>}vi`ÊvÕÀÊÌiðÊÀÊi>V
ÊV}ÊÜÀÌiÊÀiµÕiÃÌ]ÊÌ
iÊvÜ}Ê>ÀiÊÌ
iÊvÕÀÊVÀÀië`}ÊÉ"ÊÀiµÕiÃÌÃÊÊÌ
iÊ`ÃÊÃÕLÃÞÃÌi\ Ê
UÊ "iÊÀi>`ÊÉ"ÊÌÊÀi>`ÊiÝÃÌ}Ê`>Ì>ÊvÀÊÌ
iÊ`>Ì>Ê`ÃÊÜ
ÃiÊVÌiÌÊÃÊÌÊLiÊ`vi`
Ê
UÊ "iÊÀi>`ÊÉ"ÊÌÊÀi>`ÊiÝÃÌ}Ê«>ÀÌÞÊvÀ>ÌÊvÀÊÌ
iÊVÀÀië`}Ê«>ÀÌÞÊ`Ã
Ê
UÊ "iÊÜÀÌiÊÉ"ÊÌÊÜÀÌiÊÌ
iÊiÜÊ`>Ì>ÊÌÊÌ
iÊ`>Ì>Ê`ÃÊÜ
ÃiÊVÌiÌÊÃÊÌÊLiÊ`vi`
Ê
UÊ "iÊÜÀÌiÊÉ"ÊÌÊÜÀÌiÊÌ
iÊiÜÊ«>ÀÌÞÊvÀ>ÌÊÌÊÌ
iÊVÀÀië`}Ê«>ÀÌÞÊ`Ã
/
iÀivÀi]ÊÌ
iÊvÕÀÊÉ"ÃÊvÀÊi>V
ÊÜÀÌiÊÀiµÕiÃÌÊVÃÃÌÊvÊÌÜÊÀi>`ÊÉ"ÃÊ>`ÊÌÜÊÜÀÌiÊÉ"ð Ê>Ê"/*Ê`>Ì>L>Ãi]Ê>ÊÌ
iÊ`>Ì>Ê`vV>ÌÃÊ>ÀiÊi`>ÌiÞÊÜÀÌÌiÊÌÊÌ
iÊÌÀ>Ã>VÌÊ }ÊviÊ>ÃÊ«>ÀÌÊvÊÌ
iÊ`>Ì>L>ÃiÊÌÀ>Ã>VÌ]ÊLÕÌÊÌ
iÊ`>Ì>ÊÊÌ
iÊ`>Ì>ÊviÊÌÃivÊÃÊÃÞV
Àâi`Ê ÜÌ
ÊÌ
iÊÌÀ>Ã>VÌÊ}ÊviÊVÌiÌÊ>ÃÞV
ÀÕÃÞÊÊL>ÌV
Ê«iÀ>ÌðÊ/
ÃÊ«iÀ>ÌÊÃÊ >>}i`ÊLÞÊÌ
iÊÌiÀ>Ê«ÀViÃÃÊvÊ-+Ê-iÀÛiÀÊV>i`ÊÌ
iÊcheckpoint process°Ê/
iÊvÀiµÕiVÞÊvÊ this operation can be controlled by using the na_kranuejpanr]h$iej% configuration paramiÌiÀÊvÊ-+Ê-iÀÛiÀ° Because of the continuous write operation in the transaction log file for a highly transacÌ>Ê"/*Ê`>Ì>L>Ãi]Ê«>V}ÊÌÀ>Ã>VÌÊ}ÊviÃÊÊ>Ê, ÊxÊ>ÀÀ>ÞÊÜÊÃ}vV>ÌÞÊ`i}À>`iÊ Ì
iÊ>ÀÀ>Þ½ÃÊ«iÀvÀ>Vi°ÊÌ
Õ}
ÊÞÕÊÃ
Õ`ÊÌÊ«>ViÊÌ
iÊÌÀ>Ã>VÌ>Ê}ÊviÃÊÊ>Ê, Ê xÊ>ÀÀ>Þ]ÊÌ
iÊ`>Ì>ÊviÃÊ>ÞÊLiÊ«>Vi`ÊÊ, Êx]ÊÃViÊÌ
iÊÜÀÌiÊ«iÀ>ÌÃÊÌÊÌ
iÊ`>Ì>ÊviÃÊ>ÀiÊ intermittent and batched together to improve the efficiency of the write operation.
RAID 1+0 (RAID 10) , Ê£³äÊ>ÃÊÀiviÀÀi`ÊÌÊ>ÃÊä³£Ê>`Ê£ä®ÊVv}ÕÀ>ÌÊvviÀÃÊ>Ê
}
Ê`i}ÀiiÊvÊv>ÕÌÊÌiÀ>ViÊ LÞÊÀÀÀ}ÊiÛiÀÞÊ`>Ì>Ê`ÃÊÊÌ
iÊ>ÀÀ>Þ°ÊÌÊÃÊ>ÊÕV
ÊÀiÊiÝ«iÃÛiÊÃÕÌÊÌ
>Ê, Ê x]ÊÃViÊ`ÕLiÊÌ
iÊÕLiÀÊvÊ`>Ì>Ê`ÃÃÊ>ÀiÊÀiµÕÀi`ÊÌÊ«ÀÛ`iÊv>ÕÌÊÌiÀ>Vi°Ê/
ÃÊ, Ê configuration should be used where a large volume is required to save data and more than 10 «iÀViÌÊvÊ`ÃÊÀiµÕiÃÌÃÊ>ÀiÊÜÀÌiðÊ-ViÊ, Ê£³äÊÃÕ««ÀÌÃÊsplit seeks (the ability to distribute Ì
iÊÀi>`Ê«iÀ>ÌÃÊÌÊÌ
iÊ`>Ì>Ê`ÃÊ>`ÊÌ
iÊÀÀÀÊ`ÃÊ>`ÊÌ
iÊVÛiÀ}iÊÌ
iÊÌÜÊ`>Ì>Ê ÃÌÀi>î]ÊÀi>`Ê«iÀvÀ>ViÊÃÊ>ÃÊÛiÀÞÊ}`°Ê/
ÕÃ]ÊÕÃiÊ, Ê£³äÊÜ
iÀiÛiÀÊ«iÀvÀ>ViÊÃÊ critical. /
iÊÕLiÀÊvÊÉ"ÃÊ«iÀÊ`ÃÊÊ, Ê£³äÊÃÊÀi«ÀiÃiÌi`ÊLÞÊÌ
iÊvÜ}ÊiµÕ>Ì\
É"ÃÊ«iÀÊ`ÃÊrÊ,i>`ÃʳÊÓÊ Ê7ÀÌiîÊÉÊ ÕLiÀÊvÊ`ÃÃÊÊÌ
iÊ>ÀÀ>Þ
Using a SAN System -ÌÀ>}iÊ>Ài>ÊiÌÜÀÃÊ- îÊÀi> largely the domain of large-scale enterprise systems, >Ì
Õ}
ÊÌ
iÊVÃÌÊÃÊV}Ê`Ü°ÊÊ- ÊV>ÊLiÊÕÃi`ÊÌÊVÀi>ÃiÊ«iÀvÀ>ViÊvÊ>ÊÃÌÀ>}iÊ ÃÕLÃÞÃÌiÊLÞÊëÞÊ«ÀÛ`}ÊÀiÊë`iÃÊ>`Ê`ÃÊ`ÀÛiÃÊÌÊÀi>`ÊvÀÊ>`ÊÜÀÌiÊÌ°Ê
37
38
C HAPTER 2
SYS TEM P ER FOR MA NC E A NA L YS IS
ʺ`ÀÛi»Ê>ÃÊÃiiÊLÞÊÌ
iÊ«iÀ>Ì}ÊÃÞÃÌiÊÃÊÃÌÀ«i`Ê>VÀÃÃÊÕÌ«iÊÀi`Õ`>ÌÊ`ÀÛiÃÊÊ>Ê- Ê Ê>Ê>iÀÊÃ>ÀÊÌÊÌ
>ÌÊvÊ, ÊxÊLÕÌÊÜÌ
ÕÌÊÌ
iÊÛiÀ
i>`ÊvÊ, ÊxÊÀÊÌ
iÊ«iÀvÀ>ViÊ ÃÃÊV>ÕÃi`ÊLÞÊ`ÃÊv>ÕÀiÃ°Ê iV>ÕÃiÊvÊÌ
iÀÊÃâi]ÊV«iÝÌÞ]Ê>`ÊVÃÌ]Ê- ÃÊ>ÀiÊÌÊiViÃÃ>Àily a good solution in all cases. Also, depending on the amount of data, direct attached storage -®ÊV>ÊLiÊVv}ÕÀi`ÊÌÊÀÕÊv>ÃÌiÀ°Ê/
iÊ«ÀV«>ÊÃÌÀi}Ì
ÊvÊ- ÊÃÞÃÌiÃÊÃÊÌÊÀiviVÌi`ÊÊ performance but rather in the areas of scalability, availability, and maintenance.
Aligning Disks Properly Aligning a `ÃÊ`ÀÛiÊÃÊÌÊ>ÊÜiÜÊÃÃÕi]ÊLÕÌÊÀiÊ>`ÊÀiÊ ÃÊ>ÀiÊLiV}Ê>Ü>ÀiÊ vÊÌ°Ê/
iÊÜ>ÞÊ`>Ì>ÊÃÊÃÌÀi`ÊÊ>Ê`ÃÊÃÊÊ>ÊÃiÀiÃÊvÊÃiVÌÀÃÊ>ÃÊÀiviÀÀi`ÊÌÊ>ÃÊblocks®ÊÌ
>ÌÊ >ÀiÊÃÌÀi`ÊÊÌÀ>VðÊÊ`ÃÊÃÊÕÌÊvÊ>}iÌÊÜ
iÊÌ
iÊÃâiÊvÊÌ
iÊÌÀ>V]Ê`iÌiÀi`ÊLÞÊÌ
iÊ Ûi`À]ÊVÃÃÌÃÊvÊ>ÊÕLiÀÊvÊÃiVÌÀÃÊ`vviÀiÌÊvÀÊÌ
iÊ`iv>ÕÌÊÃâiÊÌ
>ÌÊÞÕ½ÀiÊÜÀÌ}ÊÌ°Ê /
ÃÊÜÊi>ÊÌ
>ÌÊiÊÃiVÌÀÊÜÊLiÊÜÀÌÌiÊVÀÀiVÌÞ]ÊLÕÌÊÌ
iÊiÝÌÊiÊÜÊ
>ÛiÊÌÊVÀÃÃÊ ÌÜÊÌÀ>VðÊ/
ÃÊV>ÊÀiÊÌ
>Ê`ÕLiÊÌ
iÊ>ÕÌÊvÊÉ"ÊÀiµÕÀi`ÊÌÊÜÀÌiÊÀÊÀi>`ÊvÀÊÌ
iÊ `ðÊ/
iÊiÞÊÃÊÌÊ>}ÊÌ
iÊ«>ÀÌÌÊÃÊÌ
>ÌÊÞÕ½ÀiÊÃÌÀ}ÊÌ
iÊVÀÀiVÌÊÕLiÀÊvÊÃiVÌÀÃÊvÀÊ Ì
iÊÌÀ>V°
Using a Battery-Backed Controller Cache ÀÊLiÃÌÊ«iÀvÀ>Vi]ÊÕÃiÊ>ÊV>V
}Ê, ÊVÌÀiÀÊvÀÊ-+Ê-iÀÛiÀÊviÃ]ÊLiV>ÕÃiÊÌ
iÊVÌÀiÀÊ}Õ>À>ÌiiÃÊÌ
>ÌÊ`>Ì>ÊiÌÀÕÃÌi`ÊÌÊÌÊÃÊÜÀÌÌiÊÌÊ`ÃÊiÛiÌÕ>Þ]ÊÊ>ÌÌiÀÊ
ÜÊ>À}iÊ the queue is or even if power fails. When using a controller cache, the write requests to the , ÊVÌÀiÀÊ>ÀiÊÜÀÌÌiÊÌÊÌ
iÊVÌÀiÀÊV>V
i]Ê>`ÊÌ
iÊÃÕLÃiµÕiÌÊ`ÃÊ«iÀ>ÌÃÊ>ÀiÊ ÃV
i`Õi`Ê>ÃÞV
ÀÕÃÞ°Ê/
ÃÊ>ÃÞV
ÀÕÃÊÌiV
µÕiÊvÊ`}ÊÉ"ÃÊvÀÊÌ
iÊÜÀÌiÊÀiµÕiÃÌÃÊ «ÀÛiÃÊÌ
iÊ«iÀvÀ>ViÊvÊ`>Ì>L>ÃiʵÕiÀiÃÊÃ}vV>ÌÞ°Ê/
iÊ>ÕÌÊvÊ«iÀvÀ>ViÊ «ÀÛiiÌÊ`i«i`ÃÊÊÌ
iÊÃâiÊvÊÌ
iÊVÌÀiÀÊV>V
iÊ>`ÊÕLiÀÊvÊÜÀÌiÊÀiµÕiÃÌð 7
iÊÌ
iÀiÊÃÊ>ÊÕÀi>LiÊL>ÌÌiÀÞÊL>VÕ«]Ê«ÜiÀÊ`ÃÀÕ«ÌÃÊÜÊV>ÕÃiÊ>ÊÃÃÊvÊiÜÉ `vi`Ê`>Ì>ÊÌ
>ÌÊÃ]Ê`ÀÌÞÊ`>Ì>®ÊÊÌ
iÊVÌÀiÀÊV>V
iÊÌ
>ÌÊÜ>ÃÊÌÊvÕÃ
i`ÊÌÊÌ
iÊ`ðÊvÊÌ
ÃÊ dirty data belonged to a committed transaction, then the consistency of the data will be compromised. ÀÊiÝ>«i]ÊvÊ>ÊÕÃiÀÊVÌÃÊ>Ê`>Ì>L>ÃiÊÌÀ>Ã>VÌ]ÊÌ
iÊ`ÀÌÞÊ`>Ì>ÊvÀÊÌ
iÊÌÀ>Ã>VÌÊ will be written to the controller cache, and the user will be informed about a successful completion of the transaction. But should the power go down before this committed data is written ÌÊÌ
iÊ`Ã]ÊÌ
iÊ>ÊÌ
iÊ`>Ì>ÊÊÌ
iÊV>V
iÊÜÊLiÊÃÌÊÕiÃÃÊÌ
iÊVÌÀiÀÊ
>ÃÊ>ÊÀi>LiÊL>ÌÌiÀÞÊ L>VÕ«° /Ê>Û`Ê`>Ì>ÊVÃÃÌiVÞ]ÊiÃÕÀiÊvÀÊÞÕÀÊ`ÃÊÛi`ÀÊÌ
>ÌÊÌ
iÊVÌÀiÀÊV>V
iÊ
>ÃÊ>Ê Ài>LiÊL>ÌÌiÀÞÊL>VÕ«°
Adding System Memory When physical iÀÞÊÃÊÃV>ÀVi]ÊÌ
iÊÃÞÃÌiÊÃÌ>ÀÌÃÊÜÀÌ}ÊÌ
iÊVÌiÌÃÊvÊiÀÞÊL>VÊÌÊ `ÃÊ>`ÊÀi>`}ÊÃ>iÀÊLVÃÊvÊ`>Ì>ÊÀiÊvÀiµÕiÌÞ]ÊV>ÕÃ}Ê>ÊÌÊvÊ«>}}°Ê/
iÊiÃÃÊ iÀÞÊÌ
iÊÃÞÃÌiÊ
>Ã]ÊÌ
iÊÀiÊÌ
iÊ`ÃÊÃÕLÃÞÃÌiÊÃÊÕÃi`°Ê/
ÃÊV>ÊLiÊÀiÃÛi`ÊÕÃ}ÊÌ
iÊ iÀÞÊLÌÌiiVÊÀiÃÕÌÃÊiÕiÀ>Ìi`ÊÊÌ
iÊ«ÀiÛÕÃÊÃiVÌ°
CHAPTER 2
S Y S T E M P E R F O R M A N C E A N A LY S I S
Creating Multiple Files and Filegroups Ê-+Ê-iÀÛiÀ]Êi>V
user database consists of one or more data files and one or more transacÌÊ}ÊviðÊ/
iÊ`>Ì>ÊviÃÊLi}}ÊÌÊ>Ê`>Ì>L>ÃiÊV>ÊLiÊ}ÀÕ«i`ÊÌ}iÌ
iÀÊÊiÊÀÊÀiÊ vi}ÀÕ«ÃÊvÀÊ>`ÃÌÀ>ÌÛiÊ>`Ê`>Ì>Ê>V>ÌÉ«>ViiÌÊ«ÕÀ«ÃiðÊÀÊiÝ>«i]ÊvÊ>Ê`>Ì>Ê file is placed in a separate filegroup, then write access to all the tables in the filegroup can be VÌÀi`ÊViVÌÛiÞÊLÞÊ>}ÊÌ
iÊvi}ÀÕ«ÊÀi>`ÞÊÌÀ>Ã>VÌÊ}ÊviÃÊ`ÊÌÊLi}ÊÌÊ >ÞÊvi}ÀÕ«®° 9ÕÊV>ÊVÀi>ÌiÊ>Êvi}ÀÕ«ÊvÀÊ>Ê`>Ì>L>ÃiÊvÀÊ-+Ê-iÀÛiÀÊ>>}iiÌÊ-ÌÕ`]Ê>ÃÊÃ
ÜÊ Ê}ÕÀiÊÓÇ°Ê/
iÊvi}ÀÕ«ÃÊvÊ>Ê`>Ì>L>ÃiÊ>ÀiÊ«ÀiÃiÌi`ÊÊÌ
iÊi}ÀÕ«ÃÊ«>iÊvÊÌ
iÊ >Ì>L>ÃiÊ*À«iÀÌiÃÊ`>}ÊLÝ°
Figure 2-7. Filegroups configuration Ê}ÕÀiÊÓÇ]ÊÞÕÊV>ÊÃiiÊÌ
>ÌÊÌ
iÀiÊÃÊÞÊ>ÊÃ}iÊvi}ÀÕ«ÊVÀi>Ìi`ÊLÞÊ`iv>ÕÌÊÜÌ
Ê `ÛiÌÕÀi7ÀÃÓään°Ê9ÕÊV>Ê>``ÊÕÌ«iÊviÃÊÌÊÕÌ«iÊvi}ÀÕ«ÃÊ`ÃÌÀLÕÌi`Ê>VÀÃÃÊ `ÀÛiÃÊÃÊÌ
>ÌÊÜÀÊV>ÊLiÊ`iÊÊ«>À>iÊ>VÀÃÃÊÌ
iÊ}ÀÕ«ÃÊ>`Êvið 9ÕÊV>Ê>``Ê>Ê`>Ì>ÊviÊÌÊ>Êvi}ÀÕ«ÊÊÌ
iÊ >Ì>L>ÃiÊ*À«iÀÌiÃÊ`>}ÊLÝÊÊÌ
iÊiÃÊ Ü`ÜÊLÞÊÃiiVÌ}ÊvÀÊÌ
iÊ`À«`ÜÊÃÌ]Ê>ÃÊÃ
ÜÊÊ}ÕÀiÊÓn°
39
40
C HAPTER 2
SYS TEM P ER FOR MA NC E A NA L YS IS
Figure 2-8. Data files configuration You can also do this programmatically, as follows (behacnkql*omhÊÊÌ
iÊ`Ü>`®\ =HPAN@=P=>=OA=`rajpqnaSkngo.,,4=@@BEHACNKQLEJ@ATAO7 =HPAN@=P=>=OA=`rajpqnaSkngo.,,4=@@BEHA $J=IA9=`rajpqnaSkngo.,,4[@]p].( BEHAJ=IA9#?6XLnkcn]iBehaoXIe_nkokbpOMH OanranXIOOMH-,*CB.,,4XIOOMHX@=P=X=`rajpqnaSkngo.,,4[.*j`b#( OEVA9-I>( BEHACNKSPD9-,!%PKBEHACNKQLEj`atao7 Ê>ÊÃÞÃÌiÊÜÌ
ÊÕÌ«iÊ«ÀViÃÃÀÃ]Ê-+Ê-iÀÛiÀÊV>ÊÌ>iÊ>`Û>Ì>}iÊvÊÕÌ«iÊviÃÊ>`Ê filegroups by performing multiple parallel scans on the data files. As tables in a file are accessed sequentially, a thread is created to read the associated data file. If a filegroup consists of four data files, then four threads will be created to read data in parallel. If the data files are placed on vÕÀÊ`vviÀiÌÊ«
ÞÃV>Ê`ÃÃ]ÊÌ
iÊ>ÊÌ
iÊ`ÃÊë`iÃÊV>ÊÜÀÊÃÕÌ>iÕÃÞ°Ê/
iÀivÀi]ÊÊ>Ê ÕÌ`ÃÊÃÕLÃÞÃÌi]ÊÌÊÃÊÕÃÕ>ÞÊ>`Û>Ì>}iÕÃÊÌÊ
>ÛiÊÕÌ«iÊviÃÊÊÌ
iÊvi}ÀÕ«° 1Ã}ÊÕÌ«iÊviÃÊ>`Êvi}ÀÕ«ÃÊ>ÃÊi>LiÃÊÞÕÊÌÊ«ÀÛiÊÌ
iÊ«iÀvÀ>ViÊvÊÊ operations. By separating tables that are frequently joined into separate filegroups and then «ÕÌÌ}ÊviÃÊÜÌ
ÊÌ
iÊvi}ÀÕ«ÃÊÊÃi«>À>ÌiÊ`ÃÃÊÀÊ1 -]ÊÌ
iÊÃi«>À>Ìi`ÊÉ"Ê«>Ì
ÃÊV>Ê ÀiÃÕÌÊÊ«ÀÛi`Ê«iÀvÀ>Vi°ÊÀÊiÝ>«i]ÊVÃ`iÀÊÌ
iÊvÜ}ʵÕiÀÞ\
CHAPTER 2
S Y S T E M P E R F O R M A N C E A N A LY S I S
OAHA?P& BNKIpEJJANFKEJp. KJp-*_-9p.*_. If the tables p- and p. are placed in separate filegroups containing one file each, then on a multiple-processor system a separate thread will be created for each table. With the files vÊÌ
iÊÌÜÊvi}ÀÕ«ÃÊÊ`vviÀiÌÊ«
ÞÃV>Ê`ÃÃ]Ê`>Ì>ÊvÀÊLÌ
ÊÌ
iÊÌ>LiÃÊV>ÊLiÊÀiÌÀiÛi`Ê simultaneously. It is recommended for performance and recovery purposes that, if multiple filegroups are to be used, the primary filegroup should be used only for system objects and secondary vi}ÀÕ«ÃÊÃ
Õ`ÊLiÊÕÃi`ÊÞÊvÀÊÕÃiÀÊLiVÌðÊ/
ÃÊ>««À>V
Ê«ÀÛiÃÊ`ÃÊÉ"Ê>`ÊÌ
iÊ>LÌÞÊÌÊÀiVÛiÀÊvÀÊVÀÀÕ«Ì°Ê/
iÊÀiVÛiÀ>LÌÞÊvÊ>Ê`>Ì>L>ÃiÊÃÊ
}
iÀÊvÊÌ
iÊ«À>ÀÞÊ`>Ì>Ê viÊ>`ÊÌ
iÊ}ÊviÃÊ>ÀiÊÌ>VÌ°Ê1ÃiÊÌ
iÊ«À>ÀÞÊvi}ÀÕ«ÊvÀÊÃÞÃÌiÊLiVÌÃÊÞ]Ê>`ÊÃÌÀiÊ>Ê user-related objects on a secondary filegroup. Spreading a databaseÊÌÊÕÌ«iÊviÃ]ÊiÛiÊÌÊÌ
iÊÃ>iÊ`ÀÛi]Ê>iÃÊÌÊi>ÃÞÊÌÊÛiÊ Ì
iÊ`>Ì>L>ÃiÊviÃÊÌÊÃi«>À>ÌiÊ`ÀÛiÃ]Ê>}ÊvÕÌÕÀiÊ`ÃÊÕ«}À>`iÃÊi>ÃiÀ°ÊÀÊiÝ>«i]ÊÌÊ move a user database file (=`rajpqnaSkngo.,,4[.*j`b®ÊÌÊ>ÊiÜÊ`ÃÊÃÕLÃÞÃÌiÊB6®]ÊÞÕÊV>Ê follow these steps: 1. iÌ>V
ÊÌ
iÊÕÃiÀÊ`>Ì>L>ÃiÊ>ÃÊvÜÃÊbeha[ikra*omhÊÊÌ
iÊV`iÊ`Ü>`®\ QOAi]opan7 CK ol[`ap]_d[`^#=`rajpqnaSkngo.,,4#7 CK 2. «ÞÊÌ
iÊ`>Ì>ÊviÊ=`rajpqnaSkngo.,,4[.*j`b to a folder B6X@]p]XÊÊÌ
iÊiÜÊ`ÃÊÃÕLsystem. 3. Reattach the user database by referring files at appropriate locations, as shown here: QOAi]opan7 CK ol[]pp]_d[`^#=`rajpqnaSkngo.,,4# (#?6XLnkcn]iBehaoXIe_nkokbpOMH OanranXIOOMH-,*CB.,,4XIOOMHX@=P=X=`rajpqnaSkngo.,,4*i`b# (#B6X@=P=X=`rajpqnaSkngo.,,4[.*j`b# (#?6XLnkcn]iBehaoXIe_nkokbpOMH OanranXIOOMH-,*CB.,,4XIOOMHX@=P=X=`rajpqnaSkngo.,,4*h`b#7 CK 4. /ÊÛiÀvÞÊÌ
iÊviÃÊLi}}ÊÌÊ>Ê`>Ì>L>Ãi]ÊiÝiVÕÌiÊÌ
iÊvÜ}ÊV>`Ã\ QOA=`rajpqnaskngo.,,4 CK OAHA?P&BNKIouo*`]p]^]oa[behao CK
41
42
C HAPTER 2
SYS TEM P ER FOR MA NC E A NA L YS IS
9ÕÊ>ÞÊ
>ÛiÊÌVi`ÊÊ}ÕÀiÊÓnÊÌ
>ÌÊiÊvÊÌ
iÊviÃÊÊÌ
iÊ`ÛiÌÕÀi7ÀÃÓäänÊ`>Ì>base is of type BehaOpna]i°Ê/
iÊBehaOpna]iÊ`>Ì>ÊÃÌÀ>}iÊÜ>ÃÊÌÀ`ÕVi`ÊÜÌ
Ê-+Ê-iÀÛiÀÊÓään°Ê ÌÃÊ«ÕÀ«ÃiÊÃÊÌÊÃi«>À>ÌiÊÌ
iÊÜÀÊvÊ>Ì>}ÊÕÃÌÀÕVÌÕÀi`Ê`>Ì>]Ê>À}iÊL>ÀÞÊviÃ]Ê8]Ê >`ÊÃÊ]ÊvÀÊÌ
iÊ`>Ì>L>Ãi]ÊÛ}ÊÌ
>ÌÊÜÀÊÌÊÌ
iÊ7`ÜÃÊ-iÀÛiÀÊviÊÃÞÃÌi°Ê-]Ê>Ì
Õ}
Ê the data in a BehaOpna]i column can be treated as a column in a table, the processing of the `>Ì>ÊÃÊÌÊ
>`i`ÊLÞÊÌ
iÊ`>Ì>L>ÃiÊi}i]ÊÌ
iÀiLÞÊ`iVÀi>Ã}ÊÌ
iÊÜÀ>`ÊvÊÌ
iÊ-+Ê-iÀÛiÀ° 9ÕÊV>ÊÃiÀÌ]ÊÕ«`>Ìi]Ê>`Ê`iiÌiÊviÃÌÀi>Ê`>Ì>ÊÌ
ÀÕ}
Ê/-+ÊV>`ÃÊÀÊLÞÊV>}Ê directly to streaming interfaces using KlajBeha$% and SnepaBeha$%ÊvÀÊ° /Ê«À}À>}Ê >}Õ>}iðÊÀÊ>À}iÀÊÃiÌÃÊvÊÕÃÌÀÕVÌÕÀi`Ê`>Ì>]ÊÜ
iÊÌ
iÊ`>Ì>ÊÃÌÀi`ÊÊÌ
iÊ>À}iÊLiVÌÊVumn is larger than 1MB, using BehaOpna]i provides a serious increase in performance. Smaller data sets are better off being stored within the database using more traditional types such as R=N?D=N$I=T% or R=N>EJ=NU$I=T%.
Placing the Table and Index on Separate Disks If a system hasÊÕÌ«iÊ«ÀViÃÃÀÃ]Ê-+Ê-iÀÛiÀÊV>ÊÌ>iÊ>`Û>Ì>}iÊvÊÕÌ«iÊvi}ÀÕ«ÃÊLÞÊ >VViÃÃ}ÊÌ>LiÃÊ>`ÊVÀÀië`}ÊVÕÃÌiÀi`Ê`iÝiÃÊÕÃ}ÊÃi«>À>ÌiÊÉ"Ê«>Ì
ðÊÊVÕÃÌiÀi`Ê`iÝÊV>ÊLiÊVÀi>Ìi`ÊÊ>ÊëiVvVÊvi}ÀÕ«Ê>ÃÊvÜÃ\ ?NA=PAEJ@ATe-KJp-$_-%KJEj`atao
Tip The nonclustered index and other types of indexes are explained in Chapter 4.
Saving Log Files to a Separate Physical Disk -+Ê-iÀÛiÀÊ}ÊviÃÊÃ
Õ`Ê>Ü>ÞÃ]ÊÜ
iÀiÊ«ÃÃLi]ÊLiÊV>Ìi`ÊÊ>ÊÃi«>À>ÌiÊ
>À`Ê`ÃÊ `ÀÛiÊvÀÊ>ÊÌ
iÀÊ-+Ê-iÀÛiÀÊ`>Ì>L>ÃiÊviðÊ/À>Ã>VÌÊ}Ê>VÌÛÌÞÊ«À>ÀÞÊVÃÃÌÃÊvÊ ÃiµÕiÌ>ÊÜÀÌiÊÉ"]ÊÕiÊÌ
iÊÃiµÕiÌ>ÊÀÊÀ>`®ÊÉ"ÊÀiµÕÀi`ÊvÀÊÌ
iÊ`>Ì>ÊviÃ°Ê -i«>À>Ì}ÊÌÀ>Ã>VÌÊ}Ê>VÌÛÌÞÊvÀÊÌ
iÀÊÃiµÕiÌ>Ê`ÃÊÉ"Ê>VÌÛÌÞÊV>ÊÀiÃÕÌÊÊÉ"Ê «iÀvÀ>ViÊ«ÀÛiiÌÃ]ÊLiV>ÕÃiÊÌÊ>ÜÃÊÌ
iÊ
>À`Ê`ÃÊ`ÀÛiÃÊVÌ>}Ê}ÊviÃÊÌÊVViÌÀ>ÌiÊÊÃiµÕiÌ>ÊÉ"° /
iÊ>ÀÊ«ÀÌÊvÊÌiÊÀiµÕÀi`ÊÌÊ>VViÃÃÊ`>Ì>ÊvÀÊ>Ê
>À`Ê`ÃÊÃÊëiÌÊÊÌ
iʫ
ÞÃV>ÊÛiiÌÊvÊÌ
iÊ`ÃÊë`iÊ
i>`ÊÌÊV>ÌiÊÌ
iÊ`>Ì>°Ê"ViÊÌ
iÊ`>Ì>ÊÃÊV>Ìi`]ÊÌ
iÊ`>Ì>ÊÃÊ read electronically, which is much faster than the physical movement of the head. With only ÃiµÕiÌ>ÊÉ"Ê«iÀ>ÌÃÊÊÌ
iÊ}Ê`Ã]ÊÌ
iÊë`iÊ
i>`ÊvÊÌ
iÊ}Ê`ÃÊV>ÊÜÀÌiÊÌÊÌ
iÊ }Ê`ÃÊÜÌ
Ê>ÊÕÊvÊ«
ÞÃV>ÊÛiiÌ°ÊvÊÌ
iÊÃ>iÊ`ÃÊÃÊÕÃi`ÊvÀÊ`>Ì>ÊviÃ]Ê
ÜiÛiÀ]ÊÌ
iÊë`iÊ
i>`Ê
>ÃÊÌÊÛiÊÌÊÌ
iÊVÀÀiVÌÊV>ÌÊLivÀiÊÜÀÌ}ÊÌÊÌ
iÊ}Êvi°Ê/
ÃÊ increases the time required to write to the log file and thereby hurts performance. ÕÀÌ
iÀÀi]ÊvÀÊ-+Ê-iÀÛiÀÊÜÌ
ÊÕÌ«iÊ"/*Ê`>Ì>L>ÃiÃ]ÊÌ
iÊÌÀ>Ã>VÌÊ}ÊviÃÊ should be physically separated from each other on different physical drives to improve pervÀ>Vi°ÊÊiÝVi«ÌÊÌÊÌ
ÃÊÀiµÕÀiiÌÊÃÊ>ÊÀi>`ÞÊ`>Ì>L>ÃiÊÀÊ>Ê`>Ì>L>ÃiÊÜÌ
ÊÛiÀÞÊ few database changes. Since no online changes are made to the read-only database, no write «iÀ>ÌÃÊ>ÀiÊ«iÀvÀi`ÊÊÌ
iÊ}Êvi°Ê/
iÀivÀi]Ê
>Û}ÊÌ
iÊ}ÊviÊÊ>ÊÃi«>À>ÌiÊ`ÃÊÃÊÌÊ required for the read-only databases.
CHAPTER 2
S Y S T E M P E R F O R M A N C E A N A LY S I S
Partitioning Tables In addition toÊëÞÊ>``}ÊviÃÊÌÊvi}ÀÕ«ÃÊ>`ÊiÌÌ}Ê-+Ê-iÀÛiÀÊ`ÃÌÀLÕÌiÊÌ
iÊ`>Ì>Ê LiÌÜiiÊÌ
i]Ê̽ÃÊ«ÃÃLiÊÌÊ`iviÊ>Ê
ÀâÌ>ÊÃi}iÌ>ÌÊvÊ`>Ì>ÊV>i`Ê>Êpartition so that data is divided up between multiple files by the partition. A filtered set of data is a segmentÆÊvÀÊiÝ>«i]ÊvÊÌ
iÊ«>ÀÌÌÊÃÊLÞÊÌ
]ÊÌ
iÊÃi}iÌÊvÊ`>Ì>ÊÃÊ>ÞÊ}ÛiÊÌ
°Ê
Ài>Ì}Ê>Ê«>ÀÌÌÊÛiÃÊÌ
iÊÃi}iÌÊvÊ`>Ì>ÊÌÊ>Ê«>ÀÌVÕ>ÀÊviÊ>`ÊÞÊÌ
>ÌÊvi°Ê/
ÃÊ provides a massive increase in speed because, when querying against well-defined partitions, only the files with the partitions of data you’re interested in will be accessed during a given query. If you assume for a moment that data is partitioned by month, then each Ì
½ÃÊ`>Ì>ÊviÊV>ÊLiÊÃiÌÊÌÊÀi>`ÞÊ>ÃÊi>V
ÊÌ
Êi`ðÊ/
>ÌÊÀi>`ÞÊÃÌ>ÌÕÃÊi>ÃÊ Ì
>ÌÊÊV}ÊÜÊVVÕÀÊÊ>ÊviÊ>ÃÊÀi>`ʵÕiÀiÃÊ>ÀiÊÀÕÊ>}>ÃÌÊÌ]ÊvÕÀÌ
iÀÊVÀi>Ã}ÊÌ
iÊ performance of your queries. Partitions can actually be mapped to the same files within a filegroup, which won’t offer any performance improvements but can be used for easier maintenance of the data.
Processor Bottleneck Analysis -+Ê-iÀÛiÀÊÓäänÊ>iÃÊ
i>ÛÞÊÕÃiÊvÊ>ÞÊprocessor resource available. You can use the Perfor>ViÊÌÀÊVÕÌiÀÃÊÊ/>LiÊÓÎÊÌÊ>>ÞâiÊ«ÀiÃÃÕÀiÊÊÌ
iÊ«ÀViÃÃÀÊÀiÃÕÀVi° Table 2-3. Performance Monitor Counters to Analyze CPU Pressure
Object(Instance[,InstanceN])
Counter
Description
Value
Lnk_aookn$[Pkp]h%
!Lnk_aooknPeia
Percentage of time processor was busy
Average value < 80%
!Lnerehaca`Peia
Percentage of processor time spent in privileged mode
Average value < 10%
Lnk_aooknMqaqaHajcpd
ÕLiÀ of requests outstanding on the processor
Average value < 2
?kjpatpOsep_dao+oa_
Rate at which Average processor is switched value < 1,000 per processor from one thread to another
>]p_dNamqaopo+oa_
-+ÊV>` batches received per second
Based on your standard ÜÀ>`
OMH?kileh]pekjo+oa_
ÕLiÀ of times -+ÊÃÊV«i`
Average value > 100
OMHNa_kileh]pekjo+oa_
ÕLiÀ of recompiles
Ouopai
OMHOanran6OMHOp]peope_o
½ÊÜÊÜ>ÊÞÕÊÌ
ÀÕ}
ÊÌ
iÃiÊVÕÌiÀÃÊÊÀiÊ`iÌ>°
43
44
C HAPTER 2
SYS TEM P ER FOR MA NC E A NA L YS IS
% Processor Time !Lnk_aooknPeia should not beÊVÃÃÌiÌÞÊ
}
Ê}Ài>ÌiÀÊÌ
>ÊÇxÊ«iÀViÌ®°Ê/
iÊivviVÌÊvÊ any sustained processor time greater than 90 percent is the same as that with 100 percent. If !Lnk_aooknPeiaÊÃÊVÃÃÌiÌÞÊ
}
Ê>`Ê`ÃÊ>`ÊiÌÜÀÊVÕÌiÀÊÛ>ÕiÃÊ>ÀiÊÜ]ÊÞÕÀÊvÀÃÌÊ priority must be to reduce the stress on the processor. ÀÊiÝ>«i]ÊvÊ!Lnk_aooknPeiaÊÃÊnxÊ«iÀViÌÊ>`Ê!@eogPeiaÊÃÊxäÊ«iÀViÌ]ÊÌ
iÊÌÊÃÊ µÕÌiÊiÞÊÌ
>ÌÊ>Ê>ÀÊ«>ÀÌÊvÊÌ
iÊ«ÀViÃÃÀÊÌiÊÃÊëiÌÊÊ>>}}ÊÌ
iÊ`ÃÊ>VÌÛÌiÃ°Ê /
ÃÊÜÊLiÊÀiviVÌi`ÊÊÌ
iÊ!Lnerehaca`PeiaÊVÕÌiÀÊvÊÌ
iÊ«ÀViÃÃÀ]Ê>ÃÊiÝ«>i`ÊÊÌ
iÊ iÝÌÊÃiVÌ°ÊÊÌ
>ÌÊV>Ãi]ÊÌÊÜÊLiÊ>`Û>Ì>}iÕÃÊÌÊ«ÌâiÊÌ
iÊ`ÃÊLÌÌiiVÊvÀÃÌ°ÊÕÀÌ
iÀ]ÊÀiiLiÀÊÌ
>ÌÊÌ
iÊ`ÃÊLÌÌiiVÊÊÌÕÀÊV>ÊLiÊLiV>ÕÃiÊvÊ>ÊiÀÞÊLÌÌiiV]Ê>ÃÊ iÝ«>i`Êi>ÀiÀÊÊÌ
iÊV
>«ÌiÀ° 9ÕÊV>ÊÌÀ>VÊ«ÀViÃÃÀÊÌiÊ>ÃÊ>Ê>}}Ài}>ÌiÊvÊ>ÊÌ
iÊ«ÀViÃÃÀÃÊÊÌ
iÊ>V
i]ÊÀÊÞÕÊ V>ÊÌÀ>VÊÌ
iÊ«iÀViÌ>}iÊÕÌâ>ÌÊ`Û`Õ>ÞÊÌÊ«>ÀÌVÕ>ÀÊ«ÀViÃÃÀðÊ/
ÃÊ>ÜÃÊÞÕÊÌÊ Ãi}Ài}>ÌiÊÌ
iÊ`>Ì>ÊViVÌÊÊÌ
iÊiÛiÌÊÌ
>ÌÊ-+Ê-iÀÛiÀÊÀÕÃÊÊÌ
ÀiiÊ«ÀViÃÃÀÃÊvÊ>ÊvÕÀ processor machine.
% Privileged Time Processing on a Windows server is done in two modes: user mode and privileged (or kernel®Ê `i°ÊÊÃÞÃÌiiÛiÊ>VÌÛÌiÃ]ÊVÕ`}Ê`ÃÊ>VViÃÃ]Ê>ÀiÊ`iÊÊ«ÀÛi}i`Ê`i°ÊvÊÞÕÊ find that !Lnerehaca`PeiaÊÊ>Ê`i`V>Ìi`Ê-+Ê-iÀÛiÀÊÃÞÃÌiÊÃÊÓäÊÌÊÓxÊ«iÀViÌÊÀÊÀi]Ê Ì
iÊÌ
iÊÃÞÃÌiÊÃÊ«ÀL>LÞÊ`}Ê>ÊÌÊvÊÉ"piÞÊÀiÊÌ
>ÊÞÕÊii`°Ê/
iÊ!Lnerehaca` PeiaÊVÕÌiÀÊÊ>Ê`i`V>Ìi`Ê-+Ê-iÀÛiÀÊÃÞÃÌiÊÃ
Õ`ÊLiÊ>ÌÊÃÌÊxÊÌÊ£äÊ«iÀViÌ°
Processor Queue Length Lnk_aooknMqaqaHajcpd isÊÌ
iÊÕLiÀÊvÊÌ
Ài>`ÃÊÊÌ
iÊ«ÀViÃÃÀʵÕiÕi°Ê/
iÀiÊÃÊ>ÊÃ}iÊ «ÀViÃÃÀʵÕiÕi]ÊiÛiÊÊV«ÕÌiÀÃÊÜÌ
ÊÕÌ«iÊ«ÀViÃÃÀð®Ê1iÊÌ
iÊ`ÃÊVÕÌiÀÃ]ÊÌ
iÊ Lnk_aooknMqaqaHajcpdÊVÕÌiÀÊ`iÃÊÌÊÀi>`ÊÌ
Ài>`ÃÊÌ
>ÌÊ>ÀiÊ>Ài>`ÞÊÀÕ}°Ê"ÊÃÞÃÌiÃÊ ÜÌ
ÊÜiÀÊ *1ÊÕÌâ>Ì]ÊÌ
iÊLnk_aooknMqaqaHajcpd counter is typically 0 or 1. A sustained Lnk_aooknMqaqaHajcpd counter of greater than 2 generally indicates proViÃÃÀÊV}iÃÌ°Ê iV>ÕÃiÊvÊÕÌ«iÊ«ÀViÃÃÀÃ]ÊÞÕÊ>ÞÊii`ÊÌÊÌ>iÊÌÊ>VVÕÌÊÌ
iÊ number of schedulers dealing with the processor queue length. A processor queue length ÀiÊÌ
>ÊÌÜÊÌiÃÊÌ
iÊÕLiÀÊvÊÃV
i`ÕiÀÃÊÕÃÕ>ÞÊ£\£ÊÜÌ
Ê«ÀViÃÃÀîÊV>Ê>ÃÊ`V>ÌiÊ >Ê«ÀViÃÃÀÊLÌÌiiV°ÊÌ
Õ}
Ê>Ê
}
Ê!Lnk_aooknPeia counter indicates a busy processor, a sustained high Lnk_aooknMqaqaHajcpd counter is a more certain indicator. If the recomi`i`ÊÛ>ÕiÊÃÊiÝVii`i`]ÊÌ
ÃÊ}iiÀ>ÞÊ`V>ÌiÃÊÌ
>ÌÊÌ
iÀiÊ>ÀiÊÀiÊÌ
Ài>`ÃÊÀi>`ÞÊÌÊÀÕÊ than the current number of processors can service in an optimal way.
Context Switches/sec /
iÊ?kjpatpOsep_dao+oa_ counter monitors the combined rate at which all processors on the V«ÕÌiÀÊ>ÀiÊÃÜÌV
i`ÊvÀÊiÊÌ
Ài>`ÊÌÊ>Ì
iÀ°ÊÊVÌiÝÌÊÃÜÌV
ÊVVÕÀÃÊÜ
iÊ>ÊÀÕ}Ê thread voluntarily relinquishes the processor, is preempted by a higher-priority ready thread, ÀÊÃÜÌV
iÃÊLiÌÜiiÊÕÃiÀÊ`iÊ>`Ê«ÀÛi}i`Ê`iÊÌÊÕÃiÊ>ÊiÝiVÕÌÛiÊÀÊ>ÊÃÕLÃÞÃÌiÊÃiÀÛVi°Ê It is the sum of Pdna]`6?kjpatpOsep_dao+oa_ for all threads running on all processors in the computer, and it is measured in numbers of switches.
CHAPTER 2
S Y S T E M P E R F O R M A N C E A N A LY S I S
Êv}ÕÀiÊvÊÎääÊÌÊ£]äääÊ?kjpatpOsep_dao+oa_Ê«iÀÊ«ÀViÃÃÀÊÃÊiÝViiÌÊÌÊv>À°ÊLÀmally high rates, greater than 20,000 per second, can be caused by page faults due to memory starvation.
Batch Requests/sec >]p_dNamqaopo+oa_ gives you a good indicator of just how much load is being placed on the system, which has a direct correlation to how much load is being placed on the processor. Since you could see a lot of low-cost queries on your system or a few high-cost queries, you V>½ÌÊÊ>ÌÊÌ
ÃÊÕLiÀÊLÞÊÌÃivÊLÕÌÊÕÃÌÊÀiviÀiViÊÌ
iÊÌ
iÀÊVÕÌiÀÃÊ`ivi`ÊÊÌ
ÃÊÃiVÌÆÊ£]äääÊÀiµÕiÃÌÃÊÊ>ÊÃiV`ÊÜÕ`ÊLiÊVÃ`iÀi`Ê>ÊLÕÃÞÊÃÞÃÌi°ÊÀi>ÌiÀÊÛ>ÕiÃÊ>ÞÊLiÊ V>ÕÃiÊvÀÊVViÀ°Ê/
iÊLiÃÌÊÜ>ÞÊÌÊÜÊÜ
V
ÊÛ>ÕiÊ
>ÃÊi>}ÊÜÌ
ÊÞÕÀÊÜÊÃÞÃÌiÃÊÃÊ to establish a baseline and then monitor from there.
SQL Compilations/sec /
iÊOMH?kileh]pekjo+oa_ counter shows both batch compiles and statement recompiles >ÃÊ«>ÀÌÊvÊÌÃÊ>}}Ài}>Ì°Ê/
ÃÊÕLiÀÊV>ÊLiÊiÝÌÀiiÞÊ
}
ÊÜ
iÊ>ÊÃiÀÛiÀÊÃÊvÀÃÌÊÌÕÀi`ÊÊ ÀÊ>vÌiÀÊ>Êv>ÛiÀÊÀÊ>ÞÊÌ
iÀÊÃÌ>ÀÌÕ«ÊÌÞ«iÊiÛiÌ®]ÊLÕÌÊÌÊÜÊÃÌ>LâiÊÛiÀÊÌi°Ê"ViÊÃÌ>Li]Ê 100 or more compilations in one second will certainly manifest as problems in the processor.
>«ÌiÀÊÊVÛiÀÃÊ-+ÊV«>ÌÊÊ`iÌ>°
SQL Recompilations/sec OMHNa_kileh]pekjo+oa_ is a measure of the recompiles of both batches and statements. A high number of recompiles will lead to processor stress. Because statement recompiles are part of Ì
ÃÊVÕÌ]ÊÌÊV>ÊLiÊÕV
Ê
}
iÀÊÌ
>ÊÊÛiÀÃÃÊvÊ-+Ê-iÀÛiÀÊ«ÀÀÊÌÊÓääx°Ê
>«ÌiÀÊ£äÊVÛers query recompilation in detail.
Processor Bottleneck Resolutions A few of theÊVÊ«ÀViÃÃÀÊLÌÌiiVÊÀiÃÕÌÃÊ>ÀiÊ>ÃÊvÜÃ\ Ê
UÊ "«Ìâ}Ê>««V>ÌÊÜÀ>`
Ê
UÊ >Ì}ÊÀÊÀi`ÕV}ÊiÝViÃÃÛiÊV«iÃÉÀiV«iÃ
Ê
UÊ 1Ã}ÊÀiÊÀÊv>ÃÌiÀÊ«ÀViÃÃÀÃ
Ê
UÊ 1Ã}Ê>Ê>À}iÊÓÉÎÊV>V
i
Ê
UÊ ,Õ}ÊÜÌ
ÊÀiÊivvViÌÊVÌÀiÀÃÉ`ÀÛiÀÃ
Ê
UÊ
ÌÊÀÕ}ÊÕiViÃÃ>ÀÞÊÃvÌÜ>Ài
i̽ÃÊVÃ`iÀÊi>V
ÊvÊÌ
iÃiÊÀiÃÕÌÃÊÊÌÕÀ°
Optimizing Application Workload /Ê`iÌvÞÊÌ
iÊ«ÀViÃÃÀÌiÃÛiʵÕiÀiÃ]ÊV>«ÌÕÀiÊ>ÊÌ
iÊ-+ʵÕiÀiÃÊÕÃ}Ê-+Ê*ÀviÀÊ Ü
V
ÊÊÜÊ`ÃVÕÃÃÊÊÌ
iÊiÝÌÊV
>«ÌiÀ®]Ê>`ÊÌ
iÊ}ÀÕ«ÊÌ
iÊ*ÀviÀÊÌÀ>ViÊÕÌ«ÕÌÊÊÌ
iÊ?LQ
45
46
C HAPTER 2
SYS TEM P ER FOR MA NC E A NA L YS IS
VÕ°Ê/
iʵÕiÀiÃÊÜÌ
ÊÌ
iÊ
}
iÃÌÊ>ÕÌÊvÊ *1ÊÌiÊVÌÀLÕÌiÊÌ
iÊÃÌÊÌÊÌ
iÊ *1Ê ÃÌÀiÃðÊ9ÕÊÃ
Õ`ÊÌ
iÊ>>ÞâiÊ>`Ê«ÌâiÊÌ
ÃiʵÕiÀiÃÊÌÊÀi`ÕViÊÃÌÀiÃÃÊÊÌ
iÊ *1°Ê9ÕÊ can also query directly against the ouo*`i[ata_[mqanu[op]po view to see immediate issues in Ài>ÊÌi°Ê>Þ]ÊÕÃ}ÊLÌ
Ê>ʵÕiÀÞÊ
>Ã
Ê>`Ê>ʵÕiÀÞÊ«>Ê
>Ã
]ÊÞÕÊV>Ê`iÌvÞÊ>`ÊÌÕiÊ VʵÕiÀiÃÊÀÊVÊiÝiVÕÌÊ«>ÃÊÌ
ÃÊÃÊ`ÃVÕÃÃi`ÊÊ`iÌ>ÊÊ
>«ÌiÀÊ®°ÊÃÌÊvÊ Ì
iÊÀiÃÌÊvÊÌ
iÊV
>«ÌiÀÃÊÊÌ
ÃÊLÊ>ÀiÊVViÀi`ÊÜÌ
Ê«Ìâ}Ê>««V>ÌÊÜÀ>`°
Eliminating Excessive Compiles/Recompiles A certain number of µÕiÀÞÊV«iÃÊ>`ÊÀiV«iÃÊÃÊëÞÊÌÊLiÊiÝ«iVÌi`°Ê̽ÃÊÜ
iÊÌ
iÀiÊÃÊ >Ê>À}iÊÕLiÀÊvÊÌ
iÃiÊÛiÀÊÃÕÃÌ>i`Ê«iÀ`ÃÊÌ
>ÌÊ>Ê«ÀLiÊiÝÃÌðÊ̽ÃÊ>ÃÊÜÀÌ
ÊÌ}ÊÌ
iÊ ratio between them. A high number of compiles and a very low number of recompiles means Ì
>ÌÊviÜʵÕiÀiÃÊ>ÀiÊLi}ÊÀiÕÃi`ÊÜÌ
ÊÌ
iÊÃÞÃÌiʵÕiÀÞÊÀiÕÃiÊÃÊVÛiÀi`ÊÊ`iÌ>ÊÊ
>«ÌiÀÊ®°ÊÊ
}
ÊÕLiÀÊvÊÀiV«iÃÊÜÊV>ÕÃiÊ
}
Ê«ÀViÃÃÀÊÕÃi°ÊiÌ
`ÃÊvÀÊ>``ÀiÃÃ}Ê ÀiV«iÃÊ>ÀiÊVÛiÀi`ÊÊ
>«ÌiÀÊ£ä°
Using More or Faster Processors "iÊvÊÌ
i easiest resolutions, and one that you will adopt most of the time, is to increase sysÌiÊ«ÀViÃÃ}Ê«ÜiÀ°ÊÜiÛiÀ]ÊLiV>ÕÃiÊvÊÌ
iÊ
}
ÊVÃÌÊÛÛi`ÊÊ>Ê«ÀViÃÃÀÊÕ«}À>`i]ÊÞÕÊ Ã
Õ`ÊvÀÃÌÊ«ÌâiÊ *1ÌiÃÛiÊ«iÀ>ÌÃÊ>ÃÊÕV
Ê>ÃÊ«ÃÃLi° /
iÊÃÞÃÌi½ÃÊ«ÀViÃÃ}Ê«ÜiÀÊV>ÊLiÊVÀi>Ãi`ÊLÞÊVÀi>Ã}ÊÌ
iÊ«ÜiÀÊvÊ`Û`Õ>Ê processors or by adding more processors. When you have a high !Lnk_aooknPeia counter and a low Lnk_aooknMqaqaHajcpdÊVÕÌiÀ]ÊÌÊ>iÃÊÃiÃiÊÌÊVÀi>ÃiÊÌ
iÊ«ÜiÀÊvÊ`Û`Õ>Ê processors. In the case of both a high !Lnk_aooknPeia counter and a high Lnk_aooknMqaqa Hajcpd counter, you should consider adding more processors. Increasing the number of proViÃÃÀÃÊ>ÜÃÊÌ
iÊÃÞÃÌiÊÌÊiÝiVÕÌiÊÀiÊÀiµÕiÃÌÃÊÃÕÌ>iÕÃÞ°
Using a Large L2/L3 Cache Modern processors have become so much faster than memory that they need at least two iÛiÃÊvÊiÀÞÊV>V
iÊÌÊÀi`ÕViÊ>ÌiVÞ°Ê"Ê*iÌÕV>ÃÃÊ>V
iÃ]ÊÌ
iÊv>ÃÌÊ£ÊV>V
iÊ
`ÃÊ n ÊvÊ`>Ì>Ê>`Ên ÊvÊÃÌÀÕVÌÃ]ÊÜ
iÊÌ
iÊÃÜiÀÊÓÊV>V
iÊ
`ÃÊÕ«ÊÌÊÈ ÊvÊÝi`ÊV`iÊ >`Ê`>Ì>°Ê iÜÊ«ÀViÃÃÀÃÊ>ÀiÊÃ
««}ÊÜÌ
Ê>ÊÎÊV>V
iÊÊÌ«ÊvÊÌ
iÊÌ
iÀÃÊÛ>ÀÞ}ÊÊÃâiÊ vÀÊÈ ÊÌÊÓxÈ Ê>`ÊÓ Ê ÊÀiviÀÃÊÌÊmebibyte, a binary representation, 220, that is ÛiÀÞÊÃ>ÀÊÊÃâiÊÌÊLÕÌÊÌÊiÝ>VÌÞÊÌ
iÊÃ>iÊ>ÃÊ>Êi}>LÞÌi]ÊÜ
V
ÊÃÊ£äÈ®°Ê,iviÀiViÃÊÌÊ VÌiÌÊvÕ`ÊÊÌ
iÊ£ÊV>V
iÊVÃÌÊiÊVÞVi]ÊÀiviÀiViÃÊÌÊÌ
iÊÓÉÎÊV>V
iÊVÃÌÊvÕÀÊÌÊÃiÛiÊ VÞViÃ]Ê>`ÊÀiviÀiViÃÊÌÊÌ
iÊ>ÊiÀÞÊVÃÌÊ`âiÃÊvÊ«ÀViÃÃÀÊVÞViðÊ7Ì
ÊÌ
iÊVÀi>ÃiÊ Ê«ÀViÃÃ}Ê«ÜiÀ]ÊÌ
iÊ>ÌÌiÀÊv}ÕÀiÊÜÊÃÊiÝVii`Ê£ääÊVÞViðÊÊ>ÞÊÜ>ÞÃ]ÊÌ
iÊV>V
iÊÃÊ iÊÃ>]Êv>ÃÌ]ÊÛÀÌÕ>ÊiÀÞÊÃ`iÊÌ
iÊ«ÀViÃÃÀ° >Ì>L>ÃiÊi}iÃÊiÊÓÉÎÊV>V
iÃÊLiV>ÕÃiÊÌ
iÞÊii«Ê«ÀViÃÃ}ÊvvÊÌ
iÊÃÞÃÌiÊLÕðÊ/
iÊ «ÀViÃÃÀÊ`iÃÊÌÊ
>ÛiÊÌÊ}ÊÌ
ÀÕ}
ÊÌ
iÊÃÞÃÌiÊLÕÃÊÌÊ>VViÃÃÊiÀÞÆÊÌÊV>ÊÜÀÊÕÌÊvÊÌ
iÊ ÓÉÎÊV>V
i°Ê ÌÊ
>Û}ÊiÕ}
ÊÓÉÎÊV>V
iÊV>ÊV>ÕÃiÊÌ
iÊ«ÀViÃÃÀÊÌÊÜ>ÌÊ>Ê}iÀÊ«iÀ`Ê vÊÌiÊvÀÊÌ
iÊ`>Ì>ÉV`iÊÌÊÛiÊvÀÊÌ
iÊ>ÊiÀÞÊÌÊÌ
iÊÓÉÎÊV>V
i°ÊÊ«ÀViÃÃÀÊÜÌ
Ê >Ê
}
ÊVVÊëii`ÊLÕÌÊ>ÊÜÊÓÉÎÊV>V
iÊ>ÞÊÜ>ÃÌiÊ>Ê>À}iÊÕLiÀÊvÊ *1ÊVÞViÃÊÜ>Ì}ÊÊ Ì
iÊÃ>ÊÓÉÎÊV>V
i°ÊÊ>À}iÊÓÉÎÊV>V
iÊ
i«ÃÊ>ÝâiÊÌ
iÊÕÃiÊvÊ *1ÊVÞViÃÊvÀÊ>VÌÕ>Ê «ÀViÃÃ}ÊÃÌi>`ÊvÊÜ>Ì}ÊÊÌ
iÊÓÉÎÊV>V
i°
CHAPTER 2
S Y S T E M P E R F O R M A N C E A N A LY S I S
/`>Þ]ÊÌÊÃÊÛiÀÞÊVÊÌÊ
>ÛiÊi}>LÞÌiÊV>V
iÃÊÊvÕÀÜ>ÞÊÃÞÃÌiðÊ7Ì
ÊiÜÊvÕÀÊ >`Êi}
ÌÜ>ÞÊÃÞÃÌiÃ]ÊÞÕÊÜÊvÌiÊ}iÌÊÕ«ÊÌÊ>ÊÈ ÊÓÊV>V
iÊ>`]Ê>ÃÊiÌi`Êi>ÀiÀ]Ê ÓxÈ ÊÊΰÊÀÊiÝ>«i]ÊÃiÌiÃÊÞÕÊ>ÞÊ}iÌÊ>Ê«iÀvÀ>ViÊ«ÀÛiiÌÊvÊÓäÊ «iÀViÌÊÀÊÀiÊëÞÊLÞÊÕÃ}Ê>Êx£Ó ÊÓÊV>V
iÊÃÌi>`ÊvÊ>ÊÓxÈ ÊÓÊV>V
i°
Running More Efficient Controllers/Drivers /
iÀi is a big difference in !Lnerehaca`Peia consumption between different controllers and VÌÀiÀÊ`ÀÛiÀÃÊÊÌ
iÊ>ÀiÌÊÌ`>Þ°Ê/
iÊÌiV
µÕiÃÊÕÃi`ÊLÞÊVÌÀiÀÊ`ÀÛiÀÃÊÌÊ`ÊÉ"Ê>ÀiÊ µÕÌiÊ`vviÀiÌÊ>`ÊVÃÕiÊ`vviÀiÌÊ>ÕÌÃÊvÊ *1ÊÌi°ÊvÊÞÕÊV>ÊV
>}iÊÌÊ>ÊVÌÀiÀÊ Ì
>ÌÊvÀiiÃÊÕ«Ê{ÊÌÊxÊ«iÀViÌÊvÊ!Lnerehaca`Peia, you can improve performance.
Not Running Unnecessary Software If you have screen savers running on your system, they may use a large amount of procesÃÀÊÌi°Ê/
ÃÊÃÊ«>ÀÌVÕ>ÀÞÊÌÀÕiÊvÊÃVÀiiÊÃ>ÛiÀÃÊÌ
>ÌÊÕÃiÊ"«iÊ>ÊÃvÌÜ>ÀiÊÌiÀv>ViÊÌ
>ÌÊ ÃÕ««ÀÌÃÊÌ
iÊ«À`ÕVÌÊvÊ
}
µÕ>ÌÞ]ÊÌ
Àii`iÃ>ÊVÀÊ>}}®]ÊLiV>ÕÃiÊÌ
iÞÊV>Ê i>ÃÞÊÌ>iÊ>Ü>ÞÊ£xÊ«iÀViÌÊvÊ«ÀViÃÃÀÊÌiÊÊ>ÊÃiÀÛiÀÊÌ
>ÌÊ>VÃÊ>Ê"«iÊ}À>«
VÃÊV>À`°Ê Because you can run your servers with the monitor switched off for the majority of the time, ëÞÊ`Ã>LiÊ>ÞÊÃVÀiiÊÃ>ÛiÀÃÊÊÞÕÀÊÃiÀÛiÀÊÀ]Ê>ÃÊ>Ê«ÀiÛiÌÛiÊi>ÃÕÀi]ÊÕÃiÊÌ
iÊL>Ê ÃVÀiiÊÃ>ÛiÀ°Ê*i>ÃiÊ`ÊÀiiLiÀÊÌÊVÊÞÕÀÊÃiÀÛiÀÊvÀÊÃiVÕÀÌÞ° 7
iÊ«ÃÃLi]ÊÊÕiViÃÃ>ÀÞÊÃvÌÜ>ÀiÊÃ
Õ`ÊLiÊÀÕ}ÊÊÌ
iÊÃ>iÊÃiÀÛiÀÊ>ÃÊ-+Ê -iÀÛiÀ°Ê ÝÌiÀÀÊ>««V>ÌÃÊÌ
>ÌÊ
>ÛiÊÌ
}ÊÌÊ`ÊÜÌ
Ê>Ì>}ÊÌ
iÊ7`ÜÃÊ-iÀÛiÀÊÀÊ -+Ê-iÀÛiÀÊ>ÀiÊLiÃÌÊ«>Vi` on a different machine.
Network Bottleneck Analysis Ê-+Ê-iÀÛiÀÊ"/*Ê«À`ÕVÌÊiÛÀiÌÃ]ÊÞÕÊv`ÊviÜÊ«iÀvÀ>ViÊÃÃÕiÃÊÌ
>ÌÊ>ÀiÊ because of problems with the iÌÜÀ°ÊÃÌÊvÊÌ
iÊiÌÜÀÊÃÃÕiÃÊÞÕÊv>ViÊÊÌ
iÊ"/*ÊiÛronment are in fact hardware or driver limitations or issues with switches or routers. Most of Ì
iÃiÊÃÃÕiÃÊV>ÊLiÊLiÃÌÊ`>}Ãi`ÊÜÌ
ÊÌ
iÊ iÌÜÀÊÌÀÊÌ°ÊÜiÛiÀ]Ê*iÀvÀ>ViÊ ÌÀÊ>ÃÊ«ÀÛ`iÃÊLiVÌÃÊÌ
>ÌÊViVÌÊ`>Ì>ÊÊiÌÜÀÊ>VÌÛÌÞ]Ê>ÃÊÃ
ÜÊÊ/>LiÊÓ{° Table 2-4. Performance Monitor Counters to Analyze Network Pressure
Object(Instance[,InstanceN])
Counter
Description
Value
JapskngEjpanb]_a$Japskng_]n`%
>upaoPkp]h+oa_
Rate at which bytes are transferred on Ì
iÊ
Average Û>ÕiÊÊxä¯ÊvÊ ÊV>«>VÌÞ
JapskngOaciajp
!JapQpehev]pekj
Percentage of iÌÜÀ bandwidth in use on a iÌÜÀÊÃi}iÌ
Average value < 80% vÊiÌÜÀÊ bandwidth
47
48
C HAPTER 2
SYS TEM P ER FOR MA NC E A NA L YS IS
Bytes Total/sec You can use the >upaoPkp]h+oa_ÊVÕÌiÀÊÌÊ`iÌiÀiÊ
ÜÊÌ
iÊiÌÜÀÊÌiÀv>ViÊV>À`Ê ®Ê ÀÊiÌÜÀÊ>`>«ÌiÀÊÃÊ«iÀvÀ}°Ê/
iÊ>upaoPkp]h+oa_ counter should report high values to `V>ÌiÊ>Ê>À}iÊÕLiÀÊvÊÃÕVViÃÃvÕÊÌÀ>ÃÃÃÃ°Ê «>ÀiÊÌ
ÃÊÛ>ÕiÊÜÌ
ÊÌ
>ÌÊÀi«ÀÌi`ÊLÞ the JapskngEjpanb]_aX?qnnajp>]j`se`pd performance counter, which reflects each adapter’s bandwidth. /Ê>ÜÊ
i>`ÀÊvÀÊëiÃÊÊÌÀ>vvV]ÊÞÕÊÃ
Õ`ÊÕÃÕ>ÞÊ>ÛiÀ>}iÊÊÀiÊÌ
>ÊxäÊ«iÀcent of capacity. If this number is close to the capacity of the connection and if processor and memory use are moderate, then the connection may well be a problem.
% Net Utilization /
iÊ!JapQpehev]pekj counter represents theÊ«iÀViÌ>}iÊvÊiÌÜÀÊL>`Ü`Ì
ÊÊÕÃiÊÊ>Ê iÌÜÀÊÃi}iÌ°Ê/
iÊÌ
ÀiÃ
`ÊvÀÊÌ
ÃÊVÕÌiÀÊ`i«i`ÃÊÊÌ
iÊÌÞ«iÊvÊiÌÜÀ°ÊÀÊ Ì
iÀiÌÊiÌÜÀÃ]ÊvÀÊiÝ>«i]ÊÎäÊ«iÀViÌÊÃÊÌ
iÊÀiVi`i`ÊÌ
ÀiÃ
`ÊÜ
iÊ-+Ê-iÀÛiÀÊÃÊÊ>Ê Ã
>Ài`ÊiÌÜÀÊ
ÕL°ÊÀÊ-+Ê-iÀÛiÀÊÊ>Ê`i`V>Ìi`ÊvÕ`Õ«iÝÊiÌÜÀ]ÊiÛiÊÌ
Õ}
Êi>ÀÊ£ääÊ «iÀViÌÊÕÃ>}iÊvÊÌ
iÊiÌÜÀÊÃÊ>VVi«Ì>Li]ÊÌÊÃÊ>`Û>Ì>}iÕÃÊÌÊii«ÊÌ
iÊiÌÜÀÊÕÌâ>ÌÊ LiÜÊ>Ê>VVi«Ì>LiÊÌ
ÀiÃ
`ÊÌÊii«ÊÀÊvÀÊÌ
iÊëiÃÊÊÌ
iÊ>`°
Note You must install the Network Monitor Driver to collect performance data using the Japskng Oaciajp object counters.
Ê7`ÜÃÊ-iÀÛiÀÊÓään]ÊÞÕÊV>ÊÃÌ>ÊÌ
iÊ iÌÜÀÊÌÀÊ ÀÛiÀÊvÀÊÌ
iÊV>Ê>Ài>Ê ViVÌÊ«À«iÀÌiÃÊvÀÊÌ
iÊiÌÜÀÊ>`>«ÌiÀ°Ê/
iÊ iÌÜÀÊÌÀÊ ÀÛiÀÊÃÊ>Û>>LiÊÊÌ
iÊ iÌÜÀÊ«ÀÌVÊÃÌÊvÊiÌÜÀÊV«iÌÃÊvÀÊÌ
iÊiÌÜÀÊ>`>«ÌiÀ°
Network Bottleneck Resolutions A few of theÊVÊiÌÜÀÊLÌÌiiVÊÀiÃÕÌÃÊ>ÀiÊ>ÃÊvÜÃ\ Ê
UÊ "«Ìâ}Ê>««V>ÌÊÜÀ>`
Ê
UÊ ``}ÊiÌÜÀÊ>`>«ÌiÀÃ
Ê
UÊ `iÀ>Ì}Ê>`Ê>Û`}ÊÌiÀÀÕ«Ìà i̽ÃÊVÃ`iÀÊÌ
iÃiÊÀiÃÕÌÃÊÊÀiÊ`iÌ>°
Optimizing Application Workload /Ê«ÌâiÊiÌÜÀÊÌÀ>vvVÊLiÌÜiiÊ>Ê`>Ì>L>ÃiÊ>««V>ÌÊ>`Ê>Ê`>Ì>L>ÃiÊÃiÀÛiÀ]Ê>iÊÌ
iÊ following design changes in the application:
CHAPTER 2
S Y S T E M P E R F O R M A N C E A N A LY S I S
Ê
UÊ ÃÌi>`ÊvÊÃi`}Ê>Ê}Ê-+ÊÃÌÀ}]ÊVÀi>ÌiÊ>ÊÃÌÀi`Ê«ÀVi`ÕÀiÊvÀÊÌ
iÊ-+ʵÕiÀÞ°Ê /
i]ÊÞÕÊÕÃÌÊii`ÊÌÊÃi`ÊÛiÀÊÌ
iÊiÌÜÀÊÌ
iÊ>iÊvÊÌ
iÊÃÌÀi`Ê«ÀVi`ÕÀiÊ>`Ê its parameters.
Ê
UÊ ÀÕ«ÊÕÌ«iÊ`>Ì>L>ÃiÊÀiµÕiÃÌÃÊÌÊiÊÃÌÀi`Ê«ÀVi`ÕÀi°Ê/
i]ÊÞÊiÊ`>Ì>L>ÃiÊ ÀiµÕiÃÌÊÜÊLiÊÀiµÕÀi`Ê>VÀÃÃÊÌ
iÊiÌÜÀÊvÀÊÌ
iÊÃiÌÊvÊ-+ʵÕiÀiÃÊ«iiÌi`ÊÊ the stored procedure.
Ê
UÊ ,iµÕiÃÌÊ>ÊÃ>Ê`>Ì>ÊÃiÌ°Ê ÊÌÊÀiµÕiÃÌÊÌ>LiÊVÕÃÊÌ
>ÌÊ>ÀiÊÌÊÕÃi`ÊÊÌ
iÊ application logic.
Ê
UÊ ÛiÊ`>Ì>ÌiÃÛiÊLÕÃiÃÃÊ}VÊÌÊÌ
iÊ`>Ì>L>ÃiÊ>ÃÊÃÌÀi`Ê«ÀVi`ÕÀiÃÊÀÊ `>Ì>L>ÃiÊÌÀ}}iÀÃÊÌÊÀi`ÕViÊiÌÜÀÊÀÕ`ÌÀ«Ã°
Adding Network Adapters You can add iÌÜÀÊ>`>«ÌiÀÃÊÃÊÌ
>ÌÊÞÕÊ
>ÛiÊiÊiÌÜÀÊ>`>«ÌiÀÊ«iÀÊ«ÀViÃÃÀ°ÊiiÀ>Þ]ÊÞÕÊÃ
Õ`Ê>``Ê>ÊiÌÜÀÊ>`>«ÌiÀÊÞÊvÊÞÕÊii`ÊÌ
iÊVÀi>Ãi`ÊL>`Ü`Ì
]ÊLiV>ÕÃiÊ i>V
Ê>``Ì>ÊiÌÜÀÊ>`>«ÌiÀÊ
>ÃÊÃiÊÌÀÃVÊÛiÀ
i>`°ÊÜiÛiÀ]ÊvÊiÊvÊÌ
iÊ«Àcessors is nearly always active (that is, Lnk_aooknX!Lnk_aooknPeia consistently equals 100 «iÀViÌ®Ê>`ÊÀiÊÌ
>Ê
>vÊvÊÌÃÊÌiÊÃÊëiÌÊÃiÀÛV}Ê`iviÀÀi`Ê«ÀVi`ÕÀiÊV>ÃÊÌ
>ÌÊÃ]Ê Lnk_aooknX!@L?PeiaÊiÝVii`ÃÊxäÊ«iÀViÌ®]ÊÌ
iÊ>``}Ê>ÊiÌÜÀÊV>À`ÊÃÊiÞÊÌÊ«ÀÛiÊ system performance. If aÊiÌÜÀÊ>`>«ÌiÀÊ`iÃÊÌÊÕÃiÊ iÌÜÀÊ ÀÛiÀÊÌiÀv>ViÊ-«iVvV>ÌÊ -®Ê«ÀÌÊ drivers, you cannot modify the distribution of deferredÊ«ÀVi`ÕÀiÊV>ÃÊ * îÊvÀÊLiÌÌiÀÊ «iÀvÀ>Vi°ÊÊ - miniport driver (also called a miniport driver®Ê>>}iÃÊ>Ê Ê>`Ê ÌiÀv>ViÃÊÜÌ
Ê
}
iÀiÛiÊ`ÀÛiÀðÊÌÊVÕV>ÌiÃÊÜÌ
ÊÌÃÊ Ê>`Ê
}
iÀiÛiÊ`ÀÛiÀÃÊ Ì
ÀÕ}
ÊÌ
iÊ -ÊLÀ>ÀÞ° /ÊLiÊ>LiÊÌÊ`vÞÊÌ
iÊ`ÃÌÀLÕÌÊvÊ * ÃÊ>`ÊLiV>ÕÃiÊÌ
iÀÊ -Ê«Ìâ>ÌÃÊ }
ÌÊLiÊÕ>Û>>Li]ÊÞÕÊ>ÞÊVÃ`iÀÊÕ«}À>`}Ê>Ê`Û`Õ>ÊiÌÜÀÊ>`>«ÌiÀÊÃÌi>`ÊvÊ adding a new adapter.
Moderating and Avoiding Interruptions When addingÊÀÊÕ«}À>`}ÊiÌÜÀÊ>`>«ÌiÀÃ]ÊV
ÃiÊ>`>«ÌiÀÃÊÜÌ
Ê`ÀÛiÀÃÊÌ
>ÌÊÃÕ««ÀÌÊ interrupt moderation and/or interrupt avoidance. Interrupt moderation allows a processor to process interrupts more efficiently by grouping several interrupts into a single hardware interrupt. Interrupt avoidance allows a processor to continue processing interrupts without new interrupts queued until all pending interrupts are completed.
SQL Server Overall Performance /Ê>>ÞâiÊÌ
iÊÛiÀ>Ê«iÀvÀ>ViÊvÊ>Ê-+Ê-iÀÛiÀ]ÊLiÃ`iÃÊiÝ>}Ê
>À`Ü>ÀiÊÀiÃÕÀViÊ ÕÌâ>Ì]ÊÞÕÊÃ
Õ`Ê>ÃÊiÝ>iÊÃiÊ}iiÀ>Ê>ëiVÌÃÊvÊ-+Ê-iÀÛiÀÊÌÃiv°Ê9ÕÊV>ÊÕÃiÊÌ
iÊ «iÀvÀ>ViÊVÕÌiÀÃÊ«ÀiÃiÌi`ÊÊ/>LiÊÓx°
49
50
C HAPTER 2
SYS TEM P ER FOR MA NC E A NA L YS IS
Table 2-5. Performance Monitor Counters to Analyze Generic SQL Pressure
Object(Instance[,InstanceN])
Counter
OMHOanran6=__aooIapdk`o
BnaaOl]_aO_]jo+oa_ BqhhO_]jo+oa_ P]^haHk_gAo_]h]pekjo+oa_ Skngp]^hao?na]pa`+oa_
OMHOanran6H]p_dao
Pkp]hH]p_dS]epPeia$io%
OMHOanran6Hk_go$[Pkp]h%
Hk_gPeiakqpo+oa_ Hk_gS]epPeia$io% Jqi^ankb@a]`hk_go+oa_
OMHOanran6OMHOp]peope_o
>]p_dNamqaopo+oa_ OMHNa)?kileh]pekjo+oa_
OMHOanran6Cajan]hOp]peope_o
Lnk_aooao>hk_ga` Qoan?kjja_pekjo
i̽ÃÊÌ>iÊ>ÊÊ>ÌÊi>V
ÊvÊÌ
iÃiÊVÕÌiÀÃÊÊVÌiÝÌ°
Missing Indexes /Ê>>ÞâiÊÌ
iÊ«ÃÃLÌÞÊvÊÃÃ}Ê`iÝiÃÊV>ÕÃ}ÊÌ>LiÊÃV>ÃÊÀÊ>À}iÊ`>Ì>ÊÃiÌÊÀiÌÀiÛ>Ã]Ê ÞÕÊV>ÊÕÃiÊÌ
iÊVÕÌiÀÃÊÊ/>LiÊÓÈ° Table 2-6. Performance Monitor Counter to Analyze Excessive Data Scans
Object(Instance[,InstanceN])
Counter
OMHOanran6=__aooIapdk`o
BnaaOl]_aO_]jo+oa_ BqhhO_]jo+oa_
FreeSpace Scans/sec /
ÃÊVÕÌiÀ represents inserts into a table with no physical ordering of its rows—such a table is also called a heap table°Ê ÝÌÀ>Ê«ÀViÃÃ}ÊÃÊÀiµÕÀi`ÊÌÊ`iviÊ>`ÊÃÌÀiÊ>Ê
i>«ÊÌ>LiÊÃViÊ -+Ê-iÀÛiÀÊÀ>ÞÊÕÃiÃÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊ>ÃÊ>ÊÃÌÀ>}iÊiV
>ÃÊvÀÊÌ
iÊÌ>LiÊ`>Ì>°ÊÊ heap table requires an additional, internal column called a uniquifier to be generated for each ÀÜÊÃiÀÌi`°Ê/
iÀivÀi]ÊÌÊÃÊÕÃÕ>ÞÊÀiVi`i`ÊÌ
>ÌÊÞÕÊ«
ÞÃV>ÞÊÀ`iÀÊÌ
iÊÌ>LiÊÀÜÃÊLÞÊ ÕÃ}Ê>ÊVÕÃÌiÀi`Ê`iÝÊÊÌ
iÊÌ>Li°Ê9ÕÊÜÊi>ÀÊ>LÕÌÊ
i>«ÊÌ>LiÃÊ>`ÊVÕÃÌiÀi`Ê`iÝiÃÊÊ
>«ÌiÀÊx°
Full Scans/sec /
ÃÊVÕÌiÀÊÌÀÃÊÌ
iÊÕLiÀÊvÊÕÀiÃÌÀVÌi`ÊvÕÊÃV>ÃÊÊL>ÃiÊÌ>LiÃÊÀÊ`iÝiðÊÊviÜÊvÊ the main causes of high BqhhO_]jo+oa_ are as follows: Ê
UÊ ÃÃ}Ê`iÝiÃ
Ê
UÊ /Ê>ÞÊÀÜÃÊÀiµÕiÃÌi`
CHAPTER 2
S Y S T E M P E R F O R M A N C E A N A LY S I S
/ÊvÕÀÌ
iÀÊÛiÃÌ}>ÌiʵÕiÀiÃÊ«À`ÕV}ÊÌ
iÊ«ÀiVi`}Ê«ÀLiÃ]ÊÕÃiÊ-+Ê*ÀviÀÊÌÊ `iÌvÞÊÌ
iʵÕiÀiÃÊÊÜÊVÛiÀÊÌ
ÃÊÌÊÊÌ
iÊiÝÌÊV
>«ÌiÀ®°Ê+ÕiÀiÃÊÜÌ
ÊÃÃ}Ê`iÝiÃÊÀÊ ÌÊ>ÞÊÀÜÃÊÀiµÕiÃÌi`ÊÜÊ
>ÛiÊ>Ê>À}iÊÕLiÀÊvÊ}V>ÊÀi>`ÃÊ>`Ê>ÊVÀi>Ãi`Ê *1ÊÌi° Be aware of the fact that full scans may be performed for the temporary tables used in a ÃÌÀi`Ê«ÀVi`ÕÀi]ÊLiV>ÕÃiÊÃÌÊvÊÌ
iÊÌiÊÞÕÊÜÊÌÊ
>ÛiÊ`iÝiÃÊÀÊÞÕÊÜÊÌÊii`Ê `iÝiîÊÊÌi«À>ÀÞÊÌ>LiðÊ-Ì]Ê>``}ÊÌ
ÃÊVÕÌiÀÊÌÊÌ
iÊL>ÃiiÊ
i«ÃÊ`iÌvÞÊÌ
iÊ«Ãsible increase in the use of temporary tables, which are usually not good for performance.
Dynamic Management Views Another wayÊÌÊV
iVÊvÀÊÃÃ}Ê`iÝiÃÊÃÊÌÊÌ
iʵÕiÀÞÊÌ
i dynamic management view ouo* `i[`^[ieooejc[ej`at[`ap]eho°Ê/
ÃÊ>>}iiÌÊÛiÜÊÀiÌÕÀÃÊvÀ>ÌÊÌ
>ÌÊV>ÊÃÕ}}iÃÌÊ V>``>ÌiÃÊvÀÊ`iÝiÃÊL>Ãi`ÊÊÌ
iÊiÝiVÕÌÊ«>ÃÊvÊÌ
iʵÕiÀiÃÊLi}ÊÀÕÊ>}>ÃÌÊÌ
iÊ `>Ì>L>Ãi°Ê/
iÊÛiÜÊouo*`i[`^[ieooejc[ej`at[`ap]ehoÊÃÊ«>ÀÌÊvÊ>ÊÃiÀiÃÊvÊ 6ÃÊViVÌÛiÞÊ referred to as the missing indexes feature°Ê/
iÃiÊ 6ÃÊ>ÀiÊL>Ãi`ÊÊ`>Ì>Ê}iiÀ>Ìi`ÊvÀÊ iÝiVÕÌÊ«>ÃÊÃÌÀi`ÊÊÌ
iÊV>V
i°Ê9ÕÊV>ʵÕiÀÞÊ`ÀiVÌÞÊ>}>ÃÌÊÌ
ÃÊÛiÜÊÌÊ}>Ì
iÀÊ`>Ì>ÊÌÊ `iV`iÊÜ
iÌ
iÀÊÞÕÊÜ>ÌÊÌÊLÕ`Ê`iÝiÃÊL>Ãi`ÊÊÌ
iÊvÀ>ÌÊ>Û>>LiÊvÀÊÜÌ
ÊÌ
iÊ ÛiÜ°ÊÃÃ}Ê`iÝiÃÊÜÊ>ÃÊLiÊÃ
ÜÊÜÌ
ÊÌ
iÊ8ÊiÝiVÕÌÊ«>ÊvÀÊ>Ê}ÛiʵÕiÀÞ]ÊLÕÌÊ ½ÊVÛiÀÊÌ
>ÌÊÀiÊÊÌ
iÊiÝÌÊV
>«ÌiÀ° /
iÊ««ÃÌiÊ«ÀLiÊÌÊ>ÊÃÃ}Ê`iÝÊÃÊiÊÌ
>ÌÊÃÊiÛiÀÊÕÃi`°Ê/
iÊ 6Êouo*`i[ `^[ej`at[qo]ca[op]po showsÊÜ
V
Ê`iÝiÃÊ
>ÛiÊLiiÊÕÃi`]Ê>ÌÊi>ÃÌÊÃViÊÌ
iÊ>ÃÌÊÀiLÌÊvÊ Ì
iÊÃÞÃÌi°Ê9ÕÊV>Ê>ÃÊÛiÜÊÌ
iÊ`iÝiÃÊÊÕÃiÊÜÌ
Ê>ÊÜiÀiÛiÊ 6]Êouo*`i[`^[ej`at[ klan]pekj]h[op]po°ÊÌÊÜÊ
i«ÊÌÊÃ
ÜÊÜ
iÀiÊ`iÝiÃÊ>ÀiÊÃÜ}Ê`ÜÊLiV>ÕÃiÊvÊVÌiÌÊ ÀÊÉ"°Ê½ÊVÛiÀÊÌ
iÃiÊLÌ
ÊÊÀiÊ`iÌ>ÊÊ
>«ÌiÀÊ£ä°
Database Blocking /Ê>>ÞâiÊÌ
iÊ«>VÌÊvÊ`>Ì>L>ÃiÊLV}ÊÊÌ
iÊ«iÀvÀ>ViÊvÊ-+Ê-iÀÛiÀ]ÊÞÕÊV>ÊÕÃiÊÌ
iÊ VÕÌiÀÃÊÃ
ÜÊÊ/>LiÊÓÇ° Table 2-7. Performance Monitor Counters to Analyze SQL Server Locking
Object(Instance[,InstanceN])
Counter
OMHOanran6H]p_dao
Pkp]hH]p_dS]epPeia$io%
OMHOanran6Hk_go$[Pkp]h%
Hk_gPeiakqpo+oa_ Hk_gS]epPeia$io% Jqi^ankb@a]`hk_go+oa_
Total Latch Wait Time (ms) >ÌV
iÃÊ>ÀiÊÕÃi`ÊÌiÀ>ÞÊLÞÊ-+Ê-iÀÛiÀÊÌÊ«ÀÌiVÌÊÌ
iÊÌi}ÀÌÞÊvÊÌiÀ>ÊÃÌÀÕVÌÕÀiÃ]ÊÃÕV
Ê >ÃÊ>ÊÌ>LiÊÀÜ]Ê>`Ê>ÀiÊÌÊ`ÀiVÌÞÊVÌÀi`ÊLÞÊÕÃiÀðÊ/
ÃÊVÕÌiÀÊÌÀÃÊÌÌ>Ê>ÌV
ÊÜ>ÌÊ ÌiÊÊÃiV`îÊvÀÊ>ÌV
ÊÀiµÕiÃÌÃÊÌ
>ÌÊ
>`ÊÌÊÜ>ÌÊÊÌ
iÊ>ÃÌÊÃiV`°ÊÊ
}
ÊÛ>ÕiÊvÀÊ Ì
ÃÊVÕÌiÀÊ`V>ÌiÃÊÌ
>ÌÊ-+Ê-iÀÛiÀÊÃÊëi`}ÊÌÊÕV
ÊÌiÊÜ>Ì}ÊÊÌÃÊÌiÀ>ÊÃÞV
Àâ>ÌÊiV
>ð
51
52
C HAPTER 2
SYS TEM P ER FOR MA NC E A NA L YS IS
Lock Timeouts/sec and Lock Wait Time (ms) 9ÕÊÃ
Õ`ÊiÝ«iVÌÊHk_gPeiakqpo+oa_ to be 0 and Hk_gS]epPeia$io% to be very low. A âiÀÊÛ>ÕiÊvÀÊHk_gPeiakqpo+oa_ and a high value for Hk_gS]epPeia$io% indicate that iÝViÃÃÛiÊLV}ÊÃÊVVÕÀÀ}ÊÊÌ
iÊ`>Ì>L>Ãi° /ÜÊ>««À>V
iÃÊV>ÊLiÊ>`«Ìi`ÊÊÌ
ÃÊV>Ãi\ Ê
UÊ 9ÕÊV>Ê`iÌvÞÊÌ
iÊVÃÌÞʵÕiÀiÃÊÕÃ}Ê`>Ì>ÊvÀÊ-+Ê*ÀviÀÊÀÊLÞʵÕiÀÞ}Êouo* `i[ata_[mqanu[op]po andÊÌ
iÊ«ÌâiÊÌ
iʵÕiÀiÃÊ>««À«À>ÌiÞ°
Ê
UÊ 9ÕÊV>ÊÕÃiÊLV}Ê>>ÞÃÃÊÌÊ`>}ÃiÊÌ
iÊV>ÕÃiÊvÊiÝViÃÃÛiÊLV}°ÊÌÊÃÊÕÃÕ>ÞÊ>`Û>Ì>}iÕÃÊÌÊVViÌÀ>ÌiÊÊ«Ìâ}ÊÌ
iÊVÃÌÞʵÕiÀiÃÊvÀÃÌ]ÊLiV>ÕÃiÊÌ
Ã]Ê ÊÌÕÀ]ÊÀi`ÕViÃÊLV}ÊvÀÊÌ
iÀðÊÊ
>«ÌiÀÊ£Ó]ÊÞÕÊÜÊi>ÀÊ
ÜÊÌÊ>>ÞâiÊ>`Ê ÀiÃÛiÊLV}°
Number of Deadlocks/sec You shouldÊiÝ«iVÌÊÌÊÃiiÊ>ÊäÊÛ>ÕiÊvÀÊÌ
ÃÊVÕÌiÀ°ÊvÊÞÕÊv`Ê>ÊâiÀÊÛ>Õi]ÊÌ
iÊÞÕÊ Ã
Õ`Ê`iÌvÞÊÌ
iÊÛVÌâi`ÊÀiµÕiÃÌÊ>`ÊiÌ
iÀÊÀiÃÕLÌÊÌ
iÊ`>Ì>L>ÃiÊÀiµÕiÃÌÊ>ÕÌ>ÌV>ÞÊ or suggest that the user do so. More important, an attempt should be made to troubleshoot >`ÊÀiÃÛiÊÌ
iÊ`i>`V°Ê}>]Ê
>«ÌiÀÊ£ÓÊÃ
ÜÃÊ
ÜÊÌÊ`ÊÌ
ð
Nonreusable Execution Plans Since generating an iÝiVÕÌÊ«>ÊvÀÊ>ÊÃÌÀi`Ê«ÀVi`ÕÀiʵÕiÀÞÊÀiµÕÀiÃÊ *1ÊVÞViÃ]ÊÞÕÊV>Ê Ài`ÕViÊÌ
iÊÃÌÀiÃÃÊÊÌ
iÊ *1ÊLÞÊÀiÕÃ}ÊÌ
iÊiÝiVÕÌÊ«>°Ê/Ê>>ÞâiÊÌ
iÊÕLiÀÊvÊÃÌÀi`Ê «ÀVi`ÕÀiÃÊÌ
>ÌÊ>ÀiÊÀiV«}]ÊÞÕÊV>ÊÊ>ÌÊÌ
iÊVÕÌiÀÊÊ/>LiÊÓn° Table 2-8. Performance Monitor Counter to Analyze Execution Plan Reusability
Object(Instance[,InstanceN])
Counter
OMHOanran6OMHOp]peope_o
OMHNa)?kileh]pekjo+oa_
Recompilations of stored procedures add overhead on the processor. You should see a Û>ÕiÊVÃiÊÌÊâiÀÊvÀÊÌ
iÊOMHNa)?kileh]pekjo+oa_ÊVÕÌiÀ°ÊvÊÞÕÊVÃÃÌiÌÞÊÃiiÊâiÀÊ Û>ÕiÃ]ÊÌ
iÊÞÕÊÃ
Õ`ÊÕÃiÊ-+Ê*ÀviÀÊÌÊvÕÀÌ
iÀÊÛiÃÌ}>ÌiÊÌ
iÊÃÌÀi`Ê«ÀVi`ÕÀiÃÊÕ`iÀ}}ÊÀiV«>ÌðÊ"ViÊÞÕÊ`iÌvÞÊÌ
iÊÀiiÛ>ÌÊÃÌÀi`Ê«ÀVi`ÕÀiÃ]ÊÞÕÊÃ
Õ`Ê>ÌÌi«ÌÊÌÊ >>ÞâiÊ>`ÊÀiÃÛiÊÌ
iÊV>ÕÃiÊvÊÀiV«>ÌðÊÊ
>«ÌiÀÊ£ä]ÊÞÕÊÜÊi>ÀÊ
ÜÊÌÊ>>ÞâiÊ and resolve various causes of recompilation.
General Behavior -+Ê-iÀÛiÀÊ«ÀÛ`iÃÊ>``Ì>Ê«iÀvÀ>ViÊVÕÌiÀÃÊÌÊÌÀ>VÊÃiÊ}iiÀ>Ê>ëiVÌÃÊvÊ>Ê-+Ê -iÀÛiÀÊÃÞÃÌi°Ê/>LiÊÓÊÃÌÃÊ>ÊviÜÊvÊÌ
iÊÃÌÊVÞÊÕÃi`ÊVÕÌiÀð
CHAPTER 2
S Y S T E M P E R F O R M A N C E A N A LY S I S
Table 2-9. Performance Monitor Counters to Analyze Volume of Incoming Requests
Object(Instance[,InstanceN])
Counter
OMHOanran6Cajan]hOp]peope_o
Qoan?kjja_pekjo
OMHOanran6OMHOp]peope_o
>]p_dNamqaopo+oa_
User Connections Multiple read-onlyÊ-+Ê-iÀÛiÀÃÊV>ÊÜÀÊÌ}iÌ
iÀÊÊ>Ê>`L>>V}ÊiÛÀiÌÊÜ
iÀiÊ -+Ê-iÀÛiÀÊÃÊëÀi>`ÊÛiÀÊÃiÛiÀ>Ê>V
iîÊÌÊÃÕ««ÀÌÊ>Ê>À}iÊÕLiÀÊvÊ`>Ì>L>ÃiÊÀiµÕiÃÌÃ°Ê In such cases, it is better to monitor the Qoan?kjja_pekjo counter to evaluate the distribution vÊÕÃiÀÊViVÌÃÊ>VÀÃÃÊÕÌ«iÊ-+Ê-iÀÛiÀÊÃÌ>ViðÊQoan?kjja_pekjo can range all over Ì
iÊëiVÌÀÕÊÜÌ
ÊÀ>Ê>««V>ÌÊLi
>ÛÀ°Ê/
ÃÊÃÊÜ
iÀiÊ>ÊÀ>ÊL>ÃiiÊÃÊiÃÃiÌ>ÊÌÊ `iÌiÀiÊÌ
iÊiÝ«iVÌi`ÊLi
>ÛÀ°Ê9ÕÊÜÊÃiiÊ
ÜÊÞÕÊV>ÊiÃÌ>LÃ
ÊÌ
ÃÊL>ÃiiÊÃ
ÀÌÞ°
Batch Requests/sec /
ÃÊVÕÌiÀÊÃÊ>Ê}`Ê`V>ÌÀÊvÊÌ
iÊ>`ÊÊ-+Ê-iÀÛiÀ°Ê >Ãi`ÊÊÌ
iÊiÛiÊvÊÃÞÃÌiÊ ÀiÃÕÀViÊÕÌâ>ÌÊ>`Ê>]p_dNamqaopo+oa_]ÊÞÕÊV>ÊiÃÌ>ÌiÊÌ
iÊÕLiÀÊvÊÕÃiÀÃÊ-+Ê-iÀÛiÀÊ >ÞÊLiÊ>LiÊÌÊÌ>iÊÜÌ
ÕÌÊ`iÛi«}ÊÀiÃÕÀViÊLÌÌiiVðÊ/
ÃÊVÕÌiÀÊÛ>Õi]Ê>ÌÊ`vviÀiÌÊ load cycles, also helps you understand its relationship with the number of database connecÌðÊ/
ÃÊ>ÃÊ
i«ÃÊÞÕÊÕ`iÀÃÌ>`Ê-+Ê-iÀÛiÀ½ÃÊÀi>ÌÃ
«ÊÜÌ
ÊSa^Namqaop+oa_, that is, =_peraOanranL]cao*Namqaopo+oa_ for web applications using Microsoft Internet Information -iÀÛViÃÊ-®Ê>`ÊVÌÛiÊ-iÀÛiÀÊ*>}iÃÊ-*®°ÊÊÌ
ÃÊ>>ÞÃÃÊ
i«ÃÊÞÕÊLiÌÌiÀÊÕ`iÀÃÌ>`Ê>`Ê predict system behavior as the user load changes. /
iÊÛ>ÕiÊvÊÌ
ÃÊVÕÌiÀÊV>ÊÀ>}iÊÛiÀÊ>ÊÜ`iÊëiVÌÀÕÊÜÌ
ÊÀ>Ê>««V>ÌÊLi
>Ûior. A normal baselineÊÃÊiÃÃiÌ>ÊÌÊ`iÌiÀiÊÌ
iÊiÝ«iVÌi`ÊLi
>ÛÀ°Êi̽ÃÊÛiÊÊÌÊÊ>ÌÊ creating one now.
Creating a Baseline Ü that youÊ
>ÛiÊi`Ê>ÌÊ>ÊviÜÊvÊÌ
iÊ>Ê«iÀvÀ>ViÊVÕÌiÀÃ]Êi̽ÃÊÃiiÊ
ÜÊÌÊLÀ}Ê Ì
iÃiÊVÕÌiÀÃÊÌ}iÌ
iÀÊÌÊVÀi>ÌiÊ>ÊÃÞÃÌiÊL>Ãii°Ê/
iÃiÊ>ÀiÊÌ
iÊÃÌi«ÃÊÞÕÊii`ÊÌÊvÜ\ 1. Ài>ÌiÊ>ÊÀiÕÃ>LiÊÃÌÊvÊ«iÀvÀ>ViÊVÕÌiÀð 2. Ài>ÌiÊ>ÊVÕÌiÀÊ}ÊÕÃ}ÊÞÕÀÊÃÌÊvÊ«iÀvÀ>ViÊVÕÌiÀð 3. âiÊ*iÀvÀ>ViÊÌÀÊÛiÀ
i>`°
Creating a Reusable List of Performance Counters Run the Performance Monitor tool on a Windows Server 2008 machine connected to the same iÌÜÀÊ>ÃÊÌ
>ÌÊvÊÌ
iÊ-+Ê-iÀÛiÀÊÃÞÃÌi°Ê``Ê«iÀvÀ>ViÊVÕÌiÀÃÊÌÊÌ
iÊ6iÜÊ
>ÀÌÊ`ë>ÞÊ of the Performance Monitor through the Properties Ê >Ì>Ê Ê``Ê ÕÌiÀÃÊ`>}ÊLÝ]Ê>ÃÊ Ã
ÜÊÊ}ÕÀiÊÓ°
53
54
C HAPTER 2
SYS TEM P ER FOR MA NC E A NA L YS IS
Figure 2-9. Adding Performance Monitor counters ÀÊiÝ>«i]ÊÌ add the performance counter Lnk_aookn$[Pkp]h%X!Lnk_aooknPeia, follow these steps: 1. -iiVÌÊÌ
iÊ«ÌÊ-iiVÌÊ ÕÌiÀÃÊvÀÊ «ÕÌiÀ]Ê>`ÊëiVvÞÊÌ
iÊV«ÕÌiÀÊ>iÊ ÀÕ}Ê-+Ê-iÀÛiÀÊÊÌ
iÊVÀÀië`}ÊiÌÀÞÊvi`° 2. VÊÌ
iÊLÝÊiÝÌÊÌÊÌ
iÊ«iÀvÀ>ViÊLiVÌÊLnk_aookn. 3.
ÃiÊÌ
iÊ!Lnk_aooknPeia counter from the list of performance counters. 4.
ÃiÊejop]j_a[Pkp]hÊvÀÊÌ
iÊÃÌ>ViÃÊÊÌ
iÊÃÌ>ViÃÊvÊ-iiVÌi`Ê"LiVÌÊÃÌ° 5. VÊÌ
iÊ``ÊLÕÌÌÊÌÊ>``ÊÌ
ÃÊ«iÀvÀ>ViÊVÕÌiÀÊÌÊÌ
iÊÃÌÊvÊVÕÌiÀÃÊÌÊLiÊ added. 6. ÌÕiÊ>ÃÊii`i`ÊÜÌ
ÊÌ
iÀÊVÕÌiÀðÊ7
iÊvÃ
i`]ÊVVÊÌ
iÊ"ÊLÕÌÌ° When creating a reusable list for your baseline, you can repeat the preceding steps to add >ÊÌ
iÊ«iÀvÀ>ViÊVÕÌiÀÃÊÃÌi`ÊÊ/>LiÊÓ£ä°
CHAPTER 2
S Y S T E M P E R F O R M A N C E A N A LY S I S
Table 2-10. Performance Monitor Counters to Analyze SQL Server Performance
Object(Instance[,InstanceN])
Counter
Iaiknu
=r]eh]^haI>upao L]cao+oa_
Lduoe_]h@eog$@]p])`eog(Hkc)`eog%
!@eogPeia ?qnnajp@eogMqaqaHajcpd @eogPn]jobano+oa_ @eog>upao+oa_
Lnk_aookn$[Pkp]h%
!Lnk_aooknPeia !Lnerehaca`Peia
Ouopai
Lnk_aooknMqaqaHajcpd ?kjpatpOsep_dao+oa_
JapskngEjpanb]_a$Japskng_]n`%
>upaoPkp]h+oa_
JapskngOaciajp
!JapQpehev]pekj
OMHOanran6=__aooIapdk`o
BnaaOl]_aO_]jo+oa_ BqhhO_]jo+oa_
OMHOanran6>qbbanI]j]can
>qbban_]_dadepn]pek Bnaal]cao
OMHOanran6H]p_dao
Pkp]hH]p_dS]epPeia$io%
OMHOanran6Hk_go$[Pkp]h%
Hk_gPeiakqpo+oa_ Hk_gS]epPeia$io% Jqi^ankb@a]`hk_go+oa_
OMHOanran6IaiknuI]j]can
IaiknuCn]jpoLaj`ejc P]ncapOanranIaiknu$G>% Pkp]hOanranIaiknu$G>%
OMHOanran6OMHOp]peope_o
>]p_dNamqaopo+oa_ OMHNa)?kileh]pekjo+oa_
OMHOanran6Cajan]hOp]peope_o
Qoan?kjja_pekjo
"ViÊÞÕÊ
>ÛiÊ>``i`Ê>ÊÌ
iÊ«iÀvÀ>ViÊVÕÌiÀÃ]ÊVÃiÊÌ
iÊ``Ê ÕÌiÀÃÊ`>}ÊLÝ°Ê/Ê save the list of counters as an *dpiÊvi]ÊÀ}
ÌVVÊ>ÞÜ
iÀiÊÊÌ
iÊÀ}
ÌÊvÀ>iÊvÊ*iÀvÀ>ViÊ Monitor, and select the Save As menu item. /
iÊ*dpi file lists all the performance counters that can be used as a base set of counters ÌÊVÀi>ÌiÊ>ÊVÕÌiÀÊ}]ÊÀÊÌÊÛiÜÊ*iÀvÀ>ViÊÌÀÊ}À>«
ÃÊÌiÀ>VÌÛiÞ]ÊvÀÊÌ
iÊÃ>iÊ-+Ê -iÀÛiÀÊ>V
i°Ê/Ê>ÃÊÕÃiÊÌ
ÃÊÃÌÊvÊVÕÌiÀÃÊvÀÊÌ
iÀÊ-+Ê-iÀÛiÀÊ>V
iÃ]Ê«iÊÌ
iÊ*dpi viÊÊ>Êi`ÌÀÊÃÕV
Ê>ÃÊ Ìi«>`]Ê>`ÊÀi«>ViÊ>ÊÃÌ>ViÃÊvÊXXOMHOanranI]_dejaJ]ia with ‘’ >ÊL>ÊÃÌÀ}®]ÊÜÌ
ÕÌÊÌ
iʵÕÌið You can also use this counter list file to view Performance Monitor graphs interactively in >ÊÌiÀiÌÊLÀÜÃiÀ]Ê>ÃÊÃ
ÜÊÊ}ÕÀiÊÓ£ä°
55
56
C HAPTER 2
SYS TEM P ER FOR MA NC E A NA L YS IS
Figure 2-10. Performance Monitor in Internet browser
Creating a Counter Log Using the List of Performance Counters Performance Monitor provides a counter log facility to save the performance data of multiple counters over a period of time. You can view the saved counter log using Performance Monitor ÌÊ>>ÞâiÊÌ
iÊ«iÀvÀ>ViÊ`>Ì>°ÊÌÊÃÊÕÃÕ>ÞÊVÛiiÌÊÌÊVÀi>ÌiÊ>ÊVÕÌiÀÊ}ÊvÀÊ>Ê`ivi`Ê ÃÌÊvÊ«iÀvÀ>ViÊVÕÌiÀðÊ-«ÞÊViVÌ}ÊÌ
iÊ`>Ì>ÊÀ>Ì
iÀÊÌ
>ÊÛiÜ}ÊÌÊÌ
ÀÕ}
ÊÌ
iÊ1Ê is the preferred method of automation to prepare for troubleshooting your server’s performance or establishing a baseline.
Ý«>`Ê >Ì>Ê iVÌÀÊ-iÌÃÊ Ê1ÃiÀÊ ivi`°Ê,}
ÌVV]Ê>`ÊÃiiVÌÊ iÜÊ Ê >Ì>Ê iVÌÀÊ -iÌ°Ê iviÊÌ
iÊ>iÊvÊÌ
iÊÃiÌ]Ê>`Ê>iÊÌ
ÃÊ>Ê>Õ>ÊVÀi>ÌÊLÞÊVV}ÊÌ
iÊ>««À«À>ÌiÊ À>`ÊLÕÌÌÆÊÌ
iÊVVÊ iÝÌ°Ê9Õ½Ê
>ÛiÊÌÊ`iviÊÜ
>ÌÊÌÞ«iÊvÊ`>Ì>ÊÞÕ½ÀiÊViVÌ}°ÊÊÌ
ÃÊ V>Ãi]ÊÃiiVÌÊÌ
iÊV
iVÊLÝÊ*iÀvÀ>ViÊ ÕÌiÀÃÊÕ`iÀÊÌ
iÊ Ài>ÌiÊ >Ì>Ê}ÃÊÀ>`ÊLÕÌÌ]Ê >`ÊÌ
iÊVVÊ iÝÌ°ÊiÀiÊÞÕÊV>Ê`iviÊÌ
iÊ«iÀvÀ>ViÊLiVÌÃÊÞÕÊÜ>ÌÊÌÊViVÌÊÕÃ}Ê Ì
iÊÃ>iÊ``Ê ÕÌiÀÃÊ`>}ÊLÝÊ>ÃÊÃ
ÜÊi>ÀiÀÊÊ}ÕÀiÊÓ°Ê V}Ê iÝÌÊ>ÜÃÊÞÕÊÌÊ `iviÊÌ
iÊ`iÃÌ>ÌÊv`iÀ°Ê VÊ iÝÌ]ÊÌ
iÊÃiiVÌÊÌ
iÊÀ>`ÊLÕÌÌÊ"«iÊ*À«iÀÌiÃÊvÀÊ/
ÃÊ >Ì>Ê iVÌÀÊ-iÌ]Ê>`ÊVVÊÃ
°Ê9ÕÊV>ÊÃV
i`ÕiÊÌ
iÊVÕÌiÀÊ}ÊÌÊ>ÕÌ>ÌV>ÞÊÃÌ>ÀÌÊ at a specific time and stop after a certain time period or at a specific time. You can configure Ì
iÃiÊÃiÌÌ}ÃÊÌ
ÀÕ}
ÊÌ
iÊ-V
i`ÕiÊ«>i°Ê}ÕÀiÊÓ££ÊÃ
ÜÃÊÌ
iÊÃÕ>ÀÞÊvÊÜ
V
ÊVÕÌiÀÃÊ have been selected as well as the frequency with which the counters will be collected.
CHAPTER 2
S Y S T E M P E R F O R M A N C E A N A LY S I S
Figure 2-11. Defining a Performance Monitor counter log
Tip I’ll offer additional suggestions for these settings in the section that follows.
ÀÊ>``Ì>ÊvÀ>ÌÊÊ
ÜÊÌÊVÀi>ÌiÊVÕÌiÀÊ}ÃÊÕÃ}Ê*iÀvÀ>ViÊÌÀ]Ê «i>ÃiÊÀiviÀÊÌÊÌ
iÊVÀÃvÌÊÜi`}iÊ >ÃiÊ>ÀÌViʺ*iÀvÀ>ViÊ/Õ}ÊÕ`iiÃÊvÀÊ 7`ÜÃÊ-iÀÛiÀÊÓään» at dppl6++`ksjhk]`*ie_nkokbp*_ki+`ksjhk]`+5+_+1+ 5_1^.-23)4,-3)0^]a)5b`a)`155^]_4-40]+Lanb)pqj)onr*`k_t.
Minimizing Performance Monitor Overhead /
i Performance Monitor tool is designed to add as little overhead as possible, if used VÀÀiVÌÞ°Ê/ÊâiÊÌ
iÊ«>VÌÊvÊÕÃ}ÊÌ
ÃÊÌÊÊ>ÊÃÞÃÌi]ÊVÃ`iÀÊÌ
iÊvÜ}Ê suggestions: Ê
UÊ ÌÊÌ
iÊÕLiÀÊvÊVÕÌiÀÃ]ÊëiVvV>ÞÊ«iÀvÀ>ViÊLiVÌð
Ê
UÊ 1ÃiÊVÕÌiÀÊ}ÃÊÃÌi>`ÊvÊÛiÜ}Ê*iÀvÀ>ViÊÌÀÊ}À>«
ÃÊÌiÀ>VÌÛiÞ°
Ê
UÊ ,ÕÊ*iÀvÀ>ViÊÌÀÊÀiÌiÞÊÜ
iÊÛiÜ}Ê}À>«
ÃÊÌiÀ>VÌÛiÞ°
Ê
UÊ ->ÛiÊÌ
iÊVÕÌiÀÊ}ÊviÊÌÊ>Ê`vviÀiÌÊV>Ê`ð
Ê
UÊ VÀi>ÃiÊÌ
iÊÃ>«}ÊÌiÀÛ>° i̽ÃÊVÃ`iÀÊi>V
ÊvÊÌ
iÃiÊ«ÌÃÊÊÀiÊ`iÌ>°
57
58
C HAPTER 2
SYS TEM P ER FOR MA NC E A NA L YS IS
Limit the Number of Counters Monitoring large numbers of performance counters with small sampling intervals could incur ÃiÊ>ÕÌÊvÊÛiÀ
i>`ÊÊÌ
iÊÃÞÃÌi°Ê/
iÊLÕÊvÊÌ
ÃÊÛiÀ
i>`ÊViÃÊvÀÊÌ
iÊÕLiÀÊvÊ «iÀvÀ>ViÊLiVÌÃÊÞÕÊ>ÀiÊÌÀ}]ÊÃÊÃiiVÌ}ÊÌ
iÊÜÃiÞÊÃÊ«ÀÌ>Ì°Ê/
iÊÕLiÀÊ of counters for the selected performance objects does not add much overhead, because it gives ÞÊ>Ê>ÌÌÀLÕÌiÊvÊÌ
iÊLiVÌÊÌÃiv°Ê/
iÀivÀi]ÊÌÊÃÊ«ÀÌ>ÌÊÌÊÜÊÜ
>ÌÊLiVÌÃÊÞÕÊÜ>ÌÊ to monitor and why.
Prefer Counter Logs 1Ãi counter logs instead of viewing a Performance Monitor graph interactively, because Performance Monitor graphing is more costly in terms of overhead. Monitoring current activities should be limited to short-term viewing of data, troubleshooting, and diagnosis. Performance data reported via a counter log is sampled, meaning that data is collected periodically rather than traced, whereas the Performance Monitor graph is updated in real time as events occur. 1Ã}ÊVÕÌiÀÊ}ÃÊÜÊÀi`ÕViÊÌ
>ÌÊÛiÀ
i>`°
View Performance Monitor Graphs Remotely Since viewing the live performance data using Performance Monitor graphs creates a fair amount of overhead on the system, run the tool remotely on a different machine, and connect ÌÊÌ
iÊ-+Ê-iÀÛiÀÊÃÞÃÌiÊÌ
ÀÕ}
ÊÌ
iÊÌ°Ê/ÊÀiÌiÞÊViVÌÊÌÊÌ
iÊ-+Ê-iÀÛiÀÊ>V
i]Ê ÀÕÊÌ
iÊ*iÀvÀ>ViÊÌÀÊÌÊÊ>Ê>V
iÊViVÌi`ÊÌÊÌ
iÊiÌÜÀÊÌÊÜ
V
ÊÌ
iÊ-+Ê Server machine is connected. ÃÊÃ
ÜÊÊ}ÕÀiÊÓ]ÊÌÞ«iÊÌ
iÊV«ÕÌiÀÊ>iÊÀÊ*Ê>``ÀiÃîÊvÊÌ
iÊ-+Ê-iÀÛiÀÊ >V
iÊÊÌ
iÊ-iiVÌÊ ÕÌiÀÃÊvÀÊ «ÕÌiÀÊLÝ°Ê iÊ>Ü>ÀiÊÌ
>ÌÊvÊÞÕÊViVÌÊÌÊÌ
iÊ production server through a Windows Server 2008 terminal service session, the major part of the tool will still run on the server.
Save Counter Log Locally
iVÌ}ÊÌ
i performance data for the counter log does not incur the overhead of displaying any graph. So, while using counter log mode, it is more efficient to log counter values locally ÊÌ
iÊ-+Ê-iÀÛiÀÊÃÞÃÌiÊÃÌi>`ÊvÊÌÀ>ÃviÀÀ}ÊÌ
iÊ«iÀvÀ>ViÊ`>Ì>Ê>VÀÃÃÊÌ
iÊiÌÜÀ°Ê*ÕÌÊ Ì
iÊVÕÌiÀÊ}ÊviÊÊ>ÊV>Ê`ÃÊÌ
iÀÊÌ
>ÊÌ
iÊiÃÊÌ
>ÌÊ>ÀiÊÌÀi`°
Increase the Sampling Interval BecauseÊÞÕÊ>ÀiÊ>ÞÊÌiÀiÃÌi`ÊÊÌ
iÊÀiÃÕÀViÊÕÌâ>ÌÊ«>ÌÌiÀÊ`ÕÀ}ÊL>ÃiiÊÌÀ}]ÊÞÕÊV>Êi>ÃÞÊVÀi>ÃiÊÌ
iÊ«iÀvÀ>ViÊ`>Ì>ÊÃ>«}ÊÌiÀÛ>ÊÌÊÈäÊÃiV`ÃÊÀÊÀiÊ ÌÊ`iVÀi>ÃiÊÌ
iÊ}ÊviÊÃâiÊ>`ÊÀi`ÕViÊ`i>`ÊÊ`ÃÊÉ"ðÊ9ÕÊV>ÊÕÃiÊ>ÊÃ
ÀÌÊÃ>«}Ê interval to detect and diagnose timing issues. Even while viewing Performance Monitor graphs interactively, increase the sampling interval from the default value of one second per sample. ÕÃÌÊÀiiLiÀ]ÊV
>}}ÊÌ
iÊÃ>«}ÊÃâi]ÊÕ«ÊÀÊ`Ü]ÊV>Ê>vviVÌÊÌ
i granularity of the data as well as the quantity. You have to weigh these choices carefully.
CHAPTER 2
S Y S T E M P E R F O R M A N C E A N A LY S I S
System Behavior Analysis Against Baseline /
i default behavior of a database application changes over time because of various factors such as the following: Ê
UÊ
>}iÊvÊ`>Ì>
Ê
UÊ
>}iÊvÊÕÃiÀÊ}ÀÕ«
Ê
UÊ
>}iÊÊÕÃ>}iÊ«>ÌÌiÀÊvÊÌ
iÊ>««V>Ì
Ê
UÊ ``ÌÊÌÊÀÊV
>}iÊÊ>««V>ÌÊvÕVÌ>ÌiÃ
Ê
UÊ
>}iÊÊÃvÌÜ>ÀiÊiÛÀiÌÊLiV>ÕÃiÊvÊÌ
iÊÃÌ>>ÌÊvÊiÜÊÃiÀÛViÊ«>VÃÊÀÊ software upgrades
Ê
UÊ
>}iÊÊ
>À`Ü>ÀiÊiÛÀiÌ
Because of the preceding changes, the baseline created for the database server slowly loses its significance. It may not always be accurate to compare the current behavior of the ÃÞÃÌiÊÜÌ
Ê>Ê`ÊL>Ãii°Ê/
iÀivÀi]ÊÌÊÃÊ«ÀÌ>ÌÊÌÊii«ÊÌ
iÊL>ÃiiÊÕ«Ì`>ÌiÊLÞÊ creating a new baseline at regular time intervals. It is also beneficial to archive the previous baseline logs so that they can be referred to later, if required. /
iÊVÕÌiÀÊ}ÊvÀÊÌ
iÊL>ÃiiÊÀÊÌ
iÊVÕÀÀiÌÊLi
>ÛÀÊvÊÌ
iÊÃÞÃÌiÊV>ÊLiÊ>>Þâi`Ê using the Performance Monitor tool by following these steps: 1. "«iÊÌ
iÊVÕÌiÀÊ}°Ê1ÃiÊ*iÀvÀ>ViÊÌÀ½ÃÊÌL>ÀÊÌiÊ6iÜÊ}ÊiÊ >Ì>]Ê>`Ê select the log file name. 2. ``Ê>ÊÌ
iÊ«iÀvÀ>ViÊVÕÌiÀÃÊÌÊ>>ÞâiÊÌ
iÊ«iÀvÀ>ViÊ`>Ì>°Ê ÌiÊÌ
>ÌÊÞÊÌ
iÊ performance objects, counters, and instances selected during the counter log creation are shown in the selection lists. 3. >ÞâiÊÌ
iÊÃÞÃÌiÊLi
>ÛÀÊ>ÌÊ`vviÀiÌÊ«>ÀÌÃÊvÊÌ
iÊ`>ÞÊLÞÊ>`ÕÃÌ}ÊÌ
iÊÌiÊÀ>}iÊ >VVÀ`}Þ]Ê>ÃÊÃ
ÜÊÊ}ÕÀiÊÓ£Ó°
Figure 2-12. Defining time range for log analysis
59
60
C HAPTER 2
SYS TEM P ER FOR MA NC E A NA L YS IS
ÕÀ}Ê>Ê«iÀvÀ>ViÊÀiÛiÜ]ÊÞÕÊV>Ê>>ÞâiÊÌ
iÊÃÞÃÌiiÛiÊLi
>ÛÀÊvÊÌ
iÊ`>Ì>L>ÃiÊ LÞÊV«>À}ÊÌ
iÊVÕÀÀiÌÊÛ>ÕiÊvÊ«iÀvÀ>ViÊVÕÌiÀÃÊÜÌ
ÊÌ
iÊ>ÌiÃÌÊL>Ãii°Ê/>iÊÌ
iÊvlowing considerations into account while comparing the performance data: Ê
UÊ 1ÃiÊÌ
iÊÃ>iÊÃiÌÊvÊ«iÀvÀ>ViÊVÕÌiÀÃÊÊLÌ
ÊV>Ãið
Ê
UÊ «>ÀiÊÌ
iÊÕ]Ê>ÝÕ]ÊÀÊ>ÛiÀ>}iÊÛ>ÕiÊvÊÌ
iÊVÕÌiÀÃÊ>ÃÊ>««V>LiÊvÀÊ Ì
iÊ`Û`Õ>ÊVÕÌiÀðÊÊiÝ«>i`ÊÌ
iÊëiVvVÊÛ>ÕiÃÊvÀÊÌ
iÊVÕÌiÀÃÊi>ÀiÀ°
Ê
UÊ -iÊVÕÌiÀÃÊ
>ÛiÊ>Ê>LÃÕÌiÊ}`ÉL>`ÊÛ>ÕiÊ>ÃÊiÌi`Ê«ÀiÛÕÃÞ°Ê/
iÊVÕÀÀiÌÊ Û>ÕiÊvÊÌ
iÃiÊVÕÌiÀÃÊii`ÊÌÊLiÊV«>Ài`ÊÜÌ
ÊÌ
iÊL>ÃiiÊÛ>ÕiðÊÀÊiÝ>«i]Ê if the current average value of the L]cao+oa_ counter is 100, then it indicates that the ÃÞÃÌiÊ
>ÃÊ`iÛi«i`Ê>ÊiÀÞÊLÌÌiiV°Ê ÛiÊÌ
Õ}
ÊÌÊ`iÃÊÌÊÀiµÕÀiÊ>ÊVparison with the baseline, it is still advantageous to review the corresponding baseline Û>Õi]ÊLiV>ÕÃiÊÌ
iÊiÀÞÊLÌÌiiVÊ}
ÌÊ
>ÛiÊiÝÃÌi`ÊvÀÊ>Ê}ÊÌi°Ê>Û}ÊÌ
iÊ >ÀV
Ûi`ÊL>ÃiiÊ}ÃÊ
i«ÃÊ`iÌiVÌÊÌ
iÊvÀÃÌÊVVÕÀÀiViÊvÊÌ
iÊiÀÞÊLÌÌiiV°
Ê
UÊ -iÊVÕÌiÀÃÊ`ÊÌÊ
>ÛiÊ>Ê`ivÌÛiÊ}`ÉL>`ÊÛ>Õi°Ê iV>ÕÃiÊÌ
iÀÊÛ>ÕiÊ`i«i`ÃÊ on the application, a relative comparison with the corresponding baseline counters is >ÊÕÃÌ°ÊÀÊiÝ>«i]ÊÌ
iÊVÕÀÀiÌÊÛ>ÕiÊvÊÌ
iÊQoan?kjja_pekjoÊVÕÌiÀÊvÀÊ-+Ê-iÀÛiÀÊ does not signify anything good or bad with the application. But comparing it with the corresponding baseline value may reveal a big increase in the number of user connecÌÃ]Ê`V>Ì}Ê>ÊVÀi>ÃiÊÊÌ
iÊÜÀ>`°
Ê
UÊ «>ÀiÊ>ÊÀ>}iÊvÊÛ>ÕiÊvÀÊÌ
iÊVÕÌiÀÃÊvÀÊÌ
iÊVÕÀÀiÌÊ>`ÊÌ
iÊL>ÃiiÊVÕÌiÀÊ }ðÊ/
iÊvÕVÌÕ>ÌÊÊÌ
iÊ`Û`Õ>ÊÛ>ÕiÃÊvÊÌ
iÊVÕÌiÀÃÊÜÊLiÊÀ>âi`ÊLÞÊÌ
iÊ range of values.
Ê
UÊ «>ÀiÊ}ÃÊvÀÊÌ
iÊÃ>iÊ«>ÀÌÊvÊÌ
iÊ`>Þ°ÊÀÊÃÌÊ>««V>ÌÃ]ÊÌ
iÊÕÃ>}iÊ«>ÌÌiÀÊ Û>ÀiÃÊ`ÕÀ}Ê`vviÀiÌÊ«>ÀÌÃÊvÊÌ
iÊ`>Þ°Ê/ÊLÌ>ÊÌ
iÊÕ]Ê>ÝÕ]Ê>`Ê>ÛiÀage value of the counters for a specific time, adjust the time range of the counter logs as shown previously.
"ViÊÌ
iÊÃÞÃÌiiÛiÊLÌÌiiVÊÃÊ`iÌvi`]ÊÌ
iÊÌiÀ>ÊLi
>ÛÀÊvÊÌ
iÊ>««V>ÌÊ Ã
Õ`ÊLiÊ>>Þâi`ÊÌÊ`iÌiÀiÊÌ
iÊV>ÕÃiÊvÊÌ
iÊLÌÌiiV°Ê`iÌvÞ}Ê>`Ê«Ìâ}ÊÌ
iÊ ÃÕÀViÊvÊÌ
iÊLÌÌiiV will help use the system resources efficiently.
Summary In thisÊV
>«ÌiÀ]ÊÞÕÊi>Ài`ÊÌ
>ÌÊÞÕÊV>ÊÕÃiÊÌ
iÊ*iÀvÀ>ViÊÌÀÊÌÊÌÊ>>ÞâiÊÌ
iÊ effect on system resources of a slow-performing database application, as well as the overall Li
>ÛÀÊvÊ-+Ê-iÀÛiÀ°ÊÀÊiÛiÀÞÊÀiÃÕÌ>ÌÊÃÞÃÌiÊLÌÌiiV]ÊÌ
iÀiÊ>ÀiÊÌÜÊÌÞ«iÃÊvÊÀiÃÕÌÃ\Ê
>À`Ü>ÀiÊÀiÃÕÌÃÊ>`Ê>««V>ÌÊ«Ìâ>Ì°Ê"vÊVÕÀÃi]ÊÌÊÃÊ>Ü>ÞÃÊLiivV>ÊÌÊ «ÌâiÊÌ
iÊ`>Ì>L>ÃiÊ>««V>ÌÊLivÀiÊVÃ`iÀ}Ê>Ê
>À`Ü>ÀiÊÕ«}À>`i° ÊÌ
iÊiÝÌÊV
>«ÌiÀ]ÊÞÕÊÜÊi>ÀÊ
ÜÊÌÊ>>ÞâiÊÌ
iÊÜÀ>`ÊvÊ>Ê`>Ì>L>ÃiÊ>««V>ÌÊ for performance tuning.
CHAPT ER
3
SQL Query Performance Analysis A
common cause of slow SQL Server performance is a heavy database application workload— the nature of the queries themselves. Thus, to analyze the cause of a system bottleneck, it is important to examine the database application workload and identify the SQL queries causing the most stress on system resources. To do this, you use the SQL Profiler and Management Studio tools. In this chapter, I cover the following topics: Ê
UÊ /
iÊL>ÃVÃÊvÊÌ
iÊ-+Ê*ÀviÀÊÌ
Ê
UÊ ÜÊÌÊ>>ÞâiÊ-+Ê-iÀÛiÀÊÜÀ>`Ê>`Ê`iÌvÞÊVÃÌÞÊ-+ʵÕiÀiÃÊÕÃ}Ê-+Ê*ÀviÀ
Ê
UÊ ÜÊÌÊVLiÊÌ
iÊL>ÃiiÊi>ÃÕÀiiÌÃÊÜÌ
Ê`>Ì>ÊViVÌi`ÊvÀÊ-+Ê*ÀviÀ
Ê
UÊ ÜÊÌÊ>>ÞâiÊÌ
iÊ«ÀViÃÃ}ÊÃÌÀ>Ìi}ÞÊvÊ>ÊVÃÌÞÊ-+ʵÕiÀÞÊÕÃ}Ê>>}iiÌÊ Studio
Ê
UÊ ÜÊÌÊÌÀ>VʵÕiÀÞÊ«iÀvÀ>ViÊÌ
ÀÕ}
Ê`Þ>VÊ>>}iiÌÊÛiÜÃ
Ê
UÊ ÜÊÌÊ>>ÞâiÊÌ
iÊivviVÌÛiiÃÃÊvÊ`iÝÊ>`ÊÊÃÌÀ>Ìi}iÃÊvÀÊ>Ê-+ʵÕiÀÞ
Ê
UÊ ÜÊÌÊi>ÃÕÀiÊÌ
iÊVÃÌÊvÊ>Ê-+ʵÕiÀÞÊÕÃ}Ê-+ÊÕÌÌiÃ
The SQL Profiler Tool SQL Profiler is a GUI and set of system stored procedures that can be used to do the following: Ê
UÊ À>«
V>ÞÊÌÀ SQL Server queries
Ê
UÊ iVÌʵÕiÀÞÊvÀ>ÌÊÊÌ
iÊL>V}ÀÕ`
Ê
UÊ >ÞâiÊ«iÀvÀ>Vi
Ê
UÊ >}ÃiÊ«ÀLiÃÊÃÕV
Ê>ÃÊ`i>`VÃ
Ê
UÊ iLÕ}Ê>Ê/À>Ã>VÌ-+Ê/-+®ÊÃÌ>ÌiiÌ
Ê
UÊ ,i«>ÞÊ-+Ê-iÀÛiÀÊ>VÌÛÌÞÊÊ>ÊÃÕ>Ì
61
62
C HAPTER 3
SQ L QU ER Y P ER FOR MA NC E A NA L YS IS
You can also use SQL Profiler to capture activities performed on a SQL Server instance. Such a capture is called a Profiler trace. You can use a Profiler trace to capture events gener ated by various subsystems within SQL Server. You can run traces from the graphical front end or through direct calls to the procedures. The most efficient way to define a trace is through the system procedures, but a good place to start learning about traces is through the GUI.
Profiler Traces Open the Profiler tool from the Start Programs Microsoft SQL Server 2008 Performance Tools menu, and select File New Ê/À>Vi°Ê9ÕÊV>Ê>ÃÊ«ÀiÃÃÊ ÌÀ³ ÊÀÊVVÊÌ
iÊ iÜÊ/À>ViÊ button. This opens a connection window, so choose the server instance you’ll be tracing and Ì
iÊViVÌ°Ê/
>ÌÊ«iÃÊÌ
iÊ/À>ViÊ*À«iÀÌiÃÊ`>}ÊLÝÊÃ
ÜÊÊ}ÕÀiÊΣ°
Figure 3-1. General trace properties You can supply a trace name to help categorize your traces later when you have lots of Ì
i°Ê vviÀiÌÊÌÀ>ViÊÌi«>ÌiÃÊ>ÀiÊ>Û>>LiÊÌ
>ÌʵÕVÞÊ
i«ÊÞÕÊÃiÌÊÕ«ÊiÜÊÌÀ>ViÃÊvÀÊ`vviÀ ent purposes. For the moment, I’ll stick with the Standard trace. Without additional changes, this trace will run as a graphical trace, but from the Trace Properties dialog box you can define the trace to save its output to a file or to a table. I’ll spend more time on these options later in this chapter. Finally, from the General tab, you can define a stop time for the trace. These options combine to give you more control over exactly what you intend to monitor and how you intend to monitor it within SQL Server.
CHAPTER 3
S Q L Q U E R Y P E R F O R M A N C E A N A LY S I S
With the initial setup complete, click the Events Selection tab to provide more detailed `ivÌÊÌÊÞÕÀÊÌÀ>Vi]Ê>ÃÊÃ
ÜÊÊ}ÕÀiÊÎÓ°
Figure 3-2. Events Selection tab
Events event represents various activities performed in SQL Server. These are categorized for easy classification into event classesÆÊVÕÀÃÀÊiÛiÌÃ]ÊVÊiÛiÌÃ]ÊÃÌÀi`Ê«ÀVi`ÕÀiÊiÛiÌÃ]Ê>`Ê/-+Ê events are a few common event classes. ÀÊ«iÀvÀ>ViÊ>>ÞÃÃ]ÊÞÕÊ>ÀiÊ>ÞÊÌiÀiÃÌi`ÊÊÌ
iÊiÛiÌÃÊÌ
>ÌÊ
i«ÊÞÕÊÕ`}iÊ levels of resource stress for various activities performed on SQL Server. By resource stress, I mean things such as the following: Ê
UÊ 7
>ÌÊ`ÊvÊ *1ÊÕÌâ>ÌÊÜ>ÃÊÛÛi`ÊvÀÊÌ
iÊ-+Ê>VÌÛÌÞ¶
Ê
UÊ ÜÊÕV
ÊiÀÞÊÜ>ÃÊÕÃi`¶
Ê
UÊ ÜÊÕV
ÊÉ"ÊÜ>ÃÊÛÛi`¶
Ê
UÊ ÜÊ}Ê``ÊÌ
iÊ-+Ê>VÌÛÌÞÊÌ>iÊÌÊiÝiVÕÌi¶
Ê
UÊ ÜÊvÀiµÕiÌÞÊÜ>ÃÊ>Ê«>ÀÌVÕ>ÀʵÕiÀÞÊiÝiVÕÌi`¶
Ê
UÊ 7
>ÌÊ`ÊvÊiÀÀÀÃÊ>`ÊÜ>À}ÃÊÜiÀiÊv>Vi`ÊLÞÊÌ
iʵÕiÀiö
63
64
C HAPTER 3
SQ L QU ER Y P ER FOR MA NC E A NA L YS IS
You can calculate the resource stress of a SQL activity after the completion of an event, so the main events you use for performance analysis are those that represent the completion of a -+Ê>VÌÛÌÞ°Ê/>LiÊΣÊ`iÃVÀLiÃÊÌ
iÃiÊiÛiÌð Table 3-1. Events to Trace Query Completion
Event Class
Event
Description
Opkna`Lnk_a`qnao
NL?6?kilhapa`
Ê,* ÊV«iÌÊiÛiÌ
POMH
OL6?kilhapa`
ÊÃÌÀi` procedure completion event
OL6Opip?kilhapa`
Ê-+ÊÃÌ>ÌiiÌ completion event within a stored procedure
OMH6>]p_d?kilhapa`
Ê/-+ batch completion event
OMH6Opip?kilhapa`
Ê/-+ÊÃÌ>ÌiiÌ completion event
Ê,* ÊiÛiÌÊ`V>ÌiÃÊÌ
>ÌÊÌ
iÊÃÌÀi`Ê«ÀVi`ÕÀiÊÜ>ÃÊiÝiVÕÌi`ÊÕÃ}ÊÌ
iÊ,iÌiÊ*ÀVi `ÕÀiÊ >Ê,* ®ÊiV
>ÃÊÌ
ÀÕ}
Ê>Ê" ÊV>`°ÊvÊ>Ê`>Ì>L>ÃiÊ>««V>ÌÊiÝiVÕÌiÃÊ a ÃÌÀi`Ê«ÀVi`ÕÀiÊÕÃ}ÊÌ
iÊ/-+ÊATA?QPA statement, then that stored procedure is resolved >ÃÊ>Ê-+ÊL>ÌV
ÊÀ>Ì
iÀÊÌ
>Ê>ÃÊ>Ê,* °Ê,* ÊÀiµÕiÃÌÃÊ>ÀiÊ}iiÀ>ÞÊv>ÃÌiÀÊÌ
>ÊATA?QPA requests, since they bypass much of the statement parsing and parameter processing in SQL Server. ÊT-SQL batch is aÊÃiÌÊvÊ-+ʵÕiÀiÃÊÌ
>ÌÊ>ÀiÊÃÕLÌÌi`ÊÌ}iÌ
iÀÊÌÊ-+Ê-iÀÛiÀ°ÊÊ/-+Ê batch is usually terminated by a CK command. The CKÊV>`ÊÃÊÌÊ>Ê/-+ÊÃÌ>ÌiiÌ°Ê Instead, the CK command is recognized by the omh_i` utility, as well as by Management Studio, >`ÊÌÊÃ}>ÃÊÌ
iÊi`ÊvÊ>ÊL>ÌV
°Ê >V
Ê-+ʵÕiÀÞÊÊÌ
iÊL>ÌV
ÊÃÊVÃ`iÀi`Ê>Ê/-+ÊÃÌ>Ìi iÌ°Ê/
ÕÃ]Ê>Ê/-+ÊL>ÌV
ÊVÃÃÌÃÊvÊiÊÀÊÀiÊ/-+ÊÃÌ>ÌiiÌðÊ-Ì>ÌiiÌÃÊÀÊ/-+Ê ÃÌ>ÌiiÌÃÊ>ÀiÊ>ÃÊÌ
iÊ`Û`Õ>]Ê`ÃVÀiÌiÊV>`ÃÊÜÌ
Ê>ÊÃÌÀi`Ê«ÀVi`ÕÀi°Ê >«ÌÕÀ}Ê individual statements with the OL6Opip?kilhapa` or OMH6Opip?kilhapa` event can be a very expensive operation, depending on the number of individual statements within your queries. ÃÃÕiÊvÀÊ>ÊiÌÊÌ
>ÌÊi>V
ÊÃÌÀi`Ê«ÀVi`ÕÀiÊÜÌ
ÊÞÕÀÊÃÞÃÌiÊVÌ>ÃÊi]Ê>`ÊÞÊ i]Ê/-+ÊÃÌ>ÌiiÌ°ÊÊÌ
ÃÊV>Ãi]ÊÌ
iÊViVÌÊvÊV«iÌi`ÊÃÌ>ÌiiÌÃÊÃÊ«ÀiÌÌÞÊÜ°Ê ÜÊ assume that you have multiple statements within your procedures and that some of those pro Vi`ÕÀiÃÊ>ÀiÊV>ÃÊÌÊÌ
iÀÊ«ÀVi`ÕÀiÃÊÜÌ
ÊÌ
iÀÊÃÌ>ÌiiÌÃ°Ê iVÌ}Ê>ÊÌ
ÃÊiÝÌÀ>Ê`>Ì>ÊÜÊ becomes a noticeable load on the system. Statement completion events should be collected iÝÌÀiiÞÊÕ`VÕÃÞ]ÊiëiV>ÞÊÊ>Ê«À`ÕVÌÊÃÞÃÌi° vÌiÀÊÞÕ½ÛiÊÃiiVÌi`Ê>ÊÌÀ>ViÊÌi«>Ìi]Ê>Ê«ÀiÃiiVÌi`ÊÃÌÊvÊiÛiÌÃÊÜÊ>Ài>`ÞÊLiÊ`ivi`Ê on the Events Selection tab. Only the events you have selected will be displayed. To see the full ÃÌÊvÊiÛiÌÃÊ>Û>>Li]ÊVVÊÌ
iÊ-
ÜÊÊ ÛiÌÃÊV
iVÊLÝ°Ê/Ê>``Ê>ÊiÛiÌÊÌÊÌ
iÊÌÀ>Vi]Êv`Ê the event under an event class in the Event column, and click the check box to the left of it. To remove events not required, deselect the check box next to the event. Ì
Õ}
ÊÌ
iÊiÛiÌÃÊÃÌi`ÊÊ/>LiÊΣÊÀi«ÀiÃiÌÊÌ
iÊÃÌÊVÊiÛiÌÃÊÕÃi`ÊvÀÊ determining performance, you can sometimes use a number of additional events to diagnose Ì
iÊÃ>iÊÌ
}°ÊÀÊiÝ>«i]Ê>ÃÊiÌi`ÊÊ
>«ÌiÀÊ£]ÊÀi«i>Ìi`ÊÀiV«>ÌÊvÊ>ÊÃÌÀi`Ê procedure adds processing overhead, which hurts the performance of the database request. The Opkna`Lnk_a`qnao event class of the Profiler tool includes an event, OL6Na_kileha, to `V>ÌiÊÌ
iÊÀiV«>ÌÊvÊ>ÊÃÌÀi`Ê«ÀVi`ÕÀiÊÌ
ÃÊiÛiÌÊÃÊiÝ«>i`ÊÊ`i«Ì
ÊÊ
>«ÌiÀÊ £ä®°Ê->ÀÞ]Ê*ÀviÀÊVÕ`iÃÊ>``Ì>ÊiÛiÌÃÊÌÊ`V>ÌiÊÌ
iÀÊ«iÀvÀ>ViÀi>Ìi`ÊÃÃÕiÃÊ ÜÌ
Ê>Ê`>Ì>L>ÃiÊÜÀ>`°Ê/>LiÊÎÓÊÃ
ÜÃÊ>ÊviÜÊvÊÌ
iÃiÊiÛiÌð
CHAPTER 3
S Q L Q U E R Y P E R F O R M A N C E A N A LY S I S
Table 3-2. Events to Trace Query Performance
Event Class
Event
Description
Oa_qnepu=q`ep
=q`epHkcej =q`epHkckqp
Keeps track of database connections when users connect to and disconnect from SQL Server.
Oaooekjo
Ateopejc?kjja_pekj
,i«ÀiÃiÌÃ all the users connected to SQL Server before the trace was started.
?qnokno
?qnoknEilhe_ep?kjranoekj
Indicates that the cursor type created is different from the requested type.
Annkno]j`S]njejco
=ppajpekj
,i«ÀiÃiÌÃ the intermediate termination of a request caused by actions such as query cancellation by a client or a broken database connection.
At_alpekj
Indicates the occurrence of an exception in SQL Server.
Ata_qpekjS]njejco
Indicates the occurrence of any warning during the execution of a query or a stored procedure.
D]odS]njejc
Indicates the occurrence of an error in a hashing operation.
Ieooejc?khqijOp]peope_o
Indicates that the statistics of a column, required by the optimizer to decide a processing strategy, are missing.
IeooejcFkejLna`e_]pa
Indicates that a query is executed with no }Ê«Ài`V>ÌiÊLiÌÜiiÊÌÜÊÌ>Lið
OknpS]njejco
Indicates that a sort operation performed in a query such as OAHA?P did not fit into memory.
Hk_g6@a]`hk_g
Flags the presence of a deadlock.
Hk_g6@a]`hk_g?d]ej
Shows a trace of the chain of queries creating the deadlock.
Hk_g6Peiakqp
Signifies that the lock has exceeded the timeout parameter, which is set by OAP HK?G[PEIAKQPpeiakqp[lanek`$io%.
OL6Na_kileha
Indicates that an execution plan for a stored procedure had to be recompiled, because one did not exist, a recompilation was forced, or the existing execution plan could not be reused.
OL6Op]npejc
,i«ÀiÃiÌÃ the starting of a stored OL6OpipOp]npejc procedure and a SQL statement within a stored procedure, respectively. They are useful to identify queries that started but could not finish because of an operation that caused an =ppajpekj event.
OMHPn]jo]_pekj
Provides information about a database transaction, including information such as Ü
iÊ>ÊÌÀ>Ã>VÌÊÃÌ>ÀÌi`ÉV«iÌi`]ÊÌ
iÊ duration of the transaction, and so on.
Hk_go
Opkna`Lnk_a`qnao
Pn]jo]_pekjo
65
66
C HAPTER 3
SQ L QU ER Y P ER FOR MA NC E A NA L YS IS
Data Columns Events are represented by different attributes called data columns. The data columns repre sent different attributes of an event, such as the class of the event, the SQL statement for the event, the resource cost of the event, and the source of the event. The data columns that represent the resource cost of an event are ?LQ, Na]`o, Snepao, and @qn]pekj°ÊÃÊ>ÊÀiÃÕÌ]ÊÌ
iÊ`>Ì>ÊVÕÃÊÞÕÊÜÊÕÃiÊÃÌÊvÀÊ«iÀvÀ>ViÊ>>ÞÃÃÊ>ÀiÊÃ
ÜÊ Ê/>LiÊÎΰ Table 3-3. Data Columns to Trace Query Completion
Data Column
Description
Arajp?h]oo
Type of event, for example, OMH6Op]paiajp?kilhapa`.
Patp@]p]
SQL statement for an event, such as OAHA?P&BNKIouok^fa_po.
?LQ
*1ÊVÃÌÊvÊ>ÊiÛiÌÊÊÃiV`ÃÊî°ÊÀÊiÝ>«i]Ê?LQ9-,, for a OAHA?P ÃÌ>ÌiiÌÊ`V>ÌiÃÊÌ
>ÌÊÌ
iÊÃÌ>ÌiiÌÊÌÊ£ääÊÃÊÌÊiÝiVÕÌi°
Na]`o
Number of logical reads performed for an event. For example, Na]`o94,, for a OAHA?P statement indicates that the statement required a total of 800 reads.
Snepao
Number of logical writes performed for an event.
@qn]pekj
Execution time of an event in ms.
OLE@
SQL Server process identifier used for the event.
Op]npPeia
Start time of an event.
Each logical read and write consists of an 8KB page activity in memory, which may require âiÀÊÀÊÀiÊ«
ÞÃV>ÊÉ"Ê«iÀ>ÌðÊ/Êv`ÊÌ
iÊÕLiÀÊvÊ«
ÞÃV>ÊÉ"Ê«iÀ>ÌÃÊÊ>Ê`ÃÊ subsystem, use the System Monitor tool; I’ll cover more about combining the output of Pro filer and System Monitor later in this chapter. You can add a data column to a Profiler trace by simply clicking the check box for that column. If no check box is available for a given column, then the event in question cannot collect that piece of data. Like the events, initially only the data columns defined by the tem «>ÌiÊ>ÀiÊ`ë>Þi`°Ê/ÊÃiiÊÌ
iÊV«iÌiÊÃÌÊvÊ`>Ì>ÊVÕÃ]ÊVVÊÌ
iÊ-
ÜÊÊ ÕÃÊ check box. You can use additional data columns from time to time to diagnose the cause of poor performance. For example, in the case of a stored procedure recompilation, the Profiler tool indicates the cause of the recompilation through the ArajpOq^?h]ooÊ`>Ì>ÊVÕ°Ê/
ÃÊ`>Ì>Ê VÕÊÃÊiÝ«>i`ÊÊ`i«Ì
ÊÊ
>«ÌiÀʣ䰮ÊÊviÜÊvÊÌ
iÊVÞÊÕÃi`Ê>``Ì>Ê`>Ì>ÊV umns are as follows: Ê
UÊ >ej]nu@]p]
Ê
UÊ Ejpacan@]p]
Ê
UÊ ArajpOq^?h]oo
Ê
UÊ @]p]^]oaE@
Ê
UÊ K^fa_pE@
CHAPTER 3
Ê
UÊ Ej`atE@
Ê
UÊ Pn]jo]_pekjE@
Ê
UÊ Annkn
Ê
UÊ Aj`Peia
S Q L Q U E R Y P E R F O R M A N C E A N A LY S I S
The >ej]nu@]p] and Ejpacan@]p] data columns provide specific information about a given SQL Server activity. For example, in the case of a cursor, they specify the type of cur ÃÀÊÀiµÕiÃÌi`Ê>`ÊÌ
iÊÌÞ«iÊvÊVÕÀÃÀÊVÀi>Ìi`°ÊÌ
Õ}
ÊÌ
iÊ>iÃÊvÊÌ
iÃiÊ>``Ì>Ê`>Ì>Ê columns indicate their purpose to a great extent, I will explain the usefulness of these data columns in later chapters as you use them.
ÕÊ`>Ì>ÊV>ÊLiÊÀi>ÀÀ>}i`ÊÌÊ>iÊÌ
iÊÃVÀiiÊÀiÊ«i>Ã}]Ê>`ÊÌ
iÞÊV>ÊLiÊ grouped to provide aggregates of the data collected. To control the column data placement, VVÊÌ
iÊ"À}>âiÊ ÕÃÊLÕÌÌ°Ê/
ÃÊ«iÃÊÌ
iÊ"À}>âiÊ ÕÃÊ`>}ÊLÝÊÃ
ÜÊÊ }ÕÀiÊÎΰ
Figure 3-3. Organize Columns dialog box 9ÕÊV>ÊV
>}iÊÌ
iÊ«ÃÌÊvÊ>ÊVÕÊLÞÊVV}ÊÌ
iÊ1«Ê>`Ê ÜÊLÕÌÌðÊÛ}Ê>Ê column into the Groups category means it will become an aggregation column.
Filters In addition to defining events and data columns for a Profiler trace, you can also define various filter criteria. These help keep the trace output small, which is usually a good idea. />LiÊÎ{Ê`iÃVÀLiÃÊÌ
iÊvÌiÀÊVÀÌiÀ>ÊÌ
>ÌÊÞÕÊÜÊVÞÊÕÃiÊ`ÕÀ}Ê«iÀvÀ>ViÊ analysis.
67
68
C HAPTER 3
SQ L QU ER Y P ER FOR MA NC E A NA L YS IS
Table 3-4. SQL Trace Filters
Events
Filter Criteria Example
Use
=llhe_]pekjJ]ia
Jkphega6OMHLnkbehan
This filters out the events generated by Profiler. This is the default behavior.
@]p]^]oaE@
Amq]ho68E@kbpda`]p]^]oa This filters out events generated by a par ticular database. You can determine the pkikjepkn:
ÊvÊ>Ê`>Ì>L>ÃiÊvÀÊÌÃÊ>iÊ>ÃÊvÜÃ\Ê OAHA?P@>[E@$#Jknpdsej`#%.
@qn]pekj
Cna]panpd]jknamq]h6.
For performance analysis, you will often capture a trace for a large workload. In a large trace, there will be many event logs with a @qn]pekj that is less than what you’re interested in. Filter out these event logs, because there is hardly any scope for optimizing these SQL activities.
Na]`o
Cna]panpd]jknamq]h6.
This is similar to the criterion on the @qn]pekj data column.
OLE@
Amq]ho68@]p]^]oaqoano pkikjepkn:
This troubleshoots queries sent by a specific database user.
}ÕÀiÊÎ{ÊÃ
ÜÃÊ>Êë«iÌÊvÊÌ
iÊ«ÀiVi`}ÊvÌiÀÊVÀÌiÀ>ÊÃiiVÌÊÊ-+Ê*ÀviÀ°
Note For a complete list of filters available in SQL Profiler, please refer to the MSDN article “Limiting Traces” (dppl6++io`j*ie_nkokbp*_ki+he^n]nu+`ab]qhp*]ol;qnh9+he^n]nu+aj)qo+]`iejomh+ ]`[ikj[lanb[2jt`*]ol).
Figure 3-4. Trace definition with filters
CHAPTER 3
S Q L Q U E R Y P E R F O R M A N C E A N A LY S I S
Trace Templates In the previous section, you learned how to define a new Profiler trace to monitor activities ÕÃ}Ê-+Ê-iÀÛiÀ°ÊÜiÛiÀ]ÊÃÌi>`ÊvÊ`iv}Ê>ÊiÜÊÌÀ>ViÊiÛiÀÞÊÌiÊÞÕÊÜ>ÌÊÌÊÕÃiÊi]Ê you can create a trace template with your customized events, data columns, and filters and then reuse the trace template to capture a trace. The procedure for defining a new trace tem plate is similar to that of defining a new trace: 1. Ài>ÌiÊ>ÊiÜÊÌÀ>Vi° 2. iviÊÌ
iÊiÛiÌÃ]Ê`>Ì>ÊVÕÃ]Ê>`ÊvÌiÀÃÊÌ
iÊÃ>iÊÜ>ÞÊ>ÃÊÃ
ÜÊi>ÀiÀ° 3. Save the trace definition as a trace template from the File Ê->ÛiÊÃÊiÕÊV
Vi°Ê Profiler will automatically populate its Templates list with your new template.
Trace Data vÌiÀÊÞÕ½ÛiÊ`ivi`Ê>ÊÌÀ>Vi]ÊVV}ÊÌ
iÊ,ÕÊLÕÌÌÊÜÊLi}ÊV>«ÌÕÀ}ÊiÛiÌÃÊ>`Ê`ë>Þ }ÊÌ
iÊÊÞÕÀÊÃVÀii]Ê>ÃÊÃ
ÜÊÊ}ÕÀiÊÎx°Ê9ÕÊÜÊÃiiÊ>ÊÃiÀiÃÊvÊiÛiÌÃÊÃVÀ}ÊLÞ]Ê and you can watch your system perform in what I’ve always called SQL TV. You have control ÛiÀÊÌ
iÊÌÀ>ViÊÀiÊÀÊiÃÃÊiÊ>Ê 6 Ê«>ÞiÀ]ÊÃÊÞÕÊV>Ê«>ÕÃi]ÊÃÌ>ÀÌ]Ê>`ÊÃÌ«ÊÌ
iÊÌÀ>ViÊÃÀÀÞ]Ê Êv>ÃÌvÀÜ>À`®ÊÕÃ}ÊÌ
iÊLÕÌÌÃÊÊÌ
iÊÌL>À°Ê9ÕÊV>ÊiÛiÊ«>ÕÃiÊÌ
iÊÌÀ>ViÊ>`Ê>iÊ changes to it as you work.
Figure 3-5. Events and data captured within a trace Once you finish capturing your SQL Server activities, you can save the trace output to a trace file or a trace table. Trace output saved to a trace file is in a native format and can be opened by Profiler to analyze the SQL queries. Saving the trace output to a trace table allows the SQL queries in the trace output to be analyzed by Profiler as well as by OAHA?P statements on the trace table.
69
70
C HAPTER 3
SQ L QU ER Y P ER FOR MA NC E A NA L YS IS
Profiler creates a trace table dynamically as per the definition of the data columns in the trace output. The ability to analyze the trace output using OAHA?P statements adds great flexibility to the analysis process. For example, if you want to find out the number of query iÝiVÕÌÃÊÜÌ
Ê>ÊÀiëÃiÊÌiÊvÊiÃÃÊÌ
>ÊxääÊÃ]ÊÞÕÊV>ÊiÝiVÕÌiÊ>ÊOAHA?P statement on the trace table as follows: OAHA?P?KQJP$&%BNKI8Pn]_aP]^ha: SDANA@qn]pekj:1,, 9ÕÊÜÊÊ>ÌÊÃiÊÃ>«iʵÕiÀiÃÊÊÌ
iʺ`iÌvÞ}Ê ÃÌÞÊ+ÕiÀiûÊÃiVÌÊ>ÌiÀÊÊ the chapter, but first I’ll describe how to automate Profiler and simultaneously reduce the amount of memory and network bandwidth that the Profiler process uses.
Trace Automation The Profiler GUI makes collecting Profiler trace information easy. Unfortunately, that ease comes at a cost. The events captured by the Profiler tool go into a cache in memory in order to feed across the network to the GUI. Your GUI is dependent on your network. Network traffic can slow things down and cause the cache to fill. This will, to a small degree, impact perfor mance on the server. Further, when the cache fills, the server will begin to drop events in À`iÀÊÌÊ>Û`ÊÃiÀÕÃÞÊ«>VÌ}ÊÃiÀÛiÀÊ«iÀvÀ>Vi°ÊÜÊ`ÊÞÕÊ>Û`ÊÌ
öÊ1ÃiÊÌ
iÊÃÞÃÌiÊ stored procedures to generate a scripted trace that outputs to a file.
Capturing a Trace Using the GUI You can create a scripted trace in one of two ways, manually or with the GUI. Until you get comfortable with all the requirements of the scripts, the easy way is to use the Profiler tool’s GUI. These are the steps you’ll need to perform: 1. iviÊ>ÊÌÀ>Vi° 2. VÊÌ
iÊiÕÊiÊ
Export
Ê-VÀ«ÌÊ/À>ViÊ ivÌ°
3. 9ÕÊÕÃÌÊÃiiVÌÊÌ
iÊÌ>À}iÌÊÃiÀÛiÀÊÌÞ«i]ÊÊÌ
ÃÊÃÌ>Vi]ÊÀÊ-+Ê-iÀÛiÀÊÓääxÉÓään° 4. Give the file a name, and save it. These steps will generate all the script commands that you need to capture a trace and ÕÌ«ÕÌÊÌÊÌÊ>Êvi°Ê9ÕÊV>Ê>ÃÊÃVÀ«ÌÊÌÀ>ViÊiÛiÌÃÊÌ
>ÌÊÜÊLiÊÕÃi`ÊÜÌ
ÊÌ
iÊiÜÊÓäänÊ >Ì>Ê
iVÌÀÊÃiÀÛVi]ÊLÕÌÊÌ
>ÌÊvÕVÌ>ÌÞÊÃÊLiÞ`ÊÌ
iÊÃV«iÊvÊÌ
ÃÊL° To manually launch this new trace, use Management Studio as follows: 1. Open the file. 2. ,i«>ViÊÜ
iÀiÊÌÊÃ>ÞÃÊEjoanpBehaJ]iaDana with the appropriate name and path for your system. 3. Execute the script. It will return a single column result set with the Pn]_aE`. 9ÕÊ>ÞÊÜ>ÌÊÌÊ>ÕÌ>ÌiÊÌ
iÊiÝiVÕÌÊvÊÌ
ÃÊÃVÀ«ÌÊÌ
ÀÕ}
ÊÌ
iÊ-+Ê}iÌ]ÊÀÊÞÕÊV>Ê even run the script from the command line using the omh_i`*ata utility. Whatever method you use, the script will start the trace. If you have not defined a trace stop time, you will need to stop the trace manually using the Pn]_aE`. I’ll show how to do that in the next section.
CHAPTER 3
S Q L Q U E R Y P E R F O R M A N C E A N A LY S I S
Capturing a Trace Using Stored Procedures If you look at the scripts defined in the previous section, you will see a series of commands, called in a specific order: Ê
UÊ ol[pn]_a[_na]pa\Ê Ài>Ìi a trace definition.
Ê
UÊ ol[pn]_a[oaparajp\Ê``ÊiÛiÌÃÊ>`ÊiÛiÌÊVÕÃÊÌÊÌ
iÊÌÀ>Vi°
Ê
UÊ ol[pn]_a[oapbehpan\Ê««ÞÊvÌiÀÃÊÌÊÌ
iÊÌÀ>Vi°
Once the SQL trace has been defined, you can run the trace using the stored procedure ol[pn]_a[oapop]pqo. The tracing of SQL activities continues until the trace is stopped. Since the SQL tracing VÌÕiÃÊ>ÃÊ>ÊL>Vi`Ê«ÀViÃÃ]ÊÌ
iÊ>>}iiÌÊ-ÌÕ`ÊÃiÃÃÊii`ÊÌÊLiÊi«ÌÊ«i°Ê9ÕÊ can identify the running traces by using theÊ-+Ê-iÀÛiÀÊLÕÌÊvÕVÌÊbj[pn]_a[capejbk, as shown in the following query: OAHA?P&BNKI66bj[pn]_a[capejbk$`ab]qhp%7 }ÕÀiÊÎÈÊÃ
ÜÃÊÌ
iÊÕÌ«ÕÌÊvÊÌ
iÊvÕVÌ°
Figure 3-6. Output of bj[pn]_a[capejbk The number of unique pn]_ae`s in the output of the function bj[pn]_a[capejbk indicates the number of traces active on SQL Server. The data value of the column r]hqa for the property vÊxÊ`V>ÌiÃÊÜ
iÌ
iÀÊÌ
iÊÌÀ>ViÊÃÊÀÕ}ÊÛ>ÕiÊrÊ£®ÊÀÊÃÌ««i`ÊÛ>ÕiÊrÊä®°Ê9ÕÊV>ÊÃÌ«Ê>Ê specific trace, say pn]_ae`9-, by executing the stored procedure ol[pn]_a[oapop]pqo: ATA?ol[pn]_a[oapop]pqo-(,7 vÌiÀÊ>ÊÌÀ>ViÊÃÊÃÌ««i`]ÊÌÃÊ`ivÌÊÕÃÌÊLiÊVÃi`Ê>`Ê`iiÌi`ÊvÀÊÌ
iÊÃiÀÛiÀÊLÞÊ executing ol[pn]_a[oapop]pqo: ATA?ol[pn]_a[oapop]pqo-(.7 To verify that the trace is stopped successfully, reexecute the function bj[pn]_a[capejbk, and ensure that the output of the function doesn’t contain the pn]_ae`. The format of the trace file created by this technique is the same as that of the trace file created by Profiler. Therefore, this trace file can be analyzed in the same way as a trace file created by Profiler.
>«ÌÕÀ}Ê>Ê-+ÊÌÀ>ViÊÕÃ}ÊÃÌÀi`Ê«ÀVi`ÕÀiÃÊ>ÃÊÕÌi`ÊÊÌ
iÊ«ÀiÛÕÃÊÃiVÌÊ>Û`ÃÊ the overhead associated with the Profiler GUI. It also provides greater flexibility in managing Ì
iÊÌÀ>V}ÊÃV
i`ÕiÊvÊ>Ê-+ÊÌÀ>ViÊÌ
>ÊÃÊ«ÀÛ`i`ÊLÞÊÌ
iÊ*ÀviÀÊÌ°ÊÊ
>«ÌiÀÊ£È]ÊÞÕÊÜÊ learn how to control the schedule of a SQL trace while capturing the activities of a SQL work load over an extended period of time.
71
72
C HAPTER 3
SQ L QU ER Y P ER FOR MA NC E A NA L YS IS
Note The time captured through a trace defined as illustrated in this section is stored in microseceonds, not milliseconds. This difference between units can cause confusion if not taken into account.
Combining Trace and Performance Monitor Output Ê
>«ÌiÀÊÓ]ÊÊÃ
Üi` how to capture Performance Monitor data to a file. In the preceding section, I showed how to capture Profiler data to a file as well. If you automate the collection of both of these sets of data so that they cover the same time periods, you can use them together inside the SQL Profiler GUI. Be sure that your trace has both the Op]npPeia and Aj`peia data fields. Follow these steps: 1. Open the trace file. 2. VÊÌ
iÊiÊ Ê«ÀÌÊ*iÀvÀ>ViÊ >Ì>° 3. Select the Performance Monitor file to be imported. *iÀvÀ}ÊÌ
iÃiÊ>VÌÃÊÜÊ«iÊÌ
iÊ`>}ÊLÝÊÃ
ÜÊÊ}ÕÀiÊÎÇ]ÊÜ
V
Ê>ÜÃÊÞÕÊ to pick Performance Monitor counters for inclusion.
Figure 3-7. Counters available for display in Profiler Once you’ve selected the counters you want to include, clicking OK will open the Profiler >`Ê*iÀvÀ>ViÊÌÀÊ`>Ì>ÊÌ}iÌ
iÀ]Ê>ÃÊÞÕÊV>ÊÃiiÊÊ}ÕÀiÊÎn°Ê ÜÊÞÕÊV>ÊLi}ÊÌÊ use the trace data and the Performance Monitor data together. If you select an event in the top
CHAPTER 3
S Q L Q U E R Y P E R F O R M A N C E A N A LY S I S
window, it will place a red line within the Performance Monitor data, showing where the event VVÕÀÀi`ÊÜÌ
ÊÌ
iÊÌiÊvÀ>iÊvÊÌ
>ÌÊ`>Ì>°Ê ÛiÀÃiÞ]ÊÞÕÊV>ÊVVÊÜÌ
ÊÌ
iÊ*iÀvÀ>ViÊ Monitor data, and the event that represents that time frame will be selected. These capabilities work so well together that you’ll be using them regularly during tuning sessions to identify the bottlenecks and stress points and to determine which specific queries are causing them.
Figure 3-8. Profiler data next to Performance Monitor data
SQL Profiler Recommendations You have already seen how to set some filters on a trace to help minimize the impact of Pro filer on system resources. To further minimize the impact, consider the following suggestions: Ê
UÊ ÌÊÌ
iÊÕLiÀÊvÊiÛiÌÃÊ>`Ê`>Ì>ÊVÕð
Ê
UÊ ÃV>À`ÊÃÌ>ÀÌÊiÛiÌÃÊvÀÊ«iÀvÀ>ViÊ>>ÞÃð
Ê
UÊ ÌÊÌÀ>ViÊÕÌ«ÕÌÊÃâi°
Ê
UÊ Û`ÊiÊ`>Ì>ÊVÕÊÃÀÌ}°
Ê
UÊ ,ÕÊ*ÀviÀÊÀiÌiÞ° In the following sections, I cover each of these suggestions in more depth.
Limiting the Number of Events and Data Columns While tracing SQL queries, you can decide which SQL activities should be captured by filtering iÛiÌÃÊ>`Ê`>Ì>ÊVÕðÊ
Ã}ÊiÝÌÀ>ÊiÛiÌÃÊVÌÀLÕÌiÃÊÌÊÌ
iÊLÕÊvÊÌÀ>V}ÊÛiÀ
i>`°Ê >Ì>ÊVÕÃÊ`ÊÌÊ>``ÊÕV
ÊÛiÀ
i>`]ÊÃViÊÌ
iÞÊ>ÀiÊÞÊ>ÌÌÀLÕÌiÃÊvÊ>ÊiÛiÌÊV>ÃÃ°Ê Therefore, it is extremely important to know why you want to trace each event selected and to select your events based only on necessity.
73
74
C HAPTER 3
SQ L QU ER Y P ER FOR MA NC E A NA L YS IS
Minimizing the number of events to be captured prevents SQL Server from wasting the L>`Ü`Ì
ÊvÊÛ>Õ>LiÊÀiÃÕÀViÃÊ}iiÀ>Ì}Ê>ÊÌ
ÃiÊiÛiÌÃ°Ê >«ÌÕÀ}ÊiÛiÌÃÊÃÕV
Ê>ÃÊVÃÊ and execution plans should be done with caution, because these events make the trace output very large and degrade SQL Server’s performance. It’s important to reduce the number of events while analyzing a production server, since you don’t want the profiler to add a large amount of load to the production server. On a test server, the amount of load contributed by Profiler is a lesser consideration than the ability to analyze every activity on SQL Server. Therefore, on a test server, you need not compromise so much with the information you might be interested in. Some events come with added cost. I mentioned earlier in the chapter that the statement completion events can be costly, but other events could negatively impact your system while you’re attempting to capture information about that system.
FILTERING STAGES There are two stages of filtering: prefiltering, which is performed by SQL Server, and postfiltering, which is performed by the user. Prefiltering is the online stage of capturing the SQL Server activities. Prefiltering offers several advantages: ated.
-
because fewer events are being captured in the first place.
The only disadvantage to prefiltering is that you may miss some vital information that might be required for thorough analysis. As with many things, choosing when and how much to prefilter involves making good judgment calls. Postfiltering overhead on SQL Server. Once you filter an existing trace file, you can save the postfiltered information to a new trace file. For example, while identifying the problematic queries, you might be interested in queries with an execution time greater than 500 ms. To identify such queries, you can apply a postfilter on the trace output for a @qn]pekj greater than or equal to 500 and save this filtered trace output as a separate trace file. This means that the next time you need to look at the same criteria, you do not have to load the original trace file and do filtering again; you can just use the new trace file.
Discarding Start Events for Performance Analysis The information you want for performance analysis revolves around the resource cost of a query. Start events, such as OL6OpipOp]npejc, do not provide this information, because it is ÞÊ>vÌiÀÊ>ÊiÛiÌÊV«iÌiÃÊÌ
>ÌÊÞÕÊV>ÊV«ÕÌiÊÌ
iÊ>ÕÌÊvÊÉ"]ÊÌ
iÊ *1Ê>`]Ê>`ÊÌ
iÊ `ÕÀ>ÌÊvÊÌ
iʵÕiÀÞ°Ê/
iÀivÀi]ÊÜ
iÊÌÀ>V}ÊÃÜÀÕ}ʵÕiÀiÃÊvÀÊ«iÀvÀ>ViÊ>>Þ sis, you need not capture the start events. This information is provided by the corresponding completion events. -]ÊÜ
iÊÃ
Õ`ÊÞÕÊV>«ÌÕÀiÊÃÌ>ÀÌÊiÛiÌöÊ7i]ÊÞÕÊÃ
Õ`ÊV>«ÌÕÀiÊÃÌ>ÀÌÊiÛiÌÃÊÜ
iÊÞÕÊ don’t expect some SQL queries to finish execution because of error conditions or when you
CHAPTER 3
S Q L Q U E R Y P E R F O R M A N C E A N A LY S I S
find frequent =ppajpekjÊiÛiÌðÊÊ=ppajpekj event usually indicates that the user cancelled the query midway or the query timeout expired, probably because the query was running for a long time.
Limiting the Trace Output Size Besides prefiltering events and data columns, other filtering criteria limit the trace output size. }>]ÊÌ}ÊÃâiÊ>ÞÊV>ÕÃiÊÞÕÊÌÊÃÃÊÌiÀiÃÌ}ÊiÛiÌÃÊvÊÞÕ½ÀiÊ}Ê>ÌÊÛiÀ>ÊÃÞÃÌiÊ behavior. But if you’re focusing on costly queries, a filter helps. From the Edit Filter dialog box, >VViÃÃi`ÊLÞÊVV}ÊÌ
iÊ ÕÊÌiÀÃÊLÕÌÌÊÊÌ
iÊ ÛiÌÃÊ-iiVÌÊÌ>L]ÊVÃ`iÀÊÌ
iÊv lowing settings: Ê
UÊ Duration – Greater than or equal: 2: SQL queries with a @qn]pekjÊiµÕ>ÊÌÊäÊÀÊ£ÊÃÊ cannot be further optimized.
Ê
UÊ Reads – Greater than or equal: 2: SQL queries with number of logical reads equal to äÊÀÊ£ÊÃÊV>ÌÊLiÊvÕÀÌ
iÀÊ«Ìâi`°
Avoiding Online Data Column Sorting ÕÀ}Ê«iÀvÀ>ViÊ>>ÞÃÃ]ÊÞÕÊÕÃÕ>ÞÊÃÀÌÊ>ÊÌÀ>ViÊÕÌ«ÕÌÊÊ`vviÀiÌÊ`>Ì>ÊVÕÃÊÃÕV
Ê as @qn]pekj, ?LQ, and Na]`o®ÊÌÊ`iÌvÞʵÕiÀiÃÊÜÌ
ÊÌ
iÊ>À}iÃÌÊVÀÀië`}Êv}ÕÀiðÊvÊÞÕÊ sort offline, you reduce the activities Profiler has to perform while interacting with SQL Server. This is how to sort a captured SQL trace output: 1. >«ÌÕÀiÊÌ
iÊÌÀ>ViÊÜÌ
ÕÌÊ>ÞÊÃÀÌ}ÊÀÊ}ÀÕ«}®° 2. Save the trace output to a trace file. 3. "«iÊÌ
iÊÌÀ>ViÊviÊ>`ÊÃÀÌÊÀÊ}ÀÕ«®ÊÌ
iÊÌÀ>ViÊviÊÕÌ«ÕÌÊÊëiVvVÊ`>Ì>ÊVÕÃÊ as required.
Running Profiler Remotely It is usually not a good practice to run test tools directly on the production server. Profiler has a heavy user interface; therefore, it is better to run it on another machine. Similar to System ÌÀ]Ê*ÀviÀÊÃ
Õ`ÊÌÊLiÊÀÕÊÌ
ÀÕ}
Ê>ÊÌiÀ>ÊÃiÀÛViÊÃiÃÃ]ÊLiV>ÕÃiÊ>Ê>ÀÊ«>ÀÌÊ of the tool still runs on the server. When collecting a trace output directly to a file, save the file V>ÞÊÜ
iÀiÊ*ÀviÀÊÃÊLi}ÊÀÕ°Ê/
ÃÊÃÊÃÌÊ>ÊÀiÊÀiÃÕÀViÌiÃÛiÊ«iÀ>ÌÊÌ
>ÊÀÕ }Ê*ÀviÀÊÌ
ÀÕ}
ÊÌ
iÊÃÞÃÌiÊÃÌÀi`Ê«ÀVi`ÕÀiÃÊ>ÃÊ>ÊÃiÀÛiÀÃ`iÊÌÀ>Vi°Ê/
>ÌÊÀi>ÃÊÌ
iÊ best option.
Limiting the Use of Certain Events SomeÊiÛiÌÃÊ>ÀiÊÀiÊVÃÌÞÊÌ
>ÊÌ
iÀðÊÃÊÊiÌi`Ê«ÀiÛÕÃÞ]Ê`i«i`}ÊÊÌ
iÊ>ÌÕÀiÊ of the queries being generated, the statement completion events can be very costly. Other iÛiÌÃÊÌÊÕÃiÊÕ`VÕÃÞ]ÊiëiV>ÞÊÊ>ÊÃÞÃÌiÊÌ
>̽ÃÊ>Ài>`ÞÊiÝ«iÀiV}ÊÃÌÀiÃÃ]Ê>ÀiÊÌ
iÊ Showplan XML events Lanbkni]j_a6Odkslh]jTIH, Lanbkni]j_a6Odkslh]jTIHbknMqanu ?kileha, and Lanbkni]j_a6Odkslh]jTIHOp]peope_oLnkbeha°ÊÌ
Õ}
ÊÌ
iÃiÊiÛiÌÃÊV>ÊLiÊ useful, keep them off the production machine.
75
76
C HAPTER 3
SQ L QU ER Y P ER FOR MA NC E A NA L YS IS
Query Performance Metrics Without Profiler Setting up a trace allows you to collect a lot of data for later use, but the collection can be somewhat expensive, and you have to wait on the results. If you need to immediately capture performance metrics about your system, especially as they pertain to query performance, then the dynamic management view ouo*`i[ata_[mqanu[op]po is what you need. If you still need a historical tracking of when queries were run and their individual costs, a trace is still the bet ÌiÀÊÌ°Ê ÕÌÊvÊÞÕÊÕÃÌÊii`ÊÌÊÜ]Ê>ÌÊÌ
ÃÊiÌ]ÊÌ
iÊ}iÃÌÀÕ}ʵÕiÀiÃÊÀÊÌ
iÊÃÌÊ physical reads, then you can get that information from ouo*`i[ata_[mqanu[op]po. Since ouo*`i[ata_[mqanu[op]poÊÃÊÕÃÌÊ>ÊÛiÜ]ÊÞÕÊV>ÊëÞʵÕiÀÞÊ>}>ÃÌÊÌÊ>`Ê}iÌÊ vÀ>ÌÊ>LÕÌÊÌ
iÊÃÌ>ÌÃÌVÃÊvʵÕiÀÞÊ«>ÃÊÊÌ
iÊÃiÀÛiÀ°Ê/>LiÊÎxÊÃ
ÜÃÊÃiÊvÊÌ
iÊ`>Ì>Ê returned from the query. Table 3-5. ouo*`i[ata_[mqanu[op]po Output
Column
Description
Lh]j[d]j`ha
Pointer that refers to the execution plan
?na]pekj[peia
Time that the plan was created
H]op[ata_qpekjpeia
Last time the plan was used by a query
Ata_qpekj[_kqjp
Number of times the plan has been used
Pkp]h[skngan[peia
/Ì>Ê *1ÊÌiÊÕÃi`ÊLÞÊÌ
iÊ«>ÊÃViÊÌÊÜ>ÃÊVÀi>Ìi`
Pkp]h[hkce_]h[na]`o
Total number of reads used since the plan was created
Pkp]h[hkce_]h[snepao
Total number of writes used since the plan was created
Mqanu[d]od
binary hash that can be used to identify queries with similar logic
Mqanu[lh]j[d]od
ÊL>ÀÞÊ
>Ã
ÊÌ
>ÌÊV>ÊLiÊÕÃi`ÊÌÊ`iÌvÞÊ«>ÃÊÜÌ
ÊÃ>ÀÊ}V
/>LiÊÎxÊÃÊÕÃÌÊ>ÊÃ>«}°ÊÀÊV«iÌiÊ`iÌ>Ã]ÊÃiiÊ ÃÊ"i° To filter the information returned from ouo*`i[ata_[mqanu[op]po]ÊÞÕ½Êii`ÊÌÊÊÌÊ with other dynamic management functions such as ouo*`i[ata_[omh[patp, which shows the query text associated with the plan, or ouo*`i[mqanu[lh]j, which has the execution plan for Ì
iʵÕiÀÞ°Ê"ViÊi`ÊÌÊÌ
iÃiÊÌ
iÀÊ Ã]ÊÞÕÊV>ÊÌÊÌ
iÊ`>Ì>L>ÃiÊÀÊ«ÀVi`ÕÀiÊÌ
>ÌÊÞÕÊ Ü>ÌÊÌÊvÌiÀ°Ê/
iÃiÊÌ
iÀÊ ÃÊ>ÀiÊVÛiÀi`Ê detail in other chapters of the book.
Costly Queries Now that you have seen what you need to consider when using the Profiler tool, let’s look at what the data represents: the costly queries themselves. When the performance of SQL Server goes bad, two things are most likely happening: Ê
UÊ ÀÃÌ]ÊViÀÌ>ʵÕiÀiÃÊVÀi>ÌiÊ
}
ÊÃÌÀiÃÃÊÊsystem resources. These queries affect the performance of the overall system, because the server becomes incapable of serving other SQL queries fast enough.
Ê
UÊ ``Ì>Þ]ÊÌ
iÊVÃÌÞʵÕiÀiÃÊLVÊ>ÊÌ
iÀʵÕiÀiÃÊÀiµÕiÃÌ}ÊÌ
iÊÃ>iÊ`>Ì>L>ÃiÊ resources, further degrading the performance of those queries. Optimizing the costly queries improves not only their own performance but also the performance of other queries by reducing database blocking and pressure on SQL Server resources.
CHAPTER 3
S Q L Q U E R Y P E R F O R M A N C E A N A LY S I S
You can use Profiler to capture the SQL Server workload, as explained previously in this V
>«ÌiÀ°Ê iviÊ>ÊÌÀ>Vi°Ê/
iÊÀÕÊÌ
iʵÕiÀiÃÊÊpn]_a[mqaneao*omh°Ê}ÕÀiÊÎÊÃ
ÜÃÊ>ÊÃ>«iÊ trace output. On a live production server, the trace output may be quite large; the solution is ÌÊÕÃiÊvÌiÀÊVÀÌiÀ>]Ê>ÃÊiÝ«>i`ÊÊÌ
iÊi>ÀiÀʺÌiÀûÊÃiVÌ]ÊÌÊÌÊÌ
iÊÃâiÊvÊÌ
iÊÌÀ>ViÊ output.
Figure 3-9. A sample trace output Once you have captured the set of SQL queries representing a complete workload, you should analyze the trace to identify two sets of queries: Ê
UÊ ÃÌÞʵÕiÀiÃÊÌ
>ÌÊ>ÀiÊV>ÕÃ}Ê>Ê}Ài>ÌÊ`i>ÊvÊ«ÀiÃÃÕÀiÊÊÌ
iÊÃÞÃÌiÊÀiÃÕÀViÃ
Ê
UÊ +ÕiÀiÃÊÌ
>ÌÊ>ÀiÊÃÜi`Ê`ÜÊÌ
iÊÃÌ
Identifying Costly Queries The goal of SQL Server is to return result sets to the user in the shortest time. To do this, SQL -iÀÛiÀÊ
>ÃÊ>ÊLÕÌ]ÊVÃÌL>Ãi`Ê«ÌâiÀÊV>i`ÊÌ
iÊquery optimizer, whichÊ}iiÀ>ÌiÃÊ>ÊVÃÌ effective strategy called a query execution plan. The query optimizer weighs many factors, VÕ`}ÊLÕÌÊÌÊÌi`ÊÌ®ÊÌ
iÊÕÃ>}iÊvÊ *1]ÊiÀÞ]Ê>`Ê`ÃÊÉ"ÊÀiµÕÀi`ÊÌÊiÝiVÕÌiÊ a query, all derived from the statistics maintained by indexes or generated on the fly, and it Ì
iÊVÀi>ÌiÃÊ>ÊVÃÌivviVÌÛiÊiÝiVÕÌÊ«>°ÊÌ
Õ}
Êâ}ÊÌ
iÊÕLiÀÊvÊÉ"ÃÊÃÊÌÊ>Ê ÀiµÕÀiiÌÊvÀÊ>ÊVÃÌivviVÌÛiÊ«>]ÊÞÕÊÜÊvÌiÊv`ÊÌ
>ÌÊÌ
iÊi>ÃÌÊVÃÌÞÊ«>Ê
>ÃÊÌ
iÊviÜ iÃÌÊÉ"ÃÊLiV>ÕÃiÊÉ"Ê«iÀ>ÌÃÊ>ÀiÊiÝ«iÃÛi°
77
78
C HAPTER 3
SQ L QU ER Y P ER FOR MA NC E A NA L YS IS
In the data returned from a trace, the ?LQ and Na]`o columns also show where a query costs you. The ?LQÊVÕÊÀi«ÀiÃiÌÃÊÌ
iÊ *1ÊÌiÊÕÃi`ÊÌÊiÝiVÕÌiÊÌ
iʵÕiÀÞ°Ê/
iÊNa]`o col umnÊÀi«ÀiÃiÌÃÊÌ
iÊÕLiÀÊvÊ}V>Ê«>}iÃÊn ÊÊÃâi®Ê>ʵÕiÀÞÊ«iÀ>Ìi`ÊÊ>`ÊÌ
iÀiLÞÊ indicates the amount of memory stress caused by the query. It also provides an indication of disk stress, since memory pages have to be backed up in the case of action queries, populated `ÕÀ}ÊvÀÃÌÌiÊ`>Ì>Ê>VViÃÃ]Ê>`Ê`ë>Vi`ÊÌÊ`ÃÊ`ÕÀ}ÊiÀÞÊLÌÌiiVðÊ/
iÊ
}
iÀÊ Ì
iÊÕLiÀÊvÊ}V>ÊÀi>`ÃÊvÀÊ>ʵÕiÀÞ]ÊÌ
iÊ
}
iÀÊÌ
iÊ«ÃÃLiÊÃÌÀiÃÃÊÊÌ
iÊ`ÃÊVÕ`ÊLi°ÊÊ iÝViÃÃÛiÊÕLiÀÊvÊ}V>Ê«>}iÃÊ>ÃÊVÀi>ÃiÃÊ>`ÊÊÌ
iÊ *1ÊÊ>>}}ÊÌ
ÃiÊ«>}ið The queries that cause a large number of logical reads usually acquire locks on a corre ë`}ÞÊ>À}iÊÃiÌÊvÊ`>Ì>°Ê ÛiÊÀi>`}Ê>ÃÊ««Ãi`ÊÌÊÜÀÌ}®ÊÀiµÕÀiÃÊÃ
>ÀiÊVÃÊÊ>Ê Ì
iÊ`>Ì>°Ê/
iÃiʵÕiÀiÃÊLVÊ>ÊÌ
iÀʵÕiÀiÃÊÀiµÕiÃÌ}ÊÌ
ÃÊ`>Ì>ÊÀÊ>Ê«>ÀÌÊvÊÌ
iÊ`>Ì>®ÊvÀÊ the purposes of modifying it, not for reading it. Since these queries are inherently costly and require a long time to execute, they block other queries for an extended period of time. The blocked queries then cause blocks on further queries, introducing a chain of blocking in the `>Ì>L>Ãi°Ê
>«ÌiÀÊ£ÓÊVÛiÀÃÊVÊ`ið® ÃÊ>ÊÀiÃÕÌ]ÊÌÊ>iÃÊÃiÃiÊÌÊ`iÌvÞÊÌ
iÊVÃÌÞʵÕiÀiÃÊ>`Ê«ÌâiÊÌ
iÊvÀÃÌ]ÊÌ
iÀiLÞÊ doing the following: Ê
UÊ «ÀÛ}ÊÌ
iÊ«iÀvÀ>ViÊvÊÌ
iÊVÃÌÞʵÕiÀiÃÊÌ
iÃiÛiÃ
Ê
UÊ ,i`ÕV}ÊÌ
iÊÛiÀ>ÊÃÌÀiÃÃÊÊÃÞÃÌiÊÀiÃÕÀViÃ
Ê
UÊ ,i`ÕV}Ê`>Ì>L>ÃiÊLV} The costly queries can be categorized into the following two types:
Ê
UÊ Single execution\ÊÊ`Û`Õ>ÊiÝiVÕÌÊvÊÌ
iʵÕiÀÞÊÃÊVÃÌÞ°
Ê
UÊ Multiple executions\ÊʵÕiÀÞÊÌÃivÊ>ÞÊÌÊLiÊVÃÌÞ]ÊLÕÌÊÌ
iÊÀi«i>Ìi`ÊiÝiVÕÌÊvÊÌ
iÊ query causes pressure on the system resources.
You can identify these two types of costly queries using different approaches, as explained in the following sections.
Costly Queries with a Single Execution You can identify the costly queries by analyzing a SQL Profiler trace output file or by querying ouo*`i[ata_[mqanu[op]po. Since you are interested in identifying queries that perform a large number of logical reads, you should sort the trace output on the Na]`o data column. You can access the trace information by following these steps: 1. >«ÌÕÀiÊ>ÊProfiler trace that represents a typical workload. 2. Save the trace output to a trace file. 3. Open the trace file for analysis. 4. Open the Properties window for the trace, and click the Events Selection tab. 5. "«iÊÌ
iÊ"À}>âiÊ ÕÃÊÜ`ÜÊLÞÊVV}ÊÌ
>ÌÊLÕÌÌ° 6. Group the trace output on the Na]`o column. To do this, move the Na]`o column under Ì
iÊÀÕ«ÃÊÃiVÌ]Ê`iÃVÀLi`Êi>ÀiÀÊÊÌ
ÃÊV
>«ÌiÀ]Ê>ÃÊÃ
ÜÊÊ}ÕÀiÊÎ£ä° 7. You can work with the trace grouped, or you can look at it sorted by changing the `ë>ÞÊÕÃ}Ê ÌÀ³ °
CHAPTER 3
S Q L Q U E R Y P E R F O R M A N C E A N A LY S I S
Figure 3-10. Trace definition sorted on the Na]`o column This process will group the trace output on the Na]`oÊVÕ]Ê>ÃÊÃ
ÜÊÊ}ÕÀiÊΣ£°Ê The trace output is sorted on Na]`o in ascending order. From there, you can select a few of the costliest queries and analyze and optimize them appropriately. ÊÃiÊV>ÃiÃ]ÊÞÕÊ>ÞÊ
>ÛiÊ`iÌvi`Ê>Ê>À}iÊÃÌÀiÃÃÊÊÌ
iÊ *1ÊvÀÊÌ
iÊ-ÞÃÌiÊÌÀÊ ÕÌ«ÕÌ°Ê/
iÊ«ÀiÃÃÕÀiÊÊÌ
iÊ *1Ê>ÞÊLiÊLiV>ÕÃiÊvÊ>Ê>À}iÊÕLiÀÊvÊ *1ÌiÃÛiÊ«iÀ> ÌÃ]ÊÃÕV
Ê>ÃÊÃÌÀi`Ê«ÀVi`ÕÀiÊÀiV«>ÌÃ]Ê>}}Ài}>ÌiÊvÕVÌÃ]Ê`>Ì>ÊÃÀÌ}]Ê
>Ã
ÊÃ]Ê and so on. In such cases, you should sort the Profiler trace output on the ?LQ column to iden tify the queries taking up a large number of processor cycles.
Figure 3-11. Trace output sorted on the Na]`o column
79
80
C HAPTER 3
SQ L QU ER Y P ER FOR MA NC E A NA L YS IS
Costly Queries with Multiple Executions à I mentioned earlier, sometimes a query may not be costly by itself, but the cumulative effect of multiple executions of the same query might put pressure on the system resources. Sort ing on the Na]`o column won’t help you identify this type of costly query. You instead want to know the total number of reads performed by multiple executions of the query. Unfortunately, Profiler doesn’t help here directly, but you can still get this information in the following ways: Ê
UÊ ÀÕ«ÊÌ
iÊÌÀ>ViÊÕÌ«ÕÌÊÊ*ÀviÀÊÊÌ
iÊvÜ}ÊVÕÃ\ÊArajp?h]oo, Patp@]p], and Na]`o. For the group of rows with the same Arajp?h]oo and Patp@]p], manually calculate the total of all the corresponding Na]`o. This approach doesn’t sound very user friendly!
Ê
UÊ ->ÛiÊÌ
iÊÌÀ>ViÊÕÌ«ÕÌÊÌÊ>ÊÌÀ>ViÊÌ>LiÊLÞÊÃiiVÌ}ÊiÊ Ê->ÛiÊÃÊ Trace Table in *ÀviÀ°ÊÃ]ÊÞÕÊV>Ê«ÀÌÊÌ
iÊÌÀ>ViÊviÊÕÌ«ÕÌÊvÊ*ÀviÀÊÌÊ>ÊÌÀ>ViÊÌ>LiÊLÞÊÕÃ}Ê Ì
iÊLÕÌÊvÕVÌÊbj[pn]_a[capp]^ha.
Ê
UÊ VViÃÃÊÌ
iÊouo*`i[ata_[mqanu[op]poÊ 6ÊÌ retrieve the information from the produc tion server. This assumes that you’re dealing with an immediate issue and not looking at a historical problem.
In this case, I’ll load the data into a table on the database so that I can run queries against it using the following script: OAHA?P&EJPKPn]_a[P]^ha BNKI66bj[pn]_a[capp]^ha$#?6XLanbkni]j_aPn]_a*pn_#(`ab]qhp% Once the SQL trace is imported into a database table, execute a OAHA?P statement to find the total number of reads performed by the multiple executions of the same query as follows na]`o*omhÊÊÌ
iÊ`Ü>`®\ OAHA?P?KQJP$&%=OPkp]hAta_qpekjo(Arajp?h]oo(Patp@]p] (OQI$@qn]pekj%=O@qn]pekj[Pkp]h (OQI$?LQ%=O?LQ[Pkp]h (OQI$Na]`o%=ONa]`o[Pkp]h (OQI$Snepao%=OSnepao[Pkp]h BNKIPn]_a[P]^ha CNKQL>UArajp?h]oo(Patp@]p] KN@AN>UNa]`o[Pkp]h@AO? The Pkp]hAta_qpekjo column in the preceding script indicates the number of times a query was executed. The Na]`o[Pkp]h column indicates the total number of reads performed by the multiple executions of the query. ÜiÛiÀ]ÊÌ
iÀiÊÃÊ>ÊÌÌiÊ«ÀLi°Ê/
iÊ`>Ì>ÊÌÞ«iÊvÊÌ
iÊPatp@]p] column for the trace table created by Profiler is JPATP, which can’t be specified in the CNKQL>U clause—SQL Server 2008 doesn’t support grouping on a column with the JPATP data type. Therefore, you may create a table similar to the trace table, with the only exception being that the data type of the Patp@]p] column should be JR=N?D=N$I=T% instead of JPATP. Using the I=T length for the JR=N?D=N data type allows you not to worry about how long the JPATP data is.
CHAPTER 3
S Q L Q U E R Y P E R F O R M A N C E A N A LY S I S
Ì
iÀÊ>««À>V
ÊÃÊÌÊÕÃiÊÌ
iÊ?=OP function as follows: OAHA?P?KQJP$&%=OPkp]hAta_qpekjo(Arajp?h]oo (?=OP$Patp@]p]=OJR=N?D=N$I=T%%Patp@]p] (OQI$@qn]pekj%=O@qn]pekj[Pkp]h (OQI$?LQ%=O?LQ[Pkp]h (OQI$Na]`o%=ONa]`o[Pkp]h (OQI$Snepao%=OSnepao[Pkp]h BNKIPn]_a[P]^ha CNKQL>UArajp?h]oo(?=OP$Patp@]p]=OJR=N?D=N$I=T%% KN@AN>UNa]`o[Pkp]h@AO? The costly queries identified by this approach are a better indication of load than the costly queries with single execution identified by Profiler. For example, a query that requires xäÊÀi>`ÃÊ}
ÌÊLiÊiÝiVÕÌi`Ê£]äääÊÌiðÊ/
iʵÕiÀÞÊÌÃivÊ>ÞÊLiÊVÃ`iÀi`ÊV
i>«ÊiÕ}
]ÊLÕÌÊ Ì
iÊÌÌ>ÊÕLiÀÊvÊÀi>`ÃÊ«iÀvÀi`ÊLÞÊÌ
iʵÕiÀÞÊÌÕÀÃÊÕÌÊÌÊLiÊxä]äääÊrÊxäÊ Ê£]äää®]ÊÜ
V
Ê V>ÌÊLiÊVÃ`iÀi`ÊV
i>«°Ê"«Ìâ}ÊÌ
ÃʵÕiÀÞÊÌÊÀi`ÕViÊÌ
iÊÀi>`ÃÊLÞÊiÛiÊ£äÊvÀÊ`Û` Õ>ÊiÝiVÕÌÊÀi`ÕViÃÊÌ
iÊÌÌ>ÊÕLiÀÊvÊÀi>`ÃÊLÞÊ£ä]äääÊrÊ£äÊ Ê£]äää®]ÊÜ
V
ÊV>ÊLiÊÀiÊ LiivV>ÊÌ
>Ê«Ìâ}Ê>ÊÃ}iʵÕiÀÞÊÜÌ
Êx]äääÊÀi>`ð The problem with this approach is that most queries will have a varying set of criteria in the SDANA clause or that procedure calls will have different values and numbers of parameters passed in. That makes the simple grouping by Patp@]p] impossible. You can take care of this problem with a number of approaches. One of the better ones is outlined on the Microsoft iÛi«iÀÃÊ iÌÜÀÊ>ÌÊdppl6++io`j*ie_nkokbp*_ki+aj)qo+he^n]nu+]]-314,,$OMH*4,%*]olt. Ì
Õ}
ÊÌÊÜ>ÃÊÜÀÌÌiÊÀ}>ÞÊvÀÊ-+Ê-iÀÛiÀÊÓäää]ÊÌÊÜÊÜÀÊviÊÜÌ
Ê-+Ê-iÀÛiÀÊÓään° Getting the same information out of the ouo*`i[ata_[mqanu[op]po view simply requires a µÕiÀÞÊ>}>ÃÌÊÌ
iÊ 6\ OAHA?Poo*oqi[ata_qpekj[_kqjp (p*PATP (oo*oqi[pkp]h[ah]loa`[peia (oo*oqi[pkp]h[skngan[peia (oo*oqi[pkp]h[hkce_]h[na]`o (oo*oqi[pkp]h[hkce_]h[snepao BNKI$OAHA?Po*lh]j[d]j`ha (OQI$o*ata_qpekj[_kqjp%oqi[ata_qpekj[_kqjp (OQI$o*pkp]h[ah]loa`[peia%oqi[pkp]h[ah]loa`[peia (OQI$o*pkp]h[skngan[peia%oqi[pkp]h[skngan[peia (OQI$o*pkp]h[hkce_]h[na]`o%oqi[pkp]h[hkce_]h[na]`o (OQI$o*pkp]h[hkce_]h[snepao%oqi[pkp]h[hkce_]h[snepao BNKIouo*`i[ata_[mqanu[op]poo CNKQL>Uo*lh]j[d]j`ha %=Ooo ?NKOO=LLHUouo*`i[ata_[omh[patp$oo*lh]j[d]j`ha%p KN@AN>Uoqi[pkp]h[hkce_]h[na]`o@AO? This is so much easier than all the work required to gather trace data that it makes you wonder why you would ever use trace data. The main reason is precision. The ouo*`i[ata_[ mqanu[op]po view is a running aggregate for the time that a given plan has been in memory.
81
82
C HAPTER 3
SQ L QU ER Y P ER FOR MA NC E A NA L YS IS
ÊÌÀ>Vi]ÊÊÌ
iÊÌ
iÀÊ
>`]ÊÃÊ>Ê
ÃÌÀV>ÊÌÀ>VÊvÀÊÜ
>ÌiÛiÀÊÌiÊvÀ>iÊÞÕÊÀ>ÊÌÊ°Ê9ÕÊV>Ê even add traces together within a database and have a list of data that you can generate totals in a more precise manner rather than simply relying on a given moment in time. But under stand that a lot of troubleshooting of performance problems is focused on that moment in time when the query is running slowly. That’s when ouo*`i[ata_[mqanu[op]po becomes irre placeably useful.
Identifying Slow-Running Queries Because a user’s experience is highly influenced by the response time of their requests, you should regularly monitor the execution time of incoming SQL queries and find out the ÀiëÃiÊÌiÊvÊÃÜÀÕ}ʵÕiÀiðÊvÊÌ
iÊÀiëÃiÊÌiÊÀÊ`ÕÀ>Ì®ÊvÊÃÜÀÕ}Ê queries becomes unacceptable, then you should analyze the cause of performance degrada Ì°Ê ÌÊiÛiÀÞÊÃÜ«iÀvÀ}ʵÕiÀÞÊÃÊV>ÕÃi`ÊLÞÊÀiÃÕÀViÊÃÃÕiÃ]ÊÌ
Õ}
°Ê"Ì
iÀÊVViÀÃÊ such as blocking can also lead to slow query performance. Blocking is covered in detail in
>«ÌiÀÊ£Ó° /Ê`ÃVÛiÀÊÌ
iÊÃÜÀÕ}Ê-+ʵÕiÀiÃ]Ê}ÀÕ«Ê>ÊÌÀ>ViÊÕÌ«ÕÌÊÊÌ
iÊ@qn]pekj column. This willÊÃÀÌÊÌ
iÊÌÀ>ViÊÕÌ«ÕÌ]Ê>ÃÊÃ
ÜÊÊ}ÕÀiÊΣӰ
Figure 3-12. Trace output sorted on the @qn]pekj column ÀÊ>ÊÃÜÀÕ}ÊÃÞÃÌi]ÊÞÕÊÃ
Õ`ÊÌiÊÌ
iÊ`ÕÀ>ÌÊvÊÃÜÀÕ}ʵÕiÀiÃÊLivÀiÊ >`Ê>vÌiÀÊÌ
iÊ«Ìâ>ÌÊ«ÀViÃðÊvÌiÀÊÞÕÊ>««ÞÊ«Ìâ>ÌÊÌiV
µÕiÃ]ÊÞÕÊÃ
Õ`ÊÌ
iÊ work out the overall effect on the system. It is possible that your optimization steps may have adversely affected other queries, making them slower.
CHAPTER 3
S Q L Q U E R Y P E R F O R M A N C E A N A LY S I S
Execution Plans Once you have identified a costly query, you need to find out why it is so costly. You can iden tify the costly query from SQL Profiler or ouo*`i[ata_[mqanu[op]po, rerun it in Management -ÌÕ`]Ê>`ÊÊ>ÌÊÌ
iÊiÝiVÕÌÊ«>ÊÕÃi`ÊLÞÊÌ
iʵÕiÀÞÊ«ÌâiÀ°ÊÊiÝiVÕÌÊ«>ÊÃ
ÜÃÊ Ì
iÊ«ÀViÃÃ}ÊÃÌÀ>Ìi}ÞÊVÕ`}ÊÕÌ«iÊÌiÀi`>ÌiÊÃÌi«Ã®ÊÕÃi`ÊLÞÊÌ
iʵÕiÀÞÊ«ÌâiÀÊÌÊ execute a query. To create an execution plan, the query optimizer evaluates various permutations of `iÝiÃÊ>`ÊÊÃÌÀ>Ìi}iÃ°Ê iV>ÕÃiÊvÊÌ
iÊ«ÃÃLÌÞÊvÊ>Ê>À}iÊÕLiÀÊvÊ«ÌiÌ>Ê«>Ã]ÊÌ
ÃÊ «Ìâ>ÌÊ«ÀViÃÃÊ>ÞÊÌ>iÊ>Ê}ÊÌiÊÌÊ}iiÀ>ÌiÊÌ
iÊÃÌÊVÃÌivviVÌÛiÊiÝiVÕÌÊ«>°Ê To prevent the overoptimization of an execution plan, the optimization process is broken into multiple phases. Each phase is a set of transformation rules that evaluate various permuta ÌÃÊvÊ`iÝiÃÊ>`ÊÊÃÌÀ>Ìi}ið vÌiÀÊ}}ÊÌ
ÀÕ}
Ê>Ê«
>Ãi]ÊÌ
iʵÕiÀÞÊ«ÌâiÀÊiÝ>iÃÊÌ
iÊiÃÌ>Ìi`ÊVÃÌÊvÊÌ
iÊ resulting plan. If the query optimizer determines that the plan is cheap enough, it will use the «>ÊÜÌ
ÕÌÊ}}ÊÌ
ÀÕ}
ÊÌ
iÊÀi>}Ê«Ìâ>ÌÊ«
>ÃiðÊÜiÛiÀ]ÊvÊÌ
iÊ«>ÊÃÊÌÊ cheap enough, the optimizer will go through the next optimization phase. I will cover execu ÌÊ«>Ê}iiÀ>ÌÊÊÀiÊ`i«Ì
ÊÊ
>«ÌiÀÊ° SQL Server displays a query execution plan in various forms and from two different types. The most commonly used forms in SQL Server 2008 are the graphical execution plan >`ÊÌ
iÊ8ÊiÝiVÕÌÊ«>°ÊVÌÕ>Þ]ÊÌ
iÊ}À>«
V>ÊiÝiVÕÌÊ«>ÊÃÊëÞÊ>ÊXML execu tion plan parsed for the screen. The two types of execution plan are the estimated plan and the actual plan. The estimated plan includes the results coming from the query optimizer, and the actual plan is the plan used by the query engine. The beauty of the estimated plan is that it doesn’t require the query to be executed. These plan types can differ, but most of the time they will be the same. The graphical execution plan uses icons to represent the pro cessing strategy of a query. To obtain a graphical estimated execution plan, select Query ë>ÞÊ ÃÌ>Ìi`Ê ÝiVÕÌÊ*>°ÊÊ8ÊiÝiVÕÌÊ«>ÊVÌ>ÃÊÌ
iÊÃ>iÊ`>Ì>Ê>Û>>LiÊ through the graphical plan but in a more immediately accessible format. Further, with the XQuery capabilities of SQL Server 2008, XML execution plans can be queried as if they were Ì>LiÃ°Ê XML execution plan is produced by the OAPODKSLH=J[TIH and OAPOP=PEOPE?OTIH ÃÌ>ÌiiÌðÊ9ÕÊV>Ê>ÃÊÀ}
ÌVVÊ>Ê}À>«
V>ÊiÝiVÕÌÊ«>Ê>` select Showplan XML. You can obtain the estimated XML execution plan for the costliest query identified previ ously using the OAPODKSLH=J[TIHÊV>`Ê>ÃÊvÜÃÊoap[odkslh]j*omhÊÊÌ
iÊ`Ü>`®\ OAPODKSLH=J[TIHKJ CK OAHA?Pokd*=__kqjpJqi^an( ok`*HejaPkp]h( ok`*Kn`anMpu( ok`*QjepLne_a( l*J]ia BNKIO]hao*O]haoKn`anDa]`anokd FKEJO]hao*O]haoKn`an@ap]ehok` KJokd*O]haoKn`anE@9ok`*O]haoKn`anE@ FKEJLnk`q_pekj*Lnk`q_pl KJok`*Lnk`q_pE@9l*Lnk`q_pE@
83
84
C HAPTER 3
SQ L QU ER Y P ER FOR MA NC E A NA L YS IS
SDANAok`*HejaPkp]h:-,,,7 CK OAPODKSLH=J[TIHKBB CK ,Õ}ÊÌ
ÃʵÕiÀÞÊÀiÃÕÌÃÊÊ>ÊÊÌÊ>ÊiÝiVÕÌÊ«>]ÊÌÊ>ÊiÝiVÕÌÊ«>ÊÀÊ>ÞÊ `>Ì>°Ê V}ÊÌ
iÊÊÜÊ«iÊ>ÊiÝiVÕÌÊ«>°ÊÌ
Õ}
ÊÌ
iÊ«>ÊÜÊLiÊ`ë>Þi`Ê>ÃÊ>Ê }À>«
V>Ê«>]ÊÀ}
ÌVV}ÊÌ
iÊ«>Ê>`ÊÃiiVÌ}Ê-
ÜÊ ÝiVÕÌÊ*>Ê8ÊÜÊ`ë>ÞÊÌ
iÊ 8Ê`>Ì>°Ê}ÕÀiÊΣÎÊÃ
ÜÃÊ>Ê«ÀÌÊvÊÌ
iÊ8ÊiÝiVÕÌÊ«>ÊÕÌ«ÕÌ°
Figure 3-13. XML execution plan output
Analyzing a Query Execution Plan Let’s start with the costly query identified in oap[odkslh]j*omh°Ê «ÞÊÌÊÕÃÊÌ
iÊOAP ODKSLH=J[TIHÊÃÌ>ÌiiÌîÊÌÊ>>}iiÌÊ-ÌÕ`]Ê>`ÊÌÕÀÊÊVÕ`iÊVÌÕ>Ê ÝiVÕÌÊ *>°Ê Ü]ÊÊiÝiVÕÌ}ÊÌ
ÃʵÕiÀÞ]ÊÞÕ½ÊÃiiÊÌ
iÊiÝiVÕÌÊ«>ÊÊ}ÕÀiÊΣ{°
Figure 3-14. Query execution plan
CHAPTER 3
S Q L Q U E R Y P E R F O R M A N C E A N A LY S I S
,i>`ÊÌ
iÊiÝiVÕÌÊ«>ÊvÀÊÀ}
ÌÊÌÊivÌÊ>`ÊvÀÊÌ«ÊÌÊLÌÌ°Ê >V
ÊÃÌi«ÊÀi«ÀiÃiÌÃÊ>Ê operation performed to get the final output of the query. Some of the aspects of a query execu tion represented by an execution plan are as follows: Ê
UÊ vÊ>ʵÕiÀÞÊVÃÃÌÃÊvÊ>ÊL>ÌV
ÊvÊÕÌ«iʵÕiÀiÃ]ÊÌ
iÊiÝiVÕÌÊ«>ÊvÀÊi>V
ʵÕiÀÞÊÜÊ be displayed in the order of execution. Each execution plan in the batch will have a Ài>ÌÛiÊiÃÌ>Ìi`ÊVÃÌ]ÊÜÌ
ÊÌ
iÊÌÌ>ÊVÃÌÊvÊÌ
iÊÜ
iÊL>ÌV
ÊLi}Ê£ääÊ«iÀViÌ°
Ê
UÊ ÛiÀÞÊVÊÊ>ÊiÝiVÕÌÊ«>ÊÀi«ÀiÃiÌÃÊ>Ê«iÀ>ÌÀ°Ê/
iÞÊÜÊi>V
Ê
>ÛiÊ>ÊÀi>ÌÛiÊ iÃÌ>Ìi`ÊVÃÌ]ÊÜÌ
ÊÌ
iÊÌÌ>ÊVÃÌÊvÊ>ÊÌ
iÊ`iÃÊÊ>ÊiÝiVÕÌÊ«>ÊLi}Ê£ääÊ«iÀ cent.
Ê
UÊ 1ÃÕ>ÞÊ>ÊÃÌ>ÀÌ}Ê«iÀ>ÌÀÊÊ>ÊiÝiVÕÌÊÀi«ÀiÃiÌÃÊ>Ê`>Ì>ÀiÌÀiÛ>ÊiV
>ÃÊ vÀÊ>Ê`>Ì>L>ÃiÊLiVÌÊ>ÊÌ>LiÊÀÊ>Ê`iÝ®°ÊÀÊiÝ>«i]ÊÊÌ
iÊiÝiVÕÌÊ«>ÊÊ }ÕÀiÊΣ{]ÊÌ
iÊÌ
ÀiiÊÃÌ>ÀÌ}Ê«ÌÃÊÀi«ÀiÃiÌÊÀiÌÀiÛ>ÃÊvÀÊÌ
iÊO]haoKn`anDa]`an, O]haoKn`an@ap]eh, and Lnk`q_p tables.
Ê
UÊ >Ì>ÊÀiÌÀiÛ>ÊÜÊÕÃÕ>ÞÊLiÊiÌ
iÀÊ>ÊÌ>LiÊ«iÀ>ÌÊÀÊ>Ê`iÝÊ«iÀ>Ì°ÊÀÊ iÝ>«i]ÊÊÌ
iÊiÝiVÕÌÊ«>ÊÊ}ÕÀiÊΣ£]Ê>ÊÌ
ÀiiÊ`>Ì>ÊÀiÌÀiÛ>ÊÃÌi«ÃÊ>ÀiÊ`iÝÊ operations.
Ê
UÊ >Ì>ÊÀiÌÀiÛ>ÊÊ> index will be either an index scan or an index seek. For example, Ì
iÊvÀÃÌÊ>`ÊÌ
À`Ê`iÝÊ«iÀ>ÌÃÊÊ}ÕÀiÊΣ{Ê>ÀiÊ`iÝÊÃV>Ã]Ê>`ÊÌ
iÊÃiV`ÊiÊ is an index seek.
Ê
UÊ /
iÊ>}ÊVÛiÌÊvÀÊ>Ê`>Ì>ÀiÌÀiÛ>Ê«iÀ>ÌÊÊ>Ê`iÝÊÃÊWP]^haJ]iaY* WEj`atJ]iaY.
Ê
UÊ >Ì>ÊvÜÃÊvÀÊÀ}
ÌÊÌÊivÌÊLiÌÜiiÊÌÜÊ«iÀ>ÌÀÃÊ>`ÊÃÊ`V>Ìi`ÊLÞÊ>ÊViVÌ}Ê arrow between the two operators.
Ê
UÊ /
iÊÌ
ViÃÃÊvÊ>ÊViVÌ}Ê>ÀÀÜÊLiÌÜiiÊ«iÀ>ÌÀÃÊÀi«ÀiÃiÌÃÊ>Ê}À>«
V>ÊÀi«Ài sentation of the number of rows transferred.
Ê
UÊ /
iÊ} mechanism between two operators in the same column will be either a iÃÌi`Ê«Ê]Ê>Ê
>Ã
Ê>ÌV
Ê]ÊÀÊ>ÊiÀ}iÊ°ÊÀÊiÝ>«i]ÊÊÌ
iÊiÝiVÕÌÊ«>Ê Ã
ÜÊÊ}ÕÀiÊΣ{]ÊÌ
iÀiÊÃÊiÊiÃÌi`Ê«ÊÊ>`ÊiÊ
>Ã
Ê>ÌV
°
Ê
UÊ ,Õ}ÊÌ
iÊÕÃiÊÛiÀÊ>Ê`iÊÊ>ÊiÝiVÕÌÊ«>ÊÃ
ÜÃÊ>Ê««Õ«ÊÜ`ÜÊÜÌ
Ê ÃiÊ`iÌ>Ã]Ê>ÃÊÞÕÊV>ÊÃiiÊÊ}ÕÀiÊΣx° Ê UÊ ÊV«iÌiÊÃiÌÊvÊ`iÌ>ÃÊ>LÕÌÊ>Ê«iÀ>ÌÀÊÃÊ>Û>>LiÊÊÌ
iÊ*À«iÀÌiÃÊÜ`Ü]Ê Ü
V
ÊÞÕÊV>Ê«iÊLÞÊÀ}
ÌVV}ÊÌ
iÊ«iÀ>ÌÀÊ>`ÊÃiiVÌ}Ê*À«iÀÌiðÊ/
ÃÊÃÊ ÛÃLiÊÊ}ÕÀiÊΣȰ Ê UÊ Ê«iÀ>ÌÀÊ`iÌ>ÊÃ
ÜÃÊLÌ
Ê«
ÞÃV>Ê>`Ê}V>Ê«iÀ>ÌÊÌÞ«iÃÊ>ÌÊÌ
iÊÌ«°Ê Physical operations represent those actually used by the storage engine, while the logical operations are the constructs used by the optimizer to build the estimated execution plan. If logical and physical operations are the same, then only the physical operation is shown. It also displays other useful information, such as row VÕÌ]ÊÉ"ÊVÃÌ]Ê *1ÊVÃÌ]Ê>`ÊÃÊ° Ê UÊ /
iÊÀ}ÕiÌÊÃiVÌÊÊ>Ê«iÀ>ÌÀÊ`iÌ>Ê««Õ«ÊÜ`ÜÊÃÊiëiV>ÞÊÕÃivÕÊÊ analysis, because it showsÊÌ
iÊvÌiÀÊÀÊÊVÀÌiÀÊÕÃi`ÊLÞÊÌ
iÊ«ÌâiÀ°
85
86
C HAPTER 3
SQ L QU ER Y P ER FOR MA NC E A NA L YS IS
Figure 3-15. Execution plan node detail
Figure 3-16. Clustered index scan properties
CHAPTER 3
S Q L Q U E R Y P E R F O R M A N C E A N A LY S I S
Identifying the Costly Steps in an Execution Plan Your main interest in the execution plan is to find out which steps are relatively costly. These steps are the starting point for your query optimization. You can choose the starting steps by adopting the following techniques: Ê
UÊ >V
Ê`iÊÊ>ÊiÝiVÕÌÊ«>ÊÃ
ÜÃÊÌÃÊÀi>ÌÛiÊVÃÌÊÊÌ
iÊV«iÌiÊiÝiVÕÌÊ«>]Ê ÜÌ
ÊÌ
iÊÌÌ>ÊVÃÌÊvÊÌ
iÊÜ
iÊ«>ÊLi}Ê£ääÊ«iÀViÌ°Ê/
iÀivÀi]ÊvVÕÃÊ>ÌÌiÌÊÊ Ì
iÊ`iîÊÜÌ
ÊÌ
iÊ
}
iÃÌÊÀi>ÌÛiÊVÃÌ°ÊÀÊiÝ>«i]ÊÌ
iÊiÝiVÕÌÊ«>ÊÊ}ÕÀiÊ Î£{Ê
>ÃÊÌÜÊÃÌi«ÃÊÜÌ
ÊÎ{Ê«iÀViÌÊVÃÌÊi>V
°
Ê
UÊ ÊiÝiVÕÌÊ«>Ê>ÞÊLiÊvÀÊ>ÊL>ÌV
ÊvÊÃÌ>ÌiiÌÃ]ÊÃÊÞÕÊ>ÞÊ>ÃÊii`ÊÌÊv`ÊÌ
iÊ ÃÌÊVÃÌÞÊÃÌ>ÌiiÌ°ÊÊ}ÕÀiÊΣxÊ>`Ê}ÕÀiÊΣ{]ÊÞÕÊV>ÊÃiiÊ>ÌÊÌ
iÊÌ«ÊvÊÌ
iÊ«>Ê Ì
iÊÌiÝÌʺ+ÕiÀÞÊ£°»ÊÊ>ÊL>ÌV
ÊÃÌÕ>Ì]ÊÌ
iÀiÊÜÊLiÊÕÌ«iÊ«>Ã]Ê>`ÊÌ
iÞÊÜÊLiÊ numbered in the order they occurred within the batch.
Ê
UÊ "LÃiÀÛiÊÌ
iÊÌ
ViÃÃÊvÊÌ
iÊViVÌ}Ê>ÀÀÜÃÊLiÌÜiiÊ`iðÊÊÛiÀÞÊÌ
VÊV necting arrow indicates a large number of rows being transferred between the VÀÀië`}Ê`iðÊ>ÞâiÊÌ
iÊ`iÊÌÊÌ
iÊivÌÊvÊÌ
iÊ>ÀÀÜÊÌÊÕ`iÀÃÌ>`ÊÜ
ÞÊÌÊ ÀiµÕÀiÃÊÃÊ>ÞÊÀÜðÊ
iVÊÌ
iÊ«À«iÀÌiÃÊvÊÌ
iÊ>ÀÀÜÃÊÌ°Ê9ÕÊ>ÞÊÃiiÊÌ
>ÌÊÌ
iÊ iÃÌ>Ìi`ÊÀÜÃÊ>`ÊÌ
iÊ>VÌÕ>ÊÀÜÃÊ>ÀiÊ`vviÀiÌ°Ê/
ÃÊV>ÊLiÊV>ÕÃi`ÊLÞÊÕÌv`>ÌiÊ statistics, among other things.
Ê
UÊ ÊvÀÊ
>Ã
ÊÊ«iÀ>ÌðÊÀÊÃ>ÊÀiÃÕÌÊÃiÌÃ]Ê>ÊiÃÌi`Ê«ÊÊÃÊÕÃÕ>ÞÊÌ
iÊ «ÀiviÀÀi`ÊÊÌiV
µÕi°Ê9ÕÊÜÊi>ÀÊÀiÊ>LÕÌÊ
>Ã
ÊÃÊV«>Ài`ÊÌÊiÃÌi`Ê «ÊÃÊ>ÌiÀÊÊÌ
ÃÊV
>«ÌiÀ°
Ê
UÊ ÊvÀÊL>ÀÊÕ«Ê«iÀ>ÌðÊÊL>ÀÊ«iÀ>ÌÊvÀÊ>Ê>À}iÊÀiÃÕÌÊÃiÌÊV>Ê cause a large number of logical reads. I will cover bookmark lookups in more detail in
>«ÌiÀÊÈ°
Ê
UÊ /
iÀiÊ>ÞÊLiÊÜ>À}Ã]Ê`V>Ìi`ÊLÞÊ>ÊiÝV>>ÌÊ«ÌÊÊiÊvÊÌ
iÊ«iÀ>ÌÀÃ]Ê which are areas of immediate concern. These can be caused by a variety of issues, VÕ`}Ê>ÊÊÜÌ
ÕÌÊÊVÀÌiÀ>ÊÀÊ>Ê`iÝÊÀÊ>ÊÌ>LiÊÜÌ
ÊÃÃ}ÊÃÌ>ÌÃÌVÃ°Ê Usually resolving the warning situation will help performance.
Ê
UÊ ÊvÀÊÃÌi«ÃÊ«iÀvÀ}Ê>Êsort operation. This indicates that the data was not retrieved in the correct sort order.
Analyzing Index Effectiveness To examineÊ>ÊVÃÌÞÊÃÌi«ÊÊ>ÊiÝiVÕÌÊ«>ÊvÕÀÌ
iÀ]ÊÞÕÊÃ
Õ`Ê>>ÞâiÊÌ
iÊ`>Ì>ÀiÌÀiÛ>Ê mechanism for the relevant table or index. First, you should check whether an index operation is a seek or a scan. Usually, for best performance, you should retrieve as few rows as possible from a table, and an index seek is usually the most efficient way of accessing a small number vÊÀÜðÊÊscan operation usually indicates that a larger number of rows have been accessed. Therefore, it is generally preferable to seek rather than scan. Next, you want to ensure that the indexing mechanism is properly set up. The query optimizer evaluates the available indexes to discover which index will retrieve data from the table in the most efficient way. If a desired index is not available, the optimizer uses the next best index. For best performance, you should always ensure that the best index is used in
87
88
C HAPTER 3
SQ L QU ER Y P ER FOR MA NC E A NA L YS IS
>Ê`>Ì>ÀiÌÀiÛ>Ê«iÀ>Ì°Ê9ÕÊV>ÊÕ`}iÊÌ
iÊ`iÝÊivviVÌÛiiÃÃÊÜ
iÌ
iÀÊÌ
iÊLiÃÌÊ`iÝÊÃÊ ÕÃi`ÊÀÊÌ®ÊLÞÊ>>Þâ}ÊÌ
iÊÀ}ÕiÌÊÃiVÌÊvÊ>Ê`iÊ`iÌ>ÊvÀÊÌ
iÊvÜ}\ Ê
UÊ Ê`>Ì>ÀiÌÀiÛ>Ê«iÀ>Ì
Ê
UÊ ÊÊ«iÀ>Ì
i̽ÃÊÊ>ÌÊÌ
iÊ`>Ì>ÀiÌÀiÛ>ÊiV
>ÃÊvÀÊÌ
iÊLnk`q_p table in the previous execution «>Ê}ÕÀiÊΣ{®°Ê}ÕÀiÊΣÇÊÃ
ÜÃÊÌ
iÊ«iÀ>ÌÀÊ«À«iÀÌið
Figure 3-17. Data-retrieval mechanism for the O]haoKn`an@ap]eh table In the operator properties for the O]haoKn`an@ap]eh table, the K^fa_p property specifies the index used, LG[O]haoKn`an@ap]eh. It uses the following naming convention: W@]p]^]oaY* WKsjanY*WP]^haJ]iaY*WEj`atJ]iaY. The OaagLna`e_]pao property specifies the column, or columns, used to seek into the index. The O]haoKn`an@ap]ehÊÌ>LiÊÃÊi`ÊÜÌ
ÊÌ
iÊ O]haoKn`anDa]`an table on the O]haoKn`anE` column. The OAAG works on the fact that the ÊVÀÌiÀ>]ÊO]haoKn`anE`, is the leading edge of the clustered index and primary key, LG[O]haoKn`an@ap]eh. -iÌiÃÊÞÕÊ>ÞÊ
>ÛiÊ>Ê`vviÀiÌÊ`>Ì>ÀiÌÀiÛ>ÊiV
>Ã]Ê>ÃÊÃ
ÜÊÊ}ÕÀiÊΣn°
CHAPTER 3
S Q L Q U E R Y P E R F O R M A N C E A N A LY S I S
Figure 3-18. A variation of the data-retrieval mechanism ÊÌ
iÊ«À«iÀÌiÃÊÊ}ÕÀiÊΣn]ÊÌ
iÀiÊÃÊÊpredicate. The lack of predicate means that the entire table, remembering that a clustered index is the table, is being scanned as input to the mergeÊÊ«iÀ>ÌÀÊÀiviÀÊÌÊÌ
iÊi>ÀiÀÊ}ÕÀiÊΣ{®°
Analyzing Join Effectiveness In additionÊÌÊ>>Þâ}ÊÌ
iÊ`iÝiÃÊÕÃi`]ÊÞÕÊÃ
Õ`ÊiÝ>iÊÌ
iÊivviVÌÛiiÃÃÊvÊÊÃÌÀ>Ìi gies decided by the optimizer. SQL ServerÊÕÃiÃÊÌ
ÀiiÊÌÞ«iÃÊvÊÃ\ Ê
UÊ >Ã
ÊÃ
Ê
UÊ iÀ}iÊÃ
Ê
UÊ
iÃÌi`Ê«ÊÃ
In many simple queries affecting a small set of rows, iÃÌi`Ê«ÊÃÊ>ÀiÊv>ÀÊÃÕ«iÀÀÊÌÊ LÌ
Ê
>Ã
Ê>`ÊiÀ}iÊðÊ/
iÊÊÌÞ«iÃÊÌÊLiÊÕÃi`ÊÊ>ʵÕiÀÞÊ>ÀiÊ`iV`i`Ê`Þ>V>ÞÊLÞÊ the optimizer.
89
90
C HAPTER 3
SQ L QU ER Y P ER FOR MA NC E A NA L YS IS
Hash Join ToÊÕ`iÀÃÌ>`Ê-+Ê-iÀÛiÀ½ÃÊ
>Ã
ÊÊÃÌÀ>Ìi}Þ]ÊVÃ`iÀÊÌ
iÊvÜ}ÊëiʵÕiÀÞÊd]od*omh ÊÌ
iÊ`Ü>`®\ OAHA?Pl*& BNKILnk`q_pekj*Lnk`q_pl FKEJLnk`q_pekj*Lnk`q_pOq^?]packnuol_ KJl*Lnk`q_pOq^?]packnuE@9ol_*Lnk`q_pOq^?]packnuE@ />LiÊÎÈÊÃ
ÜÃÊÌ
iÊÌÜÊÌ>LiýÊ`iÝiÃÊ>`ÊÕLiÀÊvÊÀÜð Table 3-6. Indexes and Number of Rows of the Lnk`q_po and Lnk`q_p?]packnuP]^hao
Table
Indexes
Lnk`q_p
ÕÃÌiÀi`Ê`iÝÊÊLnk`q_pE@
Óx
ÕÃÌiÀi`Ê`iÝÊÊLnk`q_p?]packnuE`
{£
Lnk`q_p?]packnu
Number of Rows
}ÕÀiÊΣÊÃ
ÜÃÊÌ
iÊiÝiVÕÌÊ«>ÊvÀÊÌ
iÊ«ÀiVi`}ʵÕiÀÞ°
Figure 3-19. Execution plan with a hash join 9ÕÊV>ÊÃiiÊÌ
>ÌÊÌ
iÊ«ÌâiÀÊÕÃi`Ê>Ê
>Ã
ÊÊLiÌÜiiÊÌ
iÊÌÜÊÌ>Lið Ê
>Ã
ÊÊÕÃiÃÊÌ
iÊÌÜÊÊ«ÕÌÃÊ>ÃÊ>Êbuild input and a probe input. The build input is shown as the top input in the execution plan, and the probe input is shown as the bottom input. The smaller of the two inputs serves as the build input. /
iÊ
>Ã
ÊÊ«iÀvÀÃÊÌÃÊ«iÀ>ÌÊÊÌÜÊ«
>ÃiÃ\ÊÌ
iÊbuild phase and the probe phase. ÊÌ
iÊÃÌÊVÞÊÕÃi`ÊvÀÊvÊ
>Ã
Ê]ÊÌ
iÊin-memory hash join, the entire build input is scanned or computed, and then a hash table is built in memory. Each row is inserted into a hash bucket depending on the hash value computed for the hash keyÊÌ
iÊÃiÌÊvÊVÕÃÊÊÌ
iÊ iµÕ>ÌÞÊ«Ài`V>Ìi®° This build phase is followed by the probe phase. The entire probe input is scanned or computed one row at a time, and for each probe row, a hash key value is computed. The corre sponding hash bucket is scanned for the hash key value from the probe input, and the matches >ÀiÊ«À`ÕVi`°Ê}ÕÀiÊÎÓäÊÕÃÌÀ>ÌiÃÊÌ
iÊ«ÀViÃÃÊvÊ>ÊiÀÞÊ
>Ã
Ê°
CHAPTER 3
S Q L Q U E R Y P E R F O R M A N C E A N A LY S I S
Figure 3-20. Workflow for an in-memory hash join The query optimizerÊÕÃiÃÊ
>Ã
ÊÃÊÌÊ«ÀViÃÃÊ>À}i]ÊÕÃÀÌi`]Ê`iÝi`Ê«ÕÌÃÊivv ciently. Let’s now look at theÊiÝÌÊÌÞ«iÊvÊ\ÊÌ
iÊiÀ}iÊ°
Merge Join In the previous case, input from the Lnk`q_p table is larger, and the table is not indexed on Ì
iÊ}ÊVÕÊLnk`q_p?]packnuE@®°Ê1Ã}ÊÌ
iÊvÜ}ÊëiʵÕiÀÞÊianca*omh in the `Ü>`®]ÊÞÕÊV>ÊÃiiÊ`vviÀiÌÊLi
>ÛÀ\ OAHA?Pli*& BNKILnk`q_pekj*Lnk`q_pIk`ahli FKEJLnk`q_pekj*Lnk`q_pIk`ahLnk`q_p@ao_nelpekj?qhpqnalil` KJli*Lnk`q_pIk`ahE@9lil`*Lnk`q_pIk`ahE@ }ÕÀiÊÎÓ£ÊÃ
ÜÃÊÌ
iÊÀiÃÕÌ>ÌÊiÝiVÕÌÊ«>ÊvÀÊÌ
ÃʵÕiÀÞ°
91
92
C HAPTER 3
SQ L QU ER Y P ER FOR MA NC E A NA L YS IS
Figure 3-21. Execution plan with a merge join ÀÊÌ
ÃʵÕiÀÞ]ÊÌ
iÊ«ÌâiÀÊÕÃi`Ê>ÊiÀ}iÊÊLiÌÜiiÊÌ
iÊÌÜÊÌ>LiðÊÊiÀ}iÊÊ ÀiµÕÀiÃÊLÌ
ÊÊ«ÕÌÃÊÌÊLiÊÃÀÌi`ÊÊÌ
iÊiÀ}iÊVÕÃ]Ê>ÃÊ`ivi`ÊLÞÊÌ
iÊÊVÀÌiÀ°Ê If indexesÊ>ÀiÊ>Û>>LiÊÊLÌ
Ê}ÊVÕÃ]ÊÌ
iÊÌ
iÊÊ«ÕÌÃÊ>ÀiÊÃÀÌi`ÊLÞÊÌ
iÊ`iÝ°Ê -ViÊi>V
ÊÊ«ÕÌÊÃÊÃÀÌi`]ÊÌ
iÊiÀ}iÊÊ}iÌÃÊ>ÊÀÜÊvÀÊi>V
Ê«ÕÌÊ>`ÊV«>ÀiÃÊÌ
iÊ vÀÊiµÕ>ÌÞ°ÊÊ>ÌV
}ÊÀÜÊÃÊ«À`ÕVi`ÊvÊÌ
iÞÊ>ÀiÊiµÕ>°Ê/
ÃÊ«ÀViÃÃÊÃÊÀi«i>Ìi`ÊÕÌÊ>Ê rows are processed. In this case,ÊÌ
iʵÕiÀÞÊ«ÌâiÀÊvÕ`ÊÌ
>ÌÊÌ
iÊÊ«ÕÌÃÊÜiÀiÊLÌ
ÊÃÀÌi`ÊÀÊ`iÝi`®Ê ÊÌ
iÀÊ}ÊVÕðÊÃÊ>ÊÀiÃÕÌ]ÊÌ
iÊiÀ}iÊÊÜ>ÃÊV
ÃiÊ>ÃÊ>Êv>ÃÌiÀÊÊÃÌÀ>Ìi}ÞÊÌ
>Ê Ì
iÊ
>Ã
Ê°
Nested Loop Join The final typeÊvÊʽÊVÛiÀÊ
iÀiÊÃÊÌ
iÊiÃÌi`Ê«Ê°ÊÀÊLiÌÌiÀÊ«iÀvÀ>Vi]ÊÞÕÊÃ
Õ`Ê always access a limited number of rows from individual tables. To understand the effect of ÕÃ}Ê>ÊÃ>iÀÊÀiÃÕÌÊÃiÌ]Ê`iVÀi>ÃiÊÌ
iÊÊ«ÕÌÃÊÊÞÕÀʵÕiÀÞÊ>ÃÊvÜÃÊhkkl*omh in the `Ü>`®\ OAHA?Pokd*& BNKIO]hao*O]haoKn`anDa]`anokd FKEJO]hao*O]haoKn`an@ap]ehok` KJokd*O]haoKn`anE@9ok`*O]haoKn`anE@ SDANAokd*O]haoKn`anE@93-4/. }ÕÀiÊÎÓÓÊÃ
ÜÃÊÌ
iÊÀiÃÕÌ>ÌÊiÝiVÕÌÊ«>ÊvÊÌ
iÊiÜʵÕiÀÞ°
Figure 3-22. Execution plan with a nested loop join ÃÊÞÕÊV>ÊÃii]ÊÌ
iÊ«ÌâiÀÊÕÃi`Ê>ÊiÃÌi`Ê«ÊÊLiÌÜiiÊÌ
iÊÌÜÊÌ>Lið ÊiÃÌi`Ê«ÊÊÕÃiÃÊiÊÊ«ÕÌÊ>ÃÊÌ
iÊÕÌiÀÊ«ÕÌÊÌ>LiÊ>`ÊÌ
iÊÌ
iÀÊ>ÃÊÌ
iÊiÀÊ input table. The outer input table is shown as the top input in the execution plan, and the
CHAPTER 3
S Q L Q U E R Y P E R F O R M A N C E A N A LY S I S
inner input table is shown as the bottom input table. The outer loop consumes the outer input table row by row. The inner loop, executed for each outer row, searches for matching rows in the inner input table. iÃÌi`Ê«ÊÃÊ>ÀiÊ
}
ÞÊivviVÌÛiÊvÊÌ
iÊÕÌiÀÊ«ÕÌÊÃʵÕÌiÊÃ>Ê>`ÊÌ
iÊiÀÊ«ÕÌÊ ÃÊ>À}iÊLÕÌÊ`iÝi`°ÊÊ>ÞÊëiʵÕiÀiÃÊ>vviVÌ}Ê>ÊÃ>ÊÃiÌÊvÊÀÜÃ]ÊiÃÌi`Ê«ÊÃÊ >ÀiÊv>ÀÊÃÕ«iÀÀÊÌÊLÌ
Ê
>Ã
Ê>`ÊiÀ}iÊðÊÃÊ«iÀ>ÌiÊLÞÊ}>}Êëii`ÊÌ
ÀÕ}
ÊÌ
iÀÊ Ã>VÀvViðÊÊ«Ê is fast because it uses memory to take a small set of data and compare it µÕVÞÊÌÊ>ÊÃiV`ÊÃiÌÊvÊ`>Ì>°ÊÊiÀ}iÊÊÃ>ÀÞÊÕÃiÃÊiÀÞÊ>`Ê>ÊLÌÊvÊpail`^ to do its À`iÀi`ÊV«>ÀÃðÊÊ
>Ã
ÊÊÕÃiÃÊiÀÞÊ>`Êpail`^ to build out the hash tables for the °ÊÌ
Õ}
Ê>Ê«ÊÊÃÊv>ÃÌiÀ]ÊÌÊÜÊVÃÕiÊÀiÊiÀÞÊÌ
>Ê>Ê
>Ã
ÊÀÊiÀ}iÊ>ÃÊÌ
iÊ data sets get larger, which is why SQL Server will use different plans in different situations for different sets of data.
ÛiÊvÀÊÃ>ÊÊ«ÕÌÃ]ÊÃÕV
Ê>ÃÊÊÌ
iÊ«ÀiÛÕÃʵÕiÀÞ]Ê̽ÃÊ«ÀÌ>ÌÊÌÊ
>ÛiÊ>Ê`iÝÊ ÊÌ
iÊ}ÊVÕðÊÃÊÞÕÊÃ>ÜÊÊÌ
iÊ«ÀiVi`}ÊiÝiVÕÌÊ«>]ÊvÀÊ>ÊÃ>ÊÃiÌÊvÊÀÜÃ]Ê `iÝiÃÊÊ}ÊVÕÃÊ>ÜÊÌ
iʵÕiÀÞÊ«ÌâiÀÊÌÊVÃ`iÀÊ>ÊiÃÌi`Ê«ÊÊÃÌÀ>Ìi}Þ°Ê ÊÃÃ}Ê`iÝÊÊÌ
iÊ}ÊVÕÊvÊ>Ê«ÕÌÊÜÊvÀViÊÌ
iʵÕiÀÞÊ«ÌâiÀÊÌÊÕÃiÊ>Ê
>Ã
Ê ÊÃÌi>`° />LiÊÎÇÊÃÕ>ÀâiÃÊÌ
iÊÕÃiÊvÊÌ
iÊÌ
ÀiiÊÊÌÞ«ið Table 3-7. Characteristics of the Three Join Types
Join Type
Index on Joining Columns
Usual Size of Joining Tables
Presorted
Join Clause
>Ã
Inner table: Not indexed Outer table: Optional Optimal condition: Small outer table, large inner table
Þ
No
µÕ
Merge
Both tables: Must "«Ì>ÊV`Ì\Ê ÕÃÌiÀi`ÊÀÊ covering index on both
Large
Yes
µÕ
Nested loop
Inner table: Must Outer table: Preferable
Small
Optional
Note The outer table is usually the smaller of the two joining tables in the hash and loop joins.
I will cover index types, including clustered andÊVÛiÀ}Ê`iÝiÃ]ÊÊ
>«ÌiÀÊ{°
Actual vs. Estimated Execution Plans There areÊiÃÌ>Ìi`Ê>`Ê>VÌÕ>ÊiÝiVÕÌÊ«>ðÊÌ
Õ}
ÊÌ
iÃiÊÌÜÊÌÞ«iÃÊvÊ«>ÃÊ>ÀiÊ}iiÀ ally, to a degree, interchangeable, sometimes one is clearly preferred over the other. There are iÛiÊÃÌÕ>ÌÃÊÜ
iÀiÊÌ
iÊiÃÌ>Ìi`Ê«>ÃÊÜÊÌÊÜÀÊ>ÌÊ>°Ê Ã`iÀÊÌ
iÊvÜ}ÊÃÌÀi`Ê «ÀVi`ÕÀiÊ_na]pa[l-*omhÊÊÌ
iÊ`Ü>`®\
93
94
C HAPTER 3
SQ L QU ER Y P ER FOR MA NC E A NA L YS IS
EB$OAHA?PK>FA?P[E@$#l-#% %EOJKPJQHH @NKLLNK?lCK ?NA=PALNK?l=O ?NA=PAP=>HAp-$_-EJP%7 EJOANPEJPKpOAHA?Plnk`q_pe` BNKILnk`q_pekj*Lnk`q_p OAHA?P& BNKIp-7 @NKLP=>HAp-7 CK You may try to use ODKSLH=J[TIH to obtain the estimated XML execution plan for the query >ÃÊvÜÃÊodkslh]j*omhÊÊÌ
iÊ`Ü>`®\ OAPODKSLH=J[TIHKJ CK ATA?l-7 CK OAPODKSLH=J[TIHKBB CK But this fails with the following error: Oanran6Ioc.,4(Harah-2(Op]pa-(Lnk_a`qnal-(Heja0 Ejr]he`k^fa_pj]ia#p-#* Since ODKSLH=J[TIH doesn’t actually execute the query, the query optimizer can’t generate an execution plan for EJOANP and OAHA?PÊÃÌ>ÌiiÌÃÊÊÌ
iÊÌ>LiÊp-®° Instead, you can use OP=PEOPE?OTIH as follows: OAPOP=PEOPE?OTIHKJ CK ATA?l-7 CK OAPOP=PEOPE?OTIHKBB CK Since OP=PEOPE?OTIH executes the query, a different process is used that allows the table ÌÊLiÊVÀi>Ìi`Ê>`Ê>VViÃÃi`ÊÜÌ
ÊÌ
iʵÕiÀÞ°Ê}ÕÀiÊÎÓÎÊÃ
ÜÃÊ«>ÀÌÊvÊÌ
iÊÌiÝÌÕ>ÊiÝiVÕÌÊ plan provided by OP=PEOPE?OTIH.
CHAPTER 3
S Q L Q U E R Y P E R F O R M A N C E A N A LY S I S
Figure 3-23. OP=PEOPE?OLNKBEHA output
Tip Remember to switch Query
Show Execution Plan off in Management Studio, or you will see the graphical, rather than textual, execution plan.
Plan Cache One final place to access execution plans is to read them directly from the memory space Ü
iÀiÊÌ
iÞÊ>ÀiÊÃÌÀi`]ÊÌ
iÊ«>ÊV>V
i°Ê Þ>VÊ>>}iiÌÊÛiÜÃÊ>`ÊvÕVÌÃÊ>ÀiÊ«À vided from SQL Server to access this data. To see a listing of execution plans in cache, run the following query: OAHA?Pl*mqanu[lh]j( p*patp BNKIouo*`i[ata_[_]_da`[lh]jon ?NKOO=LLHUouo*`i[ata_[mqanu[lh]j$n*lh]j[d]j`ha%l ?NKOO=LLHUouo*`i[ata_[omh[patp$n*lh]j[d]j`ha%p The query returns a list of XML execution plan links. Opening any of them will show the execution plan. Working further with columns available through the dynamic management views will allow you to search for specific procedures or execution plans.
Query Cost Even though the execution plan for a query provides a detailed processing strategy and the estimated relative costs of the individual steps involved, it doesn’t provide the actual cost of Ì
iʵÕiÀÞÊÊÌiÀÃÊvÊ *1ÊÕÃ>}i]ÊÀi>`ÃÉÜÀÌiÃÊÌÊ`Ã]ÊÀʵÕiÀÞÊ`ÕÀ>Ì°Ê7
iÊ«Ìâ}Ê>Ê query, you may add an index to reduce the relative cost of a step. This may adversely affect a dependent step in the execution plan, or sometimes it may even modify the execution plan itself. Thus, if you look only at the execution plan, you can’t be sure that your query optimiza tion benefits the query as a whole, as opposed to that one step in the execution plan. You can analyze the overall cost of a query in different ways. 9ÕÊÃ
Õ`ÊÌÀÊÌ
iÊÛiÀ>ÊVÃÌÊvÊ>ʵÕiÀÞÊÜ
iÊ«Ìâ}ÊÌ°ÊÃÊiÝ«>i`Ê«ÀiÛ ously, you can use SQL Profiler to monitor the @qn]pekj, ?LQ, Na]`o, and Snepao information for the query. To reduce the overhead of Profiler on SQL Server, you should set a filter criterion on
95
96
C HAPTER 3
SQ L QU ER Y P ER FOR MA NC E A NA L YS IS
OLE@ÊvÀÊiÝ>«i]Ê>Ê`iÌviÀÊÃ`iÊ-+Ê-iÀÛiÀÊÌÊ`iÌvÞÊ>Ê`>Ì>L>ÃiÊÕÃiÀ®ÊiµÕ>ÊÌÊÌ
iÊOLE@ vÊÞÕÀÊ>>}iiÌÊ-ÌÕ`ÊÜ`Ü°ÊÜiÛiÀ]ÊÕÃ}Ê*ÀviÀÊÃÌÊ>``ÃÊ>ÊVÃÃÌiÌÊÛiÀ
i>`Ê on SQL Server in filtering out the events for other database users or OLE@s. I will explain OLE@ in ÀiÊ`i«Ì
ÊÊ
>«ÌiÀÊ£Ó° There are other ways to collect performance data that are more immediate than running Profiler.
Client Statistics
iÌÊÃÌ>ÌÃÌVÃÊV>«ÌÕÀi execution information from the perspective of your machine as a client of the server. This means that any times recorded include the time it takes to transfer data across the network, not merely the time involved on the SQL Server machine itself. To use them, simply click Query ÊVÕ`iÊ iÌÊ-Ì>ÌÃÌVÃ°Ê Ü]Êi>V
ÊÌiÊÞÕÊÀÕÊ>ʵÕiÀÞ]Ê>Ê Ìi`ÊÃiÌÊvÊ`>Ì>ÊÃÊViVÌi`ÊVÕ`}ÊiÝiVÕÌÊÌi]ÊÌ
iÊÕLiÀÊvÊÀÜÃÊ>vviVÌi`]ÊÀÕ` trips to the server, and more. Further, each execution of the query is displayed separately on Ì
iÊ iÌÊ-Ì>ÌÃÌVÃÊÌ>L]Ê>`Ê>ÊVÕÊ>}}Ài}>Ì}ÊÌ
iÊÕÌ«iÊiÝiVÕÌÃÊÃ
ÜÃÊÌ
iÊ>ÛiÀ ages for the data collected. The statistics will also show whether a time or count has changed vÀÊiÊÀÕÊÌÊÌ
iÊiÝÌ]ÊÃ
Ü}ÊÕ«Ê>ÃÊ>ÀÀÜÃ]Ê>ÃÊÃ
ÜÊÊ}ÕÀiÊÎÓ{° For example, consider this query: OAHA?PPKL-,,l*& BNKILnk`q_pekj*Lnk`q_pl The client statistics information for the query should look something like that shown in }ÕÀiÊÎÓ{°
Figure 3-24. Client statistics Ì
Õ}
ÊV>«ÌÕÀ}ÊViÌÊÃÌ>ÌÃÌVÃÊV>ÊLiÊ>ÊÕÃivÕÊÜ>ÞÊÌÊ}>Ì
iÀÊ`>Ì>]Ê̽ÃÊ>ÊÌi`ÊÃiÌÊvÊ data, and there is no way to show how one execution is different from another. You could even run a completely different query, and its data would be mixed in with the others, making the averages useless. If you need to, you can reset the client statistics. Select the Query menu and then theÊ,iÃiÌÊ iÌÊ-Ì>ÌÃÌVÃÊiÕÊÌi°
CHAPTER 3
S Q L Q U E R Y P E R F O R M A N C E A N A LY S I S
Execution Time Both @qn]pekj and ?LQ represent the time factor of a query. To obtain detailed information on Ì
iÊ>ÕÌÊvÊÌiÊÊÃiV`îÊÀiµÕÀi`ÊÌÊ«>ÀÃi]ÊV«i]Ê>`ÊiÝiVÕÌiÊ>ʵÕiÀÞ]ÊÕÃiÊOAP OP=PEOPE?OPEIAÊ>ÃÊvÜÃÊpeiaop]po*omhÊÊÌ
iÊ`Ü>`®\ OAPOP=PEOPE?OPEIAKJ CK OAHA?Pokd*=__kqjpJqi^an( ok`*HejaPkp]h( ok`*Kn`anMpu( ok`*QjepLne_a( l*J]ia BNKIO]hao*O]haoKn`anDa]`anokd FKEJO]hao*O]haoKn`an@ap]ehok` KJokd*O]haoKn`anE@9ok`*O]haoKn`anE@ FKEJLnk`q_pekj*Lnk`q_pl KJok`*Lnk`q_pE@9l*Lnk`q_pE@ SDANAok`*HejaPkp]h:-,,,7 CK OAPOP=PEOPE?OPEIAKBB CK The output of OP=PEOPE?OPEIA for the preceding OAHA?P statement is as follows: OMHOanranl]noa]j`_kilehapeia6 ?LQpeia9,io(ah]loa`peia9,io* $/.-,-nks$o%]bba_pa`% OMHOanranAta_qpekjPeiao6 ?LQpeia9-0,io(ah]loa`peia932/io* OMHOanranl]noa]j`_kilehapeia6 ?LQpeia9,io(ah]loa`peia9,io* The ?LQpeia9-0,io part of the execution times represents the ?LQ value provided by the Profiler tool and the Server Trace option. Similarly, the corresponding Ah]loa`peia9 32/io represents the @qn]pekj value provided by the other mechanisms. ÊäÊÃÊ«>ÀÃiÊ>`ÊV«iÊÌiÊÃ}viÃÊÌ
>ÌÊÌ
iÊ«ÌâiÀÊÀiÕÃi`ÊÌ
iÊiÝÃÌ}ÊiÝiVÕ tion plan for this query and therefore didn’t have to spend any time parsing and compiling the query again. If the query is executed for the first time, then the optimizer has to parse the query first for syntax and then compile it to produce the execution plan. This can be easily verified by clearing out the cache using the system call @>??BNAALNK??=?DA and then rerun ning the query:
97
98
C HAPTER 3
SQ L QU ER Y P ER FOR MA NC E A NA L YS IS
OMHOanranl]noa]j`_kilehapeia6 ?LQpeia9/.io(ah]loa`peia9/5io* $-2.nks$o%]bba_pa`% OMHOanranAta_qpekjPeiao6 ?LQpeia9-43io(ah]loa`peia9255io* OMHOanranl]noa]j`_kilehapeia6 ?LQpeia9,io(ah]loa`peia9,io* /
ÃÊÌi]Ê-+Ê-iÀÛiÀÊëiÌÊÎÓÊÃÊvÊ *1ÊÌiÊ>`Ê>ÊÌÌ>ÊvÊÎÊÃÊ«>ÀÃ}Ê>`ÊV«}Ê the query.
Note You should not run @>??BNAALNK??=?DA on your production systems unless you are prepared to incur the to your system as a reboot.
STATISTICS IO ÃÊ`ÃVÕÃÃi`ÊÊÌ
iʺ`iÌvÞ}Ê ÃÌÞÊ+ÕiÀiûÊÃiVÌÊi>ÀiÀÊÊÌ
iÊV
>«ÌiÀ]ÊÌ
iÊÕLiÀÊvÊ reads in the Na]`o column is frequently the most significant cost factor among @qn]pekj, ?LQ, Na]`o, and Snepao. The total number of reads performed by a query consists of the sum of the number of reads performed on all tables involved in the query. The reads performed on the individual tables may vary significantly, depending on the size of the result set requested from the individual table and the indexes available. To reduce the total number of reads, it will be useful to find all the tables accessed in the query and their corresponding number of reads. This detailed information helps you concen trate on optimizing data access on the tables with a large number of reads. The number of Ài>`ÃÊ«iÀÊÌ>LiÊ>ÃÊ
i«ÃÊÞÕÊiÛ>Õ>ÌiÊÌ
iÊ«>VÌÊvÊÌ
iÊ«Ìâ>ÌÊÃÌi«Ê«iiÌi`ÊvÀÊ iÊÌ>Li®ÊÊÌ
iÊÌ
iÀÊÌ>LiÃÊÀiviÀÀi`ÊÌÊÊÌ
iʵÕiÀÞ° In a simple query, you determine the individual tables accessed by taking a close look at the query. This becomes increasingly difficult the more complex the query becomes. In the case of a stored procedure, database views, or functions, it becomes more difficult to identify all the tables actually accessed by the optimizer. You can use OP=PEOPE?OEK to get this infor mation, irrespective of query complexity. To turn OP=PEOPE?OEK on, navigate to Query Query Options Ê`Û>Vi`Ê Set Statistics IO in Management Studio. You may also get this information programmatically as follows ekop]po*omhÊÊÌ
iÊ`Ü>`®\
CHAPTER 3
S Q L Q U E R Y P E R F O R M A N C E A N A LY S I S
OAPOP=PEOPE?OEKKJ CK OAHA?Pokd*=__kqjpJqi^an( ok`*HejaPkp]h( ok`*Kn`anMpu( ok`*QjepLne_a( l*J]ia BNKIO]hao*O]haoKn`anDa]`anokd FKEJO]hao*O]haoKn`an@ap]ehok` KJokd*O]haoKn`anE@9ok`*O]haoKn`anE@ FKEJLnk`q_pekj*Lnk`q_pl KJok`*Lnk`q_pE@9l*Lnk`q_pE@ SDANAok`*O]haoKn`anE`93-4127 CK OAPOP=PEOPE?OEKKBB CK If you run this query and look at the execution plan, it consists of three clustered index ÃiiÃÊÜÌ
ÊÌÜÊ«ÊðÊvÊÞÕÊÀiÛiÊÌ
iÊSDANA clause and run the query again, you get a ÃiÌÊvÊÃV>ÃÊ>`ÊÃiÊ
>Ã
ÊðÊ/
>̽ÃÊ>ÊÌiÀiÃÌ}Êv>VÌpLÕÌÊÞÕÊ`½ÌÊÜÊ
ÜÊÌÊ>vviVÌÃÊ the query cost! You can use OAPOP=PEOPE?OEK as shown previously to compare the cost of the µÕiÀÞÊÊÌiÀÃÊvÊ}V>ÊÀi>`îÊLiÌÜiiÊÌ
iÊÌÜÊ«ÀViÃÃ}ÊÃÌÀ>Ìi}iÃÊÕÃi`ÊLÞÊÌ
iÊ«ÌâiÀ° You get following OP=PEOPE?OEKÊÕÌ«ÕÌÊÜ
iÊÌ
iʵÕiÀÞÊÕÃiÃÊÌ
iÊ
>Ã
Ê\ P]^ha#Skngp]^ha#*O_]j_kqjp,(hkce_]hna]`o,(lduoe_]hna]`o,Å P]^ha#O]haoKn`an@ap]eh#*O_]j_kqjp-(hkce_]hna]`o-.0,(lduoe_]hna]`o,Å P]^ha#O]haoKn`anDa]`an#*O_]j_kqjp-(hkce_]hna]`o242(lduoe_]hna]`o,Å P]^ha#Lnk`q_p#*O_]j_kqjp-(hkce_]hna]`o1(lduoe_]hna]`o,Å Now when you add the SDANA clause to appropriately filter the data, the resultant OP=PEOPE?OEK output turns out to be this: P]^ha#Lnk`q_p#*O_]j_kqjp,(hkce_]hna]`o0(lduoe_]hna]`o,Å P]^ha#O]haoKn`an@ap]eh#*O_]j_kqjp-(hkce_]hna]`o/(lduoe_]hna]`o,Å P]^ha#O]haoKn`anDa]`an#*O_]j_kqjp,(hkce_]hna]`o/(lduoe_]hna]`o,Å Logical reads for the O]haoKn`an@ap]ehÊÌ>LiÊ
>ÛiÊLiiÊVÕÌÊvÀÊ£]Ó{äÊÌÊÎÊLiV>ÕÃiÊvÊÌ
iÊ `iÝÊÃiiÊ>`ÊÌ
iÊ«Ê°ÊÌÊ>ÃÊ
>ýÌÊÃ}vV>ÌÞÊ>vviVÌi`ÊÌ
iÊ`>Ì>ÊÀiÌÀiÛ>ÊVÃÌÊvÊÌ
iÊ Lnk`q_p table. While interpreting the output of OP=PEOPE?OEK, you mostly refer to the number of logical reads. Sometimes you also refer to the scan count, but even if you perform few logical reads per scan, the total number of logical reads provided by OP=PEOPE?OEK can still be high. If the number of logical reads per scan is small for a specific table, then you may not be able to improve the indexing mechanism of the table any further. The number of physical reads and Ài>`>
i>`ÊÀi>`ÃÊÜÊLiÊâiÀÊÜ
iÊÌ
iÊ`>Ì>ÊÃÊÌÊvÕ`ÊÊÌ
iÊiÀÞ]ÊLÕÌÊViÊÌ
iÊ`>Ì>Ê ÃÊ««Õ>Ìi`ÊÊiÀÞ]ÊÌ
iʫ
ÞÃV>ÊÀi>`ÃÊ>`ÊÀi>`>
i>`ÊÀi>`ÃÊÜÊÌi`ÊÌÊLiÊâiÀ°
99
100
C HAPTER 3
SQ L QU ER Y P ER FOR MA NC E A NA L YS IS
There is another advantage to knowing all the tables used and their corresponding reads for a query. Both the @qn]pekj and ?LQ values may fluctuate significantly when reexecuting the Ã>iʵÕiÀÞÊÜÌ
ÊÊV
>}iÊÊÌ>LiÊÃV
i>ÊVÕ`}Ê`iÝiîÊÀÊ`>Ì>ÊLiV>ÕÃiÊÌ
iÊiÃÃiÌ>Ê services and background applications running on the SQL Server machine usually affect the processing time of the query under observation. ÕÀ}Ê«Ìâ>ÌÊÃÌi«Ã]ÊÞÕÊii`Ê>ÊvÕVÌÕ>Ì}ÊVÃÌÊv}ÕÀiÊ>ÃÊ>ÊÀiviÀiVi°Ê/
iÊÀi>`ÃÊ ÀÊ}V>ÊÀi>`îÊ`½ÌÊÛ>ÀÞÊLiÌÜiiÊÕÌ«iÊiÝiVÕÌÃÊvÊ>ʵÕiÀÞÊÜÌ
Ê>ÊvÝi`ÊÌ>LiÊÃV
i>Ê and data. For example, if you execute the previous OAHA?P statement ten times, you will prob ably get ten different figures for @qn]pekj and ?LQ, but Na]`o will remain the same each time. Therefore, during optimization, you can refer to the number of reads for an individual table to ensure that you really have reduced the data access cost of the table. Even though the number of logical reads can also be obtained from Profiler or the Server Trace option, you get another benefit when using OP=PEOPE?OEK. The number of logical reads for a query shown by Profiler or the Server Trace option increases as you use different OAP ÃÌ>ÌiiÌÃÊiÌi`Ê«ÀiÛÕÃÞ®Ê>}ÊÜÌ
ÊÌ
iʵÕiÀÞ°Ê ÕÌÊÌ
iÊÕLiÀÊvÊ}V>ÊÀi>`ÃÊ shown by OP=PEOPE?OEK doesn’t include the additional pages that are accessed as OAP state ments are used with a query. Thus, OP=PEOPE?OEK provides a consistent figure for the number of logical reads.
Summary In this chapter, you saw that you can use the Profiler tool or SQL tracing to identify the que ÀiÃÊV>ÕÃ}Ê>Ê
}
Ê>ÕÌÊvÊÃÌÀiÃÃÊÊÌ
iÊÃÞÃÌiÊÀiÃÕÀViÃÊÊ>Ê-+ÊÜÀ>`°Ê iVÌ}Ê the trace data can, and should be, automated using system stored procedures. For immediate access to statistics about running queries, use theÊ 6Êouo*`i[ata_[mqanu[op]po. You can further analyze these queries with Management Studio to find the costly steps in the process ing strategy of the query. For better performance, it is important to consider both the index >`ÊÊiV
>ÃÃÊÕÃi`ÊÊ>ÊiÝiVÕÌÊ«>ÊÜ
iÊ>>Þâ}Ê>ʵÕiÀÞ°Ê/
iÊÕLiÀÊvÊ`>Ì>Ê ÀiÌÀiÛ>ÃÊÀÊÀi>`îÊvÀÊÌ
iÊ`Û`Õ>ÊÌ>LiÃÊ«ÀÛ`i`ÊLÞÊOAPOP=PEOPE?OEK helps concentrate on the data access mechanism of the tables with most number of reads. You also should focus ÊÌ
iÊ *1ÊVÃÌÊ>`ÊÛiÀ>ÊÌiÊvÊÌ
iÊÃÌÊVÃÌÞʵÕiÀið Once you identify a costly query and finish the initial analysis, the next step should be to optimize the query for performance. Because indexing is one of the most commonly used «iÀvÀ>ViÌÕ}ÊÌiV
µÕiÃ]ÊÊÌ
iÊiÝÌÊV
>«ÌiÀÊÊÜÊ`ÃVÕÃÃÊÊ`i«Ì
ÊÌ
iÊÛ>ÀÕÃÊ`iÝ ing mechanisms available in SQL Server.
CHAPT ER
4
Index Analysis T
he right index on the right column, or columns, is the basis on which query tuning begins. On the other hand, a missing index or an index placed on the wrong column, or columns, can be the basis for all performance problems starting with basic data access, continuing through joins, and ending in filtering clauses. For these reasons, it is extremely important for everyone—not just the DBA—to understand the different indexing techniques that can be used to optimize the database design. In this chapter, I cover the following topics: Ê
UÊ 7
>ÌÊ>Ê`iÝÊÃ
Ê
UÊ /
iÊLiivÌÃÊ>`ÊÛiÀ
i>`ÊvÊ>Ê`iÝ
Ê
UÊ iiÀ>ÊÀiVi`>ÌÃÊvÀÊ`iÝÊ`iÃ}
Ê
UÊ ÕÃÌiÀi`Ê>`ÊVÕÃÌiÀi`Ê`iÝÊLi
>ÛÀÊ>`ÊV«>ÀÃÃ
Ê
UÊ ,iVi`>ÌÃÊvÀÊVÕÃÌiÀi`Ê>`ÊVÕÃÌiÀi`Ê`iÝiÃ
Ê
UÊ `Û>Vi`Ê`iÝ}ÊÌiV
µÕiÃ\ÊVÛiÀ}Ê`iÝiÃ]Ê`iÝÊÌiÀÃiVÌÃ]Ê`iÝÊÃ]Ê filtered indexes, distributed indexes, indexed views, and index compression
Ê
UÊ -«iV>Ê`iÝÊÌÞ«iÃ
Ê
UÊ ``Ì>ÊV
>À>VÌiÀÃÌVÃÊvÊ`iÝiÃ
What Is an Index? One of the best ways to reduce disk I/O and logical reads is to use an index. An index allows -+Ê-iÀÛiÀÊÌÊv`Ê`>Ì>ÊÊ>ÊÌ>LiÊÜÌ
ÕÌÊÃV>}ÊÌ
iÊiÌÀiÊÌ>Li°ÊÊ`iÝÊÊ>Ê`>Ì>L>ÃiÊÃÊ >>}ÕÃÊÌÊ>Ê`iÝÊÊ>ÊL°Ê->Þ]ÊvÀÊiÝ>«i]ÊÌ
>ÌÊÞÕÊÜ>Ìi`ÊÌÊÊÕ«ÊÌ
iʫ
À>ÃiÊtable scanÊÊÌ
ÃÊL°Ê7Ì
ÕÌÊÌ
iÊ`iÝÊ>ÌÊÌ
iÊL>VÊvÊÌ
iÊL]ÊÞÕÊÜÕ`Ê
>ÛiÊÌÊ«iÀÕÃiÊÌ
iÊ iÌÀiÊLÊÌÊv`ÊÌ
iÊÌiÝÌÊÞÕÊii`i`°Ê7Ì
ÊÌ
iÊ`iÝ]ÊÞÕÊÜÊiÝ>VÌÞÊÜ
iÀiÊÌ
iÊvÀ>tion you want is stored.
101
102
C HAPTER 4
IND EX A NA L YS IS
7
iÊÌÕ}Ê>Ê`>Ì>L>ÃiÊvÀÊ«iÀvÀ>Vi]ÊÞÕÊVÀi>ÌiÊ`iÝiÃÊÊÌ
iÊ`vviÀiÌÊVÕÃÊ ÕÃi`ÊÊ>ʵÕiÀÞÊÌÊ
i«Ê-+Ê-iÀÛiÀÊv`Ê`>Ì>ʵÕVÞ°ÊÀÊiÝ>«i]ÊÌ
iÊvÜ}ʵÕiÀÞÊ>}>ÃÌÊ the Lnk`q_pekj*Lnk`q_p table results in the data shown in Figure 4-1 (the first six of 500+ rows): OAHA?Pl*Lnk`q_pE@( l*WJ]iaY( l*Op]j`]n`?kop( l*WSaecdpY( NKS[JQI>AN$%KRAN$KN@AN>Ul*J]ia@AO?%=ONksJqi^an BNKILnk`q_pekj*Lnk`q_pl7
Figure 4-1. Sample Lnk`q_pekj*Lnk`q_p table If you need to retrieve all the products where Op]j`]n`?kop is greater than 150, without an index the table will have to be scanned, checking the value of Op]j`]n`?kop at each row to determine which rows contain a value greater than 150. An index on the Op]j`]n`?kop column could speed up this process by providing a mechanism that allows a structured search against the data rather than a row-by-row check. You can take two different, and fundamental, approaches for creating this index: Ê
UÊ Like a dictionary: A dictionary is a distinct listing of words in alphabetical order. An `iÝÊV>ÊLiÊÃÌÀi`ÊÊ>ÊÃ>ÀÊv>Ã
°Ê/
iÊ`>Ì>ÊÃÊÀ`iÀi`]Ê>Ì
Õ}
ÊÌÊÜÊÃÌÊ
>ÛiÊ `Õ«V>ÌiðÊ/
iÊvÀÃÌÊÃÝÊÀÜÃ]ÊÀ`iÀi`ÊLÞÊOp]j`]n`?kop instead of by J]ia, would look like the data shown in Figure 4-2. Notice the NksJqi^an column shows the original placement of the row when ordering by J]ia.
Figure 4-2. Product table sorted on Op]j`]n`?kop Ê
Ê -ÊÜÊvÊÞÕÊÜ>Ìi`ÊÌÊv`Ê>ÊÌ
iÊÀÜÃÊÜ
iÀiÊOp]j`]n`?kop is greater than 150, the index would allow you to find them immediately by moving down to the first value greater than 150. An index that orders the data stored based on the index order is known as a clustered index°Ê iV>ÕÃiÊvÊ
ÜÊ-+Ê-iÀÛiÀÊÃÌÀiÃÊ`>Ì>]ÊÌ
ÃÊÃÊiÊvÊÌ
iÊ most important indexes in your database design. I explain this in detail later in the chapter.
CHAPTER 4
Ê
I N D E X A N A LY S I S
UÊ Like a book’s index: An ordered list can be created without altering the layout of the table, similar to the way the index of a book is created. Just like the keyword index of a book lists the keywords in a separate section with a page number to refer to the main content of the book, the list of Op]j`]n`?kop values is created as a separate structure and refers to the corresponding row in the Lnk`q_p table through a pointer. For the example, I’ll use NksJqi^anÊ>ÃÊÌ
iÊ«ÌiÀ°Ê/>LiÊ{£ÊÃ
ÜÃÊÌ
iÊÃÌÀÕVÌÕÀiÊvÊÌ
iÊ>Õfacturer index. Table 4-1. Structure of the Manufacturer Index
Ê
StandardCost
RowNumber
,*4121
365
-*05./
375
-*422/
327
-*422/
498
-*422/
474
-+Ê-iÀÛiÀÊV>ÊÃV>ÊÌ
iÊ>Õv>VÌÕÀiÀÊ`iÝÊÌÊv`ÊÀÜÃÊÜ
iÀiÊOp]j`]n`?kop is greater Ì
>Ê£xä°Ê-ViÊÌ
iÊOp]j`]n`?kopÊÛ>ÕiÃÊ>ÀiÊ>ÀÀ>}i`ÊÊ>ÊÃÀÌi`ÊÀ`iÀ]Ê-+Ê-iÀÛiÀÊV>Ê ÃÌ«ÊÃV>}Ê>ÃÊÃÊ>ÃÊÌÊiVÕÌiÀÃÊÌ
iÊÀÜÊÜÌ
Ê>ÊÛ>ÕiÊvÊ£xä°Ê/
ÃÊÌÞ«iÊvÊ`iÝÊÃÊ called a nonclustered index, and I explain it in detail later in the chapter.
ÊiÌ
iÀÊV>Ãi]Ê-+Ê-iÀÛiÀÊÜÊLiÊ>LiÊÌÊv`Ê>ÊÌ
iÊ«À`ÕVÌÃÊÜ
iÀiÊOp]j`]n`?kop is greater than 150 more quickly than without an index under most circumstances. You can create indexes on either a single column (as described previously) or a combi>ÌÊvÊVÕÃÊÊ>ÊÌ>Li°Ê-+Ê-iÀÛiÀÊ>ÕÌ>ÌV>ÞÊVÀi>ÌiÃÊ`iÝiÃÊvÀÊViÀÌ>ÊÌÞ«iÃÊvÊ constraints (for example, LNEI=NUGAU and QJEMQA constraints).
The Benefit of Indexes -+Ê-iÀÛiÀÊ
>ÃÊÌÊLiÊ>LiÊÌÊv`Ê`>Ì>]ÊiÛiÊÜ
iÊÊ`iÝÊÃÊ«ÀiÃiÌÊÊ>ÊÌ>Li°Ê7
iÊÊVÕÃtered index is present to establish a storage order for the data, the storage engine will simply read through the entire table to find what it needs. A table without a clustered index is called a heap table. A heap is just a crude stack of data with a row identifier as a pointer to the storage V>Ì°Ê/
ÃÊ`>Ì>ÊÃÊÌÊÀ`iÀi`ÊÀÊÃi>ÀV
>LiÊiÝVi«ÌÊLÞÊÜ>}ÊÌ
ÀÕ}
ÊÌ
iÊ`>Ì>]ÊÀÜLÞ row, in a process called a scan°Ê7
iÊ>ÊVÕÃÌiÀi`Ê`iÝÊÃÊ«>Vi`ÊÊ>ÊÌ>Li]ÊÌ
iÊiÞÊÛ>ÕiÃÊvÊ the index establish an order for the data. Further, with a clustered index, the data is stored with Ì
iÊ`iÝÊÃÊÌ
>ÌÊÌ
iÊ`>Ì>ÊÌÃivÊÃÊÜÊÀ`iÀi`°Ê7
iÊ>ÊVÕÃÌiÀi`Ê`iÝÊÃÊ«ÀiÃiÌ]ÊÌ
iÊ«ÌiÀÊ ÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊVÃÃÌÃÊvÊÌ
iÊÛ>ÕiÃÊÌ
>ÌÊ`iviÊÌ
iÊVÕÃÌiÀi`Ê`iÝ°Ê/
ÃÊÃÊ>ÊL}Ê part of what makes clustered indexes so important. -ViÊ>Ê«>}iÊ
>ÃÊ>ÊÌi`Ê>ÕÌÊvÊë>Vi]ÊÌÊV>ÊÃÌÀiÊ>Ê>À}iÀÊÕLiÀÊvÊÀÜÃÊvÊÌ
iÊ ÀÜÃÊVÌ>Ê>ÊviÜiÀÊÕLiÀÊvÊVÕðÊ/
iÊVÕÃÌiÀi`Ê`iÝÊusually doesn’t contain all Ì
iÊVÕÃÊvÊÌ
iÊÌ>LiÆÊÌÊÕÃÕ>ÞÊVÌ>ÃÊÞÊ>ÊÌi`ÊÕLiÀÊvÊÌ
iÊVÕðÊ/
iÀivÀi]Ê a page will be able to store more rows of a nonclustered index than rows of the table itself, Ü
V
ÊVÌ>ÃÊ>ÊÌ
iÊVÕÃ°Ê ÃiµÕiÌÞ]Ê-+Ê-iÀÛiÀÊÜÊLiÊ>LiÊÌÊÀi>`ÊÀiÊÛ>ÕiÃÊvÀÊ a column from a page representing a nonclustered index on the column than from a page representing the table that contains the column.
103
104
C HAPTER 4
IND EX A NA L YS IS
Another benefit of the nonclustered index is that, because it is in a separate structure from the data table, it can be put in a different filegroup, with a different I/O path, as explained in
>«ÌiÀÊÓ°Ê/
ÃÊi>ÃÊÌ
>ÌÊ-+Ê-iÀÛiÀÊV>Ê>VViÃÃÊÌ
iÊ`iÝÊ>`ÊÌ>LiÊVVÕÀÀiÌÞ]Ê>}Ê searches even faster. Indexes store their information in a B-tree structure, so the number of reads required ÌÊv`Ê>Ê«>ÀÌVÕ>ÀÊÀÜÊÃÊâi`°Ê/
iÊvÜ}ÊiÝ>«iÊÃ
ÜÃÊÌ
iÊLiivÌÊvÊ>Ê ÌÀiiÊ structure.
Ã`iÀÊ>ÊÃ}iVÕÊÌ>Li with 27 rows in a random order and only 3 rows per leaf «>}i°Ê-Õ««ÃiÊÌ
iÊ>ÞÕÌÊvÊÌ
iÊÀÜÃÊÊÌ
iÊ«>}iÃÊÃÊ>ÃÊÃ
ÜÊÊ}ÕÀiÊ{ΰ
Figure 4-3. Initial layout of 27 rows /ÊÃi>ÀV
ÊÌ
iÊÀÜÊÀÊÀÜîÊvÀÊÌ
iÊVÕÊÛ>ÕiÊvÊx]Ê-+Ê-iÀÛiÀÊ
>ÃÊÌÊÃV>Ê>ÊÌ
iÊÀÜÃÊ and the pages, since even the last row in the last page may have the value 5. Because the number of reads depends on the number of pages accessed, nine read operations have to be «iÀvÀi`ÊÜÌ
ÕÌÊ>Ê`iÝÊÊÌ
iÊVÕ°Ê/
ÃÊVÌiÌÊV>ÊLiÊÀ`iÀi`ÊLÞÊVÀi>Ì}Ê>Ê`iÝÊ on the column, with the resultant layout of the rows and pages shown in Figure 4-4.
Figure 4-4. Ordered layout of 27 rows `iÝ}ÊÌ
iÊVÕÊ>ÀÀ>}iÃÊÌ
iÊVÌiÌÊÊ>ÊÃÀÌi`Êv>Ã
°Ê/
ÃÊ>ÜÃÊ-+Ê-iÀÛiÀÊ to determine the possible value for a row position in the column with respect to the value of >Ì
iÀÊÀÜÊ«ÃÌÊÊÌ
iÊVÕ°ÊÀÊiÝ>«i]ÊÊ}ÕÀiÊ{{]ÊÜ
iÊ-+Ê-iÀÛiÀÊv`ÃÊÌ
iÊ first row with the column value 6, it can be sure that there are no more rows with the column Û>ÕiÊx°Ê/
ÕÃ]ÊÞÊÌÜÊÀi>`Ê«iÀ>ÌÃÊ>ÀiÊÀiµÕÀi`ÊÌÊviÌV
ÊÌ
iÊÀÜÃÊÜÌ
ÊÌ
iÊÛ>ÕiÊxÊÜ
iÊ the content is indexed. However, what happens if you want to search for the column value 25? /
ÃÊÜÊÀiµÕÀiÊiÊÀi>`Ê«iÀ>ÌÃtÊ/
ÃÊ«ÀLiÊÃÊÃÛi`ÊLÞÊ«iiÌ}Ê`iÝiÃÊÕÃ}Ê the B-tree structure. A B-tree consists of a starting node (or page) called a root node with branch nodes (or pages) growing out of it (or linked to it).ÊÊiÞÃÊ>ÀiÊÃÌÀi`ÊÊÌ
iÊi>ÛiÃ°Ê Ì>i`ÊÊi>V
Ê interior node (above the leaf nodes) are pointers to its branch nodes and values representing the smallest value found in the branch node. Keys are kept in sorted order within each node. B-trees use a balanced tree structure for efficient record retrieval—a B-tree is balanced when the leaf nodes are all at the same level from the root node. For example, creating an index on the preceding content will generate the balanced B-tree structure shown in Figure 4-5.
Figure 4-5. B-tree layout of 27 rows
CHAPTER 4
I N D E X A N A LY S I S
/
iÊ ÌÀiiÊ>}ÀÌ
minimizes the number of pages to be accessed to locate a desired key, thereby speeding up the data access process. For example, in Figure 4-5, the search for the iÞÊÛ>ÕiÊxÊÃÌ>ÀÌÃÊ>ÌÊÌ
iÊÌ«ÊÀÌÊ`i°Ê-ViÊÌ
iÊiÞÊÛ>ÕiÊÃÊLiÌÜiiÊ£Ê>`Ê£ä]ÊÌ
iÊÃi>ÀV
Ê«Àcess follows the left branch to the next node. As the key value 5 falls between the values 4 and 7, the search process follows the middle branch to the next node with the starting key value vÊ{°Ê/
iÊÃi>ÀV
Ê«ÀViÃÃÊÀiÌÀiÛiÃÊÌ
iÊiÞÊÛ>ÕiÊxÊvÀÊÌ
ÃÊi>vÊ«>}i°ÊvÊÌ
iÊiÞÊÛ>ÕiÊxÊ`iýÌÊ iÝÃÌÊÊÌ
ÃÊ«>}i]ÊÌ
iÊÃi>ÀV
Ê«ÀViÃÃÊÜÊÃÌ«ÊÃViÊ̽ÃÊÌ
iÊi>vÊ«>}i°Ê->ÀÞ]ÊÌ
iÊiÞÊÛ>ÕiÊÓxÊ can also be searched using the same number of reads.
Index Overhead /
iÊ«iÀvÀ>ViÊLiivÌÊvÊ`iÝiÃÊ`iÃÊViÊ>ÌÊ>ÊVÃÌ°Ê/>LiÃÊÜÌ
Ê`iÝiÃÊÀiµÕÀiÊÀiÊ storage and memory space for the index pages in addition to the data pages of the table. Data manipulation queries (EJOANP, QL@=PA, and @AHAPAÊÃÌ>ÌiiÌÃ]ÊÀÊÌ
iÊ 1 Ê«>ÀÌÊvÊVÀi>Ìi]ÊÀi>`]Ê Õ«`>Ìi]Ê`iiÌiÊQ ,1 R®ÊV>ÊÌ>iÊ}iÀ]Êand more processing time is required to maintain Ì
iÊ`iÝiÃÊvÊVÃÌ>ÌÞÊV
>}}ÊÌ>LiðÊ/
ÃÊÃÊLiV>ÕÃi]ÊÕiÊ>ÊOAHA?P statement, data manipulation queries modify the data content of a table. If an EJOANP statement adds a row to the table, then it also has to add a row in the index structure. If the index is a clustered index, the overhead is greater still, because the row has to be added to the data pages themselves in the right order, which may require other data rows to be repositioned below the entry position vÊÌ
iÊiÜÊÀÜ°Ê/
iÊQL@=PA and @AHAPA data manipulation queries change the index pages in a similar manner. 7
iÊ`iÃ}}Ê`iÝiÃ]ÊÞÕ½ÊLiÊ«iÀ>Ì}ÊvÀÊÌÜÊ`vviÀiÌÊ«ÌÃÊvÊÛiÜ\ÊÌ
iÊiÝÃÌing system, already in production, where you need to measure the overall impact of an index, and the tactical approach where all you worry about is the immediate benefits of an index, ÕÃÕ>ÞÊÜ
iÊÌ>ÞÊ`iÃ}}Ê>ÊÃÞÃÌi°Ê7
iÊÞÕÊ
>ÛiÊÌÊ`i>ÊÜÌ
ÊÌ
iÊiÝÃÌ}ÊÃÞÃÌi]Ê you should ensure that the performance benefits of an index outweigh the extra cost in proViÃÃ}ÊÀiÃÕÀViðÊ9ÕÊV>Ê`ÊÌ
ÃÊLÞÊÕÃ}ÊÌ
iÊ*ÀviÀÊÌÊiÝ«>i`ÊÊ
>«ÌiÀÊήÊÌÊ`Ê>Ê ÛiÀ>ÊÜÀ>`Ê«Ìâ>ÌÊiÝ«>i`ÊÊ
>«ÌiÀʣȮ°Ê7
iÊÞÕ½ÀiÊvVÕÃi`ÊiÝVÕÃÛiÞÊ ÊÌ
iÊi`>ÌiÊLiivÌÃÊvÊ>Ê`iÝ]Ê-+Ê-iÀÛiÀÊÃÕ««iÃÊ>ÊÃiÀiÃÊvÊ`Þ>VÊ>>}iiÌÊ views that provide detailed information about the performance of indexes, ouo*`i[`^[ej`at[ klan]pekj]h[op]po or ouo*`i[`^[ej`at[qo]ca[op]po°Ê/
iÊÛiÜÊouo*`i[`^[ej`at[klan]pekj]h op]poÊÃ
ÜÃÊÌ
iÊÜiÛiÊ>VÌÛÌÞ]ÊÃÕV
Ê>ÃÊVÃÊ>`ÊÉ"]ÊÊ>Ê`iÝÊÌ
>ÌÊÃÊÊÕÃi°Ê/
iÊÛiÜÊ ouo*`i[`^[ej`at[qo]ca[op]po returns statistical counts of the various index operations that
>ÛiÊVVÕÀÀi`ÊÌÊ>Ê`iÝÊÛiÀÊÌi°Ê Ì
ÊvÊÌ
iÃiÊÜÊLiÊÕÃi`ÊÀiÊiÝÌiÃÛiÞÊÊ
>«ÌiÀÊ£ÓÊ when I discuss blocking. /ÊÕ`iÀÃÌ>`ÊÌ
iÊÛiÀ
i>`ÊVÃÌÊvÊ>Ê`iÝÊÊ`>Ì>Ê>«Õ>ÌʵÕiÀiÃ]ÊVÃ`iÀÊ the following example. First, create a test table with 10,000 rows (_na]pa[paop*omh in the download): EB$OAHA?PK>FA?P[E@$#p-#% %EOJKPJQHH @NKLP=>HA`^k*p-7 CK ?NA=PAP=>HA`^k*p-$_-EJP(_.EJP(_/?D=N$1,%%7 OAHA?PPKL-,,,, E@AJPEPU$EJP(-(-%=Oj EJPKJqio
105
106
C HAPTER 4
IND EX A NA L YS IS
BNKII]opan*`^k*Ouo?khqijoo_(I]opan*`^k*Ouo?khqijoo_.7 EJOANPEJPK`^k*p-$_-(_.(_/% OAHA?Pj (j (#?/# BNKIJqio7 @NKLP=>HAJqio7 If you then run an QL@=PA statement, like so: QL@=PA`^k*pOAP_-9-(_.9SDANA_.9-7 the number of logical reads reported by OAPOP=PEOPE?OEK is as follows: P]^ha#p-#*O_]j_kqjp-(hkce_]hna]`o43 After adding an index on column _-, like so: ?NA=PA?HQOPANA@EJ@ATe-KJp-$_-% the resultant number of logical reads for the same QL@=PA statement increases from 87 to 95: P]^ha#p-#*O_]j_kqjp-(hkce_]hna]`o51 Even though it is true that the amount of overhead required to maintain indexes increases forÊ`>Ì>Ê>«Õ>ÌʵÕiÀiÃ]ÊLiÊ>Ü>ÀiÊÌ
>ÌÊ-+Ê-iÀÛiÀÊÕÃÌÊvÀÃÌÊv`Ê>ÊÀÜÊLivÀiÊÌÊV>Ê update or delete it; therefore, indexes can be helpful for QL@=PA and @AHAPA statements with complex SDANAÊV>ÕÃiÃÊ>ÃÊÜi°Ê/
iÊVÀi>Ãi`ÊivvViVÞÊÊÕÃ}ÊÌ
iÊ`iÝÊÌÊV>ÌiÊ>ÊÀÜÊ usually offsets the extra overhead needed to update the indexes, unless the table has a lot of indexes. /ÊÕ`iÀÃÌ>`Ê
ÜÊ>Ê`iÝÊV>ÊLiivÌÊiÛiÊ`>Ì>Ê`vV>ÌʵÕiÀiÃ]Êi̽ÃÊLÕ`ÊÊÌ
iÊ iÝ>«i°Ê Ài>ÌiÊ>Ì
iÀÊ`iÝÊÊÌ>LiÊp-°Ê/
ÃÊÌi]ÊVÀi>ÌiÊÌ
iÊ`iÝÊÊVÕÊ_. referred to in the SDANA clause of the QL@=PA statement: ?NA=PAEJ@ATe.KJp-$_.%7 After adding this new index, run the QL@=PA command again: QL@=PAp-OAP_-9-(_.9-SDANA_.9-7 the total number of logical reads for this QL@=PA statement decreases from 95 to 20 (= 15 + 5): P]^ha#p-#*O_]j_kqjp-(hkce_]hna]`o-1 P]^ha#Skngp]^ha#*O_]j_kqjp-(hkce_]hna]`o1
CHAPTER 4
I N D E X A N A LY S I S
Note A worktable is a temporary table used internally by SQL Server to process the intermediate results of a query. Worktables are created in the pail`^ database and are dropped automatically after query execution. The examples in this section have demonstrated that although having an index adds some overhead cost to action queries, the overall result is a decrease in cost because of the beneficial effect of indexes on searching.
Index Design Recommendations /
i main recommendations for index design are as follows: Ê
UÊ Ý>iÊÌ
iÊSDANA clause and join criteria columns.
Ê
UÊ 1ÃiÊ>ÀÀÜÊ`iÝið
Ê
UÊ Ý>iÊVÕÊÕµÕiiÃð
Ê
UÊ Ý>iÊÌ
iÊVÕÊ`>Ì>ÊÌÞ«i°
Ê
UÊ Ã`iÀÊVÕÊÀ`iÀ°
Ê
UÊ Ã`iÀÊÌ
iÊÌÞ«iÊvÊ`iÝÊVÕÃÌiÀi`ÊÛðÊVÕÃÌiÀi`®° i̽ÃÊVÃ`iÀÊi>V
ÊvÊÌ
iÃiÊÀiVi`>ÌÃÊÊÌÕÀ°
Examine the WHERE Clause and Join Criteria Columns 7
iÊ>ʵÕiÀÞÊÃÊÃÕLÌÌi`ÊÌÊ-+Ê-iÀÛiÀ]ÊÌ
iʵÕiÀÞÊ«ÌâiÀÊÌÀiÃÊÌÊv`ÊÌ
iÊLiÃÌÊ`>Ì>Ê>VViÃÃÊ mechanism for every table referred to in the query. Here is how it does this: 1. /
iÊ«ÌâiÀÊ`iÌviÃÊÌ
iÊVÕÃÊVÕ`i`ÊÊÌ
iÊSDANA clause and the join criteria. 2. /
iÊ«ÌâiÀÊÌ
iÊiÝ>iÃÊ`iÝiÃÊÊÌ
ÃiÊVÕð 3. /
iÊ«ÌâiÀÊ>ÃÃiÃÃiÃÊÌ
iÊÕÃivÕiÃÃÊvÊi>V
Ê`iÝÊLÞÊ`iÌiÀ}ÊÌ
iÊÃiiVÌÛÌÞÊvÊ the clause (that is, how many rows will be returned) from statistics maintained on the index. 4. Finally, the optimizer estimates the least costly method of retrieving the qualifying rows, based on the information gathered in the previous steps.
Note Chapter 7 covers statistics in more depth.
/ÊÕ`iÀÃÌ>`ÊÌ
iÊÃ}vV>ViÊvÊ>ÊSDANA clause column in a query, let’s consider an iÝ>«i°Êi̽ÃÊÀiÌÕÀÊÌÊÌ
iÊÀ}>ÊV`iÊÃÌ}ÊÌ
>ÌÊ
i«i`ÊÞÕÊÕ`iÀÃÌ>`ÊÜ
>ÌÊ>Ê`iÝÊÃÆÊ the query consisted of a OAHA?P statement without any SDANA clause, as follows:
107
108
C HAPTER 4
IND EX A NA L YS IS
OAHA?Pl*Lnk`q_pE@( l*WJ]iaY( l*Op]j`]n`?kop( l*WSaecdpY BNKILnk`q_pekj*Lnk`q_pl7 /
iʵÕiÀÞÊ«ÌâiÀÊ«iÀvÀÃÊ>ÊVÕÃÌiÀi`Ê`iÝÊÃV>]ÊÌ
iÊiµÕÛ>iÌÊvÊ>ÊÌ>LiÊÃV>Ê against a heap on a table that has a clustered index, to read the rows as shown in Figure 4-6 ÃÜÌV
ÊÊÌ
iÊ-
ÜÊ ÝiVÕÌÊ*>Ê«ÌÊLÞÊÕÃ}Ê+ÕiÀÞÊ>ÞâiÀ½ÃÊ+ÕiÀÞÊiÕ]Ê>ÃÊÜiÊ>ÃÊ Ì
iÊ-iÌÊ-Ì>ÌÃÌVÃÊ"Ê«ÌÊLÞÊÕÃ}Ê+ÕiÀÞÊ>ÞâiÀ½ÃÊ/ÃÊ Options menu).
Figure 4-6. Execution plan with no SDANA clause /
iÊÕLiÀÊvÊ}V>ÊÀi>`ÃÊÀi«ÀÌi`ÊLÞÊOAPOP=PEOPE?OEK for the OAHA?P statement is as follows: P]^ha#Lnk`q_p#*O_]j_kqjp-(hkce_]hna]`o-1 /ÊÕ`iÀÃÌ>`ÊÌ
iÊivviVÌÊvÊ>ÊSDANA clause column on the query optimizer’s decision, let’s add a SDANA clause to retrieve a single row: OAHA?Pl*Lnk`q_pE@( l*WJ]iaY( l*Op]j`]n`?kop( l*WSaecdpY BNKILnk`q_pekj*Lnk`q_pl SDANAl*Lnk`q_pE@93/47 7Ì
ÊÌ
iÊSDANA clause in place, the query optimizer examines the SDANA clause column Lnk`q_pE@, identifies the availability of index LG[Lnk`q_p[Lnk`q_pE` on column Lnk`q_pE`, assesses a high selectivity (that is, only one row will be returned) for the SDANA clause from the statistics on index LG[Lnk`q_p[Lnk`q_pE`, and decides to use that index on column Lnk`q_pE`, as shown in Figure 4-7.
Figure 4-7. Execution plan with a SDANA clause /
iÊÀiÃÕÌ>ÌÊÕLiÀÊvÊ}V>ÊÀi>`ÃÊÃÊ>ÃÊvÜÃ\ P]^ha#Lnk`q_p#*O_]j_kqjp,(hkce_]hna]`o.
CHAPTER 4
I N D E X A N A LY S I S
/
iÊLi
>ÛÀÊvÊÌ
iÊquery optimizer shows that the SDANA clause column helps the optiâiÀÊV
ÃiÊ>Ê«Ì>Ê`iÝ}Ê«iÀ>ÌÊvÀÊ>ʵÕiÀÞ°Ê/
ÃÊÃÊ>ÃÊ>««V>LiÊvÀÊ>ÊVÕÊ ÕÃi`ÊÊÌ
iÊÊVÀÌiÀ>ÊLiÌÜiiÊÌÜÊÌ>LiðÊ/
iÊ«ÌâiÀÊÃÊvÀÊÌ
iÊ`iÝiÃÊÊÌ
iÊSDANA clause column or the join criterion column and, if available, considers using the index to ÀiÌÀiÛiÊÌ
iÊÀÜÃÊvÀÊÌ
iÊÌ>Li°Ê/
iʵÕiÀÞÊ«ÌâiÀÊVÃ`iÀÃÊ`iÝiîÊÊÌ
iÊSDANA clause VÕîÊ>`ÊÌ
iÊÊVÀÌiÀ>ÊVÕîÊÜ
iÊiÝiVÕÌ}Ê>ʵÕiÀÞ°Ê/
iÀivÀi]Ê
>Û}Ê`iÝiÃÊ on the frequently used columns in the SDANAÊV>ÕÃiÊ>`ÊÌ
iÊÊVÀÌiÀ>ÊvÊ>Ê-+ʵÕiÀÞÊ
i«ÃÊ the optimizer avoid scanning a base table. 7
iÊÌ
iÊ>ÕÌÊvÊ`>Ì>ÊÃ`iÊ>ÊÌ>LiÊÃÊÃÊÃ>ÊÌ
>ÌÊÌÊvÌÃÊÌÊ>ÊÃ}iÊ«>}iÊn ®]Ê>Ê table scan may work better than an index seek. If you have a good index in place but you’re still getting a scan, consider this issue.
Use Narrow Indexes You can create indexes on a combination of columns in a table. For the best performance, use as few columns in an index as you can. You should also avoid very wide data type columns in >Ê`iÝ°Ê ÕÃÊÜÌ
string data types (?D=N, R=N?D=N, J?D=N, and JR=N?D=N) sometimes can be quite wide as can binary; unless they are absolutely necessary, minimize the use of wide data type columns with large sizes in an index. A narrow index can accommodate more rows in an 8KB index page than a wide index. /
ÃÊ
>ÃÊÌ
iÊvÜ}ÊivviVÌÃ\ Ê
UÊ ,i`ÕViÃÊÉ"ÊLÞÊ
>Û}ÊÌÊÀi>`ÊviÜiÀÊn Ê«>}iî
Ê
UÊ >iÃÊ`>Ì>L>ÃiÊV>V
}ÊÀiÊivviVÌÛi]ÊLiV>ÕÃiÊ-+Ê-iÀÛiÀÊV>ÊV>V
iÊviÜiÀÊ`iÝÊ pages, consequently reducing the logical reads required for the index pages in the memory
Ê
UÊ ,i`ÕViÃÊÌ
iÊÃÌÀ>}iÊë>ViÊvÀÊÌ
iÊ`>Ì>L>Ãi
/ÊÕ`iÀÃÌ>`Ê
ÜÊ>Ê>ÀÀÜÊ`iÝÊV>ÊÀi`ÕViÊÌ
iÊÕLiÀÊvÊ}V>ÊÀi>`Ã]ÊVÀi>ÌiÊ>ÊÌiÃÌÊ table with 20 rows and an index (j]nnksE@T[p-*omh in the download): EB$OAHA?PK>FA?P[E@$#p-#%%EOJKPJQHH @NKLP=>HA`^k*pCK ?NA=PAP=>HA`^k*p-$_-EJP(_.EJP%7 SEPDJqio =O$OAHA?P-=Oj QJEKJ=HH OAHA?Pj'BNKIJqio SDANAj8., % EJOANPEJPKp$_-(_.% OAHA?Pj(. BNKIJqio ?NA=PAEJ@ATe-KJp-$_-%
109
110
C HAPTER 4
IND EX A NA L YS IS
-ViÊÌ
iÊ`iÝi`ÊVÕÊÃÊ>ÀÀÜÊÌ
iÊEJP data type is 4 bytes), all the index rows can be accommodated in one 8KB index page. As shown in Figure 4-8, you can confirm this in the dynamic management views associated with indexes (^]oa[ej`t*omh in the download): OAHA?Pe*J]ia (e*pula[`ao_ (o*l]ca[_kqjp (o*na_kn`[_kqjp (o*ej`at[harah BNKIouo*ej`ataoe FKEJouo*`i[`^[ej`at[lduoe_]h[op]po$@>[E@$J#=`rajpqnaSkngo.,,4#%( K>FA?P[E@$J#`^k*P-#%( JQHH(JQHH(#@AP=EHA@#%=Oo KJe*ej`at[e`9o*ej`at[e` SDANAe*K>FA?P[E@9K>FA?P[E@$J#`^k*P-#%
Figure 4-8. Number of pages for a narrow, nonclustered index /
iÊouo*ej`atao system table is stored in each database and contains the basic informaÌÊÊiÛiÀÞÊ`iÝÊÊÌ
iÊ`>Ì>L>Ãi°Ê/
iÊ`Þ>VÊ>>}iiÌÊvÕVÌ]Êouo*`i[`^[ej`at[ lduoe_]h[op]po, contains the more detailed information about the statistics on the index ÞÕ½Êi>ÀÊÀiÊ>LÕÌÊÌ
ÃÊ ÊÊ
>«ÌiÀÊÇ®°Ê/ÊÕ`iÀÃÌ>`ÊÌ
iÊ`Ã>`Û>Ì>}iÊvÊ>ÊÜ`iÊ index key, modify the data type of the indexed column _- from EJP to ?D=N$1,,% (j]nnks[ ]hpan*omh in the download): @NKLEJ@ATp-*e=HPANP=>HAp-=HPAN?KHQIJ_-?D=N$1,,% ?NA=PAEJ@ATe-KJp-$_-% /
iÊÜ`Ì
ÊvÊ>ÊVÕÊÜÌ
ÊÌ
iÊEJP data type is 4 bytes, and the width of a column with the ?D=N$1,,% data type is 500 bytes. Because of the large width of the indexed column, two index pages are required to contain all 20 index rows. You can confirm this in the ouo*ej`atao system table by running ouoej`at[oaha_p*omh again (see Figure 4-9).
Figure 4-9. Number of pages for a wide, nonclustered index A large index key size increases the number of index pages, thereby increasing the amount of memory and disk activities required for the index. It is always recommended that the index key size is as narrow as possible.
CHAPTER 4
I N D E X A N A LY S I S
Drop the test table before continuing: @NKLP=>HA`^k*p-
Examine Column Uniqueness
Ài>Ì}Ê> index on columns with a very low range of possible values (such as gender) will not benefit performance, because the query optimizer will not be able to use the index to ivviVÌÛiÞÊ>ÀÀÜÊ`ÜÊÌ
iÊÀÜÃÊÌÊLiÊÀiÌÕÀi`°Ê Ã`iÀÊ>ÊCaj`an column with only two unique values: I and B°Ê7
iÊÞÕÊiÝiVÕÌiÊ>ʵÕiÀÞÊÜÌ
ÊÌ
iÊCaj`an column in the SDANA clause, you end up with a large number of rows from the table (assuming the distribution of I and B is even), resulting in a costly table or clustered index scan. It is always preferable to have columns in the SDANA clause with lots of unique rows (or high selectivity) to limit the number of rows accessed. You should create an index on those column(s) to help the optimizer access a small result set. Furthermore, while creating an index on multiple columns, which is also referred to as a composite index, column order matters. In some cases, using the most selective column first will help filter the index rows more efficiently.
Note The importance of column order in a composite index is explained later in the chapter in the “Consider Column Order” section.
From this, you can see that it is important to know the selectivity of a column before creating an index on it. You can find this by executing a query like this one; just substitute the table and column name: OAHA?P?KQJP$@EOPEJ?PCaj`an%=O@eopej_p?khR]hqao (?KQJP$Caj`an%=OJqi^anKbNkso ($?=OP$?KQJP$@EOPEJ?PCaj`an%=O@A?EI=H% +?=OP$?KQJP$Caj`an%=O@A?EI=H%%=OOaha_perepu BNKIDqi]jNaokqn_ao*Ailhkuaa /
iÊVÕÊÜÌ
ÊÌ
iÊ
}
iÃÌÊÕLiÀÊvÊÕµÕiÊÛ>ÕiÃÊÀÊÃiiVÌÛÌÞ®ÊV>ÊLiÊÌ
iÊLiÃÌÊV>didate for indexing when referred to in a SDANA clause or a join criterion. /ÊÕ`iÀÃÌ>`Ê
ÜÊÌ
iÊÃiiVÌÛÌÞÊvÊ>Ê`iÝÊiÞÊVÕÊ>vviVÌÃÊÌ
iÊÕÃiÊvÊÌ
iÊ`iÝ]Ê take a look at the Caj`an column in the Dqi]jNaokqn_ao*Ailhkuaa table. If you run the previous query, you’ll see that it contains only 2 distinct values in more than 290 rows, which is a selectivity of .006. A query to look only for a Caj`an of B would look like this: OAHA?P& BNKIDqi]jNaokqn_ao*Ailhkuaa SDANACaj`an9#B# =J@Oe_gHa]raDkqno915 =J@I]nep]hOp]pqo9#I#
111
112
C HAPTER 4
IND EX A NA L YS IS
/
ÃÊÀiÃÕÌÃÊÊÌ
iÊiÝiVÕÌÊ«>ÊÊ}ÕÀiÊ{£äÊ>`ÊÌ
iÊvÜ}ÊÉ"Ê>`Êi>«Ãi`ÊÌi\ P]^ha#Ailhkuaa#*O_]j_kqjp-(hkce_]hna]`o5 ?LQpeia9-2io(ah]loa`peia9-,/io*
Figure 4-10. Execution plan with no index /
iÊ`>Ì>ÊÃÊÀiÌÕÀi`ÊLÞÊÃV>}ÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊÜ
iÀiÊÌ
iÊ`>Ì>ÊÃÊÃÌÀi`®ÊÌÊv`ÊÌ
iÊ appropriate values where Caj`an9#B#°Ê/
iÊÌ
iÀÊ«iÀ>ÌÀÃÊÜÊLiÊVÛiÀi`ÊÊ
>«ÌiÀÊ°®ÊvÊ you were to place an index on the column, like so: ?NA=PAEJ@ATET[Ailhkuaa[PaopKJDqi]jNaokqn_ao*Ailhkuaa$Caj`an% >`ÊÀÕÊÌ
iʵÕiÀÞÊ>}>]ÊÌ
iÊiÝiVÕÌÊ«>ÊÀi>ÃÊÌ
iÊÃ>i°Ê/
iÊ`>Ì>ÊÃÊÕÃÌÊÌÊÃiiVÌÛiÊ enough for the index to be used, let alone be useful. If instead you use a composite index that looks like this: ?NA=PAEJ@ATET[Ailhkuaa[PaopKJ Dqi]jNaokqn_ao*Ailhkuaa$Oe_gHa]raDkqno(Caj`an(I]nep]hOp]pqo% SEPD$@NKL[ATEOPEJC9KJ% and then rerun the query to see the execution plan in Figure 4-11 and the performance results, you get this: P]^ha#Ailhkuaa#*O_]j_kqjp-(hkce_]hna]`o2 ?LQpeia9,io(ah]loa`peia9/.io*
Figure 4-11. Execution plan with a composite index Now you’re doing better than you were with the clustered index scan. A nice clean Ej`at OaagÊ«iÀ>ÌÊÌ>iÃÊ>LÕÌÊ
>vÊÌ
iÊÌiÊÌÊ}>Ì
iÀÊÌ
iÊ`>Ì>°Ê/
iÊÀiÃÌÊÃÊëiÌÊÊÌ
iÊGauHkkgql operation. A GauHkkgql operation is commonly referred to as a bookmark lookup.
Note You will learn more about bookmark lookups in Chapter 6.
CHAPTER 4
I N D E X A N A LY S I S
Although none of the columns in question would probably be selective enough on their own to make a decent index, together they provide enough selectivity for the optimizer to take advantage of the index offered. It is possible to attempt to force the query to use the first test index you created. If you drop the compound index, create the original again, and then modify the query as follows by using a query hint to force the use of the original index: OAHA?P& BNKIDqi]jNaokqn_ao*AilhkuaaSEPD$EJ@AT$ET[Ailhkuaa[Paop%% SDANAOe_gHa]raDkqno915 =J@Caj`an9#B# =J@I]nep]hOp]pqo9#I# then the results and execution plan shown in Figure 4-12, while similar, are not the same: P]^ha#Ailhkuaa#*O_]j_kqjp-(hkce_]hna]`o-0 ?LQpeia9,io(ah]loa`peia9.5io*
Figure 4-12. Execution plan when the index is chosen with a query hint You see the same index seek, but the number of reads has more than doubled, and the estimated costs within the execution plan have changed. Although forcing the optimizer to choose an index is possible, it clearly isn’t always an optimal approach. Another way to force a different behavior Ê-+Ê-iÀÛiÀÊÓäänÊÃÊÌ
iÊBKN?AOAAG query hint. BKN?AOAAG makes it so the optimizer will choose only Ej`atOaag operations. If the query were rewritten like this: OAHA?P& BNKIDqi]jNaokqn_ao*AilhkuaaSEPD$BKN?AOAAG% SDANAOe_gHa]raDkqno915 =J@Caj`an9#B# =J@I]nep]hOp]pqo9#I# which changes the I/O, execution time, and execution plan results yet again (Figure 4-13), you end up with these results: P]^ha#Ailhkuaa#*O_]j_kqjp-(hkce_]hna]`o-3, ?LQpeia9,io(ah]loa`peia9/5io* Ì}ÊÌ
iÊ«ÌÃÊvÊÌ
iÊ«ÌâiÀÊ>`ÊvÀV}ÊLi
>ÛÀÃÊV>ÊÊÃiÊÃÌÕ>ÌÃÊ
i«]Ê but frequently, as shown with the results here, an increase in execution time and the number of reads is not helpful.
113
114
C HAPTER 4
IND EX A NA L YS IS
Figure 4-13. Forcing a Oaag operation using BKN?AOAAG query hint
Note To make the best use of your indexes, it is highly recommended that you create the index on a column (or set of columns) with very high selectivity.
Examine the Column Data Type /
iÊdata type of an index matters. For example, an index search on integer keys is very fast because of the small size and easy arithmetic manipulation of the EJPACAN (or EJP) data type. You can also use other variations of integer data types (>ECEJP, OI=HHEJP, and PEJUEJP) for index columns, whereas string data types (?D=N, R=N?D=N, J?D=N, and JR=N?D=N) require a string match operation, which is usually costlier than an integer match operation. -Õ««ÃiÊÞÕÊÜ>ÌÊÌÊVÀi>ÌiÊ>Ê`iÝÊÊiÊVÕÊ>`ÊÞÕÊ
>ÛiÊÌÜÊV>``>ÌiÊVumns—one with an EJPACAN data type and the other with a ?D=N$0% data type. Even though the ÃâiÊvÊLÌ
Ê`>Ì>ÊÌÞ«iÃÊÃÊ{ÊLÞÌiÃÊÊ-+Ê-iÀÛiÀÊÓään]ÊÞÕÊÜÊÃÌÊ«ÀiviÀÊÌ
iÊEJPACAN data type `iÝ°ÊÊ>ÌÊ>ÀÌ
iÌVÊ«iÀ>ÌÃÊ>ÃÊ>ÊiÝ>«i°Ê/
iÊÛ>ÕiÊ£ÊÊÌ
iÊ?D=N$0% data type is actually stored as 1 followed by three spaces, a combination of the following four bytes: ,t/1, ,t.,, ,t.,, and ,t.,°Ê/
iÊ *1Ê`iýÌÊÕ`iÀÃÌ>`Ê
ÜÊÌÊ«iÀvÀÊ>ÀÌ
iÌVÊ«iÀ>ÌÃÊÊ this data, and therefore it converts to an integer data type before the arithmetic operations, whereas the value 1 in an EJPACAN data type is saved as ,t,,,,,,,-°Ê/
iÊ *1ÊV>Êi>ÃÞÊ«iÀform arithmetic operations on this data. Of course, most of the time, you won’t have the simple choice between identically sized data types, allowing you to choose the more optimal type. Keep this information in mind when designing and building your indexes.
Consider Column Order An index key is sorted on the first column of the index and then subsorted on the next column ÜÌ
Êi>V
ÊÛ>ÕiÊvÊÌ
iÊ«ÀiÛÕÃÊVÕ°Ê/
iÊvÀÃÌÊVÕÊÊ>ÊV«Õ`Ê`iÝÊÃÊvÀiµÕiÌÞÊ referred to as the leading edgeÊvÊÌ
iÊ`iÝ°ÊÀÊiÝ>«i]ÊVÃ`iÀÊ/>LiÊ{Ó°
CHAPTER 4
I N D E X A N A LY S I S
Table 4-2. Sample Table
c1
c2
1
1
2
1
3
1
1
2
2
2
3
2
If a composite index is created on the columns (_-, _.), then the index will be ordered as Ã
ÜÊÊ/>LiÊ{ΰ Table 4-3. Composite Index on Columns $_-(_.%
c1
c2
1
1
1
2
2
1
2
2
3
1
3
2
ÃÊÃ
ÜÊÊ/>LiÊ{Î]ÊÌ
iÊ`>Ì>ÊÃÊÃÀÌi`ÊÊÌ
iÊvÀÃÌÊVÕÊ_-) in the composite index. 7Ì
Êi>V
ÊÛ>ÕiÊvÊÌ
iÊvÀÃÌÊVÕ]ÊÌ
iÊ`>Ì>ÊÃÊvÕÀÌ
iÀÊÃÀÌi`ÊÊÌ
iÊÃiV`ÊVÕÊ_.). /
iÀivÀi]ÊÌ
iÊVÕÊÀ`iÀÊÊ>ÊV«ÃÌiÊ`iÝÊÃÊ>Ê«ÀÌ>ÌÊv>VÌÀÊÊÌ
iÊivviVÌÛiness of the index. You can see this by considering the following: Ê
UÊ ÕÊÕµÕiiÃÃ
Ê
UÊ ÕÊÜ`Ì
Ê
UÊ ÕÊ`>Ì>ÊÌÞ«i For example, suppose most of your queries on table p- are similar to the following:
OAHA?P&BNKIp-SDANA_.9-. OAHA?P&BNKIp-SDANA_.9-.=J@_-9-An index on (_., _-) will benefit both the queries. But an index on (_-, _.) will not be appropriate, because it will sort the data initially on column _-, whereas the first OAHA?P statement needs the data to be sorted on column _.. /ÊÕ`iÀÃÌ>`ÊÌ
iÊ«ÀÌ>ViÊvÊVÕÊÀ`iÀ}ÊÊ>Ê`iÝ]ÊVÃ`iÀÊÌ
iÊvÜ}Ê example. In the Lnk`q_pekj*Lnk`q_p table, there is a column for Op]j`]n`?kop and another for HeopLne_a°Ê Ài>ÌiÊ>ÊÌi«À>ÀÞÊ`iÝÊÊÌ
iÊÌ>LiÊiÊÌ
Ã\ ?NA=PAEJ@ATET[PaopKJLanokj*=``naoo$?epu(Lkop]h?k`a%
115
116
C HAPTER 4
IND EX A NA L YS IS
A simple OAHA?P statement run against the table that will use this new index will look something like this: OAHA?P& BNKILanokj*=``naoo=O] SDANA?epu9#S]nnejcpkj# /
iÊÉ"Ê>`ÊiÝiVÕÌÊÌiÊvÀÊÌ
iʵÕiÀÞÊÃÊ>ÃÊvÜÃ\ P]^ha#=``naoo#*O_]j_kqjp-(hkce_]hna]`o-44 ?LQpeia9,io(ah]loa`peia9-23io* And the execution plan in Figure 4-14 shows the use of the index.
Figure 4-14. Execution plan for query against leading edge of index -]ÊÌ
ÃʵÕiÀÞÊÃÊÌ>}Ê>`Û>Ì>}iÊvÊÌ
iÊi>`}Êi`}iÊvÊÌ
i index to perform a Oaag operation to retrieve the data. If, instead of querying using the leading edge, you use another column in the index like the following query: OAHA?P& BNKILanokj*=``naoo=O] SDANA]*Lkop]h?k`a9#S=/3>D# then the results are as follows: P]^ha#=``naoo#*O_]j_kqjp-(hkce_]hna]`o-3/ And the execution plan is clearly different, as you can see in Figure 4-15.
Figure 4-15. Execution plan for query against inner columns
CHAPTER 4
I N D E X A N A LY S I S
/
iÊÀi>`ÃÊvÀÊÌ
iÊÃiV`ʵÕiÀÞÊ>ÀiÊÃ}
ÌÞÊÜiÀÊÌ
>ÊÌ
iÊvÀÃÌ]ÊLÕÌÊÜ
iÊÞÕÊÌ>iÊÌÊ account that the first query returned 86 rows worth of data and the second query returned only 31, you begin to see the difference between the Ej`atOaag operation in Figure 4-14 and the Ej`atO_]j operation in Figure 4-15. Also note that because it had to perform a scan, the optimizer marked the column as possibly missing an index. Finally, to see the order of the index really shine, change the query to this: OAHA?P]*=``naooE@ (]*?epu (]*Lkop]h?k`a BNKILanokj*=``naoo=O] SDANA]*?epu9#S]nnejcpkj# =J@]*Lkop]h?k`a9#S=/3>D# Executing this query will return the same 31 rows as the previous query, resulting in the following: P]^ha#=``naoo#*O_]j_kqjp-(hkce_]hna]`o. with the execution plan visible in Figure 4-16.
Figure 4-16. Execution plan using both columns /
iÊÀ>`V>ÊV
>}iÃÊÊÉ"Ê>`ÊiÝiVÕÌÊ«>ÊÀi«ÀiÃiÌÊÌ
iÊÀi>ÊÕÃiÊvÊ>ÊV«Õ`Ê `iÝ]ÊÌ
iÊVÛiÀ}Ê`iÝ°Ê/
ÃÊÃÊVÛiÀi`ÊÊ`iÌ>ÊÊÌ
iÊÃiVÌʺ ÛiÀ}Ê`iÝiûÊ>ÌiÀÊÊ the chapter. 7
i finished, drop the index: @NKLEJ@ATLanokj*=``naoo*ET[Paop
Consider the Type of Index InÊ-+Ê-iÀÛiÀ]ÊÞÕ have two main index types: clustered and nonclustered. Both types have a ÌÀiiÊÃÌÀÕVÌÕÀi°Ê/
iÊ>Ê`vviÀiViÊLiÌÜiiÊÌ
iÊÌÜÊÌÞ«iÃÊÃÊÌ
>ÌÊÌ
iÊi>vÊ«>}iÃÊÊ>ÊVÕÃtered index are the data pages of the table and are therefore in the same order as the data to Ü
V
ÊÌ
iÞÊ«Ì°Ê/
ÃÊi>ÃÊÌ
>ÌÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊÃÊÌ
iÊÌ>Li°ÊÃÊÞÕÊ«ÀVii`]ÊÞÕÊÜÊÃiiÊ that the difference at the leaf level between the two index types becomes very important when determining the type of index to use.
Clustered Indexes /
i leaf pages of a clustered index and the data pages of the table the index is on are one and the same. Because of this, table rows are physically sorted on the clustered index column, and since there can be only one physical order of the table data, a table can have only one clustered index.
117
118
C HAPTER 4
IND EX A NA L YS IS
Tip When you create a primary key constraint, SQL Server automatically creates it as a unique clustered index on the primary key if one does not already exist and if it is not explicitly specified that the index should be a unique nonclustered index. This is not a requirement; it’s just default behavior. You can change it prior to creating the table.
Heap Tables As mentioned earlier in the chapter, a table with no clustered index is called a heap table°Ê/
iÊ data rows of a heap table are not stored in any particular order or linked to the adjacent pages ÊÌ
iÊÌ>Li°Ê/
ÃÊÕÀ}>âi`ÊÃÌÀÕVÌÕÀiÊvÊÌ
iÊ
i>«ÊÌ>LiÊÕÃÕ>ÞÊVÀi>ÃiÃÊÌ
iÊÛiÀ
i>`ÊvÊ accessing a large heap table when compared to accessing a large nonheap table (a table with a clustered index).
Relationship with Nonclustered Indexes /
iÀiÊÃ an interesting relationship between a clustered index and the nonclustered indexes in -+Ê-iÀÛiÀ°ÊÊ`iÝÊÀÜÊvÊ>ÊVÕÃÌiÀi`Ê`iÝÊVÌ>ÃÊ>Ê«ÌiÀÊÌÊÌ
iÊVÀÀië`}Ê`>Ì>Ê ÀÜÊvÊÌ
iÊÌ>Li°Ê/
ÃÊ«ÌiÀÊÃÊV>i`Ê>Êrow locator°Ê/
iÊÛ>ÕiÊvÊÌ
iÊÀÜÊV>ÌÀÊ`i«i`ÃÊÊ whether the data pages are stored in a heap or are clustered. For a nonclustered index, the row locator is a pointer to the NE@ for the data row in a heap. For a table with a clustered index, the row locator is the clustered index key value. ÀÊiÝ>«i]ÊÃ>ÞÊÞÕÊ
>ÛiÊ>Ê
i>«ÊÌ>LiÊÜÌ
ÊÊVÕÃÌiÀi`Ê`iÝ]Ê>ÃÊÃ
ÜÊÊ/>LiÊ{{° Table 4-4. Data Page for a Sample Table
RowID (Not a Real Column)
c1
c2
c3
1
A1
A2
A3
2
B1
B2
B3
A nonclustered index on column _- in a heap will cause the row locator for the index rows to contain a pointer to the corresponding data row in the database table, as shown in />LiÊ{x° Table 4-5. Nonclustered Index Page with No Clustered Index
c1
Row Locator
A1
Pointer to NE@ = 1
B1
Pointer to NE@ = 2
On creating a clustered index on column _., the row locator values of the nonclustered `iÝÊÀÜÃÊ>ÀiÊV
>}i`°Ê/
iÊiÜÊÛ>ÕiÊvÊÌ
iÊÀÜÊV>ÌÀÊÜÊVÌ>ÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊiÞÊ Û>Õi]Ê>ÃÊÃ
ÜÊÊ/>LiÊ{È°
CHAPTER 4
I N D E X A N A LY S I S
Table 4-6. Nonclustered Index Page with a Clustered Index on c2
c1
Row Locator
A1
A2
B1
B2
/ÊÛiÀvÞÊÌ
ÃÊ`i«i`iVÞÊLiÌÜiiÊ>ÊVÕÃÌiÀi`Ê>`Ê>ÊVÕÃÌiÀi`Ê`iÝ]Êi̽ÃÊVÃ`iÀÊ>Ê iÝ>«i°ÊÊÌ
iÊ`ÛiÌÕÀi7ÀÃÓäänÊ`>Ì>L>Ãi]ÊÌ
iÊÌ>LiÊ`^k*@]p]^]oaHkc contains no clustered index, just a nonclustered primary key. If a query is run against it like the following: OAHA?P`h*@]p]^]oaHkcE` (`h*LkopPeia BNKI`^k*@]p]^]oaHkc=O`h SDANA@]p]^]oaHkcE`9--1 then the execution will look like Figure 4-17.
Figure 4-17. Execution plan against a heap As you can see, the index was used in a Oaag operation. But because the data is stored separately from the nonclustered index, an additional operation, the NE@Hkkgql operation, is required in order to retrieve the data, which is then joined back to the information from the Ej`atOaag operation through a Jaopa`HkklÊ«iÀ>Ì°Ê/
ÃÊÃÊ>ÊV>ÃÃVÊiÝ>«iÊvÊÜ
>ÌÊÃÊ known as a bookmark lookup]ÊÜ
V
ÊÃÊiÝ«>i`ÊÊÀiÊ`iÌ>ÊÊÌ
iʺ iv}ÊÌ
iÊ >ÀÊ Õ«»ÊÃiVÌ°ÊÊÃ>ÀʵÕiÀÞÊÀÕÊ>}>ÃÌÊ>ÊÌ>LiÊÜÌ
Ê>ÊVÕÃÌiÀi`Ê`iÝÊÊ«>ViÊÜÊÊ like this: OAHA?P`*@al]npiajpE@ (`*Ik`ebea`@]pa BNKIDqi]jNaokqn_ao*@al]npiajp=O` SDANA`*@al]npiajpE@9-, Figure 4-18 shows this execution plan returned.
. Figure 4-18. Execution plan with a clustered index
119
120
C HAPTER 4
IND EX A NA L YS IS
Although the primary key is used in the same way as the previous query, this time it’s against a clustered index. As you now know, this means the data is stored with the index, so the additional column doesn’t require a lookup operation to get the data. Everything is returned by the simple clustered Ej`atOaag operation. /Ê>Û}>ÌiÊvÀÊ>ÊVÕÃÌiÀi`Ê`iÝÊÀÜÊÌÊ>Ê`>Ì>ÊÀÜ]ÊÌ
ÃÊÀi>ÌÃ
«ÊLiÌÜiiÊÌ
iÊ two index types requires an additional indirection for navigating the B-tree structure of the VÕÃÌiÀi`Ê`iÝ°Ê7Ì
ÕÌÊÌ
iÊVÕÃÌiÀi`Ê`iÝ]ÊÌ
iÊÀÜÊV>ÌÀÊvÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊÜÕ`Ê be able to navigate directly from the nonclustered index row to the data row in the base table. /
iÊ«ÀiÃiViÊvÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊV>ÕÃiÃÊÌ
iÊ>Û}>ÌÊvÀÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊÀÜÊÌÊ the data row to go through the B-tree structure of the clustered index, since the new row locator value points to the clustered index key. On the other hand, consider inserting an intermediate row in the clustered index key order or expanding the content of an intermediate row. For example, imagine a clustered index table containing four rows per page, with clustered index column values of 1, 2, 4, and 5. Adding a new row in the table with the clustered index value 3 will require space in the page between values 2 and 4. If enough space is not available in that position, a page split will occur on the data page (or clustered index leaf page). Even though the data page split will cause reloV>ÌÊvÊÌ
iÊ`>Ì>ÊÀÜÃ]ÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊÀÜÊV>ÌÀÊÛ>ÕiÃÊii`ÊÌÊLiÊÕ«`>Ìi`°Ê/
iÃiÊ row locators continue to point to the same logical key values of the clustered index key, even though the data rows have physically moved to a different location. In the case of a data page ëÌ]ÊÌ
iÊÀÜÊV>ÌÀÃÊvÊÌ
iÊVÕÃÌiÀi`Ê`iÝiÃÊii`ÊÌÊLiÊÕ«`>Ìi`°Ê/
ÃÊÃÊ>Ê«ÀÌ>ÌÊ point, since tables often have a large number of nonclustered indexes.
Note Page splits and their effect on performance are explained in more detail in Chapter 8.
Clustered Index Recommendations /
iÊÀi>ÌÃ
« between a clustered index and a nonclustered index imposes some considerations on the clustered index, which are explained in the sections that follow.
Create the Clustered Index First -Vi all nonclustered indexes hold clustered index keys within their index rows, the order of nonclustered and clustered index creation is very important. For example, if the nonclustered indexes are built before the clustered index is created, then the nonclustered index row locator will contain a pointer to the corresponding NE@ÊvÊÌ
iÊÌ>Li°Ê Ài>Ì}ÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊ>ÌiÀÊ will modify all the nonclustered indexes to contain clustered index keys as the new row locator Û>Õi°Ê/
ÃÊivviVÌÛiÞÊÀiLÕ`ÃÊ>ÊÌ
iÊVÕÃÌiÀi`Ê`iÝið For the best performance, I recommend that you create the clustered index before you VÀi>ÌiÊ>ÞÊVÕÃÌiÀi`Ê`iÝ°Ê/
ÃÊ>ÜÃÊÌ
iÊVÕÃÌiÀi`Ê`iÝiÃÊÌÊ
>ÛiÊÌ
iÀÊÀÜÊV>ÌÀÊ ÃiÌÊÌÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊiÞÃÊ>ÌÊÌ
iÊÌiÊvÊVÀi>Ì°Ê/
ÃÊ`iÃÊÌÊ
>ÛiÊ>ÞÊivviVÌÊÊÌ
iÊv>Ê performance, but rebuilding the indexes may be quite a large job.
CHAPTER 4
I N D E X A N A LY S I S
Keep Indexes Narrow -Vi all nonclustered indexes hold the clustered keys as their row locator, for the best performance keep the overall byte size of the clustered index as small as possible. If you create a wide clustered index, say ?D=N$1,,%]ÊÌ
ÃÊÜÊ>``ÊxääÊLÞÌiÃÊÌÊiÛiÀÞÊVÕÃÌiÀi`Ê`iÝ°Ê/
ÕÃ]Ê keep the number of columns in the clustered index to a minimum, and carefully consider the byte size of each column to be included in the clustered index. A column of the EJPACAN data type usually makes a good candidate for a clustered index, whereas a string data type column will be a less-than-optimal choice. /ÊÕ`iÀÃÌ>`ÊÌ
iÊeffect of a wide clustered index on a nonclustered index, consider this iÝ>«i°Ê Ài>ÌiÊ>ÊÃ>ÊÌiÃÌÊÌ>LiÊÜÌ
Ê>ÊVÕÃÌiÀi`Ê`iÝÊ>`Ê>ÊVÕÃÌiÀi`Ê`iÝÊ_hqop[ jkj_hqop*omh in the download): EB$OAHA?PK>FA?P[E@$#p-#%%EOJKPJQHH @NKLP=>HAp-7 CK ?NA=PAP=>HAp-$_-EJP(_.EJP%7 SEPDJqio =O$OAHA?P-=Oj QJEKJ=HH OAHA?Pj'BNKIJqio SDANAj8., % EJOANPEJPKp-$_-(_.% OAHA?Pj (j'BNKIJqio ?NA=PA?HQOPANA@EJ@ATe_hKJp-$_.%7 ?NA=PAJKJ?HQOPANA@EJ@ATej_hKJp-$_-%7 -ViÊÌ
iÊÌ>LiÊ
>ÃÊ>ÊVÕÃÌiÀi`Ê`iÝ]ÊÌ
iÊÀÜÊV>ÌÀÊvÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊVÌ>ÃÊ Ì
iÊVÕÃÌiÀi`Ê`iÝÊiÞÊÛ>Õi°Ê/
iÀivÀi\
7`Ì
ÊvÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊÀÜÊrÊ7`Ì
ÊvÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊVÕʳÊ7`Ì
Ê vÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊVÕÊrÊÃâiÊvÊ /Ê`>Ì>ÊÌÞ«iʳÊ-âiÊvÊ /Ê`>Ì>ÊÌÞ«i = 4 bytes + 4 bytes = 8 bytes 7Ì
ÊÌ
ÃÊÃ>ÊÃâiÊvÊ>ÊVÕÃÌiÀi`Ê`iÝÊÀÜ]Ê>ÊÌ
iÊÀÜÃÊV>ÊLiÊÃÌÀi`ÊÊiÊ`iÝÊ page. You can confirm this by querying against the index statistics (^]oa[ej`at*omh), as shown in Figure 4-19: OAHA?Pe*J]ia (e*pula[`ao_ (o*l]ca[_kqjp (o*na_kn`[_kqjp (o*ej`at[harah
121
122
C HAPTER 4
IND EX A NA L YS IS
BNKIouo*ej`ataoe FKEJouo*`i[`^[ej`at[lduoe_]h[op]po$@>[E@$J#=`rajpqnaSkngo.,,4#%( K>FA?P[E@$J#`^k*p-#%( JQHH(JQHH(#@AP=EHA@#%=Oo KJe*ej`at[e`9o*ej`at[e` SDANAe*K>FA?P[E@9K>FA?P[E@$J#`^k*p-#%
Figure 4-19. Number of index pages for a narrow index /ÊÕ`iÀÃÌ>`ÊÌ
iÊivviVÌÊvÊ>ÊÜ`iÊVÕÃÌiÀi`Ê`iÝÊÊ>ÊVÕÃÌiÀi`Ê`iÝ]Ê`vÞ the data type of the clustered indexed column _. from EJP to ?D=N$1,,%: @NKLEJ@ATp-*e_h =HPANP=>HAp-=HPAN?KHQIJ_.?D=N$1,,% ?NA=PA?HQOPANA@EJ@ATe_hKJp-$_.% ,Õ}Êouoej`at[oaha_p.*omh again returns the result in Figure 4-20.
Figure 4-20. Number of index pages for a wide index You can see that a wide clustered index increases the width of the nonclustered index row size. Because of the large width of the nonclustered index row, one 8KB index page can’t accommodate all the index rows. Instead, two index pages will be required to store all 20 index rows. In the case of a large table, an unreasonable expansion in the size of the nonclustered indexes because of a large clustered index key size can significantly increase the number of pages of the nonclustered indexes. /
iÀivÀi]Ê>Ê>À}iÊVÕÃÌiÀi`Ê`iÝÊiÞÊÃâiÊÌÊÞÊ>vviVÌÃÊÌÃÊÜÊÜ`Ì
ÊLÕÌÊ>ÃÊÜ`iÃÊ >ÊVÕÃÌiÀi`Ê`iÝiÃÊÊÌ
iÊÌ>Li°Ê/
ÃÊVÀi>ÃiÃÊÌ
iÊÕLiÀÊvÊ`iÝÊ«>}iÃÊvÀÊ>ÊÌ
iÊ indexes on the table, increasing the logical reads and disk I/Os required for the indexes.
Rebuild the Clustered Index in a Single Step Because of the dependency of nonclustered indexes on the clustered index, rebuilding the clustered index as separate @NKLEJ@AT and ?NA=PAEJ@AT statements causes all the nonclusÌiÀi`Ê`iÝiÃÊÌÊLiÊÀiLÕÌÊÌÜVi°Ê/Ê>Û`ÊÌ
Ã]ÊÕÃiÊÌ
iÊ@NKL[ATEOPEJC clause of the ?NA=PA EJ@ATÊÃÌ>ÌiiÌÊÌÊÀiLÕ`ÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊÊ>ÊÃ}iÊ>ÌVÊÃÌi«°Ê->ÀÞ]ÊÞÕÊV>Ê>ÃÊ use the @NKL[ATEOPEJC clause with a nonclustered index.
CHAPTER 4
I N D E X A N A LY S I S
When to Use a Clustered Index In certain situations, using a clustered index is very helpful. I discuss these in the sections that follow. Retrieving a Range of Data -ViÊÌ
iÊi>vÊ«>}ià of a clustered index and the data pages of the table are the same, the order of the clustered index column not only orders the rows of the clustered index but also physically orders the data rows. If the physical order of the data rows matches the order of data requested by a query, then the disk head can read all the rows sequentially, without much disk head movement. For example, if a query requests all the employee records belonging to the database group and the corresponding Ailhkuaao table has a clustered index on the Cnkql column, then all the relevant Ailhkuaa rows will be physically arranged together ÊÌ
iÊ`ðÊ/
ÃÊ>ÜÃÊÌ
iÊ`ÃÊ
i>`ÊÌÊÛiÊÌÊÌ
iÊ«ÃÌÊvÊÌ
iÊvÀÃÌÊÀÜÊÊÌ
iÊ`ÃÊ>`Ê then electronically read all the data sequentially with minimal physical movement of the disk head. On the other hand, if the rows are not sorted on the disk in the correct physical order, the disk head has to move randomly from one location to another to fetch all the relevant ÀÜðÊ-ViÊ«
ÞÃV>ÊÛiiÌÊvÊÌ
iÊ`ÃÊ
i>`ÊVÃÌÌÕÌiÃÊ>Ê>ÀÊ«ÀÌÊvÊÌ
iÊVÃÌÊvÊ>Ê disk operation, sorting the rows in the proper physical order on the disk (using a clustered index) optimizes the I/O cost. "iʺÀ>}i»ÊvÊ`>Ì>ÊÌ
>ÌÊÃÊ>VViÃÃi`ÊvÀiµÕiÌÞÊÊÀi>Ì>ÊÃÞÃÌiÃÊÃÊÌ
iÊvÀi}ÊiÞÊ vÀÊ>Ì
iÀÊÌ>Li°Ê/
ÃÊ`>Ì>]Ê`i«i`}ÊÊÌ
iÊ>VViÃÃÊiV
>ÃÃÊvÊÌ
iÊ>««V>Ì]ÊÃÊ>Ê great candidate for inclusion in the clustered index. Retrieving Presorted Data
ÕÃÌiÀi`Ê`iÝià are particularly efficient when the data retrieval needs to be sorted. If you create a clustered index on the column or columns that you may need to sort by, then the rows will be physically stored in that order, eliminating the overhead of sorting the data after it is retrieved. i̽ÃÊÃiiÊÌ
ÃÊÊ>VÌ°Ê Ài>ÌiÊ>ÊÌiÃÌÊÌ>LiÊ>ÃÊvÜÃÊ_na]pa[oknp*omh in the download): EB$OAHA?PK>FA?P[E@$#k`#% %EOJKPJQHH @NKLP=>HA`^k*k`7 CK OAHA?P& EJPK`^k*k` BNKILqn_d]oejc*Lqn_d]oaKn`an@ap]eh=Olk` /
iÊiÜÊÌ>LiÊk` is created with data only. It doesn’t have any indexes. You can verify the indexes on the table by executing the following, which returns nothing: ol[dahlej`at`^k*k`
123
124
C HAPTER 4
IND EX A NA L YS IS
/ÊÕ`iÀÃÌ>`ÊÌ
iÊÕÃiÊvÊ>ÊVÕÃÌiÀi`Ê`iÝ]ÊviÌV
Ê>Ê>À}iÊÀ>}iÊvÊÀÜÃÊÀ`iÀi`ÊÊ>Ê certain column: OAHA?P& BNKI`^k*k` SDANAk`*Lnk`q_pE@>APSAAJ1,,=J@1-, KN@AN>Uk`*Lnk`q_pE@ You can obtain the cost of executing this query (without any indexes) from the OP=PEOPE?O EK output: P]^ha#k`#*O_]j_kqjp-(hkce_]hna]`o35 /Ê«ÀÛiÊÌ
iÊ«iÀvÀ>ViÊvÊÌ
ÃʵÕiÀÞ]ÊÞÕÊÃ
Õ`ÊVÀi>ÌiÊ>Ê`iÝÊÊÌ
iÊSDANA clause VÕ°Ê/
ÃʵÕiÀÞÊÀiµÕÀiÃÊLÌ
Ê>ÊÀ>}iÊvÊÀÜÃÊ>`Ê>ÊÃÀÌi`ÊÕÌ«ÕÌ°Ê/
iÊÀiÃÕÌÊÃiÌÊÀiµÕÀiiÌÊvÊÌ
ÃʵÕiÀÞÊiiÌÃÊÌ
iÊÀiVi`>ÌÃÊvÀÊ>ÊVÕÃÌiÀi`Ê`iÝ°Ê/
iÀivÀi]ÊVÀi>ÌiÊ>Ê clustered index as follows, and reexamine the cost of the query: ?NA=PA?HQOPANA@EJ@ATe-KJk`$Lnk`q_pE@% 7
iÊÞÕÊÀÕÊÌ
iʵÕiÀÞÊ>}>]ÊÌ
iÊÀiÃÕÌ>ÌÊVÃÌÊvÊÌ
iʵÕiÀÞÊÜÌ
Ê>ÊVÕÃÌiÀi`Ê`iÝ®ÊÃÊ>ÃÊ follows: P]^ha#k`#*O_]j_kqjp-(hkce_]hna]`o4
Ài>Ì}ÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊÀi`ÕVi`ÊÌ
iÊÕLiÀÊvÊ}V>ÊÀi>`ÃÊ>`ÊÌ
iÀivÀiÊÃ
Õ`Ê contribute to the query performance improvement. On the other hand, if you create a nonclustered index (instead of a clustered index) on Ì
iÊV>``>ÌiÊVÕ]ÊÌ
iÊÌ
iʵÕiÀÞÊ«iÀvÀ>ViÊ>ÞÊLiÊ>vviVÌi`Ê>`ÛiÀÃiÞ°Êi̽ÃÊÛiÀvÞÊÌ
iÊ effect of a nonclustered index in this case: @NKLEJ@ATk`*e?NA=PAJKJ?HQOPANA@EJ@ATe-kj`^k*k`$Lnk`q_pE@% /
iÊÀiÃÕÌ>ÌÊVÃÌÊvÊÌ
iʵÕiÀÞÊÜÌ
Ê>ÊVÕÃÌiÀi`Ê`iÝ®ÊÃÊ>ÃÊvÜÃ\ P]^ha#k`#*O_]j_kqjp-(hkce_]hna]`o43 /
iÊVÕÃÌiÀi`Ê`iÝÊÃ}vV>ÌÞÊVÀi>ÃiÃÊÌ
iÊÕLiÀÊvÊ}V>ÊÀi>`Ã]Ê>vviVÌ}ÊÌ
iÊ query performance accordingly. Drop the test table when you’re done: @NKLP=>HA`^k*k`
Note For a query that retrieves a large range of rows and/or an ordered output, a clustered index is usually a better choice than a nonclustered index.
CHAPTER 4
I N D E X A N A LY S I S
When Not to Use a Clustered Index In certain situations, you are better off not using a clustered index. I discuss these in the sections that follow. Frequently Updatable Columns If the clustered index columns are frequently updated, this will cause the row locator of all the nonclustered indexes to be updated accordingly, significantly increasing the cost of the ÀiiÛ>ÌÊ>VÌʵÕiÀiðÊ/
ÃÊ>ÃÊ>vviVÌÃÊ`>Ì>L>ÃiÊVVÕÀÀiVÞÊLÞÊblocking all other queries referring to the same part of the table and the nonclustered indexes during that period. /
iÀivÀi]Ê>Û`ÊVÀi>Ì}Ê>ÊVÕÃÌiÀi`Ê`iÝÊÊVÕÃÊÌ
>ÌÊ>ÀiÊ
}
ÞÊÕ«`>Ì>Li°
Note Chapter 12 covers blocking in more depth.
/ÊÕ`iÀÃÌ>`Ê
ÜÊÌ
iÊVÃÌÊvÊ>ÊQL@=PA statement that modifies only a clustered key column is increased by the presence of nonclustered indexes on the table, consider the folÜ}ÊiÝ>«i°Ê/
iÊO]hao*Ola_e]hKbbanLnk`q_p table has a composite clustered index on the primary key, which is also the foreign key from two different tables; this is a classic manyto-many join. In this example, I update one of the two columns using the following statement (note the use of the transaction to keep the test data intact): >ACEJPN=J OAPOP=PEOPE?OEKKJ7 QL@=PAo]hao*Ola_e]hKbbanLnk`q_p OAPLnk`q_pE@9/01 SDANAOla_e]hKbbanE@9=J@lnk`q_pe`93.,7 OAPOP=PEOPE?OEKKBB7 NKHH>=?GPN=J /
i OP=PEOPE?OEK output shows the reads necessary: P]^ha#Lnk`q_p#*O_]j_kqjp,(hkce_]hna]`o. P]^ha#O]haoKn`an@ap]eh#*O_]j_kqjp-(hkce_]hna]`o-.0, P]^ha#Ola_e]hKbbanLnk`q_p#*O_]j_kqjp,(hkce_]hna]`o-, If you added a nonclustered index to the table, you would see the reads increase: ?NA=PAJKJ?HQOPANA@EJ@ATetPaopKJo]hao*Ola_e]hKbbanLnk`q_p$Ik`ebea`@]pa% 7
iÊÞÕÊÀÕÊÌ
iÊÃ>iʵÕiÀÞÊ>}>]ÊÌ
iÊÕÌ«ÕÌÊvÊOP=PEOPE?OEK changes for the Ola_e]hKbbanLnk`q_p table: P]^ha#Lnk`q_p#*O_]j_kqjp,(hkce_]hna]`o. P]^ha#O]haoKn`an@ap]eh#*O_]j_kqjp-(hkce_]hna]`o-.0, P]^ha#Ola_e]hKbbanLnk`q_p#*O_]j_kqjp,(hkce_]hna]`o-5
125
126
C HAPTER 4
IND EX A NA L YS IS
As you can see, the number of reads caused by the update of the clustered index is increased with the addition of the nonclustered index. Be sure to drop the index: @NKLEJ@ATO]hao*Ola_e]hKbbanLnk`q_p*etPaop
Wide Keys -ViÊ>ÊVÕÃÌiÀi`Ê`iÝià hold the clustered keys as their row locator, for performance reasons you should avoid creating a clustered index on a very wide column (or columns) or on too many columns. As explained in the preceding section, a clustered index must be as narrow as possible. Too Many Concurrent Inserts in Sequential Order If you want to add many new rows concurrently, then it may be better for performance to distribute them across the data pages of the table. However, if you add all the rows in the same order as that imposed by the clustered index, then all the inserts will be attempted on the last «>}iÊvÊÌ
iÊÌ>Li°Ê/
ÃÊ>ÞÊV>ÕÃiÊ>Ê
Õ}iʺ
ÌÊë̻ÊÊÌ
iÊVÀÀië`}ÊÃiVÌÀÊvÊÌ
iÊ`Ã°Ê /Ê>Û`ÊÌ
ÃÊ`ÃÊ
ÌÊëÌ]ÊÞÕÊÃ
Õ`ÊÌÊ>ÀÀ>}iÊÌ
iÊ`>Ì>ÊÀÜÃÊÊÌ
iÊÃ>iÊÀ`iÀÊ>ÃÊÌ
iÀÊ «
ÞÃV>ÊV>ÌðÊ/
iÊÃiÀÌÃÊV>ÊLiÊÀ>`âi`ÊÌ
ÀÕ}
ÕÌÊÌ
iÊÌ>LiÊLÞÊVÀi>Ì}Ê>ÊVÕÃÌiÀi`Ê index on another column that doesn’t arrange the rows in the same order as that of the new ÀÜðÊ/
ÃÊÃÊ>ÊÃÃÕiÊÞÊÜÌ
Ê>Ê>À}iÊÕLiÀÊvÊÃÕÌ>iÕÃÊÃiÀÌð /
iÀiÊÃÊ>ÊV>Ûi>ÌÊÌÊÌ
ÃÊÀiVi`>Ì°ÊÜ}ÊÃiÀÌÃÊÊÌ
iÊLÌÌÊvÊÌ
iÊÌ>LiÊ«Àivents page splits on the intermediate pages that are required to accommodate the new rows in those pages. If the number of concurrent inserts is low, then ordering the data rows (using a clustered index) in the order of the new rows will prevent intermediate page splits. However, if the disk hot spot becomes a performance bottleneck, then new rows can be accommodated in intermediate pages without causing page splits by reducing the fill factor of the table. In addiÌ]ÊÌ
iʺ
̻ʫ>}iÃÊÜÊLi in memory, which also benefits performance.
Note Chapter 8 covers the fill factor in depth.
Nonclustered Indexes A nonclustered index does not affect the order of the data in the table pages, because the leaf pages of a nonclustered index and the data pages of the table are separate. A pointer (the row locator) is required to navigate from an index row to the data row. As you learned in the i>ÀiÀʺ ÕÃÌiÀi`Ê`iÝiûÊÃiVÌ]ÊÌ
iÊÃÌÀÕVÌÕÀiÊvÊÌ
iÊÀÜÊV>ÌÀÊ`i«i`ÃÊÊÜ
iÌ
iÀÊÌ
iÊ data pages are stored in a heap or a clustered index. For a heap, the row locator is a pointer to the NE@ for the data row; for a table with a clustered index, the row locator is the clustered index key.
CHAPTER 4
I N D E X A N A LY S I S
Nonclustered Index Maintenance /
iÊÀÜÊV>ÌÀÊÛ>Õi of the nonclustered indexes continues to have the same clustered index value, even when the clustered index rows are physically relocated. /Ê«ÌâiÊÌ
ÃÊ>Ìi>ViÊVÃÌ]Ê-+Ê-iÀÛiÀÊ>``ÃÊ>Ê«ÌiÀÊÌÊÌ
iÊ`Ê`>Ì>Ê«>}iÊÌÊ point to the new data page after a page split, instead of updating the row locator of all the relevant nonclustered indexes. Although this reduces the maintenance cost of the nonclustered indexes, it increases the navigation cost from the nonclustered index row to the data ÀÜ]ÊÃViÊ>ÊiÝÌÀ>ÊÊÃÊ>``i`ÊLiÌÜiiÊÌ
iÊ`Ê`>Ì>Ê«>}iÊ>`ÊÌ
iÊiÜÊ`>Ì>Ê«>}i°Ê/
iÀivÀi]Ê having a clustered index as the row locator decreases this overhead associated with the nonclustered index.
Defining the Bookmark Lookup 7
iÊ>ʵÕiÀÞ requests columns that are not part of the nonclustered index chosen by the «ÌâiÀ]Ê>ÊÕ«ÊÃÊÀiµÕÀi`°Ê/
ÃÊ>ÞÊLiÊ>ÊiÞÊÕ«ÊÜ
iÊ}}Ê>}>ÃÌÊ>ÊVÕÃÌiÀi`Ê`iÝÊ or an NE@ÊÕ«ÊÜ
iÊ«iÀvÀi`Ê>}>ÃÌÊ>Ê
i>«°Ê/
iÊVÊÌiÀÊvÀÊÌ
iÃiÊÕ«ÃÊViÃÊ from the old definition name, bookmark lookup°Ê/
iÊÕ«ÊviÌV
iÃÊÌ
iÊVÀÀië`}Ê`>Ì>Ê row from the table by following the row locator value from the index row, requiring a logical read on the data page besides the logical read on the index page. However, if all the columns required by the query are available in the index itself, then access to the data page is not ÀiµÕÀi`°Ê/
ÃÊÃÊÜÊ>ÃÊ>Êcovering index. /
iÃiÊL>ÀÊÕ«ÃÊ>ÀiÊÌ
iÊÀi>ÃÊÌ
>ÌÊ>À}iÊÀiÃÕÌÊÃiÌÃÊ>ÀiÊLiÌÌiÀÊÃiÀÛi`ÊÜÌ
Ê>Ê clustered index. A clustered index doesn’t require a bookmark lookup, since the leaf pages and data pages for a clustered index are the same.
Note Chapter 6 covers bookmark lookups in more detail.
Nonclustered Index Recommendations -ViÊ>ÊÌ>Li can have only one clustered index, you can use the flexibility of multiple nonclustered indexes to help improve performance. I explain the factors that decide the use of a nonclustered index in the following sections.
When to Use a Nonclustered Index A nonclustered index is most useful when all you want to do is retrieve a small number of rows from a large table. As the number of rows to be retrieved increases, the overhead cost of the L>ÀÊÕ«ÊÀÃiÃÊ«À«ÀÌ>ÌiÞ°Ê/ÊÀiÌÀiÛiÊ>ÊÃ>ÊÕLiÀÊvÊÀÜÃÊvÀÊ>ÊÌ>Li]ÊÌ
iÊ indexed column should have a very high selectivity.
127
128
C HAPTER 4
IND EX A NA L YS IS
Furthermore, there will be indexing requirements that won’t be suitable for a clustered index, as explained inÊÌ
iʺ ÕÃÌiÀi`Ê`iÝiûÊÃiVÌ\ Ê
UÊ ÀiµÕiÌÞÊÕ«`>Ì>LiÊVÕÃ
Ê
UÊ 7`iÊiÞÃ
In these cases, you can use a nonclustered index, since, unlike a clustered index, it doesn’t affect other indexes in the table. A nonclustered index on a frequently updatable column isn’t >ÃÊVÃÌÞÊ>ÃÊ
>Û}Ê>ÊVÕÃÌiÀi`Ê`iÝÊÊÌ
>ÌÊVÕ°Ê/
iÊQL@=PA operation on a nonclustered index is limited to the base table and the nonclustered index. It doesn’t affect any other nonVÕÃÌiÀi`Ê`iÝiÃÊÊÌ
iÊÌ>Li°Ê->ÀÞ]Ê>ÊVÕÃÌiÀi`Ê`iÝÊÊ>ÊÜ`iÊVÕÊÀÊÃiÌÊvÊ columns) doesn’t increase the size of any other index, unlike that with a clustered index. However, remain cautious, even while creating a nonclustered index on a highly updatable column or a wide column (or set of columns), since this can increase the cost of action queries, as explained earlier in the chapter.
Tip A nonclustered index can also help resolve blocking and deadlock issues. I cover this in more depth in Chapters 12 and 13.
When Not to Use a Nonclustered Index Nonclustered indexesÊ>ÀiÊÌÊÃÕÌ>LiÊvÀʵÕiÀiÃÊÌ
>ÌÊÀiÌÀiÛiÊ>Ê>À}iÊÕLiÀÊvÊÀÜðÊ-ÕV
Ê queries are better served with a clustered index, which doesn’t require a separate bookmark Õ«ÊÌÊÀiÌÀiÛiÊ>Ê`>Ì>ÊÀÜ°Ê-ViÊ>ÊL>ÀÊÕ«ÊÀiµÕÀiÃÊ>Ê>``Ì>Ê}V>ÊÀi>`ÊÊ the data page besides the logical read on the nonclustered index page, the cost of a query using >ÊVÕÃÌiÀi`Ê`iÝÊVÀi>ÃiÃÊÃ}vV>ÌÞÊvÀÊ>Ê>À}iÊÕLiÀÊvÊÀÜðÊ/
iÊ-+Ê-iÀÛiÀʵÕiÀÞÊ optimizer takes this cost into effect and accordingly discards the nonclustered index when retrieving a large result set. If your requirement is to retrieve a large result set from a table, then having a nonclustered index on the filter criterion (or the join criterion) column will not be useful unless you use a special type of nonclustered index called a covering index. I describe this index type in detail later in the chapter.
Clustered vs. Nonclustered Indexes /
iÊ> considerations in choosing between a clustered and a nonclustered index are as follows: Ê
UÊ
ÕLiÀÊvÊÀÜÃÊÌÊLiÊÀiÌÀiÛi`
Ê
UÊ >Ì>À`iÀ}ÊÀiµÕÀiiÌ
Ê
UÊ `iÝÊiÞÊÜ`Ì
CHAPTER 4
Ê
UÊ ÕÊÕ«`>ÌiÊvÀiµÕiVÞ
Ê
UÊ >ÀÊVÃÌ
Ê
UÊ ÞÊ`ÃÊ
ÌÊëÌÃ
I N D E X A N A LY S I S
Benefits of a Clustered Index over a Nonclustered Index 7
iÊ`iV`}ÊÕ«Ê> type of index on a table with no indexes, the clustered index is usually the preferred choice. Because the index page and the data pages are the same, the clustered index doesn’t have to jump from the index row to the base row as required in the case of a nonclustered index. /ÊÕ`iÀÃÌ>`Ê
ÜÊ>ÊVÕÃÌiÀi` index can outperform a nonclustered index in most circumstances, even in retrieving small number of rows, create a test table with a high selectivity for one column (_hqopan[^aja*omh in the download): EB$OAHA?PK>FA?P[E@$#p-#%%EOJKPJQHH @NKLP=>HApCK ?NA=PAP=>HAp-$_-EJP(_.EJP%7 OAHA?PPKL-,,,, E@AJPEPU$EJP(-(-%=Oj EJPKJqio BNKII]opan*`^k*Ouo?khqijoo_(I]opan*`^k*Ouo?khqijoo_.7 EJOANPEJPKp$_-(_.% OAHA?Pj(. BNKIJqio @NKLP=>HAJqio7 /
iÊvÜ}ÊOAHA?P statement fetches only 1 out of 10,000 rows from the table: OAHA?P_(_. BNKIpSDANA_-9with the graphical execution plan shown in Figure 4-21 and the output of OAPOP=PEOPE?OEK as follows: P]^ha#p-#*O_]j_kqjp-(hkce_]hna]`o..
Figure 4-21. Execution plan with no index
129
130
C HAPTER 4
IND EX A NA L YS IS
Ã`iÀ}ÊÌ
iÊÃ>ÊÃâiÊvÊÌ
iÊÀiÃÕÌÊÃiÌÊÀiÌÀiÛi`ÊLÞÊÌ
iÊ«ÀiVi`}ÊOAHA?P statement, a nonclustered column on _- can be a good choice: ?NA=PAJKJ?HQOPANA@EJ@ATej_hKJp-$_-% You can run the same OAHA?PÊV>`Ê>}>°Ê-ViÊÀiÌÀiÛ}Ê>ÊÃ>ÊÕLiÀÊvÊÀÜÃÊ through a nonclustered index is more economical than a table scan, the optimizer used the nonclustered index on column _-]Ê>ÃÊÃ
ÜÊÊ}ÕÀiÊ{ÓÓ°Ê/
iÊÕLiÀÊvÊ}V>ÊÀi>`ÃÊ reported by OP=PEOPE?OEK is as follows: P]^ha#p-#*O_]j_kqjp-(hkce_]hna]`o/
Figure 4-22. Execution plan with a nonclustered index Even though retrieving a small result set using a column with high selectivity is a good pointer toward creating a nonclustered index on the column, a clustered index on the same VÕÊV>ÊLiÊiµÕ>ÞÊLiivV>ÊÀÊiÛiÊLiÌÌiÀ°Ê/ÊiÛ>Õ>ÌiÊ
ÜÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊV>ÊLiÊ more beneficial than the nonclustered index, create a clustered index on the same column: ?NA=PA?HQOPANA@EJ@ATe_hKJp-$_-% ,ÕÊÌ
iÊÃ>iÊOAHA?P command again. From the resultant execution plan (see Figure 4-22) of the preceding OAHA?P statement, you can see that the optimizer used the clustered index ÃÌi>`ÊvÊÌ
iÊVÕÃÌiÀi`Ê`iÝ®ÊiÛiÊvÀÊ>ÊÃ>ÊÀiÃÕÌÊÃiÌ°Ê/
iÊÕLiÀÊvÊ}V>ÊÀi>`ÃÊvÀÊ the OAHA?P statement decreased from three to two (Figure 4-23): P]^ha#p-#*O_]j_kqjp-(hkce_]hna]`o.
Figure 4-23. Execution plan with a clustered index
Note Even though a clustered index can outperform a nonclustered index in many instances of data retrieval, a table can have only one clustered index. Therefore, reserve the clustered index for a situation in which it can be of the greatest benefit.
CHAPTER 4
I N D E X A N A LY S I S
Benefits of a Nonclustered Index over a Clustered Index As you learned in the previous section, a nonclustered index is preferred over a clustered index in the following situations: Ê
UÊ 7
iÊÌ
iÊ`iÝÊiÞÊÃâiÊÃÊ>À}i°
Ê
UÊ /Ê>Û`ÊÌ
iÊÛiÀ
i>`ÊVÃÌÊ>ÃÃV>Ìi`ÊÜÌ
Ê>ÊVÕÃÌiÀi`Ê`iÝÊÃViÊÀiLÕ`}ÊÌ
iÊ clustered index rebuilds all the nonclustered indexes of the table.
Ê
UÊ /ÊÀiÃÛi blocking by having a database reader work on the pages of a nonclustered index, while a database writer modifies other columns (not included in the nonclustered index) in the data page. In this case, the writer working on the data page won’t block a reader that can get all the required column values from the nonclustered index ÜÌ
ÕÌÊ
ÌÌ}ÊÌ
iÊL>ÃiÊÌ>Li°Ê½ÊiÝ«>ÊÌ
ÃÊÊ`iÌ>ÊÊ
>«ÌiÀÊ£Ó°
Ê
UÊ 7
iÊ>ÊÌ
iÊVÕÃÊvÀÊ>ÊÌ>Li®ÊÀiviÀÀi`ÊÌÊLÞÊ>ʵÕiÀÞÊV>ÊLiÊÃ>viÞÊ>VVdated in the nonclustered index itself, as explained in this section.
As already established, the data-retrieval performance when using a nonclustered index is generally poorer than that when using a clustered index, because of the cost associated in jumping from the nonclustered index rows to the data rows in the base table. In cases where the jump to the data rows is not required, the performance of a nonclustered index should be ÕÃÌÊ>ÃÊ}`Ê>ÃpÀÊiÛiÊLiÌÌiÀÊÌ
>p>ÊVÕÃÌiÀi`Ê`iÝ°Ê/
ÃÊÃÊ«ÃÃLiÊvÊÌ
iÊVÕÃÌiÀi`Ê index key includes all the columns required from the table. /ÊÕ`iÀÃÌ>`ÊÌ
iÊÃÌÕ>ÌÊÜ
iÀiÊ>ÊVÕÃÌiÀi`Ê`iÝÊV>ÊÕÌ«iÀvÀÊ>ÊVÕÃÌiÀi`Ê index, consider the following example. Assume for our purposes that you need to examine the VÀi`ÌÊV>À`ÃÊÌ
>ÌÊ>ÀiÊiÝ«À}ÊLiÌÜiiÊÌ
iÊÌ
ÃÊvÊÕiÊ>`Ê-i«ÌiLiÀÊvÊÓään°Ê9ÕÊ>ÞÊ have a query that returns a large number of rows and looks like this: OAHA?P__*?na`ep?]n`E@ (__*?]n`Jqi^an (__*AtlIkjpd (__*AtlUa]n BNKIO]hao*?na`ep?]n`__ SDANA__*AtlIkjpd>APSAAJ2=J@5 =J@__*AtlUa]n9.,,4 KN@AN>U__*AtlIkjpd /
iÊvÜ}Ê>ÀiÊÌ
iÊÉ"ÊÀiÃÕÌÃ]Ê>`Ê}ÕÀiÊ{Ó{ÊÃ
ÜÃÊÌ
iÊiÝiVÕÌÊ«>\ P]^ha#?na`ep?]n`#*O_]j_kqjp-(hkce_]hna]`o-45
Figure 4-24. Execution plan scanning the clustered index /
iÊVÕÃÌiÀi`Ê`iÝÊÃÊÊÌ
iÊ«À>ÀÞÊiÞ]Ê>`Ê>Ì
Õ}
ÊÃÌÊ>VViÃÃÊ>}>ÃÌÊÌ
iÊÌ>LiÊ>ÞÊ be through that key, making the index useful, the cluster in this instance is just not performing
131
132
C HAPTER 4
IND EX A NA L YS IS
in the way you need. Although you could expand the definition of the index to include all the other columns in the query, they’re not really needed to make the cluster function, and they would interfere with the operation of the primary key. In this instance, creating a different index is in order: ?NA=PAJKJ?HQOPANA@EJ@ATetPaopKJO]hao*?na`ep?]n`$AtlIkjpd(AtlUa]n(?]n`Jqi^an% Now when the query is run again, this is the result: P]^ha#?na`ep?]n`#*O_]j_kqjp-(hkce_]hna]`o/, Figure 4-25 shows the corresponding execution plan.
Figure 4-25. Execution plan with a nonclustered index In this case, the OAHA?P statement doesn’t include any column that requires a jump from the nonclustered index page to the data page of the table, which is what usually makes a nonVÕÃÌiÀi`Ê`iÝÊVÃÌiÀÊÌ
>Ê>ÊVÕÃÌiÀi`Ê`iÝÊvÀÊ>Ê>À}iÊÀiÃÕÌÊÃiÌÊ>`ÉÀÊÃÀÌi`ÊÕÌ«ÕÌ°Ê/
ÃÊ kind of nonclustered index is called a covering index.
i>ÊÕ«ÊÌ
iÊ`iÝÊ>vÌiÀ the testing is done: @NKLEJ@ATO]hao*?na`ep?]n`*etPaop
Advanced Indexing Techniques A few of the more advanced indexing techniques that you can also consider are as follows: Ê
UÊ Covering indexes\Ê/
iÃi were introduced in the preceding section.
Ê
UÊ Index intersections\Ê1Ãi multiple nonclustered indexes to satisfy all the column requirements (from a table) for a query.
Ê
UÊ Index joins\Ê1Ãi the index intersection and covering index techniques to avoid hitting the base table.
Ê
UÊ Filtered indexes\Ê/ÊLi able to index fields with odd data distributions or sparse columns, a filter can be applied to an index so that it indexes only some data.
Ê
UÊ Indexed views\Ê/
à materializes the output of a view on disk. I cover these topics in more detail in the following sections.
Covering Indexes A covering index is a nonclusteredÊ`iÝÊLÕÌÊÕ«Ê>ÊÌ
iÊVÕÃÊÀiµÕÀi`ÊÌÊÃ>ÌÃvÞÊ>Ê-+Ê query without going to the base table. If a query encounters an index and does not need to refer to the underlying data table at all, then the index can be considered a covering index.
CHAPTER 4
I N D E X A N A LY S I S
For example, in the following OAHA?P statement, irrespective of where the columns are referred, all the columns (Op]paLnkrej_aE` and Lkop]h?k`a) should be included in the nonclustered index to cover the query fully: OAHA?P]*Lkop]h?k`a BNKILanokj*=``naoo=O] SDANA]*Op]paLnkrej_aE@90. /
iÊ>ÊÌ
iÊÀiµÕÀi`Ê`>Ì>ÊvÀÊÌ
iʵÕiÀÞÊV>ÊLiÊLÌ>i`ÊvÀÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊ «>}i]ÊÜÌ
ÕÌÊ>VViÃÃ}ÊÌ
iÊ`>Ì>Ê«>}i°Ê/
ÃÊ
i«ÃÊ-+Ê-iÀÛiÀÊÃ>ÛiÊ}V>Ê>`Ê«
ÞÃV>ÊÀi>`ðÊvÊ you run the query, you’ll get the following I/O and execution time as well as the execution plan in Figure 4-26: P]^ha#=``naoo#*O_]j_kqjp-(hkce_]hna]`o-4 ?LQpeia9-1io(ah]loa`peia9/.io*
Figure 4-26. Query without a covering index Here you have a classic bookmark lookup with the GauHkkgql operator pulling the Lkop]h?k`a data from the clustered index and joining it with the Ej`atOaag operator against the ET[=``naoo[Op]paLnkrej_aE` index. Although you can re-create the index with both key columns, another way to make an index a covering index is to use the new EJ?HQ@AÊ«iÀ>ÌÀ°Ê/
ÃÊÃÌÀiÃÊ`>Ì>ÊÜÌ
ÊÌ
iÊ`iÝÊÜÌ
ÕÌÊV
>}}ÊÌ
iÊÃÌÀÕVÌÕÀiÊvÊÌ
iÊ`iÝÊÌÃiv°Ê1ÃiÊÌ
iÊvÜ}ÊÌÊÀiVÀi>ÌiÊÌ
iÊ`iÝ\ ?NA=PAJKJ?HQOPANA@EJ@ATWET[=``naoo[Op]paLnkrej_aE@YKJ WLanokjY*W=``naooY$WOp]paLnkrej_aE@Y=O?% EJ?HQ@A$Lkop]h?k`a% SEPD$ @NKL[ATEOPEJC9KJ% If you rerun the query, the execution plan (Figure 4-27), I/O, and execution time change: P]^ha#=``naoo#*O_]j_kqjp-(hkce_]hna]`o. ?LQpeia9,io(ah]loa`peia9,io*
Figure 4-27. Query with a covering index
133
134
C HAPTER 4
IND EX A NA L YS IS
/
iÊÀi>`ÃÊ
>ÛiÊ`À««i`ÊvÀÊ£nÊÌÊÓ]Ê>`ÊÌ
iÊiÝiVÕÌÊ«>ÊÃÊÕÃÌÊ>LÕÌÊ>ÃÊëiÊ>ÃÊ̽ÃÊ possible to be; it’s a single Ej`atOaag operation against the new and improved index, which is now covering. A covering index is a useful technique for reducing the number of logical reads of a query. Adding columns using the EJ?HQ@A statement makes this functionality easier to achieve without adding to the number of columns in an index or the size of the index key since the included columns are stored only at the leaf level of the index. /
iÊEJ?HQ@A is best used in the following cases: Ê
UÊ 9ÕÊ`½ÌÊÜ>ÌÊÌÊVÀi>ÃiÊÌ
iÊÃâiÊvÊÌ
iÊ`iÝÊiÞÃ]ÊLÕÌÊÞÕÊÃÌÊÜ>ÌÊÌÊ>iÊÌ
iÊ index a covering index.
Ê
UÊ 9Õ½ÀiÊ`iÝ}Ê>Ê`>Ì>ÊÌÞ«iÊÌ
>ÌÊV>½ÌÊLiÊ`iÝi`ÊiÝVi«ÌÊÌiÝÌ]ÊÌiÝÌ]Ê>`Ê>}iî°
Ê
UÊ 9Õ½ÛiÊ>Ài>`ÞÊiÝVii`i`ÊÌ
iÊ>ÝÕÊÕLiÀÊvÊiÞÊVÕÃÊvÀÊ>Ê`iÝÊ>Ì
Õ}
Ê this is a problem best avoided).
A Pseudoclustered Index /
i covering index physically organizes the data of all the indexed columns in a sequential À`iÀ°Ê/
ÕÃ]ÊvÀÊ>Ê`ÃÊÉ"Ê«iÀëiVÌÛi]Ê>ÊVÛiÀ}Ê`iÝÊÌ
>ÌÊ`iýÌÊÕÃiÊVÕ`i`ÊVÕÃÊ becomes a clustered index for all queries satisfied completely by the columns in the covering index. If the result set of a query requires a sorted output, then the covering index can be used to physically maintain the column data in the same order as required by the result set— it can then be used in the same way as a clustered index for sorted output. As shown in the previous example, covering indexes can give better performance than clustered indexes for µÕiÀiÃÊÀiµÕiÃÌ}Ê>ÊÀ>}iÊvÊÀÜÃÊ>`ÉÀÊÃÀÌi`ÊÕÌ«ÕÌ°Ê/
iÊVÕ`i`ÊVÕÃÊ>ÀiÊÌÊ«>ÀÌÊ of the key and therefore wouldn’t offer the same benefits for ordering as the key columns of the index.
Recommendations /ÊÌ>iÊ>`Û>Ì>}iÊvÊVÛiÀ}Ê`iÝiÃ]ÊLiÊV>ÀivÕÊÜÌ
ÊÌ
iÊVÕÊÃÌÊÊOAHA?P statements. 1ÃiÊ>ÃÊviÜÊVÕÃÊ>ÃÊ«ÃÃLiÊÌÊii«ÊÌ
iÊ`iÝÊiÞÊÃâiÊÃ>ÊvÀÊÌ
iÊVÛiÀ}Ê`iÝiÃ°Ê Add columns using the EJ?HQ@AÊÃÌ>ÌiiÌÊÊ«>ViÃÊÜ
iÀiÊÌÊ>iÃÊÃiÃi°Ê-ViÊ>ÊVÛiÀ}Ê index includes all columns used in a query, it has a tendency to be very wide, increasing the maintenance cost of the covering indexes. You must balance the maintenance cost with the performance gain that the covering index brings. If the number of bytes from all the columns in the index is small compared to the number of bytes in a single data row of that table and you are certain the query taking advantage of the covered index will be executed frequently, then it may be beneficial to use a covering index.
Tip Covering indexes can also help resolve blocking and deadlocks, as you will see in Chapters 12 and 13.
CHAPTER 4
I N D E X A N A LY S I S
ivÀiÊLÕ`}Ê>ÊÌÊvÊVÛiÀ}Ê`iÝiÃ]ÊVÃ`iÀÊ
ÜÊ-+Ê-iÀÛiÀÊV>ÊivviVÌÛiÞÊ>`Ê automatically create covering indexes for queries on the fly using index intersection.
Index Intersections If a table has ÕÌ«iÊ`iÝiÃ]ÊÌ
iÊ-+Ê-iÀÛiÀÊV>ÊÕÃiÊÕÌ«iÊ`iÝiÃÊÌÊiÝiVÕÌiÊ>ʵÕiÀÞ°Ê -+Ê-iÀÛiÀÊV>ÊÌ>iÊ>`Û>Ì>}i of multiple indexes, selecting small subsets of data based on each index and then performing an intersection of the two subsets (that is, returning only Ì
ÃiÊÀÜÃÊÌ
>ÌÊiiÌÊ>ÊÌ
iÊVÀÌiÀ>®°Ê-+Ê-iÀÛiÀÊV>ÊiÝ«ÌÊÕÌ«iÊ`iÝiÃÊÊ>ÊÌ>LiÊ>`Ê then employ a join algorithm to obtain the index intersection between the two subsets. In the following OAHA?P statement, for the SDANA clause columns the table has a nonclustered index on the O]haoLanokjE@ column, but it has no index on the Kn`an@]pa column: OAHA?Pokd*& BNKIO]hao*O]haoKn`anDa]`an=Ookd SDANAokd*O]haoLanokjE@9.32 =J@okd*Kn`an@]pa>APSAAJ#0+-+.,,.#]j`#3+-+.,,.# Figure 4-28 shows the execution plan for this query.
Figure 4-28. Execution plan with no index on the Kn`an@]pa column As you can see, the optimizer didn’t use the nonclustered index on the O]haoLanokjE@ VÕ°Ê-ViÊÌ
iÊÛ>ÕiÊvÊÌ
iÊKn`an@]pa column is also required, the optimizer chose the clustered index to fetch the value of all the referred columns. /Ê«ÀÛiÊÌ
iÊ«iÀvÀ>ViÊvÊÌ
iʵÕiÀÞ]ÊÌ
iÊKn`an@]pa column can be added to the nonclustered index on the O]haoLanokjE` column or defined as an include column on the same index. But in this real-world scenario, you may have to consider the following while modifying an existing index: Ê
UÊ ÌÊ>ÞÊÌÊLiÊ«iÀÃÃLiÊÌÊ`vÞÊ>ÊiÝÃÌ}Ê`iÝÊvÀÊÛ>ÀÕÃÊÀi>Ãð
Ê
UÊ /
iÊiÝÃÌ}ÊVÕÃÌiÀi`Ê`iÝÊiÞÊ>ÞÊLiÊ>Ài>`ÞʵÕÌiÊÜ`i°
Ê
UÊ /
iÊVÃÌÊvÊÌ
iʵÕiÀiÃÊÕÃ}ÊÌ
iÊiÝÃÌ}Ê`iÝÊÜÊLiÊ>vviVÌi`ÊLÞÊÌ
iÊ`vV>Ì° In such cases, you can create a new nonclustered index on the Kn`an@]pa column:
?NA=PAJKJ?HQOPANA@EJ@ATET[PaopKJO]hao*O]haoKn`anDa]`an$Kn`an@]pa% ,ÕÊÞÕÀÊOAHA?P command again. Figure 4-29 shows the resultant execution plan of the OAHA?P statement.
135
136
C HAPTER 4
IND EX A NA L YS IS
Figure 4-29. Execution plan with an index on the Kn`an@]pa column ÃÊÞÕÊV>ÊÃii]Ê-+Ê-iÀÛiÀÊiÝ«Ìi`ÊLÌ
ÊÌ
iÊVÕÃÌiÀi`Ê`iÝiÃÊ>ÃÊ`iÝÊÃiiÃÊÀ>Ì
iÀÊ than scans) and then employed an intersection algorithm to obtain the index intersection of the two subsets. It then did a GauHkkgql from the resulting data to retrieve the rest of the data not included in the indexes. /Ê«ÀÛiÊÌ
iÊ«iÀvÀ>ViÊvÊ>ʵÕiÀÞ]Ê-+Ê-iÀÛiÀÊV>ÊÕÃiÊÕÌ«iÊ`iÝiÃÊÊ>ÊÌ>Li°Ê /
iÀivÀi]ÊÃÌi>`ÊvÊVÀi>Ì}ÊÜ`iÊ`iÝÊiÞÃ]ÊVÃ`iÀÊVÀi>Ì}ÊÕÌ«iÊ>ÀÀÜÊ`iÝiÃ°Ê -+Ê-iÀÛiÀÊÜÊLiÊ>LiÊÌÊÕÃiÊÌ
iÊÌ}iÌ
iÀÊÜ
iÀiÊÀiµÕÀi`]Ê>`ÊÜ
iÊÌÊÀiµÕÀi`]ʵÕiÀiÃÊ LiivÌÊvÀÊ>ÀÀÜÊ`iÝiðÊ7
iÊVÀi>Ì}Ê>ÊVÛiÀ}Ê`iÝ]Ê`iÌiÀiÊÜ
iÌ
iÀÊÌ
iÊÜ`Ì
ÊvÊ the index will be acceptable and whether using include columns will get the job done. If not, then identify the existing nonclustered indexes that include most of the columns required by the covering index. You may already have two existing nonclustered indexes that jointly serve all the columns required by the covering index. If it is possible, rearrange the column order of the existing nonclustered indexes appropriately, allowing the optimizer to consider an index intersection between the two nonclustered indexes. At times, it is possible that you may have to create a separate nonclustered index for the following reasons: Ê
UÊ ,iÀ`iÀ}ÊÌ
iÊVÕÃÊÊiÊvÊÌ
iÊiÝÃÌ}Ê`iÝiÃÊÃÊÌÊ>Üi`°
Ê
UÊ -iÊvÊÌ
iÊVÕÃÊÀiµÕÀi`ÊLÞÊÌ
iÊVÛiÀ}Ê`iÝÊ>ÞÊÌÊLiÊVÕ`i`ÊÊÌ
iÊiÝÃÌing nonclustered indexes.
Ê
UÊ /
iÊÌÌ>ÊÕLiÀÊvÊVÕÃÊÊÌ
iÊÌÜÊiÝÃÌ}ÊVÕÃÌiÀi`Ê`iÝiÃÊ>ÞÊLiÊÀiÊ than the number of columns required by the covering index.
In such cases, you can create a nonclustered index on the remaining columns. If the combined column order of the new index and an existing nonclustered index meets the ÀiµÕÀiiÌÊvÊÌ
iÊVÛiÀ}Ê`iÝ]ÊÌ
iÊ«ÌâiÀÊÜÊLiÊ>LiÊÌÊÕÃiÊ`iÝÊÌiÀÃiVÌ°Ê7
iÊ identifying the columns and their order for the new index, try to maximize their benefit by keeping an eye on other queries, too. Drop the index that was created for the tests: @NKLEJ@ATO]hao*O]haoKn`anDa]`an*ET[Paop
CHAPTER 4
I N D E X A N A LY S I S
Index Joins /
iÊindex join is a variation of index intersection, where the covering index technique is applied to the index intersection. If no single index covers a query but multiple indexes Ì}iÌ
iÀÊV>ÊVÛiÀÊÌ
iʵÕiÀÞ]Ê-+Ê-iÀÛiÀÊV>ÊÕÃiÊ>Ê`iÝÊÊÌÊÃ>ÌÃvÞÊÌ
iʵÕiÀÞÊvÕÞÊÜÌ
out going to the base table. i̽ÃÊÊ>ÌÊÌ
ÃÊ`iÝ}ÊÌiV
µÕiÊ>ÌÊÜÀ°Ê>iÊ>ÊÃ}
ÌÊ`vV>ÌÊÌÊÌ
iʵÕiÀÞÊvÀÊ Ì
iʺ`iÝÊÌiÀÃiVÌûÊÃiVÌÊiÊÌ
Ã\ OAHA?Pokd*O]haoLanokjE@(okd*Kn`an@]pa BNKIO]hao*O]haoKn`anDa]`an=Ookd SDANAokd*O]haoLanokjE@9.32 =J@okd*Kn`an@]pa>APSAAJ#0+-+.,,.#]j`#3+-+.,,.# /
iÊiÝiVÕÌÊ«>ÊvÀÊÌ
ÃʵÕiÀÞÊÃÊÃ
ÜÊÊ}ÕÀiÊ{Îä]Ê>`ÊÌ
iÊÀi>`ÃÊ>ÀiÊ>ÃÊvÜÃ\ P]^ha#O]haoKn`anDa]`an#*O_]j_kqjp-(hkce_]hna]`o242
Figure 4-30. Execution plan with no index join As shown in Figure 4-30, the optimizer didn’t use the existing nonclustered index on the O]haoLanokjE@ÊVÕ°Ê-ViÊÌ
iʵÕiÀÞÊÀiµÕÀiÃÊÌ
iÊÛ>ÕiÊvÊÌ
iÊKn`an@]pa column also, the optimizer selected the clustered index to retrieve values for all the columns referred to in the query. If an index is created on the Kn`an@]pa column like this: ?NA=PAJKJ?HQOPANA@EJ@ATWET[PaopYKJWO]haoY*WO]haoKn`anDa]`anY$WKn`an@]paY=O?%7 and the query is rerun, then Figure 4-31 shows the result, and you can see the reads here: P]^ha#Skngp]^ha#*O_]j_kqjp,(hkce_]hna]`o, P]^ha#O]haoKn`anDa]`an#*O_]j_kqjp.(hkce_]hna]`o4
Figure 4-31. Execution plan with an index join /
iÊVL>ÌÊvÊÌ
iÊÌÜÊ`iÝiÃÊ>VÌÃÊiÊ>ÊVÛiÀ}Ê`iÝÊÀi`ÕV}ÊÌ
iÊÀi>`ÃÊ>}>ÃÌÊ the table from 686 to 8 because it’s using two Ej`atOaag operations joined together instead of a clustered index scan.
137
138
C HAPTER 4
IND EX A NA L YS IS
But what if the SDANA clause didn’t result in both indexes being used? Instead, you know that both indexes exist and that a seek against each would work like the previous query, so you choose to use an index hint: OAHA?Pokd*O]haoLanokjE@ (okd*Kn`an@]pa BNKIO]hao*O]haoKn`anDa]`an=OokdSEPD$EJ@AT$ET[Paop( ET[O]haoKn`anDa]`an[O]haoLanokjE`%% SDANAokd*Kn`an@]pa>APSAAJ#0+-+.,,.#=J@#3+-+.,,.#7 /
iÊÀiÃÕÌÃÊvÊÌ
ÃÊiÜʵÕiÀÞÊ>ÀiÊÃ
ÜÊÊ}ÕÀiÊ{ÎÓ]Ê>`ÊÌ
iÊÉ"ÊÃÊ>ÃÊvÜÃ\ P]^ha#Skngp]^ha#*O_]j_kqjp,(hkce_]hna]`o, P]^ha#O]haoKn`anDa]`an#*O_]j_kqjp.(hkce_]hna]`o2.
Figure 4-32. Execution plan with index join through a hint /
iÊÀi>`ÃÊ
>ÛiÊVi>ÀÞÊVÀi>Ãi`]Ê>`ÊÌ
iÊiÃÌ>Ìi`ÊVÃÌÃÊ>}>ÃÌÊÌ
iÊÛiÀÞÊÃ>iÊ`iÝiÃÊ Ì
>ÌÊ
>`ÊLiiÊ«Vi`ÊLÞÊÌ
iʵÕiÀÞÊ«ÌâiÀÊ>ÀiÊÜÊÕV
Ê
}
iÀ°ÊÃÌÊvÊÌ
iÊÌi]ÊÌ
iÊ optimizer makes very good choices when it comes to indexes and execution plans. Although query hints are available to allow you to take control from the optimizer, this control can cause as many problems as it solves. In attempting to force an index join as a performance benefit, instead the forced selection of indexes slowed down the execution of the query.
Note While generating a query execution plan, the SQL Server optimizer goes through the optimization phases not only to determine the type of index and join strategy to be used but also to evaluate the advanced indexing techniques such as index intersection and index join. Therefore, instead of creating wide covering indexes, consider creating multiple narrow indexes. SQL Server can use them together to serve as a covering index yet use them separately where required.
Filtered Indexes A filtered index is a nonclustered index that uses a filter, basically a SDANA clause, to create a highly selective set of keys against a column or columns that may not have good selectivity otherwise. For example, a column with a large number of jqhh values may be stored as a sparse column to reduce the overhead of those jqhh values. Adding a filtered index to the column will
CHAPTER 4
I N D E X A N A LY S I S
allow you to have an index available on the data that is not jqhh°Ê/
iÊLiÃÌÊÜ>ÞÊÌÊÕ`iÀÃÌ>`Ê this is to see it in action. /
iÊO]hao*O]haoKn`anDa]`an table has more than 30,000 rows. Of those rows, 27,000+ have a jqhh value in the Lqn_d]oaKn`anJqi^an column and the O]haoLanokjE` column. If you wanted to get a simple list of purchase order numbers, the query might look like this: OAHA?Pokd*Lqn_d]oaKn`anJqi^an (okd*Kn`an@]pa (okd*Odel@]pa (okd*O]haoLanokjE@ BNKIO]hao*O]haoKn`anDa]`an=Ookd SDANALqn_d]oaKn`anJqi^anHEGA#LK1!# =J@okd*O]haoLanokjE@EOJKPJQHH7 ,Õ}ÊÌ
iʵÕiÀÞÊÀiÃÕÌÃÊ]Ê>ÃÊÞÕÊ}
ÌÊiÝ«iVÌ]Ê>ÊVÕÃÌiÀi`Ê`iÝÊÃV>]Ê>`ÊÌ
iÊvÜing I/O and execution time, as shown in Figure 4-33: P]^ha#O]haoKn`anDa]`an#*O_]j_kqjp-(hkce_]hna]`o242 ?LQpeia9,io(ah]loa`peia92-5io*
Figure 4-33. Execution plan without an index /ÊvÝÊÌ
Ã]ÊÌÊÃÊ«ÃÃLiÊÌÊVÀi>ÌiÊ>Ê`iÝÊ>`ÊVÕ`iÊÃiÊvÊÌ
iÊVÕÃÊvÀÊÌ
iÊ query to make this a covering index: ?NA=PAJKJ?HQOPANA@EJ@ATET[PaopKJO]hao*O]haoKn`anDa]`an$Lqn_d]oaKn`anJqi^an (O]haoLanokjE`% EJ?HQ@A$Kn`an@]pa(Odel@]pa%7 7
iÊÞÕÊÀiÀÕÊÌ
iʵÕiÀÞ]ÊÌ
iÊ«iÀvÀ>ViÊ«ÀÛiiÌÊÃÊv>ÀÞÊÀ>`V>ÊÃiiÊ}ÕÀiÊ{ÎÎÊ and the I/O and time in the following result): P]^ha#O]haoKn`anDa]`an#*O_]j_kqjp-(hkce_]hna]`o1 ?LQpeia9,io(ah]loa`peia9-/5io*
Figure 4-34. Execution plan with a covering index As you can see, the covering index dropped the reads from 686 down to 5 and the time from 619 ms to 139 ms. Normally, this would be enough. Assume for a moment that this query has to be called frequently. Very frequently. Now, every bit of speed you can wring from it will pay dividends. Knowing that so much of the data in the indexed columns is jqhh, you can
139
140
C HAPTER 4
IND EX A NA L YS IS
adjust the index so that it filters out the jqhh values, which aren’t used by the index anyway, reducing the size of the tree and therefore the amount of searching required: ?NA=PAJKJ?HQOPANA@EJ@ATET[PaopKJ O]hao*O]haoKn`anDa]`an$Lqn_d]oaKn`anJqi^an (O]haoLanokjE`% EJ?HQ@A$Kn`an@]pa(Odel@]pa% SDANALqn_d]oaKn`anJqi^anEOJKPJQHH =J@O]haoLanokjE`EOJKPJQHH SEPD$@NKL[ATEOPEJC9KJ%7 /
iÊv>ÊÀÕÊvÊÌ
iʵÕiÀÞÊÃÊÛÃLiÊÊÌ
iÊvÜ}ÊÀiÃÕÌÊ>`ÊÊ}ÕÀiÊ{Îx\ P]^ha#O]haoKn`anDa]`an#*O_]j_kqjp-(hkce_]hna]`o0 ?LQpeia9,io(ah]loa`peia9/2io*
Figure 4-35. Execution plan with a filtered index Although in terms of sheer numbers reducing the reads from 5 to 4 isn’t much, it is a 20 percent reduction in the I/O cost of the query, and if this query were running hundreds or even thousands of times in a minute, as some queries do, that 20 percent reduction would be a }Ài>ÌÊ«>ÞvvÊ`ii`°Ê/
iÊÀi>Ê«>ÞvvÊÃÊÛÃLiÊÊÌ
iÊiÝiVÕÌÊÌi]ÊÜ
V
Ê`À««i`Ê>}>ÊvÀÊ 139 ms to 36 ms. Filtered indexes pay off in many ways: Ê
UÊ «ÀÛ}ÊÌ
iÊivvViVÞÊvʵÕiÀiÃÊLÞÊÀi`ÕV}ÊÌ
iÊÃâiÊvÊÌ
iÊ`iÝ
Ê
UÊ ,i`ÕV}ÊÃÌÀ>}iÊVÃÌÃÊLÞÊ>}ÊÃ>iÀÊ`iÝiÃ
Ê
UÊ ÕÌÌ}Ê`ÜÊÊÌ
iÊVÃÌÃÊvÊ`iÝÊ>Ìi>ViÊLiV>ÕÃiÊvÊÌ
iÊÀi`ÕVi`ÊÃâi
One of the first places suggested for their use is just like the previous example, eliminating jqhh values from the index. You can also isolate frequently accessed sets of data with a special index so that the queries against that data perform much faster. You can use the SDANA clause to filter data in a fashion similar to creating an indexed view (covered in more detail in the º`iÝi`Ê6iÜûÊÃiVÌ®ÊÜÌ
ÕÌÊÌ
iÊ`>Ì>Ê>Ìi>ViÊ
i>`>V
iÃÊ>ÃÃV>Ìi`ÊÜÌ
Ê`iÝi`Ê views by creating a filtered index that is a covering index, just like the earlier example. ÌiÀi`Ê`iÝiÃÊÀiµÕÀiÊ>ÊëiVvVÊÃiÌÊvÊ -ÊÃiÌÌ}ÃÊÜ
iÊÌ
iÞÊ>ÀiÊ>VViÃÃi`ÊÀÊVÀi>Ìi`\ Ê
UÊ KJ: =JOE[JQHHO, =JOE[L=@@EJC, =JOE[S=NJEJCO, =NEPD=>KNP, ?KJ?=P[JQHH[UEAH@O[JQHH, MQKPA@[E@AJPEBEAN
Ê
UÊ KBB: JQIANE?[NKQJ@=>KNP 7
iÊV«iÌi`]Ê`À«ÊÌ
i testing index:
@NKLEJ@ATO]hao*O]haoKn`anDa]`an*ET[Paop7
CHAPTER 4
I N D E X A N A LY S I S
Indexed Views A database viewÊÊ-+Ê-iÀÛiÀÊÃÊiÊ>ÊÛÀÌÕ>ÊÌ>LiÊÌ
>ÌÊÀi«ÀiÃiÌÃÊÌ
iÊÕÌ«ÕÌÊvÊ>ÊOAHA?P statement. You create a view using the ?NA=PAREAS statement, and you can query it exactly like a table. In general, a view doesn’t store any data—only the OAHA?P statement associated with it. Every time a view is queried, it further queries the underlying tables by executing its associated OAHA?P statement. A database view can be materialized on the disk by creating a unique clustered index on Ì
iÊÛiÜ°Ê-ÕV
Ê>ÊÛiÜÊÃÊÀiviÀÀi`ÊÌÊ>ÃÊ>Êindexed view or a materialized view. After a unique clustered index is created on the view, the view’s result set is materialized immediately and persisted in physical storage in the database, saving the overhead of performing costly operations during query execution. After the view is materialized, multiple nonclustered indexes can be created on the indexed view.
Benefit You can use an indexed view to increase the performance of a query in the following ways: Ê
UÊ }}Ài}>ÌÃÊV>ÊLiÊ«ÀiV«ÕÌi`Ê>`ÊÃÌÀi`ÊÊÌ
iÊ`iÝi`ÊÛiÜÊÌÊâiÊ expensive computations during query execution.
Ê
UÊ />LiÃÊV>ÊLiÊ«Àii`]Ê>`ÊÌ
iÊÀiÃÕÌ}Ê`>Ì>ÊÃiÌÊV>ÊLiÊ>ÌiÀ>âi`°
Ê
UÊ L>ÌÃÊvÊÃÊÀÊ>}}Ài}>ÌÃÊV>ÊLiÊ>ÌiÀ>âi`°
Overhead Indexed ÛiÜÃÊV>Ê«À`ÕViÊ>ÀÊÛiÀ
i>`ÊÊ>Ê"/*Ê`>Ì>L>Ãi°Ê-iÊvÊÌ
iÊÛiÀ
i>`ÃÊvÊ indexed views are as follows: Ê
UÊ ÞÊV
>}iÊÊÌ
iÊL>ÃiÊÌ>LiîÊ
>ÃÊÌÊLiÊÀiviVÌi`ÊÊÌ
iÊ`iÝi`ÊÛiÜÊLÞÊiÝiVÕÌ}ÊÌ
iÊ view’s OAHA?P statement.
Ê
UÊ ÞÊV
>}iÃÊÌÊ>ÊL>ÃiÊÌ>LiÊÊÜ
V
Ê>Ê`iÝi`ÊÛiÜÊÃÊ`ivi`Ê>ÞÊÌ>ÌiÊiÊÀÊ ÀiÊV
>}iÃÊÊÌ
iÊVÕÃÌiÀi`Ê`iÝiÃÊvÊÌ
iÊ`iÝi`ÊÛiÜ°Ê/
iÊVÕÃÌiÀi`Ê`iÝÊÜÊ also have to be changed if the clustering key is updated.
Ê
UÊ /
iÊ`iÝi`ÊÛiÜÊ>``ÃÊÌÊÌ
iÊ}}Ê>Ìi>ViÊÛiÀ
i>`ÊvÊÌ
iÊ`>Ì>L>Ãi°
Ê
UÊ ``Ì>ÊÃÌÀ>}iÊÃÊÀiµÕÀi`ÊÊÌ
iÊ`>Ì>L>Ãi° /
iÊrestrictions on creating an indexed view include the following:
Ê
UÊ /
iÊvÀÃÌÊ`iÝÊÊÌ
iÊÛiÜÊÕÃÌÊLiÊ>ÊÕµÕiÊVÕÃÌiÀi`Ê`iÝ°
Ê
UÊ
Ê
UÊ /
iÊÛiÜÊ`ivÌÊÕÃÌÊLiÊdeterministic—that is, it is able to return only one possible result for a given query. (A list of deterministic and nondeterministic functions is «ÀÛ`i`ÊÊ-+Ê-iÀÛiÀÊ ÃÊ"i°®
Ê
UÊ /
iÊ`iÝi`ÊÛiÜÊÕÃÌÊÀiviÀiViÊÞÊL>ÃiÊÌ>LiÃÊÊÌ
iÊÃ>iÊ`>Ì>L>Ãi]ÊÌÊÌ
iÀÊÛiÜð
Ê
UÊ /
iÊ`iÝi`ÊÛiÜÊ>ÞÊVÌ>Êv>ÌÊVÕðÊÜiÛiÀ]ÊÃÕV
ÊVÕÃÊV>ÌÊLiÊ included in the clustered index key.
VÕÃÌiÀi`Ê`iÝiÃÊÊ>Ê`iÝi`ÊÛiÜÊV>ÊLiÊVÀi>Ìi`ÊÞÊ>vÌiÀÊÌ
iÊÕµÕiÊ clustered index is created.
141
142
C HAPTER 4
IND EX A NA L YS IS
Ê
UÊ /
iÊ`iÝi`ÊÛiÜÊÕÃÌÊLiÊ>ÊÃV
i>ÊLÕ`ÊÌÊÌ
iÊÌ>LiÃÊÀiviÀÀi`ÊÌÊÊÌ
iÊÛiÜÊÌÊ prevent modifications of the table schema.
Ê
UÊ /
iÀiÊ>ÀiÊÃiÛiÀ>ÊÀiÃÌÀVÌÃÊÊÌ
iÊÃÞÌ>ÝÊvÊÌ
iÊÛiÜÊ`ivÌ°ÊÊÃÌÊvÊÌ
iÊÃÞÌ>ÝÊ Ì>ÌÃÊÊÌ
iÊÛiÜÊ`ivÌÊÃÊ«ÀÛ`i`ÊÊ-+Ê-iÀÛiÀÊ ÃÊ"i°®
Ê
UÊ /
iÊÃÌÊvÊOAP options that must be fixed are as follows: Ê UÊ KJ: =NEPD=>KNP, ?KJ?=P[JQHH[UEAH@O[JQHH, MQKPA@[E@AJPEBEAN, =JOE[JQHHO, =JOE[ L=@@EJC, and =JOE[S=NJEJC Ê UÊ KBB: JQIANE?[NKQJ@=>KNP
Usage Scenarios ,i«ÀÌ}ÊÃÞÃÌiÃÊLiivÌÊÌ
iÊÃÌÊvÀÊ`iÝi`ÊÛiÜðÊ"/*ÊÃÞÃÌiÃÊÜÌ
ÊvÀiµÕiÌÊÜÀÌiÃÊ may not be able to take advantage of the indexed views because of the increased maintenance VÃÌÊ>ÃÃV>Ìi`ÊÜÌ
ÊÕ«`>Ì}ÊLÌ
ÊÌ
iÊÛiÜÊ>`ÊÌ
iÊÕ`iÀÞ}ÊL>ÃiÊÌ>LiðÊ/
iÊiÌÊ«iÀvÀmance improvement provided by an indexed view is the difference between the total query execution savings offered by the view and the cost of storing and maintaining the view. An indexed view need not be referenced in the query for the query optimizer to use it `ÕÀ}ʵÕiÀÞÊiÝiVÕÌ°Ê/
ÃÊ>ÜÃÊiÝÃÌ}Ê>««V>ÌÃÊÌÊLiivÌÊvÀÊÌ
iÊiÜÞÊVÀi>Ìi`Ê `iÝi`ÊÛiÜÃÊÜÌ
ÕÌÊV
>}}ÊÌ
ÃiÊ>««V>ÌðÊ/
iʵÕiÀÞÊ«ÌâiÀÊVÃ`iÀÃÊ`iÝi`Ê views only for queries with nontrivial cost. i̽ÃÊÃiiÊ
ÜÊ`iÝi`ÊÛiÜÃÊÜÀÊÜÌ
ÊÌ
iÊvÜ}ÊiÝ>«i°Ê Ã`iÀÊÌ
iÊÌ
ÀiiʵÕiÀiÃÊ ÊÃÌ}ÃÊ{£ÊÌ
ÀÕ}
Ê{ΰ Listing 4-1. Mqanu-*omh OAHA?Pl*WJ]iaY=OLnk`q_pJ]ia (OQI$lk`*Kn`anMpu%=OKn`anMpu (OQI$lk`*Na_aera`Mpu%=ONa_aera`Mpu (OQI$lk`*Nafa_pa`Mpu%=ONafa_pa`Mpu BNKILqn_d]oejc*Lqn_d]oaKn`an@ap]eh=Olk` FKEJLnk`q_pekj*Lnk`q_p=Ol KJl*Lnk`q_pE@9lk`*Lnk`q_pE@ CNKQL>Ul*WJ]iaY
Listing 4-2. Mqanu.*omh OAHA?Pl*WJ]iaY=OLnk`q_pJ]ia (OQI$lk`*Kn`anMpu%=OKn`anMpu (OQI$lk`*Na_aera`Mpu%=ONa_aera`Mpu (OQI$lk`*Nafa_pa`Mpu%=ONafa_pa`Mpu BNKILqn_d]oejc*Lqn_d]oaKn`an@ap]eh=Olk` FKEJLnk`q_pekj*Lnk`q_p=Ol KJl*Lnk`q_pE@9lk`*Lnk`q_pE@ CNKQL>Ul*WJ]iaY D=REJC$OQI$lk`*Nafa_pa`Mpu%+OQI$lk`*Na_aera`Mpu%%:*,4
CHAPTER 4
I N D E X A N A LY S I S
Listing 4-3. Mqanu/*omh OAHA?Pl*WJ]iaY=OLnk`q_pJ]ia (OQI$lk`*Kn`anMpu%=OKn`anMpu (OQI$lk`*Na_aera`Mpu%=ONa_aera`Mpu (OQI$lk`*Nafa_pa`Mpu%=ONafa_pa`Mpu BNKILqn_d]oejc*Lqn_d]oaKn`an@ap]eh=Olk` FKEJLnk`q_pekj*Lnk`q_p=Ol KJl*Lnk`q_pE@9lk`*Lnk`q_pE@ SDANAl*WJ]iaYHEGA#?d]ej!# CNKQL>Ul*WJ]iaY All the three queries use the aggregation function OQI on columns of the Lqn_d]oaKn`an@ap]ehÊÌ>Li°Ê/
iÀivÀi]ÊÞÕÊV>ÊVÀi>ÌiÊ>Ê`iÝi`ÊÛiÜÊÌÊ«ÀiV«ÕÌiÊÌ
iÃiÊ aggregations and minimize the cost of these complex computations during query execution. ÃÌ}ÃÊ{{ÊÌ
ÀÕ}
Ê{ÈÊÃ
ÜÊÌ
iÊÕLiÀÊvÊ}V>ÊÀi>`ÃÊ«iÀvÀi`ÊLÞÊÌ
iÃiʵÕiÀiÃÊÌÊ access the appropriate tables. Listing 4-4. Logical Reads by MqanuP]^ha#Skngp]^ha#*O_]j_kqjp,(hkce_]hna]`o, P]^ha#Lnk`q_p#*O_]j_kqjp-(hkce_]hna]`o1 P]^ha#Lqn_d]oaKn`an@ap]eh#*O_]j_kqjp-(hkce_]hna]`o22 ?LQpeia9-1io(ah]loa`peia9-,,io*
Listing 4-5. Logical Reads by Mqanu. P]^ha#Skngp]^ha#*O_]j_kqjp,(hkce_]hna]`o, P]^ha#Lnk`q_p#*O_]j_kqjp-(hkce_]hna]`o1 P]^ha#Lqn_d]oaKn`an@ap]eh#*O_]j_kqjp-(hkce_]hna]`o22 ?LQpeia9-2io(ah]loa`peia9-,io*
Listing 4-6. Logical Reads by Mqanu/ P]^ha#Lqn_d]oaKn`an@ap]eh#*O_]j_kqjp1(hkce_]hna]`o450 P]^ha#Lnk`q_p#*O_]j_kqjp-(hkce_]hna]`o. ?LQpeia9,io(ah]loa`peia902io*
Ài>ÌiÊ>Ê`iÝi`ÊÛiÜÊÌÊ«ÀiV«ÕÌiÊÌ
iÊVÃÌÞÊV«ÕÌ>ÌÃÊ>`ÊÊÌ
iÊÌ>LiÃÊ (k`Reas*omh in the download): EBATEOPO$OAHA?P& BNKIouo*reaso SDANAk^fa_p[e`9K>FA?P[E@$J#WLqn_d]oejcY*WEj`ata`ReasY#%% @NKLREASWLqn_d]oejcY*WEj`ata`ReasY CK ?NA=PAREASLqn_d]oejc*Ej`ata`Reas SEPDO?DAI=>EJ@EJC
143
144
C HAPTER 4
IND EX A NA L YS IS
=OOAHA?Plk`*Lnk`q_pE@ (OQI$lk`*Kn`anMpu%=OKn`anMpu (OQI$lk`*Na_aera`Mpu%=ONa_aera`Mpu (OQI$lk`*Nafa_pa`Mpu%=ONafa_pa`Mpu (?KQJP[>EC$&%=OW?kqjpY BNKILqn_d]oejc*Lqn_d]oaKn`an@ap]eh=Olk` CNKQL>Ulk`*Lnk`q_pE@ CK ?NA=PAQJEMQA?HQOPANA@EJ@ATerKJLqn_d]oejc*Ej`ata`Reas$Lnk`q_pE`%7 CK
iÀÌ>ÊVÃÌÀÕVÌÃÊÃÕV
Ê>ÃÊ=RC are disallowed. (For the complete list of disallowed conÃÌÀÕVÌÃ]ÊÀiviÀÊÌÊ-+Ê-iÀÛiÀÊ ÃÊ"i°®ÊvÊ>}}Ài}>ÌiÃÊ>ÀiÊVÕ`i`ÊÊÌ
iÊÛiÜ]Ê>ÃÊÊÌ
ÃÊi]Ê you must include ?KQJP[>EC by default. /
iÊ`iÝi`ÊÛiÜÊ>ÌiÀ>âiÃÊÌ
iÊÕÌ«ÕÌÊvÊÌ
iÊ>}}Ài}>ÌiÊvÕVÌÃÊÊÌ
iÊ`ðÊ/
ÃÊ eliminates the need for computing the aggregate functions during the execution of a query interested in the aggregate outputs. For example, Mqanu/*omh requests the sum of Na_aera`Mpu and Nafa_pa`Mpu for certain products from the Lqn_d]oaKn`an@ap]eh table. Because these values are materialized in the indexed view for every product in the Lqn_d]oaKn`an@ap]eh table, you can fetch these preaggregated values using the following OAHA?P statement on the indexed view: OAHA?PLnk`q_pE@( Na_aera`Mpu( Nafa_pa`Mpu BNKILqn_d]oejc*Ej`ata`Reas As shown in the execution plan in Figure 4-36, the OAHA?P statement retrieves the values directly from the indexed view without accessing the base table (Lqn_d]oaKn`an@ap]eh).
Figure 4-36. Execution plan with an indexed view /
iÊ`iÝi`ÊÛiÜÊLiivÌÃÊÌÊÞÊÌ
iʵÕiÀiÃÊL>Ãi`ÊÊÌ
iÊÛiÜÊ`ÀiVÌÞÊLÕÌÊ>ÃÊÌ
iÀÊ queries that may be interested in the materialized data. For example, with the indexed view in place, the three queries on Lqn_d]oaKn`an@ap]eh benefit without being rewritten (see the execution plan in Figure 4-37 for the execution plan from the first query), and the number of }V>ÊÀi>`ÃÊ`iVÀi>ÃiÃ]Ê>ÃÊÃ
ÜÊÊÃÌ}ÃÊ{ÇÊÌ
ÀÕ}
Ê{° Listing 4-7. Logical Reads by Mqanu- with an Indexed View P]^ha#Lnk`q_p#*O_]j_kqjp-(hkce_]hna]`o-/ P]^ha#Ej`ata`Reas#*O_]j_kqjp-(hkce_]hna]`o0 ?LQpeia9,io(ah]loa`peia904io*
CHAPTER 4
I N D E X A N A LY S I S
Listing 4-8. Logical Reads by Mqanu. with an Indexed View P]^ha#Lnk`q_p#*O_]j_kqjp-(hkce_]hna]`o-/ P]^ha#Ej`ata`Reas#*O_]j_kqjp-(hkce_]hna]`o0 ?LQpeia9,io(ah]loa`peia9,io*
Listing 4-9. Logical Reads by Mqanu/ with an Indexed View P]^ha#Ej`ata`Reas#*O_]j_kqjp,(hkce_]hna]`o-, P]^ha#Lnk`q_p#*O_]j_kqjp-(hkce_]hna]`o. ?LQpeia9,io(ah]loa`peia9-2io*
Figure 4-37. Execution plan with the indexed view automatically used Even though the queries are not modified to refer to the new indexed view, the optimizer ÃÌÊÕÃiÃÊÌ
iÊ`iÝi`ÊÛiÜÊÌÊ«ÀÛiÊ«iÀvÀ>Vi°Ê/
ÕÃ]ÊiÛiÊiÝÃÌ}ʵÕiÀiÃÊÊÌ
iÊ`>Ì>base application can benefit from new indexed views without any modifications to the queries.
Index Compression Data and `iÝÊV«ÀiÃÃÊÜ>ÃÊÌÀ`ÕVi`ÊÊ-+Ê-iÀÛiÀÊÓäänÊ>Û>>LiÊÊÌ
iÊ ÌiÀ«ÀÃiÊ and Developer Editions). Compressing an index means getting more key information onto >ÊÃ}iÊ«>}i°Ê/
ÃÊV>Êi>`ÊÌÊÃiÀÕÃÊ«iÀvÀ>ViÊ«ÀÛiiÌÃÊLiV>ÕÃiÊviÜiÀÊ«>}iÃÊ >`ÊviÜiÀÊ`iÝÊiÛiÃÊ>ÀiÊii`i`ÊÌÊÃÌÀiÊÌ
iÊ`iÝ°Ê/
iÀiÊÜÊLiÊÛiÀ
i>`ÊÊÌ
iÊ *1Ê>`Ê memory as the key values in the index are compressed and decompressed, so this may not be a solution for all indexes. By default, an index will be not be compressed. You have to explicitly call for the index ÌÊLiÊV«ÀiÃÃi`ÊÜ
iÊÞÕÊVÀi>ÌiÊÌ
iÊ`iÝ°Ê/
iÀiÊ>ÀiÊÌÜÊÌÞ«iÃÊvÊV«ÀiÃÃ\ÊÀÜÊ>`Ê page-level compression. Nonleaf pages in an index receive no compression under the page ÌÞ«i°Ê/ÊÃiiÊÌÊÊ>VÌ]ÊVÃ`iÀÊÌ
iÊvÜ}Ê`iÝÊ`½ÌÊÀÕÊÌ
ÃÊV`iÊÃViÊÌ
iÊ`iÝÊ already exists): ?NA=PAJKJ?HQOPANA@EJ@ATWET[PaopYKJ WLanokjY*W=``naooY$W?epuY=O?(WLkop]h?k`aY=O?% /
ÃÊ`iÝÊÜ>ÃÊVÀi>Ìi`Êi>ÀiÀÊÊÌ
iÊV
>«ÌiÀ°ÊvÊÞÕÊÜiÀiÊÌÊÀiVÀi>ÌiÊÌÊ>ÃÊ`ivi`Ê
iÀi ?NA=PAJKJ?HQOPANA@EJ@ATET[?kil[Paop KJLanokj*=``naoo$W?epuY(WLkop]h?k`aY% SEPD$@=P=[?KILNAOOEKJ9NKS%7
145
146
C HAPTER 4
IND EX A NA L YS IS
this creates a row type of compression on an index with the same two columns as the first test index ET[Paop.
Ài>ÌiÊiÊÀiÊ`iÝ\ ?NA=PAJKJ?HQOPANA@EJ@ATET[?kil[L]ca[Paop KJLanokj*=``naoo$W?epuY(WLkop]h?k`aY% SEPD$@=P=[?KILNAOOEKJ9L=CA%7 /ÊiÝ>iÊÌ
iÊ`iÝiÃÊLi}ÊÃÌÀi`]Ê`vÞ the original query against ouo*`i[`^[ej`at[ lduoe_]h[op]po to add another column, _kilnaooa`[l]ca[_kqjp: OAHA?Pe*J]ia (e*pula[`ao_ (o*l]ca[_kqjp (o*na_kn`[_kqjp (o*ej`at[harah (_kilnaooa`[l]ca[_kqjp BNKIouo*ej`ataoe FKEJouo*`i[`^[ej`at[lduoe_]h[op]po$@>[E@$J#=`rajpqnaSkngo.,,4#%( K>FA?P[E@$J#Lanokj*=``naoo#%(JQHH( JQHH(#@AP=EHA@#%=Oo KJe*ej`at[e`9o*ej`at[e` SDANAe*K>FA?P[E@9K>FA?P[E@$J#Lanokj*=``naoo#% ,Õ}ÊÌ
iʵÕiÀÞ]ÊÞÕÊ}iÌÊÌ
iÊÀiÃÕÌÃÊÊ}ÕÀiÊ{În°
Figure 4-38. ouo*`i[`^[ej`at[lduoe_]h[op]po output about compressed indexes For this index, you can see that the page compression was able move the index from 99 «>}iÃÊ`ÜÊÌÊÓÇ]ÊvÊÜ
V
ÊÓÈÊÜiÀiÊV«ÀiÃÃi`°Ê/
iÊÀÜÊÌÞ«iÊV«ÀiÃÃÊÊÌ
ÃÊÃÌ>ViÊ didn’t make much of a difference; in fact, the overhead associated with compression increased the number of pages it took to define the index. /ÊÃiiÊÌ
iÊV«ÀiÃÃÊÊ>VÌ]ÊÀÕÊÌ
iÊvÜ}ʵÕiÀÞ\
CHAPTER 4
I N D E X A N A LY S I S
OAHA?P]*?epu(]*Lkop]h?k`a BNKILanokj*=``naoo=O] SDANA]*?epu9#Jaspkj# =J@]*Lkop]h?k`a9#R.I-J3# /
iÊ«ÌâiÀÊV
Ãi]ÊÊÞÊÃÞÃÌi]ÊÌÊÕÃiÊÌ
iÊET[?kil[L]ca[Paop index. Even if I forced it to use the ET[Paop index, thusly, OAHA?P]*?epu(]*Lkop]h?k`a BNKILanokj*=``naoo=O]SEPD$EJ@AT9ET[Paop% SDANA]*?epu9#Jaspkj# =J@]*Lkop]h?k`a9#R.I-J3# Ì
iÊ«iÀvÀ>ViÊÜ>ÃÊ`iÌV>°Ê-Ê>Ì
Õ}
ÊiÊ`iÝÊÃÊÌ>}ÊÕ«ÊÀ>`V>ÞÊiÃÃÊÀÊÊ approximately one quarter as many pages, it’s done at no cost in performance.
«ÀiÃÃÊ
>ÃÊ>ÊÃiÀiÃÊvÊ«>VÌÃÊÊÌ
iÀÊ«ÀViÃÃiÃÊÜÌ
Ê-+Ê-iÀÛiÀ]ÊÃÊvÕÀÌ
iÀÊ understanding of the possible impacts as well as the possible benefits should be explored thoroughly prior to implementation.
i>ÊÕ«ÊÌ
iÊ`iÝiÃÊ>vÌiÀ you finish testing: @NKLEJ@ATLanokj*=``naoo*ET[Paop7 @NKLEJ@ATLanokj*=``naoo*ET[?kil[Paop7 @NKLEJ@ATLanokj*=``naooET[?kil[L]ca[Paop7
Special Index Types As special data typesÊ>`ÊÃÌÀ>}iÊiV
>ÃÃÊ>ÀiÊÌÀ`ÕVi`ÊÌÊ-+Ê-iÀÛiÀÊLÞÊVÀÃvÌ]Ê methods for indexing these special storage types are also developed. Explaining all the details possible for each of these special index types is outside the scope of the book. In the following sections, I introduce the basic concepts of each index type in order to facilitate the possibility of their use in tuning your queries.
Full-Text You can store largeÊ>ÕÌÃÊvÊÌiÝÌÊÊ-+Ê-iÀÛiÀÊLÞÊÕÃ}ÊÌ
iÊI=T value in the R=N?D=N, JR=N?D=N, ?D=N, and J?D=N fields. A normal clustered or nonclustered index against these large fields would be unsupportable because a single value can far exceed the page size within an `iÝ°Ê-]Ê>Ê`vviÀiÌÊiV
>ÃÊvÊ`iÝ}ÊÌiÝÌÊÃÊÌÊÕÃiÊÌ
iÊÕ/iÝÌÊ }i]ÊÜ
V
ÊÕÃÌÊLiÊ running to work with full-text indexes. You can also build a full-text index on R=N>EJ=NU data. 9ÕÊii`ÊÌÊ
>ÛiÊiÊVÕÊÊÌ
iÊÌ>LiÊÌ
>ÌÊÃÊÕµÕi°Ê/
iÊLiÃÌÊV>``>ÌiÃÊvÀÊ«iÀvÀmance are integers: EJP or >ECEJP°Ê/
ÃÊVÕÊÃÊÌ
iÊÕÃi`Ê>}ÊÜÌ
ÊÌ
iÊÜÀ`ÊÌÊ`iÌvÞÊ Ü
V
ÊÀÜÊÜÌ
ÊÌ
iÊÌ>LiÊÌÊLi}ÃÊÌ]Ê>ÃÊÜiÊ>ÃÊÌÃÊV>ÌÊÜÌ
ÊÌ
iÊvi`°Ê-+Ê-iÀÛiÀÊ allows for incremental changes, either change tracking or time based, to the full-text indexes as well as complete rebuilds. For more details, check out Pro Full-Text Search in SQL Server 2008ÊLÞÊ>ÀÞÊ ÌÌiÀÊ>`Ê V
>iÊ iÃÊ«ÀiÃÃ]ÊÓään®°
147
148
C HAPTER 4
IND EX A NA L YS IS
Spatial Introduced inÊ-+Ê-iÀÛiÀÊÓäänÊÃÊÌ
iÊ>LÌÞÊÌÊÃÌÀiÊë>Ì>Ê`>Ì>°Ê/
ÃÊ`>Ì>ÊV>ÊLiÊiÌ
iÀÊ>Ê geometry type or the very complex geographical type, literally identifying a point on the earth. /ÊÃ>ÞÊÌ
iÊi>ÃÌ]Ê`iÝ}ÊÌ
ÃÊÌÞ«iÊvÊ`>Ì>ÊÃÊV«V>Ìi`°Ê-+Ê-iÀÛiÀÊÃÌÀiÃÊÌ
iÃiÊ`iÝiÃÊÊ a flat B-tree, similar to regular indexes, except that it is also a hierarchy of four grids linked together. Each of the grids can be given a density of low, medium, or high, outlining how big i>V
Ê}À`ÊðÊÀÊÌ
iÀiÊÌÊLi}ÃÊÌÊ}iÌÊÌÀÕÞÊV«V>Ìi`°Ê-ÕvvViÊÌÊÃ>Þ]ÊÌ
iÀiÊ>ÀiÊiV
>ÃÃÊ to support indexing of the spatial data types so that different types of queries, such as finding when one object is within the boundaries or near another object, can benefit from performance increases inherent in indexing. A spatial index can be created only against a column of type geometry or geography. It has to be on a base table, it must have no indexed views, and the table must have a primary key. You can create up to 249 spatial indexes on any given column on a table. Different indexes >ÀiÊÕÃi`ÊÌÊ`iviÊ`vviÀiÌÊÌÞ«iÃÊvÊ`iÝÊLi
>ÛÀ°ÊÀiÊvÀ>ÌÊÃÊ>Û>>LiÊÊÌ
iÊLÊ Beginning Spatial with SQL Server 2008 by Alastair Aitchison (Apress, 2009).
XML Introduced asÊ>Ê`>Ì>ÊÌÞ«iÊÊ-+Ê-iÀÛiÀÊÓääx]Ê8ÊV>ÊLiÊÃÌÀi`ÊÌÊ>ÃÊÌiÝÌÊLÕÌÊ>ÃÊÜivÀi`Ê 8Ê`>Ì>ÊÜÌ
Ê-+Ê-iÀÛiÀ°Ê/
ÃÊ`>Ì>ÊV>ÊLiʵÕiÀiÃÊÕÃ}ÊÌ
iÊ8+ÕiÀÞÊ>}Õ>}iÊ>ÃÊÃÕ««ÀÌi`Ê LÞÊ-+Ê-iÀÛiÀ°Ê/Êi
>ViÊÌ
iÊ«iÀvÀ>ViÊV>«>LÌiÃ]Ê>ÊëiV>ÊÃiÌÊvÊ`iÝiÃÊ
>ÃÊLiiÊ `ivi`°ÊÊ8ÊVÕÊV>Ê
>ÛiÊiÊ«À>ÀÞÊ>`ÊÃiÛiÀ>ÊÃiV`>ÀÞÊ`iÝiðÊ/
iÊ«À>ÀÞÊ 8ÊÃ
Ài`ÃÊÌ
iÊ«À«iÀÌiÃ]Ê>ÌÌÀLÕÌiÃ]Ê>`ÊiiiÌÃÊvÊÌ
iÊ8Ê`>Ì>Ê>`ÊÃÌÀiÃÊÌÊ>ÃÊ>ÊÌiÀ>ÊÌ>Li°Ê/
iÀiÊÕÃÌÊLiÊ>Ê«À>ÀÞÊiÞÊÊÌ
iÊÌ>Li]Ê>`ÊÌ
>ÌÊ«À>ÀÞÊiÞÊÕÃÌÊLiÊVÕÃÌiÀi`Ê ÊÀ`iÀÊÌÊVÀi>ÌiÊ>Ê8Ê`iÝ°ÊvÌiÀÊÌ
iÊ8Ê`iÝÊÃÊVÀi>Ìi`]ÊÌ
iÊÃiV`>ÀÞÊ`iÝiÃÊV>ÊLiÊ VÀi>Ìi`°Ê/
iÃiÊ`iÝiÃÊ
>ÛiÊÌÞ«iÃÊL]pd, R]hqa, and Lnklanpu, depending on how you query the 8°ÊÀÊÀi details, check out Pro SQL Server 2008 XMLÊLÞÊV
>iÊ iÃÊ«ÀiÃÃ]ÊÓään®°
Additional Characteristics of Indexes Other index properties can affect performance, positively and negatively. A few of these behaviors are explored here.
Different Column Sort Order -+Ê-iÀÛiÀÊÃÕ««ÀÌÃÊVÀi>Ì}Ê>ÊV«ÃÌiÊ`iÝÊÜÌ
Ê>Ê`vviÀiÌÊÃÀÌÊÀ`iÀÊvÀÊÌ
iÊ`vviÀiÌÊVÕÃÊvÊÌ
iÊ`iÝ°Ê-Õ««ÃiÊÞÕÊÜ>ÌÊ>Ê`iÝÊÜÌ
ÊÌ
iÊvÀÃÌÊVÕÊÃÀÌi`ÊÊ>ÃVi`}ÊÀ`iÀÊ and the second column sorted in descending order. You could achieve this as follows: ?NA=PAJKJ?HQOPANA@EJ@ATe-KJp-$_-=O?(_.@AO?%
Index on Computed Columns You can create an index on a computed column, as long as the expression defined for the computed column meets certain restrictions, such as that it references columns only from the table containing the computed column and it is deterministic.
CHAPTER 4
I N D E X A N A LY S I S
Index on BIT Data Type Columns -+Ê-iÀÛiÀÊ>ÜÃ you to create an index on columns with the >EPÊ`>Ì>ÊÌÞ«i°Ê/
iÊ>LÌÞÊÌÊ create an index on a >EP data type column by itself is not a big advantage since such a column can have only two unique values. As mentioned previously, columns with such low selectivity (number of unique values) are not usually good candidates for indexing. However, this feature comes into its own when you consider covering indexes. Because covering indexes require including all the columns in the index, the ability to add the >EP data type column to an index allows covering indexes to include such a column, if required.
CREATE INDEX Statement Processed As a Query /
iÊ?NA=PAEJ@AT operationÊÃÊÌi}À>Ìi`ÊÌÊÌ
iʵÕiÀÞÊ«ÀViÃÃÀ°Ê/
iÊ«ÌâiÀÊV>ÊÕÃiÊ existing index(es) to reduce scan cost and sort while creating an index. />i]ÊvÀÊiÝ>«i]ÊÌ
iÊLanokj*=``naoo table. A nonclustered index exists on a number of columns: =``naooHeja-, =``naooHeja., ?epu, Op]paLnkrej_aE`, and Lkop]h?k`a. If you needed to run queries against the ?epu column with the existing index, you’ll get a scan of that index. Now create a new index like this: ?NA=PAEJ@ATET[PaopKJLanokj*=``naoo$?epu% You can see in Figure 4-39 that, instead of scanning the table, the optimizer chose to scan the index in order to create the new index because the column needed for the new index was contained within the other nonclustered index.
Figure 4-39. Execution plan for ?NA=PAEJ@AT
Parallel Index Creation -+Ê-iÀÛiÀÊÃÕ««ÀÌÃ parallel plans for a ?NA=PAEJ@ATÊÃÌ>ÌiiÌ]Ê>ÃÊÃÕ««ÀÌi`ÊÊÌ
iÀÊ-+Ê queries. On a multiprocessor machine, index creation won’t be restricted to a single processor but will benefit from the multiple processors. You can control the number of processors to be used in a ?NA=PAEJ@AT statement with the i]t`acnaakbl]n]hhaheoi configuration paramiÌiÀÊvÊ-+Ê-iÀÛiÀ°Ê/
iÊ`iv>ÕÌÊÛ>ÕiÊvÀÊÌ
ÃÊ«>À>iÌiÀÊÃÊä]Ê>ÃÊÞÕÊV>ÊÃiiÊLÞÊiÝiVÕÌ}ÊÌ
iÊ ol[_kjbecqna stored procedure: ATA?ol[_kjbecqna#i]t`acnaakbl]n]hhaheoi# /
iÊ`iv>ÕÌÊÛ>ÕiÊvÊäÊi>ÃÊÌ
>ÌÊ-+Ê-iÀÛiÀÊV>ÊÕÃiÊ>ÊÌ
iÊ>Û>>LiÊ *1ÃÊÊÌ
iÊÃÞÃÌiÊ vÀÊÌ
iÊ«>À>iÊiÝiVÕÌÊvÊ>Ê/-+ÊÃÌ>ÌiiÌ°Ê"Ê>ÊÃÞÃÌiÊÜÌ
ÊvÕÀÊ«ÀViÃÃÀÃ]ÊÌ
iÊ>Ýmum degree of parallelism can be set to 2 by executing ol[_kjbecqna: ATA?ol[_kjbecqna#i]t`acnaakbl]n]hhaheoi#(. NA?KJBECQNASEPDKRANNE@A
149
150
C HAPTER 4
IND EX A NA L YS IS
/
ÃÊ>ÜÃÊ-+Ê-iÀÛiÀÊÌÊÕÃiÊÕ«ÊÌÊÌÜÊ *1ÃÊvÀÊÌ
iÊ«>À>iÊiÝiVÕÌÊvÊ>Ê/-+ÊÃÌ>ÌiiÌ°Ê/
ÃÊVv}ÕÀ>ÌÊÃiÌÌ}ÊÌ>iÃÊivviVÌÊi`>ÌiÞ]ÊÜÌ
ÕÌÊ>ÊÃiÀÛiÀÊÀiÃÌ>ÀÌ° /
iÊquery hint I=T@KL can be used for the ?NA=PAEJ@AT statement. Also, be aware that the parallel ?NA=PAEJ@ATÊvi>ÌÕÀiÊÃÊ>Û>>LiÊÞÊÊ-+Ê-iÀÛiÀÊÓääxÊ>`ÊÓäänÊ ÌiÀ«ÀÃiÊ `Ìð
Online Index Creation /
iÊ`iv>ÕÌÊVÀi>ÌÊvÊ>Ê`iÝÊÃÊ`iÊ>ÃÊ>ÊvviÊ«iÀ>Ì°Ê/
ÃÊi>ÃÊÌ
>ÌÊiÝVÕÃÛiÊ locks are placed on the table, restricting user access while the index is created. It is possible ÌÊVÀi>ÌiÊÌ
iÊ`iÝiÃÊ>ÃÊ>ÊiÊ«iÀ>Ì°Ê/
ÃÊ>ÜÃÊÕÃiÀÃÊÌÊVÌÕiÊÌÊ>VViÃÃÊÌ
iÊ`>Ì>Ê Ü
iÊÌ
iÊ`iÝÊÃÊLi}ÊVÀi>Ìi`°Ê/
ÃÊViÃÊ>ÌÊÌ
iÊVÃÌÊvÊVÀi>Ã}ÊÌ
iÊ>ÕÌÊvÊÌiÊ>`Ê ÀiÃÕÀViÃÊÌÊÌ>iÃÊÌÊVÀi>ÌiÊÌ
iÊ`iÝ°Ê"iÊ`iÝÊ«iÀ>ÌÃÊ>ÀiÊ>Û>>LiÊÞÊÊ-+Ê-iÀÛiÀÊ 2005 and 2008 Enterprise Editions.
Considering the Database Engine Tuning Advisor A simple approach to indexing is to use the >Ì>L>ÃiÊ }iÊ/Õ}Ê`ÛÃÀÊÌÊ«ÀÛ`i`ÊLÞÊ -+Ê-iÀÛiÀ°Ê/
ÃÊÌÊÃÊ>ÊÕÃ>}iL>Ãi`ÊÌÊÌ
>ÌÊÃÊ>ÌÊ>Ê«>ÀÌVÕ>ÀÊÜÀ>`Ê>`ÊÜÀÃÊÜÌ
Ê the query optimizer to determine the costs associated with various index combinations. Based on the tool’s analysis, you can add or drop indexes as appropriate.
Note I will cover the Database Engine Tuning Advisor tool in more depth in Chapter 5.
Summary In this chapter, you learned that indexing is an effective method for reducing the number of logical reads and disk I/O for a query. Although an index may add overhead to action queries, even action queries such as QL@=PA and @AHAPA can benefit from an index. /Ê`iV`iÊÌ
i index key columns for a particular query, evaluate the SDANA clause and the join criteria of the query. Factors such as column selectivity, width, data type, and column À`iÀÊ>ÀiÊ«ÀÌ>ÌÊÊ`iV`}ÊÌ
iÊVÕÃÊÊ>Ê`iÝÊiÞ°Ê-ViÊ>Ê`iÝÊÃÊ>ÞÊÕÃivÕÊ in retrieving a small number of rows, the selectivity of an indexed column should be very high. It is important to note that nonclustered indexes contain the value of a clustered index key as their row locator, because this behavior greatly influences the selection of an index type. ÀÊLiÌÌiÀÊ«iÀvÀ>Vi]ÊÌÀÞÊÌÊVÛiÀÊ>ʵÕiÀÞÊvÕÞÊÕÃ}Ê>ÊVÛiÀ}Ê`iÝ°Ê-ViÊ-+Ê-iÀÛiÀÊ can benefit from multiple indexes, use the index intersection and index join techniques, and VÃ`iÀÊ
>Û}ÊÕÌ«iÊ>ÀÀÜÊ`iÝiÃÊÃÌi>`ÊvÊiÊÛiÀÞÊÜ`iÊ`iÝ°Ê7
iÊÜÀ}ÊÜÌ
Ê special data types, apply the indexes that work with those special data types in order to help performance. ÊÌ
iÊiÝÌÊV
>«ÌiÀ]ÊÞÕÊÜÊi>ÀÊÀiÊ>LÕÌÊÌ
iÊ >Ì>L>ÃiÊ }iÊ/Õ}Ê`ÛÃÀ]ÊÌ
iÊ -+Ê-iÀÛiÀ«ÀÛ`i`ÊÌÊÌ
>ÌÊV>Ê
i«ÊÞÕÊ`iÌiÀiÊÌ
iÊVÀÀiVÌÊ`iÝiÃÊÊ>Ê`>Ì>L>ÃiÊvÀÊ>Ê }ÛiÊ-+ÊÜÀ>`°
CHAPT ER
5
Database Engine Tuning Advisor S
QL Server’s performance largely depends upon having proper indexes on the database tables. However, as the workload and data change over time, the existing indexes may not be entirely appropriate, and new indexes may be required. The task of deciding upon the correct indexes is complicated by the fact that an index change that benefits one set of queries may be detrimental to another set of queries. To help you through this process, SQL Server provides a tool called the Database Engine Tuning Advisor. This tool helps identify an optimal set of indexes and statistics for a given workload without requiring an expert understanding of the database schema, workload, or SQL Server internals. It can also recommend tuning options for a small set of problem queries. In addition to the tool’s benefits, I cover its limitations in this chapter, because it is a tool that can cause more harm than good if used incorrectly. In this chapter, I cover the following topics: Ê
UÊ ÜÊÌ
iÊ >Ì>L>ÃiÊ }iÊ/Õ}Ê`ÛÃÀÊÜÀÃ
Ê
UÊ ÜÊÌÊÕÃiÊÌ
iÊ >Ì>L>ÃiÊ }iÊ/Õ}Ê`ÛÃÀÊÊ>ÊÃiÌÊvÊ«ÀLi>ÌVʵÕiÀiÃÊvÀÊ index recommendations, including how to define traces
Ê
UÊ /
iÊÌ>ÌÃÊvÊÌ
iÊ >Ì>L>ÃiÊ }iÊ/Õ}Ê`ÛÃÀ
Database Engine Tuning Advisor Mechanisms You can run the Database Engine Tuning Advisor directly by selecting Microsoft SQL Server 2008 Performance Tools Database Engine Tuning Advisor. You can also run it from the command prompt (`p]*ata), from SQL Profiler (Tools Database Engine Tuning Advisor), from a query in Management Studio (highlight the required query, and select Query Analyze Query in Database Engine Tuning Advisor), or from Management Studio (select Tools Database Engine Tuning Advisor). Once the tool is opened and you’re connected to a server, you should see a window like the one in Figure 5-1. I’ll run through the options to define and run an analysis in this section and then follow up in the next session with some detailed examples.
151
152
CHAPTER 5
DATABASE ENGINE TUNING ADVISOR
Figure 5-1. Selecting the server and database in the Database Engine Tuning Advisor The Database Engine Tuning Advisor is already connected to a server. From here, you begin to outline the workload and the objects you want to tune. Creating a session name is necessary to label the session for documentation purposes. Then you need to pick a workload, either a file or a table, and browse to the appropriate location. The workload is defined depending on how you launched the Database Engine Tuning Advisor. If you launched it from a query window, you would see a Query radio button, and the File and Table radio buttons would be disabled. You also have to define the Database for Workload Analysis setting and finally select a database to tune.
Tip The Database Engine Tuning Advisor recommends indexed views only for platforms that support them. SQL Server 2008 Enterprise Edition does, but Standard Edition doesn’t.
When you select a database, you can also select individual tables to be tuned by clicking the drop-down box on the right side of the screen; you’ll see a list of tables like those shown in Figure 5-2.
CHAPTER 5
DATABASE ENGINE TUNING ADVISOR
Figure 5-2. Clicking the boxes defines individual tables for tuning in the Database Engine Tuning Advisor. Once you define the workload, you need to select the Tuning Options tab, which is shown in Figure 5-3.
Figure 5-3. Defining options in the Database Engine Tuning Advisor You define the length of time you want the Database Engine Tuning Advisor to run by selecting Limit Tuning Time and then defining a date and time for the tuning to stop. The longer the Database Engine Tuning Advisor runs, the better recommendations it should make. You pick the type of physical design structures to be considered for creation by the Database Engine Tuning Advisor, and you can also set the partitioning strategy so that the tuning advisor knows whether it should consider partitioning the tables and indexes as part of the analysis. Just remember, partitioning isn’t necessarily a desirable outcome if your data and structures don’t warrant it. Finally, you can define the physical design structures that you want
153
154
CHAPTER 5
DATABASE ENGINE TUNING ADVISOR
left alone within the database. Changing these options will narrow or widen the choices that the Database Engine Tuning Advisor can make to improve performance. You can click the Advanced Options button to see even more options, as shown in Figure 5-4.
Figure 5-4. Advanced Tuning Options dialog box This dialog box allows you to limit the space of the recommendations and the number of columns that can be included in an index. Finally, you can define whether the new indexes or changes in indexes are done as an online or offline index operation. Once you’ve appropriately defined all of these settings, you can start the Database Engine Tuning Advisor by clicking the Start Analysis button. The sessions created are kept in the io`^ database for any server instance that you run the Database Engine Tuning Advisor against. It displays details about what is being analyzed and the progress made, which you can see in Figure 5-5.
Figure 5-5. Tuning progress
CHAPTER 5
DATABASE ENGINE TUNING ADVISOR
You’ll see more detailed examples of the progress displayed in the example analysis in the next session. After the analysis completes, you’ll get a list of recommendations (visible in Figure 5-6), and a number of reports become available. Table 5-1 describes the reports. Table 5-1. Database Engine Tuning Advisor Reports
Report Name
Report Description
Column Access
Lists the columns and tables referenced in the workload.
Database Access
Lists each database referenced in the workload and percentage of workload statements for each database.
Event Frequency
Lists all events in the workload ordered by frequency of occurrence.
Index Detail (Current)
Defines indexes and their properties referenced by the workload.
Index Detail (Recommended)
Is the same as the Index Detail (Current) report but shows the information about the indexes recommended by the Database Engine Tuning Advisor.
Index Usage (Current)
Lists the indexes and the percentage of their use referenced by the workload.
Index Usage (Recommended)
Is the same as the Index Usage (Current) report but from the recommended indexes.
Statement Cost
Lists the performance improvements for each statement if the recommendations are implemented.
Statement Cost Range
Breaks down the costs improvements by percentiles to show how much benefit you can achieve for any given set of changes.
Statement Detail
Lists the statements in the workload, their cost, and the reduced cost if the recommendations are implemented.
Statement-to-Index Relationship
Lists the indexes referenced by individual statements. Current and recommended versions of the report are available.
Table Access
Lists the tables referenced by the workload.
View-to-Table Relationship
Lists the tables referenced by materialized views.
Workload Analysis
Gives details about the workload, including the number of statements, the number of statements whose cost is decreased, and the number where the cost remains the same.
Database Engine Tuning Advisor Examples The best way to learn how to use the Database Engine Tuning Advisor is to use it. It’s not a terribly difficult tool to master, so I recommend opening it and getting started.
Tuning a Query You can use the Database Engine Tuning Advisor to recommend indexes for a complete database by using a workload that fairly represents all SQL activities. You can also use it to recommend indexes for a set of problematic queries.
155
156
CHAPTER 5
DATABASE ENGINE TUNING ADVISOR
To learn how you can use the Database Engine Tuning Advisor to get index recommendations on a set of problematic queries, say you have a simple query that is called rather frequently. Because of the frequency, you want a quick turnaround for some tuning. This is the query: OAHA?Pokd*@qa@]pa (okd*?qopkianE@ BNKIO]hao*O]haoKn`anDa]`an=Ookd SDANA@qa@]pa>APSAAJ#-+-+-53,#=J@#-+-+-53-# =J@Op]pqo:.,, To analyze the query, right-click it in the query window, and select Analyze Query in the Database Engine Tuning Advisor. The advisor opens with a window where you can change the session name to something meaningful. In this case, I chose Report Query Round 1 – 10/5/2008. The database and tables don’t need to be edited. The first tab, General, will look like Figure 5-6 when you’re done.
Figure 5-6. Query tuning general settings Because this query is important and tuning it is extremely critical to the business, I’m going to change some settings on the Tuning Options tab. For the purposes of the example, I’m going to let the Database Engine Tuning Advisor run for 15 minutes, but a more realistic approach here would be to let it run for an hour. I’m going to select the Include Filtered Indexes check box so that if a filtered index will help, it can be considered. I’m also going to switch the Partitioning Strategy to Employ setting from No Partitioning to Full Partitioning. Finally, I’m going to allow the Database Engine Tuning Advisor to come up with structural changes if it can find any that will help by switching from Keep All Existing PDS to Do Not Keep Any Existing PDS. Once completed, the Tuning Options tab will look like Figure 5-7.
CHAPTER 5
DATABASE ENGINE TUNING ADVISOR
Figure 5-7. Tuning Options tab adjusted After starting the analysis, the progress screen should appear. Although the settings were for 15 minutes of evaluations, it took only about a minute for it to evaluate this query. The initial recommendations were not good. As you can see in Figure 5-8, the Database Engine Tuning Advisor has recommended dropping a huge swath of indexes in the database. This is not the type of recommendation that you want when running the tool.
Figure 5-8. Query tuning initial recommendations
157
158
CHAPTER 5
DATABASE ENGINE TUNING ADVISOR
This is because the Database Engine Tuning Advisor assumes that the load being tested is the full load of the database. If there are indexes not being used, then they should be removed. This is a best practice and one that should be implemented on any database. However, in this case, this is a single query, not a full load of the system. To see whether the advisor can come up with a meaningful recommendation, you must start a new session. This time, I’ll adjust the options so that the Database Engine Tuning Advisor will not be able to drop any of the existing structure. This is set on the Tuning Options tab (shown earlier in Figure 5-7). There I’ll change the Physical Design Structure (PDS) to Keep in Database setting from Do Not Keep Any Existing PDS to Keep All Existing PDS. I’ll keep the running time the same because the evaluation worked well within the time frame. Running the Database Engine Tuning Advisor again, it finishes in less than a minute and displays the recommendations shown in Figure 5-9.
Figure 5-9. Query tuning recommendations The first time through, the Database Engine Tuning Advisor suggested dropping most of the indexes on the tables being tested and a bunch of the related tables. This time it suggests creating a covering index on the two columns referenced in the query. As outlined in Chapter 4, a covering index is one of the most performant methods of indexing. The Database Engine Tuning Advisor was able to recognize that creating an index with all the columns referenced by the query, a covering index, would perform best. Once you’ve received a recommendation, you should take a look at the proposed T-SQL command. Finally, you’ll want to apply the recommendation. Select Actions Evaluate Recommendations. This opens a new Database Engine Tuning Advisor session and allows you to evaluate whether the recommendations will work. It looks just like a regular evaluation report. Next select Actions Apply Recommendation. This opens a dialog box that allows you to apply the recommendation immediately or schedule the application (see Figure 5-10).
CHAPTER 5
DATABASE ENGINE TUNING ADVISOR
Figure 5-10. Apply Recommendations dialog box If you click the OK button, the Database Engine Tuning Advisor will apply the index to the database where you’ve been testing queries (see Figure 5-11).
Figure 5-11. A successful tuning session applied After you generate recommendations, you may want to, instead of applying them on the spot, save the T-SQL statements to a file and accumulate a series of changes for release to your production environment during scheduled deployment windows. Remember that applying indexes to tables, especially large tables, can cause a performance impact to processes actively running on the system while the index is being created. Although getting index suggestions one at a time is nice, it would be better to be able to get large swaths of the database checked all at once. That’s where tuning a trace workload comes in.
Tuning a Trace Workload Capturing a trace from the real-world queries that are running against a production server is a way to feed meaningful data to the Database Engine Tuning Advisor. (Capturing traces was covered in Chapter 3.) The easiest way to define a trace for use in the Database Engine Tuning Advisor is to implement the trace using the Tuning template. Start the trace on the system you need to tune. I generated three different loads by running queries in a loop from the omhlo*ata command prompt. This is the PowerShell command prompt with the SQL Server configuration settings. It gets installed with SQL Server.
159
160
CHAPTER 5
DATABASE ENGINE TUNING ADVISOR
These are the scripts run (mqanuhk]`*lo-): r]h9,7sdeha$ r]h)hp1,%w r]h''7Ejrkga)Omh_i` )OanranUkqnOanranJ]ia)@]p]^]oa=`rajpqnaSkngo.,,4 )EjlqpBehal]pdXmqanuhk]`*omhy r]h9,7sdeha$ r]h)hp.,,,,%w r]h''7Ejrkga)omh_i` )OanranUkqnOanranJ]ia)@]p]^]oa=`rajpqnaSkngo.,,4 )MqanuATA?`^k*qolCapAilhkuaaI]j]canoqoejaooAjpepuE@9 r]hy r]h9,7sdeha$ r]h)hp.,,,,%w r]h''7Ejrkga)omh_i` )OanranUkqnOanranJ]ia)@]p]^]oa=`rajpqnaSkngo.,,4 )MqanuATA?`^k*qolCapI]j]canAilhkuaaoqoejaooAjpepuE@9 r]hy The mqanuhk]`*omh file is also available in the download file. It’s just a collection of queries with various parameters to provide a simulated load.
Note For more information on PowerShell, check out Pro Windows PowerShell by Hristo Deshev (Apress, 2008).
Once you’ve created the trace file, open the Database Engine Tuning Advisor. It defaults to a file type, so you’ll only have to browse to the trace file location. Set the appropriate tuning options, and start the analysis. This time, it will take more than a minute to run (see Figure 5-12).
Figure 5-12. Database tuning engine in progress As you can see, simply passing any number to the qolCapAilhkuaaI]j]cano procedure, and others, can generate instances where no tables were referenced. The processing runs for about 15 minutes on my machine. Then it generates output, shown in Figure 5-13.
CHAPTER 5
DATABASE ENGINE TUNING ADVISOR
Figure 5-13. No recommendations After running all the queries through the Database Engine Tuning Advisor, the best recommendation it could currently come up with was to drop a couple of indexed views. That doesn’t mean there aren’t other possible improvements; it just means that the advisor is not always able to recognize all the possible improvements.
Database Engine Tuning Advisor Limitations The Database Engine Tuning Advisor recommendations are based on the input workload. If the input workload is not a true representation of the actual workload, then the recommended indexes may sometimes have a negative effect on some queries that are missing in the workload. But most important, as you saw in the second example of this chapter, the Database Engine Tuning Advisor may not recognize possible tuning opportunities. It has a sophisticated testing engine, but in some scenarios, its capabilities are limited. For a production server, you should ensure that the SQL trace includes a complete representation of the database workload. For most database applications, capturing a trace for a complete day usually includes most of the queries executed on the database. A few of the other considerations/limitations with the Database Engine Tuning Advisor are as follows:
161
162
C HAPTER 5
D ATA B A S E ENG INE TU NING A DVIS OR
Ê
UÊ Trace input using the OMH6>]p_d?kilhapa` event: As mentioned earlier, the SQL trace input to the Database Engine Tuning Advisor must include the OMH6>]p_d?kilhapa` event; otherwise, the wizard won’t be able to identify the queries in the workload.
Ê
UÊ Query distribution in the workload: In a workload, a query may be executed multiple times with the same parameter value. Even a small performance improvement to the most common query can make a bigger contribution to the performance of the overall workload, compared to a large improvement in performance of a query that is executed only once.
Ê
UÊ Index hints: Index hints in a SQL query can prevent the Database Engine Tuning Advisor from choosing a better execution plan. The wizard includes all index hints used in a SQL query as part of its recommendations. Because these indexes may not be optimal for the table, remove all index hints from queries before submitting the workload to the wizard, bearing in mind that you need to add them back in to see whether they do actually improve performance.
Summary As you learned in this chapter, the Database Engine Tuning Advisor is a useful tool for analyzing the effectiveness of existing indexes and recommending new indexes for a SQL workload. As the SQL workload changes over time, you can use this tool to determine which existing indexes are no longer in use and which new indexes are required to improve performance. It can be a good idea to run the wizard occasionally just to check that your existing indexes really are the best fit for your current workload. It also provides many useful reports for analyzing the SQL workload and the effectiveness of its own recommendations. Just remember that the limitations of the tool prevent it from spotting all tuning opportunities. If your database is in bad shape, this tool can give you a quick leg up. If you’re already monitoring and tuning your queries regularly, you may see no benefit from the recommendations of the Database Engine Tuning Advisor. Frequently, you will rely on nonclustered indexes to improve the performance of a SQL workload. This assumes that you’ve already assigned a clustered index to your tables. Because the performance of a nonclustered index is highly dependent on the cost of the bookmark lookup associated with the nonclustered index, you will see in the next chapter how to analyze and resolve a bookmark lookup.
CHAPT ER
6
Bookmark Lookup Analysis T
o maximize the benefit from nonclustered indexes, you must minimize the cost of the data retrieval as much as possible. A major overhead associated with nonclustered indexes is the cost of excessive key lookups, commonly known as bookmark lookups, which are a mechanism to navigate from a nonclustered index row to the corresponding data row in the clustered index or the base table. Therefore, it makes sense to look at the cause of bookmark lookups and to evaluate how to avoid this cost. In this chapter, I cover the following topics: Ê
UÊ /
iÊ«ÕÀ«ÃiÊvÊL>ÀÊÕ«Ã
Ê
UÊ À>ÜL>VÃÊvÊÕÃ}ÊL>ÀÊÕ«Ã
Ê
UÊ >ÞÃÃÊvÊÌ
iÊV>ÕÃiÊvÊL>ÀÊÕ«Ã
Ê
UÊ /iV
µÕiÃÊÌÊÀiÃÛiÊL>ÀÊÕ«Ã
Purpose of Bookmark Lookups 7
iÊ>Ê-+ʵÕiÀÞÊÀiµÕiÃÌà a small number of rows, the optimizer can use the nonclustered index, if available, on the column(s) in the SDANA or FKEJÊV>ÕÃiÊÌÊÀiÌÀiÛiÊÌ
iÊ`>Ì>°ÊvÊÌ
iʵÕiÀÞÊ refers to columns that are not part of the nonclustered index used to retrieve the data, then >Û}>ÌÊÃÊÀiµÕÀi`ÊvÀÊÌ
iÊ`iÝÊÀÜÊÌÊÌ
iÊVÀÀië`}Ê`>Ì>ÊÀÜÊÊÌ
iÊÌ>LiÊÌÊ>VViÃÃÊ these columns. For example, in the following OAHA?P statement, if the nonclustered index used by the «ÌâiÀÊ`iýÌÊVÕ`iÊ>ÊÌ
iÊVÕÃ]Ê>Û}>ÌÊÜÊLiÊÀiµÕÀi`ÊvÀÊ>ÊVÕÃÌiÀi`Ê index row to the data row in the base table to retrieve the value of those columns: OAHA?Pl*WJ]iaY (=RC$ok`*HejaPkp]h% BNKIO]hao*O]haoKn`an@ap]eh=Ook` FKEJLnk`q_pekj*Lnk`q_pl KJok`*Lnk`q_pE@9l*Lnk`q_pE@ SDANAok`*Lnk`q_pE@9332 CNKQL>Uok`*?]nneanPn]_gejcJqi^an (l*WJ]iaY D=REJCI=T$Kn`anMpu%:KN@AN>UIEJ$ok`*HejaPkp]h% 163
164
C HAPTER 6
BO OK MA R K L OOK U P A NA L YS IS
The O]haoKn`an@ap]eh table has a nonclustered index on the Lnk`q_pE@ column. The optimizer can use the index to filter the rows from the table. The table has a clustered index on O]haoKn`anE@ and O]haoKn`an@ap]ehE@, so they would be included in the nonclustered `iÝ°Ê ÕÌÊÃViÊÌ
iÞ½ÀiÊÌÊÀiviÀiVi`ÊÊÌ
iʵÕiÀÞ]ÊÌ
iÞÊܽÌÊ
i«ÊÌ
iʵÕiÀÞÊ>ÌÊ>°Ê/
iÊ other columns (HejaPkp]h, ?]nneanPn]_gejcJqi^an, Kn`anMpu, and HejaPkp]h) referred to by Ì
iʵÕiÀÞÊ>ÀiÊÌÊ>Û>>LiÊÊÌ
iÊVÕÃÌiÀi`Ê`iÝ°Ê/ÊviÌV
ÊÌ
iÊÛ>ÕiÃÊvÀÊÌ
ÃiÊVÕÃ]Ê navigation from the nonclustered index row to the corresponding data row through the clusÌiÀi`Ê`iÝÊÃÊÀiµÕÀi`]Ê>`ÊÌ
ÃÊ«iÀ>ÌÊÃÊ>ÊL>ÀÊÕ«°Ê9ÕÊV>ÊÃiiÊÌ
ÃÊÊ>VÌÊ in Figure 6-1.
Figure 6-1. Bookmark lookup in a complicated execution plan To better understand how a nonclustered index can cause a bookmark lookup, consider the following OAHA?PÊÃÌ>ÌiiÌ]ÊÜ
V
ÊÀiµÕiÃÌÃÊÞÊ>ÊviÜÊÀÜÃÊvÀÊÌ
iÊO]haoKn`an@ap]eh table by using a filter criterion on column Lnk`q_pE@: OAHA?P& BNKIO]hao*O]haoKn`an@ap]eh=Ook` SDANAok`*Lnk`q_pE@9332 The optimizer evaluates the SDANA clause and finds that the column Lnk`q_pE@ included in the SDANA clause has a nonclustered index on it that filters the number of rows down. Since ÞÊ>ÊviÜÊÀÜÃ]ÊÓÓÎ]Ê>ÀiÊÀiµÕiÃÌi`]ÊÀiÌÀiÛ}ÊÌ
iÊ`>Ì>ÊÌ
ÀÕ}
ÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊÜÊLiÊ cheaper than scanning the clustered index (containing more than 120,000 rows) to identify the matching rows. The nonclustered index on column Lnk`q_pE@ will help identify the match}ÊÀÜÃʵÕVÞ°Ê/
iÊVÕÃÌiÀi`Ê`iÝÊVÕ`iÃÊVÕÊLnk`q_pE@ and the clustered index columns O]haoKn`anE@ and O]haoKn`an@ap]ehE@; all the other columns are not included. Therefore, as you may have guessed, to retrieve the rest of the columns while using the nonclustered `iÝ]ÊÞÕÊÀiµÕÀiÊ>ÊL>ÀÊÕ«° This is shown in the following metrics and in the execution plan in Figure 6-2 (you can turn on OP=PEOPE?OEK using the Query Query Options menu). Look for the GauHkkgql $?hqopana`% operator. That is the bookmark lookup in action. P]^ha#O]haoKn`an@ap]eh#*O_]j_kqjp-(hkce_]hna]`o3,5 ?LQpeia9,io(ah]loa`peia9/./io*
CHAPTER 6
B O O K M A R K LO O K U P A N A LY S I S
Figure 6-2. Execution plan with a bookmark lookup
Drawbacks of Bookmark Lookups A bookmark lookupÊÀiµÕÀiÃÊ`>Ì>Ê«>}iÊ>VViÃÃÊÊ>``ÌÊÌÊ`iÝÊ«>}iÊ>VViÃðÊVViÃÃ}Ê ÌÜÊÃiÌÃÊvÊ«>}iÃÊVÀi>ÃiÃÊÌ
iÊÕLiÀÊvÊ}V>ÊÀi>`ÃÊvÀÊÌ
iʵÕiÀÞ°Ê``Ì>Þ]ÊvÊÌ
iÊ «>}iÃÊ>ÀiÊÌÊ>Û>>LiÊÊiÀÞ]Ê>ÊL>ÀÊÕ«ÊÜÊ«ÀL>LÞÊÀiµÕÀiÊ>ÊÀ>`ÊÀÊ ÃiµÕiÌ>®ÊÉ"Ê«iÀ>ÌÊÊÌ
iÊ`ÃÊÌÊÕ«ÊvÀÊÌ
iÊ`iÝÊ«>}iÊÌÊÌ
iÊ`>Ì>Ê«>}iÊ>ÃÊ ÜiÊ>ÃÊÀiµÕÀ}ÊÌ
iÊiViÃÃ>ÀÞÊ *1Ê«ÜiÀÊÌÊ>ÀÃ
>ÊÌ
ÃÊ`>Ì>Ê>`Ê«iÀvÀÊÌ
iÊiViÃÃ>ÀÞÊ operations. This is because, for a large table, the index page and the corresponding data page usually won’t be close to each other on the disk. /
iÊVÀi>Ãi`Ê}V>ÊÀi>`ÃÊ>`ÊVÃÌÞÊ«
ÞÃV>ÊÀi>`ÃÊvÊÀiµÕÀi`®Ê>iÊÌ
iÊ`>Ì>ÀiÌÀiÛ>Ê «iÀ>ÌÊvÊÌ
iÊL>ÀÊիʵÕÌiÊVÃÌÞ°Ê/
ÃÊVÃÌÊv>VÌÀÊÃÊÌ
iÊÀi>ÃÊÌ
>ÌÊVÕÃÌiÀi`Ê `iÝiÃÊ>ÀiÊLiÌÌiÀÊÃÕÌi`ÊvÀʵÕiÀiÃÊÌ
>ÌÊÀiÌÕÀÊ>ÊÃ>ÊÃiÌÊvÊÀÜÃÊvÀÊÌ
iÊÌ>Li°ÊÃÊÌ
iÊnumLiÀÊvÊÀÜÃÊÀiÌÀiÛi`ÊLÞÊ>ʵÕiÀÞÊVÀi>ÃiÃ]ÊÌ
iÊÛiÀ
i>`ÊVÃÌÊvÊ>ÊL>ÀÊÕ«ÊLiViÃÊ unacceptable. To understand how a bookmark lookup makes a nonclustered index ineffective as the ÕLiÀÊvÊÀÜÃÊÀiÌÀiÛi`ÊVÀi>ÃiÃ]Êi̽ÃÊÊ>ÌÊ>Ê`vviÀiÌÊiÝ>«i°Ê/
iʵÕiÀÞÊÌ
>ÌÊ«À`ÕVi`Ê the execution plan in Figure 6-2 returned just a few rows from the O]haoKn`an@ap]eh table. i>Û}ÊÌ
iʵÕiÀÞÊÌ
iÊÃ>iÊLÕÌÊV
>}}ÊÌ
iÊ«>À>iÌiÀÊÌÊ>Ê`vviÀiÌÊÛ>ÕiÊÜ]ÊvÊVÕÀÃi]Ê change the number of rows returned. If you change the parameter value to look like this: OAHA?P& BNKIO]hao*O]haoKn`an@ap]eh=Ook` SDANAok`*Lnk`q_pE@935/ Ì
iÊÀÕ}ÊÌ
iʵÕiÀÞÊÀiÌÕÀÃÊÀiÊÌ
>ÊÇääÊÀÜÃ]ÊÜÌ
Ê`vviÀiÌÊ«iÀvÀ>ViÊiÌÀVÃÊ>`Ê>Ê completely different execution plan (Figure 6-3): P]^ha#O]haoKn`an@ap]eh#*O_]j_kqjp-(hkce_]hna]`o-.0, ?LQpeia9-1io(ah]loa`peia9//.io*
Figure 6-3. Execution plan with more rows
165
166
C HAPTER 6
BO OK MA R K L OOK U P A NA L YS IS
To determine how costly it will be to use the nonclustered index, consider the number of }V>ÊÀi>`ÃÊ£]Ó{ä®Ê«iÀvÀi`ÊLÞÊÌ
iʵÕiÀÞÊ`ÕÀ}ÊÌ
iÊÌ>LiÊÃV>°ÊvÊÞÕÊforce the optimizer to use the nonclustered index by using an index hint, like this: OAHA?P& BNKIO]hao*O]haoKn`an@ap]eh=Ook`SEPD$EJ@AT$ET[O]haoKn`an@ap]eh[Lnk`q_pE@%% SDANAok`*Lnk`q_pE@935/ then the number of logical reads increases from 9 to 1,004: P]^ha#O]haoKn`an@ap]eh#*O_]j_kqjp-(hkce_]hna]`o.-3/ ?LQpeia9,io(ah]loa`peia9//2io* Figure 6-4 shows the corresponding execution plan.
Figure 6-4. Execution plan for fetching more rows with an index hint To benefit fromÊVÕÃÌiÀi`Ê`iÝiÃ]ʵÕiÀiÃÊÃ
Õ`ÊÀiµÕiÃÌÊ>ÊÀi>ÌÛiÞÊÃ>ÊÕLiÀÊvÊ ÀÜðʫ«V>ÌÊ`iÃ}Ê«>ÞÃÊ>Ê«ÀÌ>ÌÊÀiÊvÀÊÌ
iÊÀiµÕÀiiÌÃÊÌ
>ÌÊ
>`iÊ>À}iÊÀiÃÕÌÊ sets. For example, search engines on the Web mostly return a limited number of articles at a Ìi]ÊiÛiÊvÊÌ
iÊÃi>ÀV
ÊVÀÌiÀÊÀiÌÕÀÃÊÌ
ÕÃ>`ÃÊvÊ>ÌV
}Ê>ÀÌViðÊvÊÌ
iʵÕiÀiÃÊÀiµÕiÃÌÊ a large number of rows, then the increased overhead cost of a bookmark lookup makes the VÕÃÌiÀi`Ê`iÝÊÕÃÕÌ>LiÆÊÃÕLÃiµÕiÌÞ]ÊÞÕÊ
>ÛiÊÌÊVÃ`iÀÊÌ
iÊ«ÃÃLÌiÃÊvÊ>Û`ing the bookmark lookup operation.
Analyzing the Cause of a Bookmark Lookup Since a bookmarkÊÕ«ÊV>ÊLiÊ>ÊVÃÌÞÊ«iÀ>Ì]ÊÞÕÊÃ
Õ`Ê>>ÞâiÊÜ
>ÌÊV>ÕÃiÃÊ>ʵÕiÀÞÊ «>ÊÌÊV
ÃiÊ>ÊiÞÊÕ«ÊÃÌi«ÊÊ>ÊiÝiVÕÌÊ«>°Ê9ÕÊ>ÞÊv`ÊÌ
>ÌÊÞÕÊ>ÀiÊ>LiÊÌÊ>Û`Ê the bookmark lookup by including the missing columns in the nonclustered index key or as EJ?HQ@A columns at the index page level and thereby avoid the cost overhead associated with the bookmark lookup. To learn how to identify the columns not included in the nonclustered index, consider the vÜ}ʵÕiÀÞ]ÊÜ
V
Ê«ÕÃÊvÀ>ÌÊvÀÊÌ
iÊDqi]jNaokqn_ao*Ailhkuaa table based on J]pekj]hE@Jqi^an:
CHAPTER 6
B O O K M A R K LO O K U P A N A LY S I S
OAHA?PJ]pekj]hE@Jqi^an (Fk^Pepha (Dena@]pa BNKIDqi]jNaokqn_ao*Ailhkuaa=Oa SDANAa*J]pekj]hE@Jqi^an9#25/-242-/# This produces the following performance metrics and execution plan (see Figure 6-5): P]^ha#Ailhkuaa#*O_]j_kqjp,(hkce_]hna]`o0 ?LQpeia9,io(ah]loa`peia9-43io*
Figure 6-5. Execution plan with a bookmark lookup As shown in the execution plan, you have a key lookup. The OAHA?P statement refers to columns J]pekj]hE@Jqi^an, Fk^Pepha, and Dena@]pa. The nonclustered index on column J]pekj]hE@Jqi^an doesn’t provide values for columns Fk^Pepha and Dena@]pa, so a bookmark Õ«Ê«iÀ>ÌÊÜ>ÃÊÀiµÕÀi`ÊÌÊÀiÌÀiÛiÊÌ
ÃiÊVÕÃÊvÀÊÌ
iÊL>ÃiÊÌ>Li°ÊÜiÛiÀ]ÊÊÌ
iÊ Ài>ÊÜÀ`]ÊÌÊÕÃÕ>ÞÊܽÌÊLiÊÌ
ÃÊi>ÃÞÊÌÊ`iÌvÞÊ>ÊÌ
iÊVÕÃÊÕÃi`ÊLÞÊ>ʵÕiÀÞ°Ê,iiLiÀÊ that a bookmark lookup operation will be caused if all the columns referred to in any part of Ì
iʵÕiÀÞÊÌÊÕÃÌÊÌ
iÊÃiiVÌÊÃÌ®Ê>Ài½ÌÊVÕ`i`ÊÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊÕÃi`° ÊÌ
iÊV>ÃiÊvÊ>ÊV«iÝʵÕiÀÞÊL>Ãi`ÊÊÛiÜÃÊ>`ÊÕÃiÀ`ivi`ÊvÕVÌÃ]ÊÌÊ>ÞÊLiÊ ÌÊ`vvVÕÌÊÌÊv`Ê>ÊÌ
iÊVÕÃÊÀiviÀÀi`ÊÌÊLÞÊÌ
iʵÕiÀÞ°ÊÃÊ>ÊÀiÃÕÌ]ÊÞÕÊii`Ê>ÊÃÌ>`>À`Ê mechanism to find the columns returned by the bookmark lookup that are not included in the nonclustered index. If you look at the tool tip on the GauHkkgql$?hqopana`% operation, you can see the output list for the operation. This shows you the columns being output by the bookmark lookup. ÜiÛiÀ]ÊvÊÌ
iÊÃÌÊÃÊÛiÀÞÊ}]ÊÌÊÜÊLiʵÕÌiÊ`vvVÕÌÊÌÊÀi>`]Ê>`ÊÞÕÊV>½ÌÊV«ÞÊ`>Ì>Ê vÀÊÌ
iÊÌÊÌ«°Ê-]ÊÌÊ}iÌÊÌ
iÊÃÌÊvÊÕÌ«ÕÌÊVÕÃʵÕVÞÊ>`Êi>ÃÞÊ>`ÊLiÊ>LiÊÌÊV«ÞÊ them, right-click the operator, which in this case is GauHkkgql$?hqopana`%. Then select the *À«iÀÌiÃÊiÕÊÌi°Ê-VÀÊ`ÜÊÌÊÌ
iÊ"ÕÌ«ÕÌÊÃÌÊ«À«iÀÌÞÊÊÌ
iÊ*À«iÀÌiÃÊÜ`ÜÊÌ
>ÌÊ opens (Figure 6-6). This property has a plus sign, which allows you to expand the column list, and has plus signs next to each column, which allows you to expand the properties of the column.
167
168
C HAPTER 6
BO OK MA R K L OOK U P A NA L YS IS
Figure 6-6. Key lookup Properties window /Ê}iÌÊÌ
iÊÃÌÊvÊVÕÃÊ`ÀiVÌÞÊvÀÊÌ
iÊ*À«iÀÌiÃÊÜ`Ü]ÊVVÊÌ
iÊi«ÃÃÊÊÌ
iÊ right side of the Output List property. This opens the output list in a text window from which ÞÕÊV>ÊV«ÞÊÌ
iÊ`>Ì>ÊvÀÊÕÃiÊÜ
iÊ`vÞ}ÊÞÕÀÊ`iÝÊ}ÕÀiÊÈÇ®°
Figure 6-7. The required columns that were not available in the nonclustered index
CHAPTER 6
B O O K M A R K LO O K U P A N A LY S I S
Resolving Bookmark Lookups Since the relative cost of a bookmark lookup can be very high, you should, wherever possible, try to get rid of bookmark lookup operations. In the preceding section, you needed to obtain the values of columns Fk^Pepha and Dena@]pa without navigating from the index row to the `>Ì>ÊÀÜ°Ê9ÕÊV>Ê`ÊÌ
ÃÊÊÌ
ÀiiÊ`vviÀiÌÊÜ>ÞÃ]Ê>ÃÊiÝ«>i`ÊÊÌ
iÊvÜ}ÊÃiVÌð
Using a Clustered Index For a clustered index, the leaf page of the index is the same as the data page of the table. Therefore, when reading the values of the clustered index key columns, the database engine can also read the values of other columns without any navigation from the index row. In the previous example, if you convert the nonclustered index to a clustered index for a particular row, SQL Server can retrieve values of all the columns from the same page. Simply saying that you want to convert the nonclustered index to a clustered index is easy ÌÊ`°ÊÜiÛiÀ]ÊÊÌ
ÃÊV>Ãi]Ê>`ÊÊÃÌÊV>ÃiÃÊÞÕ½ÀiÊiÞÊÌÊiVÕÌiÀ]ÊÌÊýÌÊ«ÃÃLiÊÌÊ do so, since the table already has a clustered index in place. The clustered index on this table >ÃÊ
>««iÃÊÌÊLiÊÌ
iÊ«À>ÀÞÊiÞ°Ê9ÕÊÜÕ`Ê
>ÛiÊÌÊ`À«Ê>ÊvÀi}ÊiÞÊVÃÌÀ>ÌÃ]Ê`À«Ê and re-create the primary key as a nonclustered index, and then re-create the index against J]pekj]hE@Jqi^an. Not only do you need to take into account the work involved, but you may ÃiÀÕÃÞÊ>vviVÌÊÌ
iÀʵÕiÀiÃÊÌ
>ÌÊ>ÀiÊ`i«i`iÌÊÊÌ
iÊiÝÃÌ}ÊVÕÃÌiÀi`Ê`iÝ°
Note Remember that a table can have only one clustered index.
Using a Covering Index Ê
>«ÌiÀÊ{]ÊÞÕÊi>Ài`ÊÌ
>ÌÊ>ÊVÛiÀ}Ê`iÝÊÃÊiÊ>Ê«ÃiÕ`VÕÃÌiÀi`Ê`iÝÊvÀÊÌ
iʵÕiÀiÃ]Ê since it can return results without recourse to the table data. So, you can also use a covering index to avoid a bookmark lookup. To understand how you can use a covering index to avoid a bookmark lookup, examine Ì
iʵÕiÀÞÊ>}>ÃÌÊÌ
iÊDqi]jNaokqn_ao*Ailhkuaa table again: OAHA?PJ]pekj]hE@Jqi^an (Fk^Pepha (Dena@]pa BNKIDqi]jNaokqn_ao*Ailhkuaa=Oa SDANAa*J]pekj]hE@Jqi^an9#25/-242-/# /Ê>Û`ÊÌ
ÃÊL>À]ÊÞÕÊV>Ê>``ÊÌ
iÊVÕÃÊÀiviÀÀi`ÊÌÊÊÌ
iʵÕiÀÞ]ÊFk^Pepha and Dena@]pa, directly to the nonclustered index key. This will make the nonclustered index a coviÀ}Ê`iÝÊvÀÊÌ
ÃʵÕiÀÞÊLiV>ÕÃiÊ>ÊVÕÃÊV>ÊLiÊÀiÌÀiÛi`ÊvÀÊÌ
iÊ`iÝÊÜÌ
ÕÌÊ
>Û}Ê to go to the base table or clustered index.
169
170
C HAPTER 6
BO OK MA R K L OOK U P A NA L YS IS
?NA=PAQJEMQAJKJ?HQOPANA@EJ@ATW=G[Ailhkuaa[J]pekj]hE@Jqi^anYKJ WDqi]jNaokqn_aoY*WAilhkuaaY $ J]pekj]hE@Jqi^an=O?(Fk^Pepha=O?(Dena@]pa=O? %SEPD`nkl[ateopejc ÜÊÜ
iÊÌ
iʵÕiÀÞÊ}iÌÃÊÀÕ]ÊÞÕ½ÊÃiiÊÌ
iÊvÜ}ÊiÌÀVÃÊ>`Ê>Ê`vviÀiÌÊiÝiVÕÌÊ plan (Figure 6-8): P]^ha#Ailhkuaa#*O_]j_kqjp-(hkce_]hna]`o. ?LQpeia9,io(ah]loa`peia9,io*
Figure 6-8. Execution plan with a covering index There are a couple of caveats to creating a covering index by changing the key, however. If you add too many columns to a nonclustered index, it becomes too wide, and the index >Ìi>ViÊVÃÌÊ>ÃÃV>Ìi`ÊÜÌ
ÊÌ
iÊ>VÌʵÕiÀiÃÊV>ÊVÀi>Ãi]Ê>ÃÊ`ÃVÕÃÃi`ÊÊ
>«ÌiÀÊ{°Ê Therefore, evaluate closely the number of columns (for size and data type) to be added to the nonclustered index key. If the total width of the additional columns is not too large (best determined through testing and measuring the resultant index size), then those columns can be added in the nonclustered index key to be used as a covering index. Also, if you add columns to Ì
iÊ`iÝÊiÞ]Ê`i«i`}ÊÊÌ
iÊ`iÝ]ÊvÊVÕÀÃi]ÊÞÕÊ>ÞÊLiÊ>vviVÌ}ÊÌ
iÀʵÕiÀiÃÊÊ>Êi}>tive fashion. They may have expected to see the index key columns in a particular order or may not refer to some of the columns in the key, causing the index to not be used by the optimizer. Another way to arrive at the covering index, without reshaping the index by adding key columns, is to use the EJ?HQ@AÊVÕðÊ
>}iÊÌ
iÊ`iÝÊÌÊÊiÊÌ
Ã\ ?NA=PAQJEMQAJKJ?HQOPANA@EJ@ATW=G[Ailhkuaa[J]pekj]hE@Jqi^anY KJWDqi]jNaokqn_aoY*WAilhkuaaY $ J]pekj]hE@Jqi^an=O? %EJ?HQ@A$Fk^Pepha(Dena@]pa% SEPD`nkl[ateopejc ÜÊÜ
iÊÌ
iʵÕiÀÞÊÃÊÀÕ]ÊÞÕÊ}iÌÊÌ
iÊvÜ}ÊiÌÀVÃÊ>`ÊiÝiVÕÌÊ«>Ê}ÕÀiÊÈ®\ P]^ha#Ailhkuaa#*O_]j_kqjp,(hkce_]hna]`o. ?LQpeia9,io(ah]loa`peia9,io*
Figure 6-9. Execution plan with EJ?HQ@A columns
CHAPTER 6
B O O K M A R K LO O K U P A N A LY S I S
The index is still covering, exactly as it was in the execution plan displayed in Figure 6-8. Because the data is stored at the leaf level of the index, when the index is used to retrieve the key values, the rest of the columns in the EJ?HQ@A statement are available for use, almost like they were part of the key. Another way to get a covering index is to take advantage of the structures within SQL -iÀÛiÀ°ÊvÊÌ
iÊ«ÀiÛÕÃʵÕiÀÞÊÜiÀiÊ`vi`ÊÃ}
ÌÞÊÌÊÀiÌÀiÛiÊ>Ê`vviÀiÌÊÃiÌÊvÊ`>Ì>ÊÃÌi>`ÊvÊ a particular J]pekj]hE@Jqi^an and its associated Fk^Pepha and Dena@]pa]ÊÌ
ÃÊÌiÊÌ
iʵÕiÀÞÊ would retrieve the J]pekj]hE@Jqi^an as an alternate key and the >qoejaooAjpepuE@, the primary key for the table, over a range of values: OAHA?PJ]pekj]hE@Jqi^an (>qoejaooAjpepuE@ BNKIDqi]jNaokqn_ao*Ailhkuaa=Oa SDANAa*J]pekj]hE@Jqi^an>APSAAJ#25/-242-/# =J@#3,,,,,,,,,# The original index on the table doesn’t reference the >qoejaooAjpepuE@ column in any way: ?NA=PAQJEMQAJKJ?HQOPANA@EJ@ATW=G[Ailhkuaa[J]pekj]hE@Jqi^anY KJWDqi]jNaokqn_aoY*WAilhkuaaY $ WJ]pekj]hE@Jqi^anY=O? %SEPD`nkl[ateopejc 7
iÊÌ
iʵÕiÀÞÊÃÊÀÕÊ>}>ÃÌÊÌ
iÊÌ>Li]ÊÞÕÊV>ÊÃiiÊÌ
iÊÀiÃÕÌÃÊÃ
ÜÊÊ}ÕÀiÊÈ£ä°
Figure 6-10. Unexpected covering index ÜÊ``ÊÌ
iÊ«ÌâiÀÊ>ÀÀÛiÊ>ÌÊ>ÊVÛiÀ}Ê`iÝÊvÀÊÌ
ÃʵÕiÀÞÊL>Ãi`ÊÊÌ
iÊ`iÝÊ«Àvided? It’s aware that on a table with a clustered index, the clustered index key, in this case the >qoejaooAjpepuE@ column, is stored as a pointer to the data with the nonclustered index. That i>ÃÊÌ
>ÌÊ>ÞʵÕiÀÞÊÌ
>ÌÊVÀ«À>ÌiÃÊ>ÊVÕÃÌiÀi`Ê`iÝÊ>`Ê>ÊÃiÌÊvÊVÕÃÊvÀÊ>ÊVÕÃÌiÀi`Ê`iÝÊ>ÃÊ«>ÀÌÊvÊÌ
iÊvÌiÀ}ÊiV
>ÃÃÊvÊÌ
iʵÕiÀÞ]ÊÌ
iÊSDANA clause, or the join criteria can take advantage of the covering index. To see how these three different indexes are reflected in storage, you can look at the statistics of the indexes themselves using @>??ODKS[OP=PEOPE?O°Ê7
iÊÞÕÊÀÕÊÌ
iÊvÜ}ʵÕiÀÞÊ against the index, you can see the output in Figure 6-11: @>??ODKS[OP=PEOPE?O$#Dqi]jNaokqn_ao*Ailhkuaa#(=G[Ailhkuaa[J]pekj]hE@Jqi^an%
Figure 6-11. @>??ODKS[OP=PEOPE?O output for original index
171
172
C HAPTER 6
BO OK MA R K L OOK U P A NA L YS IS
As you can see, the J]pekj]hE@Jqi^an is listed first, but the primary key for the table is included as part of the index, so a second row that includes the >qoejaooAjpepuE@ column is there. It makes the average length of the key about 22 bytes. This is how indexes that refer to the primary key values as well as the index key values can function as covering indexes. If you run the same @>??ODKS[OP=PEOPE?O on the first alternate index you tried, with all three columns included in the key, like so: ?NA=PAQJEMQAJKJ?HQOPANA@EJ@ATW=G[Ailhkuaa[J]pekj]hE@Jqi^anY KJWDqi]jNaokqn_aoY*WAilhkuaaY $ J]pekj]hE@Jqi^an=O?(Fk^Pepha=O?(Dena@]pa=O? %SEPD`nkl[ateopejc you will see a different set of statistics (Figure 6-12).
Figure 6-12. @>??ODKS[OP=PEOPE?O output for a wide key covering index 9ÕÊÜÊÃiiÊÌ
iÊVÕÃÊ>``i`ÊÕ«]Ê>ÊÌ
ÀiiÊvÊÌ
iÊ`iÝÊiÞÊVÕÃ]Ê>`Êv>ÞÊÌ
iÊ«À>ÀÞÊiÞÊ>``i`Ê°ÊÃÌi>`ÊvÊ>ÊÜ`Ì
ÊvÊÓÓÊLÞÌiÃ]Ê̽ÃÊ}ÀÜÊÌÊÇ{°Ê/
>ÌÊÀiviVÌÃÊÌ
iÊ>``ÌÊvÊ the Fk^Pepha column, a R=N?D=N$1,% as well as the 16-byte-wide datetime field. Finally, looking at the statistics for the second alternate index: ?NA=PAQJEMQAJKJ?HQOPANA@EJ@ATW=G[Ailhkuaa[J]pekj]hE@Jqi^anY KJWDqi]jNaokqn_aoY*WAilhkuaaY $ J]pekj]hE@Jqi^an=O? %EJ?HQ@A$Fk^Pepha(Dena@]pa% SEPD`nkl[ateopejc With the included columns you’ll see the output in Figure 6-13.
Figure 6-13. @>??ODKS[OP=PEOPE?O output for a covering index using EJ?HQ@A Now the key width is back to the original size because the columns in the EJ?HQ@A statement are stored not with the key but at the leaf level of the index. There is more interesting information to be gleaned from the data stored about statistics, LÕÌʽÊVÛiÀÊÌ
>ÌÊÊ
>«ÌiÀÊÇ°
CHAPTER 6
B O O K M A R K LO O K U P A N A LY S I S
Using an Index Join If the coveringÊ`iÝÊLiViÃÊÛiÀÞÊÜ`i]ÊÌ
iÊÞÕÊ}
ÌÊVÃ`iÀÊ>Ê`iÝÊÊÌiV
µÕi°Ê ÃÊiÝ«>i`ÊÊ
>«ÌiÀÊ{]ÊÌ
iÊ`iÝÊÊÌiV
µÕiÊÕÃiÃÊ>Ê`iÝ intersection between two ÀÊÀiÊ`iÝiÃÊÌÊVÛiÀÊ>ʵÕiÀÞÊvÕÞ°Ê-ViÊÌ
iÊ`iÝÊÊÌiV
µÕiÊÀiµÕÀiÃÊ>VViÃÃÊÌÊÀiÊ Ì
>ÊiÊ`iÝ]ÊÌÊ
>ÃÊÌÊ«iÀvÀÊ}V>ÊÀi>`ÃÊÊ>ÊÌ
iÊ`iÝiÃÊÕÃi`ÊÊÌ
iÊ`iÝÊ°Ê ÃiµÕiÌÞ]ÊÌÊÀiµÕÀiÃÊ>Ê
}
iÀÊÕLiÀÊvÊ}V>ÊÀi>`ÃÊÌ
>ÊÌ
iÊVÛiÀ}Ê`iÝ°Ê ÕÌÊÃViÊÌ
iÊ ÕÌ«iÊ>ÀÀÜÊ`iÝiÃÊÕÃi`ÊvÀÊÌ
iÊ`iÝÊÊV>ÊÃiÀÛiÊÀiʵÕiÀiÃÊÌ
>Ê>ÊÜ`iÊVÛiÀ}Ê `iÝÊ>ÃÊiÝ«>i`ÊÊ
>«ÌiÀÊ{®]ÊÞÕÊV>ÊViÀÌ>ÞÊVÃ`iÀÊÌ
iÊ`iÝÊÊ>ÃÊ>ÊÌiV
µÕiÊÌÊ avoid bookmark lookups. To better understand how an index join can be used to avoid bookmark lookups, run the vÜ}ʵÕiÀÞÊ>}>ÃÌÊÌ
iÊLqn_d]oaKn`anDa]`an table in order to retrieve a Lqn_d]oaKn`anE@ for a particular vendor on a particular date: OAHA?Plkd*Lqn_d]oaKn`anE@ (lkd*Raj`knE@ (lkd*Kn`an@]pa BNKILqn_d]oejc*Lqn_d]oaKn`anDa]`an=Olkd SDANARaj`knE@9-2/2 =J@lkd*Kn`an@]pa9#-.+1+.,,/# 7
iÊÀÕ]ÊÌ
ÃʵÕiÀÞÊÀiÃÕÌÃÊÊ>ÊGauHkkgqlÊ«iÀ>ÌÊ}ÕÀiÊÈ£{®Ê>`ÊÌ
iÊvÜ}ÊÉ"\ P]^ha#Lqn_d]oaKn`anDa]`an#*O_]j_kqjp-(hkce_]hna]`o-, ?LQpeia9,io(ah]loa`peia902io*
Figure 6-14. A GauHkkgql operation The bookmark lookup is caused since all the columns referred to by the OAHA?P statement and SDANA clause are not included in the nonclustered index on column Raj`knE@°Ê1Ã}ÊÌ
iÊ VÕÃÌiÀi`Ê`iÝÊÃÊÃÌÊLiÌÌiÀÊÌ
>ÊÌÊÕÃ}ÊÌ]ÊÃViÊÌ
>ÌÊÜÕ`ÊÀiµÕÀiÊ>ÊÃV>ÊÊÌ
iÊÌ>LiÊ (in this case, a clustered index scan) with a larger number of logical reads. To avoid the bookmark lookup, you can consider a covering index on the column Kn`an@]pa as explained in the previous section. But in addition to the covering index solution, ÞÕÊV>ÊVÃ`iÀÊ>Ê`iÝÊ°ÊÃÊÞÕÊi>Ài`]Ê>Ê`iÝÊÊÀiµÕÀiÃÊ>ÀÀÜiÀÊ`iÝiÃÊÌ
>Ê the covering index and thereby provides the following two benefits: Ê
UÊ ÕÌ«iÊ>ÀÀÜÊ`iÝiÃÊV>ÊÃiÀÛiÊ>Ê>À}iÀÊÕLiÀÊvʵÕiÀiÃÊÌ
>ÊÌ
iÊÜ`iÊVÛiÀ}Ê index.
Ê
UÊ
>ÀÀÜÊ`iÝiÃÊÀiµÕÀiÊiÃÃÊ>Ìi>ViÊÛiÀ
i>`ÊÌ
>ÊÌ
iÊÜ`iÊVÛiÀ}Ê`iÝ°
173
174
C HAPTER 6
BO OK MA R K L OOK U P A NA L YS IS
To avoid the bookmark lookup using an index join, create a narrow nonclustered index on column Kn`an@]pa that is not included in the existing nonclustered index: ?NA=PAJKJ?HQOPANA@EJ@ATEt[PAOPKJLqn_d]oejc*Lqn_d]oaKn`anDa]`an$Kn`an`]pa% If you run the OAHA?P statement again, the following output and the execution plan shown in Figure 6-15 are returned: P]^ha#Lqn_d]oaKn`anDa]`an#*O_]j_kqjp.(hkce_]hna]`o0 ?LQpeia9,io(ah]loa`peia9/1io*
Figure 6-15. Execution plan without a bookmark lookup From the preceding execution plan, you can see that the optimizer used the nonclustered index, ET[Lqn_d]oaKn`an[Raj`knE@, on column Raj`knE@ and the new nonclustered index, Et[PAOP, on column Kn`anE@ÊÌÊÃiÀÛiÊÌ
iʵÕiÀÞÊvÕÞÊÜÌ
ÕÌÊ
ÌÌ}ÊÌ
iÊL>ÃiÊÌ>Li°Ê/
ÃÊ`iÝÊ Ê«iÀ>ÌÊ>Û`i`ÊÌ
iÊL>ÀÊÕ«Ê>`ÊVÃiµÕiÌÞÊ`iVÀi>Ãi`ÊÌ
iÊÕLiÀÊvÊ}cal reads from 10 to 4. It is true that a covering index on columns Raj`knE@ and Kn`anE@ (_-, _.) could reduce the number of logical reads further. But it may not always be possible to use covering indexes, since they can be wide and have their associated overhead. In such cases, an index join can be a good alternative.
Summary As demonstrated in this chapter, the bookmark lookup step associated with a nonclustered index makes data retrieval through a nonclustered index very costly. The SQL Server optimizer takes this into account when generating an execution plan, and if it finds the overhead cost of using a nonclustered index to be very high, it discards the index and performs a table scan (or a clustered index scan if the table contains a clustered index). Therefore, to improve the effectiveness of a nonclustered index, it makes sense to analyze the cause of a bookmark lookup and consider whether you can avoid it completely by adding fields to the index key or to the EJ?HQ@A column (or index join) and creating a covering index. 1«ÊÌÊÌ
ÃÊ«Ì]ÊÞÕÊ
>ÛiÊVViÌÀ>Ìi`ÊÊ`iÝ}ÊÌiV
µÕiÃÊ>`Ê«ÀiÃÕi`ÊÌ
>ÌÊÌ
iÊ -+Ê-iÀÛiÀÊ«ÌâiÀÊÜÕ`ÊLiÊ>LiÊÌÊ`iÌiÀiÊÌ
iÊivviVÌÛiiÃÃÊvÊ>Ê`iÝÊvÀÊ>ʵÕiÀÞ°ÊÊ the next chapter, you will see the importance of statistics in helping the optimizer determine the effectiveness of an index.
CHAPT ER
7
Statistics Analysis B
y now, you should have a good understanding of the importance of indexes. It is equally important for the optimizer to have the necessary statistics on the data distribution so that it can choose indexes effectively. In SQL Server, this information is maintained in the form of statistics on the index key. In this chapter, you’ll learn the importance of statistics in query optimization. Specifically, I cover the following topics: Ê
UÊ /
iÊÀiÊvÊÃÌ>ÌÃÌVÃÊʵÕiÀÞÊ«Ìâ>Ì
Ê
UÊ /
iÊ«ÀÌ>ViÊvÊÃÌ>ÌÃÌVÃÊÊVÕÃÊÜÌ
Ê`iÝiÃ
Ê
UÊ /
iÊ«ÀÌ>ViÊvÊÃÌ>ÌÃÌVÃÊÊ`iÝi`ÊVÕÃÊÕÃi`ÊÊÊ>`ÊvÌiÀÊVÀÌiÀ>
Ê
UÊ >ÞÃÃÊvÊÃ}iVÕÊ>`ÊÕÌVÕÊÃÌ>ÌÃÌVÃ]ÊVÕ`}ÊÌ
iÊV«ÕÌ>ÌÊvÊ selectivity of a column for indexing
Ê
UÊ -Ì>ÌÃÌVÃÊ>Ìi>Vi
Ê
UÊ vviVÌÛiÊiÛ>Õ>ÌÊvÊÃÌ>ÌÃÌVÃÊÕÃi`ÊÊ>ʵÕiÀÞÊiÝiVÕÌ
The Role of Statistics in Query Optimization SQL Server’s query optimizer is aÊVÃÌL>Ãi`Ê«ÌâiÀÆÊÌÊ`iV`iÃÊÊÌ
iÊLiÃÌÊ`>Ì>Ê>VViÃÃÊ iV
>ÃÊ>`ÊÊÃÌÀ>Ìi}ÞÊLÞÊ`iÌvÞ}ÊÌ
iÊÃiiVÌÛÌÞ]Ê
ÜÊÕµÕiÊÌ
iÊ`>Ì>ÊÃ]Ê>`ÊÜ
V
Ê columns are used in filtering the data (meaning via the SDANA or FKEJ clause). Statistics exist with an index, but they also exist on columns without an index that are used as part of a predi V>Ìi°ÊÃÊÞÕÊi>Ài`ÊÊ
>«ÌiÀÊ{]ÊÞÕÊÃ
Õ`ÊÕÃiÊ>ÊVÕÃÌiÀi`Ê`iÝÊÌÊÀiÌÀiÛiÊ>ÊÃ>Ê result set, whereas for data in related sets, a clustered index works better. With a large result set, going to the clustered index or table directly is usually more beneficial. 1«Ì`>ÌiÊvÀ>ÌÊÊ`>Ì>Ê`ÃÌÀLÕÌÊÊÌ
iÊVÕÃÊÀiviÀiVi`Ê>ÃÊpredicates helps the optimizer determine the query strategy to use. In SQL Server, this information is maintained in the form of statistics, which are essential for the VÃÌL>Ãi`Ê«ÌâiÀÊÌÊVÀi>ÌiÊ >ÊivviVÌÛiʵÕiÀÞÊiÝiVÕÌÊ«>°Ê/
ÀÕ}
ÊÌ
iÊÃÌ>ÌÃÌVÃ]ÊÌ
iÊ«ÌâiÀÊV>Ê>iÊÀi>Ã>LÞÊ
175
176
C HAPTER 7
ST A TIS TIC S A NA L YS IS
accurate estimates about how long it will take to return a result set or an intermediate result ÃiÌÊ>`ÊÌ
iÀivÀiÊ`iÌiÀiÊÌ
iÊÃÌÊivviVÌÛiÊ«iÀ>ÌÃÊÌÊÕÃi°ÊÃÊ}Ê>ÃÊÞÕÊiÃÕÀiÊÌ
>ÌÊÌ
iÊ default statistical settings for the database are set, the optimizer will be able to do its best to `iÌiÀiÊivviVÌÛiÊ«ÀViÃÃ}ÊÃÌÀ>Ìi}iÃÊ`Þ>V>Þ°ÊÃ]Ê>ÃÊ>ÊÃ>viÌÞÊi>ÃÕÀiÊÜ
iÊÌÀÕLi shooting performance, you should ensure that the automatic statistics maintenance routine ÃÊ`}ÊÌÃÊLÊ>ÃÊ`iÃÀi`°Ê7
iÀiÊiViÃÃ>ÀÞ]ÊÞÕÊ>ÞÊiÛiÊ
>ÛiÊÌÊÌ>iÊ>Õ>ÊVÌÀÊÛiÀÊ the creation and/or maintenance of statistics. (I cover this in the “Manual Maintenance” sec Ì]Ê>`ÊÊVÛiÀÊÌ
iÊ«ÀiVÃiÊ>ÌÕÀiÊvÊÌ
iÊvÕVÌÃÊ>`ÊÃ
>«iÊvÊÃÌ>ÌÃÌVÃÊÊÌ
iʺ>Þâ}Ê Statistics” section.) In the following section, I show you why statistics are important to indexed columns and nonindexed columns functioning as predicates.
Statistics on an Indexed Column /
iÊÕÃivÕiÃÃÊvÊ>Ê`iÝÊÃÊvÕÞÊ`i«i`iÌÊÊÌ
iÊÃÌ>ÌÃÌVÃÊvÊÌ
iÊ`iÝi`ÊVÕÃÆÊÜÌ
ÕÌÊ ÃÌ>ÌÃÌVÃ]Ê-+Ê-iÀÛiÀ½ÃÊVÃÌL>Ãi`ʵÕiÀÞÊ«ÌâiÀÊV>½ÌÊ`iV`iÊÕ«ÊÌ
iÊÃÌÊivviVÌÛiÊÜ>ÞÊvÊ ÕÃ}Ê>Ê`iÝ°Ê/ÊiiÌÊÌ
ÃÊÀiµÕÀiiÌ]Ê-+Ê-iÀÛiÀÊ>ÕÌ>ÌV>ÞÊcreates the statistics of an index key whenever the index is created. It isn’t possible to turn this feature off. ÃÊ`>Ì>ÊV
>}iÃ]ÊÌ
iÊ`>Ì>ÀiÌÀiÛ>ÊiV
>ÃÊÀiµÕÀi`ÊÌÊii«ÊÌ
iÊVÃÌÊvÊ>ʵÕiÀÞÊÜÊ may also change. For example, if a table has only one matching row for a certain column value, then it makes sense to retrieve the matching rows from the table by going through the nonclustered index on the column. But if the data in the table changes so that a large number of rows are added with the same column value, then using the nonclustered index no longer >iÃÊÃiÃi°Ê/ÊLiÊ>LiÊÌÊ
>ÛiÊ-+Ê-iÀÛiÀÊ`iV`iÊÌ
ÃÊV
>}iÊÊ«ÀViÃÃ}ÊÃÌÀ>Ìi}ÞÊ>ÃÊÌ
iÊ `>Ì>ÊV
>}iÃÊÛiÀÊÌi]ÊÌÊÃÊÛÌ>ÊÌÊ
>ÛiÊÕ«Ì`>ÌiÊÃÌ>ÌÃÌVð SQL Server can keep the statistics on an index updated as the contents of the indexed column are modified. By default, this feature is turned on and is configurable through the Properties Options ÊÕÌÊ1«`>ÌiÊ-Ì>ÌÃÌVà setting of a database. Updating statistics VÃÕiÃÊiÝÌÀ>Ê *1ÊVÞViðÊ/Ê«ÌâiÊÌ
iÊÕ«`>ÌiÊ«ÀViÃÃ]Ê-+Ê-iÀÛiÀÊÕÃiÃÊ>ÊivvViÌÊ>} rithm to decide when to execute the update statistics procedure, based on factors such as the number of modifications and the size of the table: Ê
UÊ 7
iÊ>ÊÌ>LiÊÜÌ
ÊÊÀÜÃÊ}iÌÃÊ>ÊÀÜ
Ê
UÊ 7
iÊ>ÊÌ>LiÊ
>ÃÊviÜiÀÊÌ
>ÊxääÊÀÜÃÊ>`ÊÃÊVÀi>Ãi`ÊLÞÊxääÊÀÊÀiÊÀÜÃ
Ê
UÊ 7
iÊ>ÊÌ>LiÊ
>ÃÊÀiÊÌ
>ÊxääÊÀÜÃÊ>`ÊÃÊVÀi>Ãi`ÊLÞÊxääÊÀÜÃʳÊÓäÊ«iÀViÌÊvÊÌ
iÊ number of rows
/
ÃÊLÕÌÊÌi}iViÊii«ÃÊÌ
iÊ *1ÊÕÌâ>ÌÊLÞÊi>V
Ê«ÀViÃÃÊÛiÀÞÊÜ°Ê̽ÃÊ>ÃÊ«Ã ÃLiÊÌÊÕ«`>ÌiÊÌ
iÊÃÌ>ÌÃÌVÃÊ>ÃÞV
ÀÕÃÞ°Ê/
ÃÊi>ÃÊÜ
iÊ>ʵÕiÀÞÊÜÕ`ÊÀ>ÞÊV>ÕÃiÊ statistics to be updated, instead that query proceeds with the old statistics, and the statistics >ÀiÊÕ«`>Ìi`Êvvi°Ê/
ÃÊV>Êëii`ÊÕ«ÊÌ
iÊÀiëÃiÊÌiÊvÊÃiʵÕiÀiÃ]ÊÃÕV
Ê>ÃÊÜ
iÊÌ
iÊ database is large or when you have a short timeout period. You can manually disable (or enable) the auto update statistics and the auto update sta tistics asynchronously features by using the =HPAN@=P=>=OA command. By default, the auto update statistics feature is enabled, and it is strongly recommended that you keep it enabled. /
iÊ>ÕÌÊÕ«`>ÌiÊÃÌ>ÌÃÌVÃÊ>ÃÞV
ÀÕÃÞÊvi>ÌÕÀiÊÃÊ`Ã>Li`ÊLÞÊ`iv>ÕÌ°Ê/ÕÀÊÌ
ÃÊvi>ÌÕÀiÊÊ only if you’ve determined it will help with timeouts on your database.
CHAPTER 7
STATISTICS ANALYSIS
Note I explain =HPAN@=P=>=OA later in this chapter in the “Manual Maintenance” section.
Benefits of Updated Statistics /
iÊLiivÌÃÊvÊ«iÀvÀ}Ê>Ê>ÕÌÊÕ«`>Ìi usually outweigh its cost on the system resources. /ÊÀiÊ`ÀiVÌÞÊVÌÀÊÌ
iÊLi
>ÛÀÊvÊÌ
iÊ`>Ì>]ÊÃÌi>`ÊvÊÕÃ}ÊÌ
iÊÌ>LiÃÊÊ`ÛiÌÕÀi Works, for this set of examples you will create one manually. Specifically, create a test table (_na]pa[p-*omh in the download) with only three rows and a nonclustered index: EB$OAHA?PK>FA?P[E@$#p-#% %EOJKPJQHH @NKLP=>HA`^k*p-7 CK ?NA=PAP=>HA`^k*p-$_-EJP(_.EJPE@AJPEPU%7 OAHA?PPKL-1,, E@AJPEPU$EJP(-(-%=Oj EJPKJqio BNKII]opan*`^k*Ouo?khqijoo_(I]opan*`^k*Ouo?khqijoo_.7 EJOANPEJPK`^k*p-$_-% OAHA?Pj BNKIJqio @NKLP=>HAJqio ?NA=PAJKJ?HQOPANA@EJ@ATe-KJ`^k*p-$_-%7 If you execute a OAHA?P statement with a very selective filter criterion on the indexed col umn to retrieve only one row, as shown in the following line of code, then the optimizer uses a VÕÃÌiÀi`Ê`iÝÊÃii]Ê>ÃÊÃ
ÜÊÊÌ
iÊiÝiVÕÌÊ«>ÊÊ}ÕÀiÊÇ£\ OAHA?P&BNKIp-SDANA_-9.))Napneara-nks
Figure 7-1. Execution plan for a very small result set
177
178
CHAPTER 7
STATISTICS ANALYSIS
/ÊÕ`iÀÃÌ>`ÊÌ
iÊivviVÌÊvÊÃ>Ê`>Ì>Ê`vV>ÌÃÊÊ>ÊÃÌ>ÌÃÌVÃÊÕ«`>Ìi]ÊVÀi>ÌiÊ>ÊÌÀ>ViÊ using Profiler. In the trace, add the event =qpkOp]po, which captures statistics update and cre ate events, and add OMH6>]p_d?kilhapa` with a filter on the Patp@]p]ÊVÕ°Ê/
iÊvÌiÀÊÃ
Õ`Ê look like JkpHegaOAP!ÊÜ
iÊÞÕÊ>ÀiÊ`i°Ê``ÊÞÊiÊÀÜÊÌÊÌ
iÊÌ>Li\ EJOANPEJPKp-$_-%R=HQAO$.% When you reexecute the preceding OAHA?P statement, you get the same execution plan as Ã
ÜÊÊ}ÕÀiÊÇ£°Ê}ÕÀiÊÇÓÊÃ
ÜÃÊÌ
iÊÌÀ>ViÊiÛiÌÃÊ}iiÀ>Ìi`ÊLÞÊÌ
iʵÕiÀÞ°
Figure 7-2. Trace output on the addition of a small number of rows /
iÊÌÀ>ViÊÕÌ«ÕÌÊ`iýÌÊVÌ>Ê>ÞÊ-+Ê>VÌÛÌÞÊÀi«ÀiÃiÌ}Ê>ÊÃÌ>ÌÃÌVÃÊÕ«`>ÌiÊLiV>ÕÃiÊ Ì
iÊÕLiÀÊvÊV
>}iÃÊviÊLiÜÊÌ
iÊÌ
ÀiÃ
`ÊÜ
iÀiÊ>ÞÊÌ>LiÊÌ
>ÌÊ
>ÃÊÀiÊÌ
>ÊxääÊÀÜÃÊ ÕÃÌÊ
>ÛiÊ>ÊVÀi>ÃiÊvÊxääÊÀÜÃÊ«ÕÃÊÓäÊ«iÀViÌÊvÊÌ
iÊÕLiÀÊvÊÀÜð /ÊÕ`iÀÃÌ>`ÊÌ
iÊivviVÌÊvÊ>À}iÊ`>Ì>Ê`vV>ÌÊÊÃÌ>ÌÃÌVÃÊÕ«`>Ìi]Ê>``Ê£]xääÊÀÜÃÊÌÊ the table (]``[nkso*omh in the download): OAHA?PPKL-1,, E@AJPEPU$EJP(-(-%=Oj EJPKJqio BNKII]opan*`^k*Ouo?khqijoo_(I]opan*`^k*Ouo?khqijoo_.7 EJOANPEJPK`^k*p-$ _%OAHA?P. BNKIJqio7 @NKLP=>HAJqio7 Now, if you reexecute the OAHA?P statement, like so: OAHA?P&BNKI`^k*p-SDANA_-9.7 >Ê>À}iÊÀiÃÕÌÊÃiÌÊ£]xäÓÊÀÜÃÊÕÌÊvÊÎ]ää£ÊÀÜîÊÜÊLiÊÀiÌÀiÛi`°Ê-ViÊ>Ê>À}iÊÀiÃÕÌÊÃiÌÊÃÊ requested, scanning the base table directly is preferable to going through the nonclustered `iÝÊÌÊÌ
iÊL>ÃiÊÌ>LiÊ£]xäÓÊÌiðÊVViÃÃ}ÊÌ
iÊL>ÃiÊÌ>LiÊ`ÀiVÌÞÊÜÊ«ÀiÛiÌÊÌ
iÊÛiÀ
i>`Ê VÃÌÊvÊL>ÀÊÕ«ÃÊ>ÃÃV>Ìi`ÊÜÌ
ÊÌ
iÊVÕÃÌiÀi`Ê`iÝ°Ê/
ÃÊÃÊÀi«ÀiÃiÌi`ÊÊÌ
iÊ ÀiÃÕÌ>ÌÊiÝiVÕÌÊ«>ÊÃiiÊ}ÕÀiÊÇή°
Figure 7-3. Execution plan for a large result set }ÕÀiÊÇ{ÊÃ
ÜÃÊÌ
iÊÀiÃÕÌ>ÌÊ*ÀviÀÊÌÀ>ViÊÕÌ«ÕÌ°
CHAPTER 7
STATISTICS ANALYSIS
Figure 7-4. Trace output on the addition of a large number of rows /
iÊ*ÀviÀÊÌÀ>ViÊÕÌ«ÕÌÊVÕ`ià an =qpkOp]po event since the threshold was exceeded LÞÊÌ
iÊ>À}iÃV>iÊÕ«`>ÌiÊÌ
ÃÊÌi°Ê/
iÃiÊ-+Ê>VÌÛÌiÃÊVÃÕiÊÃiÊiÝÌÀ>Ê *1ÊVÞViÃ°Ê ÜiÛiÀ]ÊLÞÊ`}ÊÌ
Ã]ÊÌ
iÊ«ÌâiÀÊ`iÌiÀiÃÊ>ÊLiÌÌiÀÊ`>Ì>«ÀViÃÃ}ÊÃÌÀ>Ìi}ÞÊ>`Êii«ÃÊ the overall cost of the query low.
Drawbacks of Outdated Statistics ÃÊiÝ«>i`ÊÊÌ
iÊ«ÀiVi`}ÊÃiVÌ]ÊÌ
i auto update statistics feature allows the optimizer to decide on an efficient processing strategy for a query as the data changes. If the statistics become outdated, however, then the processing strategies decided on by the optimizer may not be applicable for the current data set and thereby will degrade performance. /ÊÕ`iÀÃÌ>`ÊÌ
iÊ`iÌÀiÌ>ÊivviVÌÊvÊ
>Û}ÊÕÌ`>Ìi`ÊÃÌ>ÌÃÌVÃ]ÊvÜÊÌ
iÃiÊÃÌi«Ã\ 1. ,iVÀi>ÌiÊÌ
iÊ«ÀiVi`}ÊÌiÃÌÊÌ>LiÊÜÌ
Ê£]xääÊÀÜÃÊÞÊ>`ÊÌ
iÊVÀÀië`}ÊVÕà tered index. 2. *ÀiÛiÌÊ-+Ê-iÀÛiÀÊvÀÊÕ«`>Ì}ÊÃÌ>ÌÃÌVÃÊ>ÕÌ>ÌV>ÞÊ>ÃÊÌ
iÊ`>Ì>ÊV
>}iðÊ/Ê`Ê so, disable the auto update statistics feature by executing the following SQL statement: =HPAN@=P=>=OA=`rajpqnaSkngo.,,4OAP=QPK[QL@=PA[OP=PEOPE?OKBB 3. ``Ê£]xääÊÀÜÃÊÌÊÌ
iÊÌ>LiÊ>ÃÊLivÀi° Now, reexecute the OAHA?P statement to understand the effect of the outdated statistics on Ì
iʵÕiÀÞÊ«ÌâiÀ°Ê/
iʵÕiÀÞÊÃÊÀi«i>Ìi`Ê
iÀiÊvÀÊV>ÀÌÞ\ OAHA?P&BNKI`^k*p-SDANA_-9. }ÕÀiÊÇxÊ>`Ê}ÕÀiÊÇÈÊÃ
ÜÊÌ
iÊÀiÃÕÌ>ÌÊiÝiVÕÌÊ«>Ê>`ÊÌ
iÊ*ÀviÀÊÌÀ>ViÊÕÌ«ÕÌÊ for this query, respectively.
Figure 7-5. Execution plan with =QPK[QL@=PA[OP=PEOPE?OKBB
Figure 7-6. Trace output with =QPK[QL@=PA[OP=PEOPE?OKBB
179
180
C HAPTER 7
ST A TIS TIC S A NA L YS IS
With the auto update statistics feature switched off, the query optimizer has selected a different execution plan from the one it selected with this feature on. Based on the outdated statistics, which have only one row for the filter criterion (_-ÊrÊÓ®]ÊÌ
iÊ«ÌâiÀÊ`iV`i`ÊÌÊÕÃiÊ >ÊVÕÃÌiÀi`Ê`iÝÊÃii°Ê/
iÊ«ÌâiÀÊVÕ`½ÌÊ>iÊÌÃÊ`iVÃÊL>Ãi`ÊÊÌ
iÊVÕÀÀiÌÊ`>Ì>Ê distribution in the column. For performance reasons, it would have been better to hit the base Ì>LiÊ`ÀiVÌÞÊÃÌi>`ÊvÊ}}ÊÌ
ÀÕ}
ÊÌ
iÊVÕÃÌiÀi`Ê`iÝ]ÊÃViÊ>Ê>À}iÊÀiÃÕÌÊÃiÌÊ£]xä£Ê ÀÜÃÊÕÌÊvÊÎ]äääÊÀÜîÊÃÊÀiµÕiÃÌi`° You can see that turning off the auto update statistics feature has a negative effect on per vÀ>ViÊLÞÊV«>À}ÊÌ
iÊVÃÌÊvÊÌ
ÃʵÕiÀÞÊÜÌ
Ê>`ÊÜÌ
ÕÌÊÕ«`>Ìi`ÊÃÌ>ÌÃÌVðÊ/>LiÊÇ£Ê shows the difference in the cost of this query. Table 7-1. Cost of the Query with and Without Updated Statistics
Statistics Update Status
Figure
Cost (SQL:Batch Completed Event) CPU (ms)
Number of Reads
Updated
}ÕÀiÊÇ{
ä
Î{
Not updated
}ÕÀiÊÇÈ
£x
£xä
/
iÊÕLiÀÊvÊ}V>ÊÀi>`ÃÊ>`ÊÌ
iÊ *1ÊÕÌâ>ÌÊÃÊÃ}vV>ÌÞÊ
}
iÀÊÜ
iÊÌ
iÊÃÌ> ÌÃÌVÃÊ>ÀiÊÕÌv`>ÌiÊiÛiÊÌ
Õ}
ÊÌ
iÊ`>Ì>ÊÀiÌÕÀi`ÊÃÊi>ÀÞÊ`iÌV>Ê>`ÊÌ
iʵÕiÀÞÊÜ>ÃÊ «ÀiVÃiÞÊÌ
iÊÃ>i°Ê/
iÀivÀi]ÊÌÊÃÊÀiVi`i`ÊÌ
>ÌÊÞÕÊii«ÊÌ
iÊ>ÕÌÊÕ«`>ÌiÊÃÌ>ÌÃÌVÃÊ vi>ÌÕÀiÊ°Ê/
iÊLiivÌÃÊvÊii«}ÊÃÌ>ÌÃÌVÃÊÕ«`>Ìi`ÊÕÌÜi}
ÊÌ
iÊVÃÌÃÊvÊ«iÀvÀ}ÊÌ
iÊ update. Before you leave this section, turn =QPK[QL@=PA[OP=PEOPE?O back on (although you can also use ol[]qpkop]po): =HPAN@=P=>=OA=`rajpqnaSkngo.,,4OAP=QPK[QL@=PA[OP=PEOPE?OKJ
Statistics on a Nonindexed Column SometimesÊÞÕÊ>ÞÊ
>ÛiÊVÕÃÊÊÊÀÊvÌiÀÊVÀÌiÀ>ÊÜÌ
ÕÌÊ>ÞÊ`iÝ°Ê ÛiÊvÀÊÃÕV
Ê nonindexed columns, the query optimizer is more likely to make the best choice if it knows the data distribution (or statistics) of those columns. In addition to statistics on indexes, SQL Server can build statistics on columns with no `iÝiðÊ/
iÊvÀ>ÌÊÊ`>Ì>Ê`ÃÌÀLÕÌ]ÊÀÊÌ
iÊi
`ÊvÊ>Ê«>ÀÌVÕ>ÀÊÛ>ÕiÊVVÕÀÀ}Ê in a nonindexed column, can help the query optimizer determine an optimal processing strat i}Þ°Ê/
ÃÊLiivÌÃÊÌ
iʵÕiÀÞÊ«ÌâiÀÊiÛiÊvÊÌÊV>½ÌÊÕÃiÊ>Ê`iÝÊÌÊ>VÌÕ>ÞÊV>ÌiÊÌ
iÊÛ>ÕiÃ°Ê SQL Server automatically builds statistics on nonindexed columns if it deems this informa tion valuable in creating a better plan, usually when the columns are used in a predicate. By default, this feature is turned on, and it’s configurable through the Properties Options ÕÌÊ Ài>ÌiÊ-Ì>ÌÃÌVÃÊÃiÌÌ}ÊvÊ>Ê`>Ì>L>Ãi°Ê9ÕÊV>ÊÛiÀÀ`iÊÌ
ÃÊÃiÌÌ}Ê«À}À>>ÌV>ÞÊLÞÊ using the =HPAN@=P=>=OA command. However, for better performance, it is strongly recom mended that you keep this feature on. In general, you should not disable the automatic creation of statistics on nonindexed col umns. One of the scenarios in which you may consider disabling this feature is while executing
CHAPTER 7
S T A T I S T I C S A N A LY S I S
a series of ad hoc SQL activities that you will not execute again. In such a case, you must decide whether you want to pay the cost of automatic statistics creation to get a better plan in this one case and affect the performance of other SQL Server activities. It is worthwhile noting that SQL Server eventually removes statistics when it realizes that they have not been used for a while. So, in general, you should keep this feature on and not be concerned about it.
Benefits of Statistics on a Nonindexed Column /ÊÕ`iÀÃÌ>`ÊÌ
iÊLiivÌÊvÊ
>Û}ÊÃÌ>ÌÃÌVÃÊÊ>ÊVÕÊÜÌ
ÊÊ`iÝ]ÊVÀi>ÌiÊÌÜÊÌiÃÌÊÌ>LiÃÊ with disproportionate data distributions, as shown in the following code (_na]pa[p-[p.*omh ÊÌ
iÊ`Ü>`®°Ê Ì
ÊÌ>LiÃÊVÌ>Ê£ä]ää£ÊÀÜðÊ/>LiÊp- contains only one row for a value of the second column (p-[_.®ÊiµÕ>ÊÌÊ£]Ê>`ÊÌ
iÊÀi>}Ê£ä]äääÊÀÜÃÊVÌ>ÊÌ
ÃÊVÕÊ Û>ÕiÊ>ÃÊÓ°Ê/>LiÊp. contains exactly the opposite data distribution. ))?na]pabenopp]^hasepd-,,,-nkso EB$OAHA?PK>FA?P[E@$#`^k*p-#%%EOJKPJQHH @NKLP=>HA`^k*p-7 CK ?NA=PAP=>HA`^k*p-$p-[_-EJPE@AJPEPU(p-[_.EJP%7 EJOANPEJPK`^k*p-$p-[_.%R=HQAO$-%7 OAHA?PPKL--5,.2 E@AJPEPU$EJP(-(-%=Oj EJPKJqio BNKII]opan*`^k*Ouo?khqijoo_(I]opan*`^k*Ouo?khqijoo_.7 EJOANPEJPK`^k*p-$ p-[_. %OAHA?P. BNKIJqio CK ?NA=PA?HQOPANA@EJ@ATe-KJ`^k*p-$p-[_-% ))?na]paoa_kj`p]^hasepd-,,,-nkso( ))^qpkllkoepa`]p]`eopne^qpekj EB$OAHA?PK>FA?P[E@$#`^k*p.#%%EOJKPJQHH @NKLP=>HA`^k*p.7 CK ?NA=PAP=>HA`^k*p.$p.[_-EJPE@AJPEPU(p.[_.EJP%7 EJOANPEJPK`^k*p.$p.[_.%R=HQAO$.%7 EJOANPEJPK`^k*p.$ p.[_. %OAHA?PBNKIJqio7 @NKLP=>HAJqio7 CK ?NA=PA?HQOPANA@EJ@ATe-KJ`^k*p.$p.[_-%7
181
182
CHAPTER 7
STATISTICS ANALYSIS
/>LiÊÇÓÊÕÃÌÀ>ÌiÃÊ
ÜÊÌ
iÊÌ>LiÃÊÜÊ° Table 7-2. Sample Tables
Table t1
Table t2
Column
t1_c1
t1_c2
t2_c1
t2_c2
Nks-
£
£
£
Ó
Nks.
Ó
Ó
Ó
£
NksJ
N
Ó
N
£
Nks-,,,-
£äää£
Ó
£äää£
£
/ÊÕ`iÀÃÌ>`ÊÌ
iÊimportance of statistics on a nonindexed column, use the default set ting for the auto create statistics feature. By default, this feature is on. You can verify this using the @=P=>=OALNKLANPUAT function (although you can also query the ouo*`]p]^]oao view): OAHA?P@=P=>=OALNKLANPUAT$#=`rajpqnaSkngo.,,4#(#Eo=qpk?na]paOp]peope_o#%
Note You can find a detailed description of configuring the auto create statistics feature later in this chapter.
Use the following OAHA?P statement (jkjej`ata`[oaha_p*omh in the download) to access a large result set from table p- and a small result set from table p.°Ê/>LiÊp-Ê
>ÃÊ£ä]äääÊÀÜÃÊvÀÊ the column value of p-[_.ÊrÊÓ]Ê>`ÊÌ>LiÊp.Ê
>ÃÊ£ÊÀÜÊvÀÊp.[_.ÊrÊÓ°Ê ÌiÊÌ
>ÌÊÌ
iÃiÊVÕÃÊ ÕÃi`ÊÊÌ
iÊÊ>`ÊvÌiÀÊVÀÌiÀ>Ê
>ÛiÊÊ`iÝÊÊiÌ
iÀÊÌ>Li° OAHA?Pp-*p-[_. (p.*p.[_. BNKI`^k*pFKEJ`^k*p. KJp-*p-[_.9p.*p.[_. SDANAp-*p-[_.9.7 }ÕÀiÊÇÇÊÃ
ÜÃÊÌ
iÊiÝiVÕÌÊ«>ÊvÀÊÌ
ÃʵÕiÀÞ°
Figure 7-7. Execution plan with =QPK[?NA=PA[OP=PEOPE?OKJ
CHAPTER 7
STATISTICS ANALYSIS
}ÕÀiÊÇnÊÃ
ÜÃÊÌ
iÊ*ÀviÀÊÌÀ>ViÊÕÌ«ÕÌÊÜÌ
Ê>ÊV«iÌi`ÊiÛiÌÃÊ>`ÊÌ
iÊ=qpkOp]po event for this query. You can use this to evaluate some of the added costs for a given query.
Tip To keep the Profiler trace output compact, I used a prefilter on Patp@]p]JkphegaOAP!. This filters the OAP statements used to show the graphical execution plan.
/
iÊ*ÀviÀÊÌÀ>ViÊÕÌ«ÕÌÊÃ
ÜÊÊ}ÕÀiÊÇnÊVÕ`iÃÊÌÜÊ=qpkOp]po events creating sta tistics on the nonindexed columns referred to in the FKEJ and SDANA clauses, p.[_. and p-[_.. /
ÃÊ>VÌÛÌÞÊVÃÕiÃÊ>ÊviÜÊiÝÌÀ>Ê *1ÊVÞViÃÊÃViÊiÊVÕ`ÊLiÊ`iÌiVÌi`®]ÊLÕÌÊLÞÊVÃÕ }ÊÌ
iÃiÊiÝÌÀ>Ê *1ÊVÞViÃ]ÊÌ
iÊ«ÌâiÀÊ`iV`iÃÊÕ«Ê>ÊLiÌÌiÀÊ«ÀViÃÃ}ÊÃÌÀ>Ìi}ÞÊvÀÊii«}Ê the overall cost of the query low.
Figure 7-8. Trace output with =QPK[?NA=PA[OP=PEOPE?OKJ /ÊÛiÀvÞÊÌ
iÊÃÌ>ÌÃÌVÃÊ>ÕÌ>ÌV>ÞÊVÀi>Ìi`ÊLÞÊ-+Ê-iÀÛiÀÊÊÌ
iÊ`iÝi`ÊVÕÃÊvÊ each table, run this OAHA?P statement against the ouo*op]po table: OAHA?P& BNKIouo*op]po SDANAk^fa_p[e`9K>FA?P[E@$#p-#% }ÕÀiÊÇÊÃ
ÜÃÊÌ
i automatic statistics created for table p-.
Figure 7-9. Automatic statistics for table p/ÊÛiÀvÞÊ
ÜÊ>Ê`vviÀiÌÊÀiÃÕÌÊÃiÌÊÃâiÊvÀÊÌ
iÊÌÜÊÌ>LiÃÊvÕiViÃÊÌ
iÊ`iVÃÊvÊÌ
iÊ query optimizer, modify the filter criteria of jkjej`ata`[oaha_p*omh to access an opposite result set size from the two tables (small from p- and large from p.). Instead of filtering on p-*p-[_.ÊrÊÓ]ÊV
>}iÊÌÊÌÊvÌiÀÊÊ£\ OAHA?Pp-*p-[_. (p.*p.[_. BNKI`^k*pFKEJ`^k*p. KJp-*p-[_.9p.*p.[_. SDANAp-*p-[_.9-
183
184
CHAPTER 7
STATISTICS ANALYSIS
}ÕÀiÊÇ£äÊÃ
ÜÃÊÌ
iÊÀiÃÕÌ>ÌÊiÝiVÕÌÊ«>]Ê>`Ê}ÕÀiÊÇ££ÊÃ
ÜÃÊÌ
iÊ*ÀviÀÊÌÀ>ViÊ output of this query.
Figure 7-10. Execution plan for a different result set
Figure 7-11. Trace output for a different result set /
iÊÀiÃÕÌ>ÌÊ*ÀviÀÊÌÀ>ViÊÕÌ«ÕÌÊ`iýÌÊ«iÀvÀÊ>ÞÊ>``Ì>Ê-+Ê>VÌÛÌiÃÊÌÊ> >}iÊÃÌ>ÌÃÌVðÊ/
iÊÃÌ>ÌÃÌVÃÊÊÌ
iÊ`iÝi`ÊVÕÃÊp-*p-[_. and p.*p.[_.) had already been created during the previous execution of the query. For effective cost optimization, in each case the query optimizer selected different pro cessing strategies, depending upon the statistics on the nonindexed columns (p-*p-[_. and p.*p.[_.). You can see this from the last two execution plans. In the first, table p- is the outer Ì>LiÊvÀÊÌ
iÊiÃÌi`Ê«Ê]ÊÜ
iÀi>ÃÊÊÌ
iÊ>ÌiÃÌÊi]ÊÌ>LiÊp. is the outer table. By having statistics on the nonindexed columns (p-*p-[_. and p.*p.[_.), the query optimizer can create >ÊVÃÌivviVÌÛiÊ«>ÊÃÕÌ>LiÊvÀÊi>V
ÊV>Ãi° ÊiÛiÊLiÌÌiÀÊÃÕÌÊÜÕ`ÊLiÊÌÊ
>ÛiÊ>Ê`iÝÊÊÌ
iÊVÕ°Ê/
ÃÊÜÕ`ÊÌÊÞÊ create the statistics on the column but also allow fast data retrieval through an Ej`atOaag operation, while retrieving a small result set. However, in the case of a database application with queries referring to nonindexed columns in the SDANA clause, keeping the auto create statistics feature on still allows the optimizer to determine the best processing strategy for the existing data distribution in the column. If you need to know which column or columns might be covered by a given statistic, you need to look into the ouo*op]po[_khqijo system table. You can query it in the same way as you did the ouo*op]po table: OAHA?P& BNKIouo*op]po[_khqijo SDANAk^fa_p[e`9K>FA?P[E@$#p-#% /
ÃÊÜÊÃ
ÜÊÌ
iÊVÕÊÀÊVÕÃÊLi}ÊÀiviÀiVi`ÊLÞÊÌ
iÊ>ÕÌ>ÌV>ÞÊVÀi>Ìi`Ê ÃÌ>ÌÃÌVðÊ/
ÃÊÜÊ
i«ÊÞÕÊvÊÞÕÊ`iV`iÊÞÕÊii`ÊÌÊVÀi>ÌiÊ>Ê`iÝÊÌÊÀi«>ViÊÌ
iÊÃÌ>ÌÃÌVÃ]Ê because you will need to know which columns to create the statistics on.
CHAPTER 7
STATISTICS ANALYSIS
Drawback of Missing Statistics on a Nonindexed Column /ÊÕ`iÀÃÌ>`ÊÌ
iÊ`iÌÀiÌ>ÊivviVÌÊvÊÌÊ
>Û}ÊÃÌ>ÌÃÌVÃÊÊ`iÝi`ÊVÕÃ]Ê`À«Ê the statistics automatically created by SQL Server and prevent SQL Server from automatically creating statistics on columns with no index by following these steps: 1. Drop the automatic statistics created on column p-*p-[_. through the Manage Statistics dialog box as shown in the section “Benefits of Statistics on a Nonindexed
Õ]»ÊÀÊÕÃiÊÌ
i following SQL command: @NKLOP=PEOPE?OWp-Y*Op]peope_oJ]ia 2. Similarly, drop the corresponding statistics on column p.*p.[_.. 3. Ã>LiÊÌ
iÊ>ÕÌÊVÀi>ÌiÊÃÌ>ÌÃÌVÃÊvi>ÌÕÀiÊLÞÊ`iÃiiVÌ}ÊÌ
iÊÕÌÊ Ài>ÌiÊ-Ì>ÌÃÌVÃÊV
iVÊ box for the corresponding database or by executing the following SQL command: =HPAN@=P=>=OA=`rajpqnaSkngo.,,4OAP=QPK[?NA=PA[OP=PEOPE?OKBB Now reexecute the OAHA?P statement jkjej`ata`[oaha_p*omh: OAHA?Pp-*p-[_. (p.*p.[_. BNKI`^k*pFKEJ`^k*p. KJp-*p-[_.9p.*p.[_. SDANAp-*p-[_.9.7 }ÕÀiÊÇ£ÓÊ>`Ê}ÕÀiÊÇ£ÎÊÃ
ÜÊÌ
iÊÀiÃÕÌ>ÌÊiÝiVÕÌÊ«>Ê>`Ê*ÀviÀÊÌÀ>ViÊÕÌ«ÕÌ]Ê respectively.
Figure 7-12. Execution plan with =QPK[?NA=PA[OP=PEOPE?OKBB
Figure 7-13. Trace output with =QPK[?NA=PA[OP=PEOPE?OKBB
185
186
CHAPTER 7
STATISTICS ANALYSIS
With the auto create statistics feature off, the query optimizer selected a different execu tion plan compared to the one it selected with the auto create statistics feature on. On not finding statistics on the relevant columns, the optimizer chose the first table (p-) in the BNKI V>ÕÃiÊ>ÃÊÌ
iÊÕÌiÀÊÌ>LiÊvÊÌ
iÊiÃÌi`Ê«ÊÊ«iÀ>Ì°Ê/
iÊ«ÌâiÀÊVÕ`½ÌÊ>iÊÌÃÊ decision based on the actual data distribution in the column. Not only that, but the optimizer and the query engine determined that this query passed the threshold for parallelism, making this a parallel execution (those are the little arrows on the operators, marking them as parallel ÀiÊÊ«>À>iÊiÝiVÕÌÊ«>ÃÊÊ
>«ÌiÀÊ®°ÊÀÊiÝ>«i]ÊvÊÞÕÊ`vÞÊÌ
iʵÕiÀÞÊÌÊÀiviÀ ence table p. as the first table in the BNKI clause: OAHA?Pp-*p-[_. (p.*p.[_. BNKI`^k*p. FKEJ`^k*pKJp-*p-[_.9p.*p.[_. SDANAp-*p-[_.9.7 then the optimizer selects table p.Ê>ÃÊÌ
iÊÕÌiÀÊÌ>LiÊvÊÌ
iÊiÃÌi`Ê«ÊÊ«iÀ>Ì°Ê }ÕÀiÊÇ£{ÊÃ
ÜÃÊÌ
iÊiÝiVÕÌÊ«>°
Figure 7-14. Execution plan with =QPK[?NA=PA[OP=PEOPE?OKBB (a variation) You can see that turning off the auto create statistics feature has a negative effect on per formance by comparing the cost of this query with and without statistics on a nonindexed VÕ°Ê/>LiÊÇÎÊÃ
ÜÃÊÌ
iÊ`vviÀiViÊÊÌ
iÊVÃÌÊvÊÌ
ÃʵÕiÀÞ° Table 7-3. Cost Comparison of a Query with and Without Statistics on a Nonindexed Column
Statistics on Nonindexed Column
Figure
Cost (SQL:Batch Completed Event) Duration (ms)
Number of Reads
With statistics
}ÕÀiÊÇ££
£x
{n
Without statistics
}ÕÀiÊÇ£Î
ÎäÎ
Óä]ΣÎ
/
iÊÕLiÀÊvÊ}V>ÊÀi>`ÃÊ>`ÊÌ
iÊ *1ÊÕÌâ>ÌÊ>ÀiÊÛiÀÞÊ
}
ÊÜÌ
ÊÊÃÌ>ÌÃÌVÃÊÊÌ
iÊ `iÝi`ÊVÕðÊ7Ì
ÕÌÊÌ
iÃiÊÃÌ>ÌÃÌVÃ]ÊÌ
iÊ«ÌâiÀÊV>½ÌÊVÀi>ÌiÊ>ÊVÃÌivviVÌÛiÊ«>° ʵÕiÀÞÊiÝiVÕÌÊ«>Ê
}
}
ÌÃÊÌ
iÊÃÃ}ÊÃÌ>ÌÃÌVÃÊLÞÊ«>V}Ê>ÊiÝV>>ÌÊ«ÌÊ on the operator that would have used the statistics. You can see this in the clustered index ÃV>Ê«iÀ>ÌÀÃÊÊÌ
iÊ«ÀiÛÕÃÊiÝiVÕÌÊ«>Ê}ÕÀiÊÇ£{®]Ê>ÃÊÜiÊ>ÃÊÊÌ
iÊ`iÌ>i`Ê`iÃVÀ« ÌÊÊÌ
iÊ7>À}ÃÊÃiVÌÊvÀÊ>Ê`iÊÊ>Ê}À>«
V>ÊiÝiVÕÌÊ«>]Ê>ÃÊÃ
ÜÊÊ}ÕÀiÊÇ£xÊ for table p..
CHAPTER 7
STATISTICS ANALYSIS
Figure 7-15. Missing statistics indication in a graphical plan /
iÊ8ÊiÝiVÕÌÊ«>Ê«ÀÛ`iÃÊÌ
iÊÃÃ}ÊÃÌ>ÌÃÌVÃÊvÀ>ÌÊÕ`iÀÊÌ
iÊS]njejco VÕ]Ê>ÃÊÃ
ÜÊÊ}ÕÀiÊǣȰÊ,iiLiÀÊÌ
>ÌÊÞÕÊV>ÊLÌ>ÊÌ
iÊ8ÊiÝiVÕÌÊ«>ÊLÞÊ À}
ÌVV}ÊÜÌ
ÊÌ
iÊ}À>«
V>ÊiÝiVÕÌÊ«>Ê>`ÊÃiiVÌ}Ê-
ÜÊ ÝiVÕÌÊ*>Ê8ÊÀÊ by using OAPOP=PEOPE?OTIH in the query window.
Figure 7-16. Missing statistics indication in an XML plan
Note In a database application, there is always the possibility of queries using columns with no indexes. Therefore, for performance reasons, leaving the auto create statistics feature of SQL Server databases on is recommended.
Analyzing Statistics Statistics are collections of informationÊÃÌÀi`Ê>ÃÊ
ÃÌ}À>ðÊÊhistogram is a statistical con ÃÌÀÕVÌÊÌ
>ÌÊÃ
ÜÃÊ
ÜÊvÌiÊ`>Ì>Êv>ÃÊÌÊÛ>ÀÞ}ÊV>Ìi}ÀiðÊ/
iÊ
ÃÌ}À>ÊÃÌÀi`ÊLÞÊ-+Ê Server consists of a sampling of data distribution for a column or an index key (or the first VÕÊvÊ>ÊÕÌVÕÊ`iÝÊiÞ®ÊvÊÕ«ÊÌÊÓääÊÀÜðÊ/
iÊvÀ>ÌÊÊÌ
iÊrange of index
187
188
C HAPTER 7
ST A TIS TIC S A NA L YS IS
key values between two consecutive samples is called a step°Ê/
iÃi steps consist of varying size ÌiÀÛ>ÃÊLiÌÜiiÊÌ
iÊÓääÊÛ>ÕiÃÊÃÌÀi`°ÊÊÃÌi«Ê«ÀÛ`iÃÊÌ
iÊvÜ}ÊvÀ>Ì\ Ê
UÊ /
i top value of a given step (N=JCA[DE[GAU).
Ê
UÊ /
i number of rows equal to N=JCA[DE[GAU (AM[NKSO).
Ê
UÊ /
i range of rows between the previous top value and the current top value, without counting either of these samples (N=JCA[NKSO).
Ê
UÊ /
iÊÕLiÀ of distinct rows in the range (@EOPEJ?P[N=JCA[NKSO). If all values in the range are unique, then N=JCA[NKSO equals @EOPEJ?P[N=JCA[NKSO.
Ê
UÊ /
iÊ>ÛiÀ>}iÊÕLiÀ of rows equal to a key value within a range (=RC[N=JCA[NKSO).
/
iÊÛ>Õi of AM[NKSO for an index key value (N=JCA[DE[GAU) helps the optimizer decide how (and whether) to use the index when the indexed column is referred to in a SDANA clause. Because the optimizer can perform a OAAG or O?=J operation to retrieve rows from a table, the optimizer can decide which operation to perform based on the number of matching rows (AM[NKSO) for the index key value. /ÊÕ`iÀÃÌ>`Ê
ÜÊÌ
iÊ«ÌâiÀ½ÃÊ`>Ì>ÀiÌÀiÛ>ÊÃÌÀ>Ìi}ÞÊ`i«i`ÃÊÊÌ
iÊÕLiÀÊvÊ matching rows, create a test table (_na]pa[p/*omh in the download) with different data distri butions on an indexed column: EB$OAHA?PK>FA?P[E@$#`^k*p-#% %EOJKPJQHH @NKLP=>HA`^k*p-7 CK ?NA=PAP=>HA`^k*p-$_-EJP(_.EJPE@AJPEPU%7 EJOANPEJPK`^k*p-$_-% R=HQAO$-%7 OAHA?PPKL--5,.2 E@AJPEPU$EJP(-(-%=Oj EJPKJqio7 EJOANPEJPK`^k*p-$_-% OAHA?P. BNKIJqio7 ?NA=PAJKJ?HQOPANA@EJ@ATe-KJ`^k*p-$_-%7 When the preceding nonclustered index is created, SQL Server automatically creates statistics on the index key. You can obtain statistics for this nonclustered index key (e-) by executing the @>??ODKS[OP=PEOPE?O command: @>??ODKS[OP=PEOPE?O$p-(e-% }ÕÀiÊÇ£ÇÊÃ
ÜÃÊÌ
iÊÃÌ>ÌÃÌVÃÊÕÌ«ÕÌ°
CHAPTER 7
STATISTICS ANALYSIS
Figure 7-17. Statistics on index eÜ]ÊÌÊÕ`iÀÃÌ>`Ê
ÜÊivviVÌÛiÞÊÌ
iÊ«ÌâiÀÊ`iV`iÃÊÕ«Ê`vviÀiÌÊ`>Ì>ÀiÌÀiÛ>Ê strategies based on statistics, execute the following two queries requesting different numbers of rows: OAHA?P&BNKI`^k*p-SDANA_-9-))Napneara-nks7 OAHA?P&BNKI`^k*p-SDANA_-9.))Napneara-,,,,nkso7 }ÕÀiÊÇ£nÊÃ
ÜÃÊiÝiVÕÌÊ«>ÃÊvÊÌ
iÃiʵÕiÀið
Figure 7-18. Execution plans of small and large result set queries From the statistics, the optimizer can find the number of rows affected by the preceding two queries. Understanding that there is only one row to be retrieved for the first query, the optimizer chose an Ej`atOaag operation, followed by the necessary NE@Hkkgql to retrieve the data not stored with the clustered index. For the second query, the optimizer knows that a >À}iÊÕLiÀÊvÊÀÜÃÊ£ä]äääÊÀÜîÊÜÊLiÊ>vviVÌi`Ê>`ÊÌ
iÀivÀiÊ>Û`i`ÊÌ
iÊ`iÝÊÌÊ>ÌÌi«ÌÊ ÌÊ«ÀÛiÊ«iÀvÀ>Vi°Ê
>«ÌiÀÊ{ÊiÝ«>ÃÊ`iÝ}ÊÃÌÀ>Ìi}iÃÊÊ`iÌ>°®
189
190
CHAPTER 7
STATISTICS ANALYSIS
Besides the information on steps, other useful information in the statistics includes the following: Ê
UÊ /
iÊÌiÊÃÌ>ÌÃÌVÃÊÜiÀiÊ>ÃÌÊÕ«`>Ìi`
Ê
UÊ /
iÊÕLiÀÊvÊÀÜÃÊÊÌ
iÊÌ>Li
Ê
UÊ /
iÊ>ÛiÀ>}iÊ`iÝÊiÞÊi}Ì
Ê
UÊ /
iÊÕLiÀÊvÊÀÜÃÊÃ>«i`ÊvÀÊÌ
iÊ
ÃÌ}À>
Ê
UÊ iÃÌiÃÊvÀÊVL>ÌÃÊvÊVÕÃ
Information on the time of the last update can help you decide whether you should man Õ>ÞÊÕ«`>ÌiÊÌ
iÊÃÌ>ÌÃÌVðÊ/
iÊaverage key length represents the average size of the data in the index key column(s). It helps you understand the width of the index key, which is an important i>ÃÕÀiÊÊ`iÌiÀ}ÊÌ
iÊivviVÌÛiiÃÃÊvÊÌ
iÊ`iÝ°ÊÃÊiÝ«>i`ÊÊ
>«ÌiÀÊ{]Ê>ÊÜ`iÊ`iÝÊ is usually costly to maintain and requires more disk space and memory pages.
Density When creating an execution plan, the query optimizer analyzes the statistics of the columns used in the filter and FKEJÊV>ÕÃiðÊÊfilter criterion with high selectivity limits the number vÊÀÜÃÊvÀÊ>ÊÌ>LiÊÌÊ>ÊÃ>ÊÀiÃÕÌÊÃiÌÊ>`Ê
i«ÃÊÌ
iÊ«ÌâiÀÊii«ÊÌ
iʵÕiÀÞÊVÃÌÊÜ°ÊÊ column with a unique index will have a very high selectivity, since it can limit the number of matching rows to one. On the other hand, a filter criterion with low selectivity will return a large result set from Ì
iÊÌ>Li°ÊÊvÌiÀÊVÀÌiÀÊÜÌ
ÊÛiÀÞ low selectivity makes a nonclustered index on the column ineffective. Navigating through a nonclustered index to the base table for a large result set is usually costlier than scanning the base table (or clustered index) directly because of the cost overhead of bookmark lookups associated with the nonclustered index. You can observe this Li
>ÛÀÊÊÌ
iÊiÝiVÕÌÊ«>ÊÊ}ÕÀiÊÇ£n° -Ì>ÌÃÌVÃÊÌÀ>VÊÌ
iÊÃiiVÌÛÌÞÊvÊ>ÊVÕÊÊÌ
iÊvÀÊvÊ>Ê`iÃÌÞÊÀ>Ì°ÊÊVÕÊÜÌ
high selectivity (or uniqueness) willÊ
>ÛiÊÜÊ`iÃÌÞ°ÊÊVÕÊÜÌ
ÊÜÊ`iÃÌÞÊÌ
>ÌÊÃ]Ê
}
Ê selectivity) is suitable for a nonclustered index, because it helps the optimizer retrieve a small ÕLiÀÊvÊÀÜÃÊÛiÀÞÊv>ÃÌ°Ê/
ÃÊÃÊ>ÃÊÌ
iÊ«ÀV«>ÊÊÜ
V
ÊvÌiÀi` indexes operate since the filter’s goal is to increase the selectivity, or density, of the index. Density can be expressed as follows:
iÃÌÞÊrÊ£ÊÉÊ ÕLiÀÊvÊ`ÃÌVÌÊÛ>ÕiÃÊvÀÊ>ÊVÕ iÃÌÞÊÜÊ>Ü>ÞÃÊViÊÕÌÊ>ÃÊ>ÊÕLiÀÊÃiÜ
iÀiÊLiÌÜiiÊäÊ>`Ê£°Ê/
iÊÜiÀÊÌ
iÊV umn density, the more suitable it is for use in a nonclustered index. You can perform your own calculations to determine the density of columns within your own indexes and statistics. For example, to calculate the density of column _- from the test table built by _na]pa[p/*omh, use Ì
iÊvÜ}ÊÀiÃÕÌÃÊÊ}ÕÀiÊÇ£®\ OAHA?P-*,+?KQJP$@EOPEJ?P_-%BNKIp-
Figure 7-19. Results of density calculation for column _-
CHAPTER 7
STATISTICS ANALYSIS
You can see this as actual data in the =hh`ajoepu column in the output from @>??ODKS[ OP=PEOPE?O°Ê/
ÃÊ
}
`iÃÌÞÊÛ>ÕiÊvÀÊÌ
iÊVÕÊ>iÃÊÌÊ>ÊiÃÃÊÃÕÌ>LiÊV>``>ÌiÊvÀÊ>Ê index, even a filtered index. However, the statistics of the index key values maintained in the steps help the query optimizer use the index for the predicate _-9-, as shown in the previous execution plan.
Statistics on a Multicolumn Index In the case of an index with one column, statistics consist of a histogram and a density value for that column. Statistics for a composite index with multiple columns consist of one his togram for the first column only and multipleÊ`iÃÌÞÊÛ>ÕiðÊ/
ÃÊÃÊiÊÀi>ÃÊÜ
ÞÊ̽ÃÊ wise to put the more selective column, the one with the lowest density, first when building a V«Õ`Ê`iÝÊÀÊV«Õ`ÊÃÌ>ÌÃÌVðÊ/
iÊ`iÃÌÞÊÛ>ÕiÃÊVÕ`iÊÌ
iÊ`iÃÌÞÊvÀÊÌ
iÊvÀÃÌÊ column and for each prefix combination of the index key columns. Multiple density values help the optimizer find the selectivity of the composite index when multiple columns are referred to by predicates in the SDANA and FKEJÊV>ÕÃiðÊÌ
Õ}
ÊÌ
iÊvÀÃÌÊVÕÊV>Ê
i«Ê determine the histogram, the final density of the column itself would be the same regardless of column order. /ÊLiÌÌiÀÊÕ`iÀÃÌ>`ÊÌ
iÊ`iÃÌÞÊÛ>ÕiÃÊ>Ì>i`ÊvÀÊ>ÊÕÌVÕÊ`iÝ]ÊÞÕÊV>Ê modify the nonclustered index used earlier to include two columns: ?NA=PAJKJ?HQOPANA@EJ@ATe-KJ`^k*p-$_-(_.%SEPD@NKL[ATEOPEJC }ÕÀiÊÇÓäÊÃ
ÜÃÊÌ
iÊÀiÃÕÌ>ÌÊÃÌ>ÌÃÌVÃÊ«ÀÛ`i`ÊLÞÊ@>??ODKS[OP=PEOPE?O.
Figure 7-20. Statistics on the multicolumn index eÃÊÞÕÊV>ÊÃii]ÊÌ
iÀiÊ>ÀiÊÌÜÊ`iÃÌÞÊÛ>ÕiÃÊÕ`iÀÊÌ
iÊ=hh`ajoepu column: Ê
UÊ /
iÊ`iÃÌÞÊvÊÌ
iÊvÀÃÌÊVÕ
Ê
UÊ /
iÊ`iÃÌÞÊvÊÌ
iÊvÀÃÌʳÊÃiV`®ÊVÕÃ
For a multicolumn index with three columns, the statistics for the index would also con Ì>ÊÌ
iÊ`iÃÌÞÊÛ>ÕiÊvÊÌ
iÊvÀÃÌʳÊÃiV`ʳÊÌ
À`®ÊVÕðÊ/
iÊÃÌ>ÌÃÌVÃÊܽÌÊVÌ>Ê>Ê `iÃÌÞÊÛ>ÕiÊvÀÊ>ÞÊÌ
iÀÊVL>ÌÊvÊVÕðÊ/
iÀivÀi]ÊÌ
ÃÊ`iÝÊe-) won’t be very useful for filtering rows only on the second column (_.), because the density value of the sec ond column (_.) alone isn’t maintained in the statistics. You can computeÊÌ
iÊÃiV`Ê`iÃÌÞÊÛ>ÕiÊä°£äÓÈ®ÊÃ
ÜÊÊ}ÕÀiÊÇ£ÊÌ
ÀÕ}
Ê Ì
iÊvÜ}ÊÃÌi«Ã°Ê/
ÃÊÃÊÌ
iÊÕLiÀÊvÊ`ÃÌVÌÊÛ>ÕiÃÊvÀ a column combination of (_-, _.): OAHA?P-*,+?KQJP$&% BNKI$OAHA?P@EOPEJ?P_-(_.BNKI`^k*p-%@eopej_pNkso
191
192
CHAPTER 7
STATISTICS ANALYSIS
Statistics on a Filtered Index /
iÊ«ÕÀ«ÃiÊvÊ>Êfiltered index is to change the data that makes up the index and therefore change the density and histogram to make the index more performant. Instead of a test table, Ì
ÃÊiÝ>«iÊÜÊÕÃiÊÌ
iÊ`ÛiÌÕÀi7ÀÃÓäänÊ`>Ì>L>Ãi°Ê Ài>ÌiÊ>Ê`iÝÊÊÌ
iÊO]hao* Lqn_d]oaKn`anDa]`an table on the Lqn_d]oaKn`anJqi^an column: ?NA=PAEJ@ATET[PaopKJO]hao*O]haoKn`anDa]`an$Lqn_d]oaKn`anJqi^an% }ÕÀiÊÇÓ£ÊÃ
ÜÃÊÌ
iÊ
i>`iÀÊ>`ÊÌ
iÊ`iÃÌÞÊvÊÌ
iÊÕÌ«ÕÌÊvÀÊ@>??ODKS[OP=PEOPE?O run against this new index: @>??ODKS[OP=PEOPE?O$#O]hao*O]haoKn`anDa]`an#(ET[Paop%
Figure 7-21. Statistics header of an unfiltered index vÊÌ
iÊÃ>iÊ`iÝÊÃÊÀiVÀi>Ìi`ÊÌÊ`i>ÊÜÌ
ÊÛ>ÕiÃÊvÊÌ
iÊVÕÊÌ
>ÌÊ>ÀiÊÌÊjqhh, it would look something like this: ?NA=PAEJ@ATET[PaopKJO]hao*O]haoKn`anDa]`an$Lqn_d]oaKn`anJqi^an% SDANALqn_d]oaKn`anJqi^anEOJKPJQHH SEPD@NKL[ATEOPEJC `ÊÜ]ÊÊ}ÕÀiÊÇÓÓ]ÊÌ>iÊ>ÊÊ>ÌÊÌ
iÊÃÌ>ÌÃÌVÃÊvÀ>Ì°
Figure 7-22. Statistics header for a filtered index First you can see that the number of rows that compose the statistics have radically dropped in the filtered index because there is a filter in place. Notice also that the average key i}Ì
Ê
>ÃÊVÀi>Ãi`ÊÃViÊÞÕ½ÀiÊÊ}iÀÊ`i>}ÊÜÌ
ÊâiÀi}Ì
ÊÃÌÀ}ðÊÊvÌiÀÊiÝ«ÀiÃÃÊ has been defined rather than the JQHHÊÛ>ÕiÊÛÃLiÊÊ}ÕÀiÊÇÓ£°Ê ÕÌÊÌ
iÊÕvÌiÀi`ÊÀÜÃÊvÊ both sets of data are the same. /
iÊ`iÃÌÞÊi>ÃÕÀiiÌÃÊ>ÀiÊÛiÀÞÊÌiÀiÃÌ}°Ê ÌViÊÌ
>ÌÊÌ
iÊ`iÃÌÞÊÃÊVÃiÊÌÊÌ
iÊ same for both values, but the filtered density is slightly lower, meaning fewer unique values. /
ÃÊÃÊLiV>ÕÃiÊÌ
iÊvÌiÀi`Ê`>Ì>]ÊÜ
iÊ>À}>ÞÊiÃÃÊÃiiVÌÛi]ÊÃÊ>VÌÕ>ÞÊÀiÊ>VVÕÀ>Ìi]Ê i>Ì}Ê>ÊÌ
iÊi«ÌÞÊÛ>ÕiÃÊÌ
>ÌÊܽÌÊVÌÀLÕÌiÊÌÊ>ÊÃi>ÀV
°Ê`ÊÌ
iÊ`iÃÌÞÊvÊÌ
iÊ second value, which represents the clustered index pointer, is identical with the value of the density of the Lqn_d]oaKn`anJqi^an alone because each represents the same amount of unique `>Ì>°Ê/
iÊ`iÃÌÞÊvÊÌ
i additional clustered index in the previous column is a much smaller
CHAPTER 7
S T A T I S T I C S A N A LY S I S
number because of all the unique values of O]haoKn`anE` that are not included in the filtered data because of the elimination of the jqhh values. "iÊÌ
iÀÊ«ÌÊ«iÊÌÊÞÕÊÃÊÌÊVÀi>ÌiÊvÌiÀi`ÊÃÌ>ÌÃÌVðÊ/
ÃÊ>ÜÃÊÞÕÊÌÊVÀi>ÌiÊiÛiÊ ÀiÊviÌÕi`Ê
ÃÌ}À>ÃÊÊ«>ÀÌÌi`ÊÌ>LiðÊ/
ÃÊÃÊiViÃÃ>ÀÞÊLiV>ÕÃiÊÃÌ>ÌÃÌVÃÊ>ÀiÊ not automatically created on partitioned tables and you can’t create your own using ?NA=PA OP=PEOPE?O. You can create filtered indexes by partition and get statistics or create filtered sta tistics specifically by partition. Before going on, clean the indexes created, if any: @NKLEJ@ATO]hao*O]haoKn`anDa]`an*ET[Paop7
Statistics Maintenance SQL Server allows a user to manually override the maintenance of statistics in an individual `>Ì>L>Ãi°Ê/
iÊvÕÀÊ>Êconfigurations controlling automatic statistics maintenance behavior of SQL Server are as follows: Ê
UÊ
iÜÊÃÌ>ÌÃÌVÃ on columns with no index (auto create statistics)
Ê
UÊ 1«`>Ì} existing statistics (auto update statistics)
Ê
UÊ /
iÊ`i}ÀiiÊvÊsampling used to collect statistics
Ê
UÊ ÃÞV
ÀÕÃ updating of existing statistics (auto update statistics async)
You can control the preceding configurations at the levels of a database (all indexes and ÃÌ>ÌÃÌVÃÊÊ>ÊÌ>LiîÊÀÊÊ>ÊV>ÃiLÞV>ÃiÊL>ÃÃÊÊ`Û`Õ>Ê`iÝiÃÊÀÊÃÌ>ÌÃÌVðÊ/
iÊ>ÕÌÊ create statistics setting is applicable for nonindexed columns only, because SQL Server always VÀi>ÌiÃÊÃÌ>ÌÃÌVÃÊvÀÊ>Ê`iÝÊiÞÊÜ
iÊÌ
iÊ`iÝÊÃÊVÀi>Ìi`°Ê/
iÊ>ÕÌÊÕ«`>ÌiÊÃÌ>ÌÃÌVÃÊÃiÌÌ}]Ê and the asynchronous version, is applicable for statistics on both indexes and SDANA clause col umns with no index.
Automatic Maintenance By default, SQL Server automatically takes care of statistics. Both the auto create statistics and >ÕÌÊÕ«`>ÌiÊÃÌ>ÌÃÌVÃÊÃiÌÌ}ÃÊ>ÀiÊÊLÞÊ`iv>ÕÌ°Ê/
iÃiÊÌÜÊvi>ÌÕÀiÃÊÌ}iÌ
iÀÊ>ÀiÊÀiviÀÀi`ÊÌÊ as autostats°ÊÃÊiÝ«>i`Ê«ÀiÛÕÃÞ]ÊÌÊÃÊÕÃÕ>ÞÊLiÌÌiÀÊÌÊii«ÊÌ
iÃiÊÃiÌÌ}ÃÊ°Ê/
i auto update statistics async setting is off by default.
Auto Create Statistics /
iÊauto create statistics feature automatically creates statistics on nonindexed columns when referred to in the SDANA clause of a query. For example, when this OAHA?P statement is run against the O]hao*O]haoKn`anDa]`an table on a column with no index, statistics for the column are created: OAHA?P& BNKIO]hao*O]haoKn`anDa]`an=Ookd SDANAOdelPk=``naooE@9.525.
193
194
CHAPTER 7
STATISTICS ANALYSIS
/
iÊÌ
iÊ>ÕÌÊVÀi>ÌiÊÃÌ>ÌÃÌVÃÊvi>ÌÕÀiÊ>iÊÃÕÀiÊÌÊÃÊÌÕÀi`ÊL>VÊÊvÊÞÕÊ
>ÛiÊÌÕÀi`ÊÌÊ off) automatically creates statistics on column OdelPk=``naooE@. You can see this in the Profiler ÌÀ>ViÊÕÌ«ÕÌÊÊ}ÕÀiÊÇÓΰ
Figure 7-23. Trace output with =QPK[?NA=PA[OP=PEOPE?OKJ
Auto Update Statistics /
i auto update statistics feature automatically updates existing statistics on the indexes and columns of a permanent table when the table is referred to in a query, provided the statistics
>ÛiÊLiiÊ>Ài`Ê>ÃÊÕÌv`>Ìi°Ê/
iÊÌÞ«iÃÊvÊV
>}iÃÊ>ÀiÊ>VÌÊÃÌ>ÌiiÌÃ]ÊÃÕV
Ê>ÃÊEJOANP, QL@=PA, and @AHAPA°Ê/
iÊÌ
ÀiÃ
`ÊvÀÊÌ
iÊÕLiÀÊvÊV
>}iÃÊ`i«i`ÃÊÊÌ
iÊÕLiÀÊvÊÀÜÃÊ ÊÌ
iÊÌ>Li]Ê>ÃÊÃ
ÜÊÊ/>LiÊÇ{° Table 7-4. Update Statistics Threshold for Number of Changes
Number of Rows
Threshold for Number of Changes
ä
Ê£ÊÃiÀÌ
Êxää
ÊxääÊV
>}iÃ
Êxää
xääʳÊÓä¯ÊvÊV>À`>ÌÞÊV
>}iÃ
In SQL Server, cardinality is counted as the number of rows in the table. Using an internal threshold reduces the frequency of the automatic update of statistics, and using column changes rather than rows changes further reduces the frequency. For exam ple, consider the following table (_na]pa[p0*omh in the download): EB$OAHA?PK>FA?P[E@$#`^k*p-#%%EOJKPJQHH @NKLP=>HA`^k*p-7 ?NA=PAP=>HA`^k*p-$_-EJP%7 ?NA=PAEJ@ATet-KJ`^k*p-$_-%7 EJOANPEJPK`^k*p-$ _%R=HQAO$,%7 vÌiÀÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊÃÊVÀi>Ìi`]Ê>ÊÃ}iÊÀÜÊÃÊ>``i`ÊÌÊÌ
iÊÌ>Li°Ê/
ÃÊÕÌ`>ÌiÃÊ the existing statistics on the nonclustered index. If the following OAHA?P statement is executed with a reference to the indexed column in the SDANA clause, like so: OAHA?P&BNKI`^k*p-SDANA_-9, then the auto update statistics feature automatically updates statistics on the nonclustered `iÝ]Ê>ÃÊÃ
ÜÊÊÌ
iÊ*ÀviÀÊÌÀ>ViÊÕÌ«ÕÌÊÊ}ÕÀiÊÇÓ{°
CHAPTER 7
STATISTICS ANALYSIS
Figure 7-24. Trace output with =QPK[QL@=PA[OP=PEOPE?OKJ "ViÊÌ
iÊÃÌ>ÌÃÌVÃÊ>ÀiÊÕ«`>Ìi`]ÊÌ
iÊV
>}iÌÀ>V}ÊiV
>ÃÃÊvÀÊÌ
iÊVÀÀië`}Ê Ì>LiÃÊ>ÀiÊÃiÌÊÌÊä°Ê/
ÃÊÜ>Þ]Ê-+Ê-iÀÛiÀÊii«ÃÊÌÀ>VÊvÊÌ
iÊÕLiÀÊvÊV
>}iÃÊÌÊÌ
iÊÌ>LiÃÊ>`Ê manages the frequency of automatic updates of statistics.
Auto Update Statistics Asynchronously If auto update statistics asynchronously is set to on, the basic behavior of statistics in SQL -iÀÛiÀÊýÌÊV
>}i`ÊÀ>`V>Þ°Ê7
iÊ>ÊÃiÌÊvÊÃÌ>ÌÃÌVÃÊÃÊ>Ài`Ê>ÃÊÕÌv`>ÌiÊ>`Ê>ʵÕiÀÞÊÃÊ then run against those statistics, the statistics update does not interrupt the query, as normally happens. Instead, the query finishes execution using the older set of statistics. Once the query V«iÌiÃ]ÊÌ
iÊÃÌ>ÌÃÌVÃÊ>ÀiÊÕ«`>Ìi`°Ê/
iÊÀi>ÃÊÌ
ÃÊ>ÞÊLiÊ>ÌÌÀ>VÌÛiÊÃÊÌ
>ÌÊÜ
iÊÃÌ>ÌÃÌVÃÊ are updated, query plans in the procedure cache are removed, and the query being run must be recompiled. So, rather than make a query wait for both the update of the statistics and >ÊÀiV«iÊvÊÌ
iÊ«ÀVi`ÕÀi]ÊÌ
iʵÕiÀÞÊV«iÌiÃÊÌÃÊÀÕ°Ê/
iÊiÝÌÊÌiÊÌ
iÊÃ>iʵÕiÀÞÊÃÊ called, it will have updated statistics waiting for it, and it will have to recompile only. Ì
Õ}
ÊÌ
ÃÊvÕVÌ>ÌÞÊ`iÃÊ>iÊÀiV«iÃÊÃiÜ
>ÌÊv>ÃÌiÀ]ÊÌÊV>Ê>ÃÊV>ÕÃiÊ queries that could benefit from updated statistics and a new execution plan to work with the `ÊiÝiVÕÌÊ«>°Ê >ÀivÕÊÌiÃÌ}ÊÃÊÀiµÕÀi`ÊLivÀiÊÌÕÀ}ÊÌ
ÃÊvÕVÌ>ÌÞÊÊÌÊiÃÕÀiÊÌÊ doesn’t cause more harm than good.
Note If you are attempting to update statistics asynchronously, you must also have =QPK[QL@=PA[ OP=PEOPE?O set to KJ.
Manual Maintenance /
i following are situations in which you need to interfere with the automatic maintenance of statistics: Ê
UÊ When experimenting with statistics: Just a friendly suggestion: please spare your pro duction servers from experiments such as the ones you are doing in this book.
Ê
UÊ After upgrading from a previous version to SQL Server 2008: Since the statistics main Ìi>ViÊvÊ-+Ê-iÀÛiÀÊÓäänÊ
>ÃÊLiiÊÕ«}À>`i`]ÊÞÕÊÃ
Õ`Ê>Õ>ÞÊÕ«`>ÌiÊÌ
iÊ statistics of the complete database immediately after the upgrade instead of waiting for SQL Server to update it over time with the help of automatic statistics.
Ê
UÊ While executing a series of ad hoc SQL activities that you won’t execute again: In such cases, you must decide whether you want to pay the cost of automatic statistics main tenance to get a better plan in that one case and affect the performance of other SQL -iÀÛiÀÊ>VÌÛÌiðÊ-]ÊÊ}iiÀ>]ÊÞÕÊ`½ÌÊii`ÊÌÊLiÊVViÀi`ÊÜÌ
ÊÃÕV
ÊiÌiÀð
195
196
C HAPTER 7
ST A TIS TIC S A NA L YS IS
Ê
UÊ When you come upon an issue with the automatic statistics maintenance and the only workaround for the time being is to keep the automatic statistics maintenance feature off\Ê ÛiÊÊÌ
iÃiÊV>ÃiÃÊÞÕÊV>ÊÌÕÀÊÌ
iÊvi>ÌÕÀiÊvvÊvÀÊÌ
iÊëiVvVÊ`>Ì>L>ÃiÊÌ>LiÊÌ
>ÌÊ faces the problem instead of disabling it for the complete database.
Ê
UÊ While analyzing the performance of a query, you realize that the statistics are missing for a few of the database objects referred to by the query\Ê/
ÃÊV>ÊLiÊiÛ>Õ>Ìi`ÊvÀÊÌ
iÊ }À>«
V>Ê>`Ê8ÊiÝiVÕÌÊ«>Ã]Ê>ÃÊiÝ«>i`Êi>ÀiÀÊÊÌ
iÊV
>«ÌiÀ°
Ê
UÊ While analyzing the effectiveness of statistics, you realize that they are inaccurate\Ê/
ÃÊ can be determined when poor execution plans are being created from what should be good sets of indexes.
SQL Server allows a user to control many of its automatic statistics maintenance features. You can enable (or disable) the automatic statistics creation and update features by using the auto create statistics and auto update statistics settings, respectively, and then you can get your hands dirty.
Manage Statistics Settings You can control the auto create statisticsÊÃiÌÌ}Ê>ÌÊ>Ê`>Ì>L>ÃiÊiÛi°Ê/Ê`Ã>LiÊÌ
ÃÊÃiÌÌ}]ÊÕÃiÊ the =HPAN@=P=>=OA command: =HPAN@=P=>=OA=`rajpqnaSkngo.,,4OAP=QPK[?NA=PA[OP=PEOPE?OKBB You can control the auto update statistics setting at different levels of a database, includ }Ê>Ê`iÝiÃÊ>`ÊÃÌ>ÌÃÌVÃÊÊ>ÊÌ>Li]ÊÀÊ>ÌÊÌ
iÊ`Û`Õ>Ê`iÝÊÀÊÃÌ>ÌÃÌVÃÊiÛi°Ê/Ê`Ã>LiÊ auto update statistics at the database level, use the =HPAN@=P=>=OA command: =HPAN@=P=>=OA=`rajpqnaSkngo.,,4OAP=QPK[QL@=PA[OP=PEOPE?OKBB Disabling this setting at the database level overrides individual settings at lower levels. ÕÌÊÕ«`>ÌiÊÃÌ>ÌÃÌVÃ asynchronously requires that the auto update statistics be on first. /
iÊÞÕÊV> enable the asynchronous update: =HPAN@=P=>=OA=`rajpqnaSkngo.,,4OAP=QPK[QL@=PA[OP=PEOPE?O[=OUJ?KJ /ÊVv}ÕÀi auto update statistics for all indexes and statistics on a table in the current database, use the ol[]qpkop]po system stored procedure: QOA=`rajpqnaSkngo.,,4 ATA?ol[]qpkop]po#Dqi]jNaokqn_ao*@al]npiajp#(#KBB# You can also use the same stored procedure to configure this setting for individual indexes ÀÊÃÌ>ÌÃÌVðÊ/Ê`Ã>LiÊÌ
ÃÊÃiÌÌ}ÊvÀÊÌ
iÊ=G[@al]npiajp[J]ia index on =`rajpqnaSkngo.,,4* Dqi]jNaokqn_ao*@al]npiajp, execute the following statements: QOA=`rajpqnaSkngo.,,4 ATA?ol[]qpkop]poÐDqi]jNaokqn_ao*@al]npiajpÑ(#KBB#(=G[@al]npiajp[J]ia
CHAPTER 7
S T A T I S T I C S A N A LY S I S
You can also use the QL@=PAOP=PEOPE?O command’s SEPDJKNA?KILQPA option to disable Ì
ÃÊÃiÌÌ}ÊvÀÊ>ÊÀÊ`Û`Õ>Ê`iÝiÃÊ>`ÊÃÌ>ÌÃÌVÃÊÊ>ÊÌ>LiÊÊÌ
iÊVÕÀÀiÌÊ`>Ì>L>Ãi°Ê/
i ol[_na]paop]po stored procedure also has the JKNA?KILQPAÊ«Ì°Ê/
iÊJKNA?KILQPA option will not disable automatic update of statistics directly, but it will prevent them, which is almost the same. Û`Ê`Ã>L}ÊÌ
iÊ>ÕÌ>ÌVÊÃÌ>ÌÃÌVÃÊvi>ÌÕÀiÃ]ÊÕiÃÃÊÞÕÊ
>ÛiÊVvÀi`ÊÌ
ÀÕ}
ÊÌiÃÌ ing that this brings a performance benefit. If the automatic statistics features are disabled, then you should manually identify and create missing statistics on the columns that are not `iÝi`Ê>`ÊÌ
iÊii«ÊÌ
iÊiÝÃÌ}ÊÃÌ>ÌÃÌVÃÊÕ«Ì`>Ìi° Reset the automatic maintenance of the index so that it is on where it has been turned off: ATA?ol[]qpkop]po#Dqi]jNaokqn_ao*@al]npiajp#(#KJ# ATA?ol[]qpkop]poÐDqi]jNaokqn_ao*@al]npiajpÑ(#KJ#(=G[@al]npiajp[J]ia
Generate Statistics / create statistics manually, use one of the following options: Ê
UÊ ?NA=PAOP=PEOPE?O: You can use this option to create statistics on single or multiple columns of a table or an indexed view. Unlike the ?NA=PAEJ@AT command, ?NA=PA OP=PEOPE?O uses sampling by default.
Ê
UÊ ol[_na]paop]po: Use this storedÊ«ÀVi`ÕÀiÊÌÊVÀi>ÌiÊÃ}iVÕÊÃÌ>ÌÃÌVÃÊvÀÊ>Ê eligibleÊVÕÃÊvÀÊ>ÊÕÃiÀÊÌ>LiÃÊÊÌ
iÊVÕÀÀiÌÊ`>Ì>L>Ãi°Ê/
ÃÊVÕ`iÃÊ>ÊVÕÃÊ iÝVi«ÌÊV«ÕÌi`ÊVÕÃÆÊVÕÃÊÜÌ
ÊÌ
iÊJPATP, PATP, CAKIAPNU, CAKCN=LDU, or EI=CA `>Ì>ÊÌÞ«iÆÊë>ÀÃiÊVÕÃÆÊ>`ÊVÕÃÊÌ
>ÌÊ>Ài>`ÞÊ
>ÛiÊÃÌ>ÌÃÌVÃÊÀÊ>ÀiÊÌ
iÊvÀÃÌÊ column of an index. Similarly, to update statistics manually, use one of the following options:
Ê
UÊ QL@=PAOP=PEOPE?O: You can use this option to update the statistics of individual or all index keys and nonindexed columns of a table or an indexed view.
Ê
UÊ ol[ql`]paop]po: Use this stored procedure to update statistics of all user tables in the current database.
You may find that allowing the automatic updating of statistics is not quite adequate for your system. Scheduling QL@=PAOP=PEOPE?OÊvÀÊÌ
iÊ`>Ì>L>ÃiÊ`ÕÀ}Êvv
ÕÀÃÊÃÊ>Ê>VVi«Ì>LiÊ way to deal with this issue. QL@=PAOP=PEOPE?O is the preferred mechanism because it offers a greater degree of flexibility and control. It’s possible, because of the types of data inserted, that the sampling method for gathering the statistics, used because it’s faster, may not gather the appropriate data. In these cases, you can force a BQHHO?=J so that all the data is used to update Ì
iÊÃÌ>ÌÃÌVÃÊÕÃÌÊiÊÜ
>ÌÊ
>««iÃÊÜ
iÊÌ
iÊÃÌ>ÌÃÌVÃÊ>ÀiÊÌ>ÞÊVÀi>Ìi`°Ê/
ÃÊV>ÊLiÊ>ÊÛiÀÞÊ costly operation, so it’s best to be very selective about which indexes receive this treatment and when it is run.
Note In general, you should always use the default settings for automatic statistics. Consider modifying these settings only after identifying that the default settings appear to detract from performance.
197
198
CHAPTER 7
STATISTICS ANALYSIS
Statistics Maintenance Status You can verify the current settings for the autostats feature using the following: Ê
UÊ @=P=>=OALNKLANPUAT
Ê
UÊ ol[]qpkop]po
Status of Auto Create Statistics You can verify the current setting for auto create statistics by running a query against the ouo* `]p]^]oao system table: OAHA?Peo[]qpk[_na]pa[op]po[kj BNKIouo*`]p]^]oao SDANAWj]iaY9#=`rajpqnaSkngo.,,4# ÊÀiÌÕÀÊÛ>ÕiÊvÊ£Êi>ÃÊi>Li`]Ê>`Ê>ÊÛ>ÕiÊvÊäÊi>ÃÊ`Ã>Li`° You can also verify the status of this feature using the ol[]qpkop]po system stored proce dure, as shown in the following code. Supplying any table name to the stored procedure will provide the configuration value of auto create statistics for the current database under the Kqplqp section of the global statistics settings: QOA=`rajpqnaSkngo.,,4 ATA?ol[]qpkop]poÑDqi]jNaokqn_ao*@al]npiajpÑ }ÕÀiÊÇÓxÊÃ
ÜÃÊ>ÊiÝViÀ«ÌÊvÊÌ
iÊ«ÀiVi`}Êol[]qpkop]po statement’s output.
Figure 7-25. ol[]qpkop]po output ÊÀiÌÕÀÊÛ>ÕiÊvÊKJ means enabled, and a value of KBB means disabled. /
ÃÊÃÌÀi`Ê«ÀVi`ÕÀiÊÃÊÀiÊÕÃivÕÊÜ
iÊÛiÀvÞ}ÊÌ
iÊÃÌ>ÌÕÃÊvÊ>ÕÌÊÕ«`>ÌiÊÃÌ>ÌÃÌVÃ]Ê>ÃÊ explained later in this chapter.
Status of Auto Update Statistics You can verify the current setting for auto update statistics, and auto update statistics asyn chronously, in a similar manner to auto create statistics. Here’s how to do it using the function @=P=>=OALNKLANPUAT: OAHA?P@=P=>=OALNKLANPUAT$#=`rajpqnaSkngo.,,4#(#Eo=qpkQl`]paOp]peope_o#% Here’s how to do it using ol[]qpkop]po: QOA=`rajpqnaSkngo.,,4 ATA?ol[]qpkop]po#O]hao*O]haoKn`an@ap]eh#
CHAPTER 7
S T A T I S T I C S A N A LY S I S
Analyzing the Effectiveness of Statistics for a Query For performance reasons, it is extremely important to maintain proper statistics on your data L>ÃiÊLiVÌðÊÃÃÕiÃÊÜÌ
ÊÃÌ>ÌÃÌVÃÊ>ÀiÊÕV°ÊÜiÛiÀ]ÊÞÕÊÃÌÊii`ÊÌÊii«ÊÞÕÀÊiÞiÃÊ open to the possibility of problems with statistics while analyzing the performance of a query. If an issue with statistics does arise, then it can really take you for a ride. In fact, checking that Ì
iÊÃÌ>ÌÃÌVÃÊ>ÀiÊÕ«Ì`>ÌiÊ>ÌÊÌ
iÊLi}}ÊvÊ>ʵÕiÀÞÌÕ}ÊÃiÃÃÊi>ÌiÃÊ>Êi>ÃÞÊ fixed problem. In this section, you’ll see what you can do should you find statistics to be miss }ÊÀÊÕÌv`>Ìi° While analyzing an execution plan for a query, look for the following points to ensure a VÃÌivviVÌÛiÊ«ÀViÃÃ}ÊÃÌÀ>Ìi}Þ\ Ê
UÊ `iÝiÃÊ>ÀiÊ>Û>>LiÊÊÌ
iÊVÕÃÊÀiviÀÀi`ÊÌÊÊÌ
iÊvÌiÀÊ>`ÊÊVÀÌiÀ>°
Ê
UÊ ÊÌ
iÊV>ÃiÊvÊ>ÊÃÃ}Ê`iÝ]Êstatistics should be available on the columns with no index. It is preferable to have the index itself.
Ê
UÊ -ViÊÕÌ`>Ìi`ÊÃÌ>ÌÃÌVÃÊ>ÀiÊvÊÊÕÃiÊ>`ÊV>ÊiÛiÊLiÊÃi>`}]ÊÌÊÃÊ«ÀÌ>ÌÊÌ
>ÌÊ Ì
iÊiÃÌ>ÌiÃÊÕÃi`ÊLÞÊÌ
iÊ«ÌâiÀÊvÀÊÌ
iÊÃÌ>ÌÃÌVÃÊ>ÀiÊÕ«Ì`>Ìi°
9ÕÊ>>Þâi`ÊÌ
iÊÕÃiÊvÊ>Ê«À«iÀÊ`iÝÊÊ
>«ÌiÀÊ{°ÊÊÌ
ÃÊÃiVÌ]ÊÞÕÊÜÊ>>ÞâiÊÌ
iÊ effectiveness of statistics for a query.
Resolving a Missing Statistics Issue /ÊÃiiÊ
ÜÊÌÊ`iÌvÞÊ>`Êresolve a missing statistics issue, consider the following example. /ÊÀiÊ`ÀiVÌÞÊVÌÀÊÌ
iÊ`>Ì>]ʽÊÕÃiÊ>ÊÌiÃÌÊÌ>LiÊÃÌi>`ÊvÊiÊvÊÌ
iÊ`ÛiÌÕÀi 7ÀÃÓäänÊÌ>LiðÊÀÃÌ]Ê`Ã>LiÊLÌ
Ê>ÕÌÊVÀi>ÌiÊÃÌ>ÌÃÌVÃÊ>`Ê>ÕÌÊÕ«`>ÌiÊÃÌ>ÌÃÌVÃÊÕÃ}ÊÌ
iÊ =HPAN@=P=>=OA command: =HPAN@=P=>=OA=`rajpqnaSkngo.,,4OAP=QPK[?NA=PA[OP=PEOPE?OKBB7 =HPAN@=P=>=OA=`rajpqnaSkngo.,,4OAP=QPK[QL@=PA[OP=PEOPE?OKBB7
Ài>ÌiÊ>ÊÌiÃÌÊÌ>LiÊÜÌ
Ê>Ê>À}iÊÕLiÀÊvÊÀÜÃÊ>`Ê>ÊVÕÃÌiÀi`Ê`iÝÊ_na]pa[p2*omh in the download): EBATEOPO$OAHA?P& BNKIouo*k^fa_po SDANAk^fa_p[e`9K>FA?P[E@$J#W`^kY*Wp-Y#% =J@pulaEJ$J#Q#%% @NKLP=>HAW`^kY*Wp-Y CK ?NA=PAP=>HA`^k*p-$_-EJP(_.EJP(_/?D=N$1,%%7 EJOANPEJPK`^k*p-$_-(_.(_/% R=HQAO$1-(-(#_/#%7 EJOANPEJPK`^k*p-$_-(_.(_/% R=HQAO$1.(-(#_/#%7 ?NA=PAJKJ?HQOPANA@EJ@ATe-KJ`^k*p-$_-(_.%7
199
200
CHAPTER 7
STATISTICS ANALYSIS
OAHA?PPKL-,,,, E@AJPEPU$EJP(-(-%=Oj EJPKJqio BNKII]opan*`^k*Ouo?khqijoo_(I]opan*`^k*Ouo?khqijoo_.7 EJOANPEJPK`^k*p-$_-(_.(_/% OAHA?Pj!1, (j (#_/# BNKIJqio7 @NKLP=>HAJqio7 Since the index is created on (_-, _.), the statistics on the index contain a histogram for the first column, _-, and density values for the prefixed column combinations (_- and _-ʳÊ_.). /
iÀiÊ>ÀiÊÊ
ÃÌ}À>ÃÊÀÊ`iÃÌÞÊÛ>ÕiÃÊvÀÊVÕÊ_.. /ÊÕ`iÀÃÌ>`Ê
ÜÊÌÊ`iÌvÞÊÃÃ}ÊÃÌ>ÌÃÌVÃÊÊ>ÊVÕÊÜÌ
ÊÊ`iÝ]ÊiÝiVÕÌiÊÌ
iÊ following OAHA?P statement. Since the auto create statistics feature is off, the optimizer won’t be able to find the data distribution for the column _. used in the SDANA clause. You can see this in the execution plan: OAHA?P&BNKI`^k*p-SDANAp-*_.9-7 vÊÞÕÊÀ}
ÌVVÊÌ
iÊiÝiVÕÌÊ«>]ÊÞÕÊV>ÊÌ>iÊ>ÊÊ>ÌÊÌ
iÊ8Ê`>Ì>ÊLi
`ÊÌ°ÊÃÊ Ã
ÜÊÊ}ÕÀiÊÇÓÈ]ÊÌ
iÊ8ÊiÝiVÕÌÊ«>Ê`V>ÌiÃÊÃÃ}ÊÃÌ>ÌÃÌVÃÊvÀÊ>Ê«>ÀÌVÕ>ÀÊ execution step under its S]njejcoÊiiiÌ°Ê/
ÃÊÃ
ÜÃÊÌ
>ÌÊÌ
iÊÃÌ>ÌÃÌVÃÊÊVÕÊp-*_. are missing.
Figure 7-26. Missing statistics indication in an XML plan /
iÊvÀ>ÌÊÊÃÃ}ÊÃÌ>ÌÃÌVÃÊÃÊ>ÃÊ«ÀÛ`i`ÊLÞÊÌ
iÊ}À>«
V>ÊiÝiVÕÌÊ«>]Ê>ÃÊ Ã
ÜÊÊ}ÕÀiÊÇÓÇ°
Figure 7-27. Missing statistics indication in a graphical plan /
iÊ}À>«
V>ÊiÝiVÕÌÊ«>ÊVÌ>ÃÊ>Ê`iÊÜÌ
ÊÌ
iÊÞiÜÊiÝV>>ÌÊ«Ì°Ê/
ÃÊ `V>ÌiÃÊÃiÊ«ÀLiÊÜÌ
ÊÌ
iÊ`>Ì>ÀiÌÀiÛ>ÊiV
>ÃÊÕÃÕ>ÞÊÃÃ}ÊÃÌ>ÌÃÌVî°Ê9ÕÊ can obtain a detailed description of the error by moving your mouse over the corresponding `iÊvÊÌ
iÊiÝiVÕÌÊ«>ÊÌÊÀiÌÀiÛiÊÌ
iÊÌÊÌ«]Ê>ÃÊÃ
ÜÊÊ}ÕÀiÊÇÓn°
CHAPTER 7
STATISTICS ANALYSIS
Figure 7-28. Tool tip of a graphical plan’s node }ÕÀiÊÇÓnÊÃ
ÜÃÊÌ
>ÌÊÌ
iÊÃÌ>ÌÃÌVÃÊvÀÊÌ
iÊVÕÊ>ÀiÊÃÃ}°Ê/
ÃÊ>ÞÊ«ÀiÛiÌÊÌ
iÊ optimizer from selectingÊÌ
iÊLiÃÌÊ«ÀViÃÃ}ÊÃÌÀ>Ìi}Þ°Ê/
iÊVÕÀÀiÌÊVÃÌÊvÊÌ
ÃʵÕiÀÞÊ>ÃÊÃ
ÜÊ by OAPOP=PEOPE?OEK and OAPOP=PEOPE?OPEIA is as follows: P]^ha#p-#*O_]j_kqjp-(hkce_]hna]`o40 OMHOanranAta_qpekjPeiao6 ?LQpeia9,io(ah]loa`peia900io* /ÊÀiÃÛiÊÌ
ÃÊÃÃ}ÊÃÌ>ÌÃÌVÃÊÃÃÕi]ÊÞÕÊV>ÊVÀi>ÌiÊÌ
iÊÃÌ>ÌÃÌVÃÊÊVÕÊp-*_. by using the ?NA=PAOP=PEOPE?O statement: ?NA=PAOP=PEOPE?Oo-KJp-$_.%7 Before rerunning the procedure, be sure to clean out the procedure cache because this query will benefit from simple parameterization: @>??BNAALNK??=?DA$%7 }ÕÀiÊÇÓÊÃ
ÜÃÊÌ
iÊÀiÃÕÌ>ÌÊiÝiVÕÌÊ«>ÊÜÌ
ÊÃÌ>ÌÃÌVÃÊVÀi>Ìi`ÊÊVÕÊ_.. P]^ha#p-#*O_]j_kqjp-(hkce_]hna]`o0/ OMHOanranAta_qpekjPeiao6 ?LQpeia9,io(ah]loa`peia9-1io*
201
202
CHAPTER 7
STATISTICS ANALYSIS
Figure 7-29. Execution plan with statistics in place /
iʵÕiÀÞÊoptimizer uses statistics on a noninitial column in a composite index to deter mine whether scanning the leaf level of the composite index to obtain the bookmarks will be a more efficient processing strategy than scanning the whole table. In this case, creating sta tistics on column _. allows the optimizer to determine that instead of scanning the base table, it will be less costly to scan the composite index on (_-, _.) and bookmark lookup to the base Ì>LiÊvÀÊÌ
iÊviÜÊ>ÌV
}ÊÀÜÃ°Ê ÃiµÕiÌÞ]ÊÌ
iÊÕLiÀÊvÊ}V>ÊÀi>`ÃÊ
>Ã decreased vÀÊnÇÊÌÊ{Ó]ÊLÕÌÊÌ
iÊi>«Ãi`ÊÌiÊ
>ÃÊ`iVÀi>Ãi`ÊÞÊÃ}
ÌÞ°
Resolving an Outdated Statistics Issue Sometimes outdated or incorrect statistics can be more damaging than missing statistics. Based on old statistics or a partial scan of changed data, the optimizer may decide upon a particular indexing strategy, which may be highly inappropriate for the current data distribu tion. Unfortunately, the execution plans don’t show the same glaring warnings for outdated or incorrect statistics as they do for missing statistics. /Ê`iÌvÞÊÕÌ`>Ìi`ÊÃÌ>ÌÃÌVÃ]ÊÞÕÊÃ
Õ`ÊiÝ>iÊ
ÜÊVÃiÊÌ
iÊ«ÌâiÀ½ÃÊiÃÌ>ÌÊ of the number of rows affected is to the actual number of rows affected. /
iÊvÜ}ÊiÝ>«iÊÃ
ÜÃÊÞÕÊ
ÜÊÌÊ`iÌvÞÊ>`ÊÀiÃÛiÊ>ÊÕÌ`>Ìi`ÊÃÌ>ÌÃÌVÃÊÃÃÕi°Ê }ÕÀiÊÇÎäÊÃ
ÜÃÊÌ
iÊÃÌ>ÌÃÌVÃÊÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊiÞÊ column _- provided by @>?? ODKS[OP=PEOPE?O. @>??ODKS[OP=PEOPE?O$p-(e-%7 /
iÃiÊÀiÃÕÌÃÊÃ>ÞÊÌ
>ÌÊÌ
iÊ`iÃÌÞÊÛ>ÕiÊvÀÊVÕÊ_-ÊÃÊä°x°Ê ÜÊVÃ`iÀÊÌ
iÊvÜ}Ê OAHA?P statement: OAHA?P&BNKI`^k*p-SDANA_-91-7 -ViÊÌ
iÊÌÌ>ÊÕLiÀÊvÊÀÜÃÊÊÌ
iÊÌ>LiÊÃÊVÕÀÀiÌÞÊ£ä]ääÓ]ÊÌ
iÊÕLiÀÊvÊ>ÌV
}Ê rows for the filter criteria _-91-ÊV>ÊLiÊiÃÌ>Ìi`ÊÌÊLiÊx]ää£ÊrÊä°xÊ Ê£ä]ääÓ®°Ê/
ÃÊiÃÌ>Ìi`Ê ÕLiÀÊvÊÀÜÃÊx]ä䣮ÊÃÊÜ>ÞÊvvÊÌ
iÊ>VÌÕ>ÊÕLiÀÊvÊ>ÌV
}ÊÀÜÃÊvÀÊÌ
ÃÊVÕÊÛ>Õi°Ê /
iÊÌ>LiÊ>VÌÕ>ÞÊVÌ>ÃÊÞÊiÊÀÜÊvÀÊ_-91-.
Figure 7-30. Statistics on index e-
CHAPTER 7
STATISTICS ANALYSIS
You can get the information on both the estimated and actual number of rows from the >VÌÕ>ÊiÝiVÕÌÊ«>°ÊÊiÃÌ>Ìi`Ê«>ÊÀiviÀÃÊÌÊ>`ÊÕÃiÃÊÌ
iÊÃÌ>ÌÃÌVÃÊÞ]ÊÌÊÌ
iÊ>VÌÕ>Ê `>Ì>°Ê/
ÃÊi>ÃÊÌÊV>ÊLiÊÜ`ÞÊ`vviÀiÌÊvÀÊÌ
iÊÀi>Ê`>Ì>]Ê>ÃÊÞÕ½ÀiÊÃii}ÊÜ°Ê/
iÊ>VÌÕ>Ê execution plan, on the other hand, has both the estimated and actual number of rows available.
ÝiVÕÌ}ÊÌ
iʵÕiÀÞÊÀiÃÕÌÃÊÊÌ
ÃÊiÝiVÕÌÊ«>Ê}ÕÀiÊÇΣ®Ê>`Ê«iÀvÀ>Vi\ P]^ha#p-#*O_]j_kqjp-(hkce_]hna]`o40 OMHOanranAta_qpekjPeiao6 ?LQpeia9-2io(ah]loa`peia9.io*
Figure 7-31. Execution plan with outdated statistics /ÊÃiiÊÌ
iÊiÃÌ>Ìi`Ê>`Ê>VÌÕ>ÊÀÜÃ]ÊÞÕÊV>ÊÛiÜÊÌ
iÊÌÊÌ«ÊLÞÊ
ÛiÀ}ÊÛiÀÊÌ
iÊP]^ha O_]jÊ«iÀ>ÌÀÊ}ÕÀiÊÇÎÓ®°
Figure 7-32. Tool tip showing row count discrepancy From the estimated rows value vs. the actual rows value, it’s clear that the optimizer made >ÊVÀÀiVÌÊiÃÌ>ÌÊL>Ãi`ÊÊÕÌv`>ÌiÊÃÌ>ÌÃÌVðÊvÊÌ
iÊ`vviÀiViÊLiÌÜiiÊÌ
iÊiÃÌ>Ìi`Ê ÀÜÃÊ>`Ê>VÌÕ>ÊÀÜÃÊÃÊÀiÊÌ
>Ê>Êv>VÌÀÊvÊ£ä]ÊÌ
iÊ̽ÃʵÕÌiÊ«ÃÃLiÊÌ
>ÌÊÌ
iÊ«ÀViÃÃ}Ê ÃÌÀ>Ìi}ÞÊV
ÃiÊ>ÞÊÌÊLiÊÛiÀÞÊVÃÌÊivviVÌÛiÊvÀÊÌ
iÊVÕÀÀiÌÊ`>Ì>Ê`ÃÌÀLÕÌ°ÊÊ>VVÕÀ>ÌiÊ estimation may misguide the optimizer in deciding the processing strategy. /Ê
i«ÊÌ
iÊ«ÌâiÀÊ>iÊ>Ê>VVÕÀ>ÌiÊiÃÌ>Ì]ÊÞÕÊÃ
Õ`ÊÕ«`>ÌiÊÌ
iÊÃÌ>ÌÃÌVÃÊÊ the nonclustered index key on column _-Ê>ÌiÀ>ÌÛiÞ]ÊvÊVÕÀÃi]ÊÞÕÊV>ÊÕÃÌÊi>ÛiÊÌ
iÊ>ÕÌÊ update statistics feature on): QL@=PAOP=PEOPE?Op-e-7
203
204
CHAPTER 7
STATISTICS ANALYSIS
If you run the query again, you’ll get the following statistics, and the resultant output is as Ã
ÜÊÊ}ÕÀiÊÇÎÎ\ P]^ha#p-#*O_]j_kqjp-(hkce_]hna]`o/ OMHOanranAta_qpekjPeiao6 ?LQpeia9,io(ah]loa`peia9,io*
Figure 7-33. Actual and estimated number of rows with up-to-date statistics /
iÊ«ÌâiÀÊ>VVÕÀ>ÌiÞÊiÃÌ>Ìi`ÊÌ
iÊÕLiÀÊvÊÀÜÃÊÕÃ}ÊÕ«`>Ìi`ÊÃÌ>ÌÃÌVÃÊ>`ÊVÃi µÕiÌÞÊÜ>ÃÊ>LiÊÌÊViÊÕ«ÊÜÌ
Ê>Ê«>°Ê-ViÊÌ
iÊiÃÌ>Ìi`ÊÕLiÀÊvÊÀÜÃÊÃÊ£]ÊÌÊ>iÃÊÃiÃiÊ to retrieve the row through the nonclustered index on _- instead of scanning the base table. Updated, accurate statistics on the index key column help the optimizer come to a better `iVÃÊÊÌ
iÊ«ÀViÃÃ}ÊÃÌÀ>Ìi}ÞÊ>`ÊÌ
iÀiLÞÊÀi`ÕViÊÌ
iÊÕLiÀÊvÊ}V>ÊÀi>`ÃÊvÀÊn{ÊÌÊ ÎÊ>`ÊÀi`ÕViÊÌ
iÊiÝiVÕÌÊÌiÊvÀÊ£ÈÊÃÊÌÊHäÊÃÊÌ
iÀiÊÃÊ>ÊH{ÊÃÊ>}ÊÌi®° Before continuing, turn the statistics back on for the database: =HPAN@=P=>=OA=`rajpqnaSkngo.,,4OAP=QPK[?NA=PA[OP=PEOPE?OKJ7 =HPAN@=P=>=OA=`rajpqnaSkngo.,,4OAP=QPK[QL@=PA[OP=PEOPE?OKJ7
Recommendations /
ÀÕ}
ÕÌÊÌ
à chapter, I covered various recommendations for statistics. For easy reference, I’ve consolidated and expanded upon these recommendations in the sections that follow.
Backward Compatibility of Statistics -Ì>ÌÃÌV>ÊvÀ>ÌÊÊ-+Ê-iÀÛiÀÊÓäänÊÃ different from that in previous versions of SQL -iÀÛiÀ°ÊÜiÛiÀ]Ê-+Ê-iÀÛiÀÊÓäänÊÌÀ>ÃviÀÃÊÌ
iÊÃÌ>ÌÃÌVÃÊ`ÕÀ}ÊÕ«}À>`iÊ>`]ÊLÞÊ`iv>ÕÌ]Ê>ÕÌ matically updates these statistics over time. For the best performance, however, manually update the statistics immediately after an upgrade.
Auto Create Statistics /
ÃÊvi>ÌÕÀi should usually be left on. With the default setting, during the creation of an execu tion plan, SQL Server determines whether statistics on a nonindexed column will be useful. If this is deemed beneficial, SQL Server creates statistics on the nonindexed column. However, if you plan to create statistics on nonindexed columns manually, then you have to identify exactly for which nonindexed columns statistics will be beneficial.
CHAPTER 7
STATISTICS ANALYSIS
Auto Update Statistics /
ÃÊvi>ÌÕÀiÊÃ
Õ`ÊÕÃÕ>ÞÊLi left on, allowing SQL Server to decide on the appropriate execution plan as the data distribution changes over time. Usually the performance benefit provided by this feature outweighs the cost overhead. You will seldom need to interfere with the automatic maintenance of statistics, and such requirements are usually identified while ÌÀÕLiÃ
Ì}ÊÀÊ>>Þâ}Ê«iÀvÀ>Vi°Ê/ÊiÃÕÀiÊÌ
>ÌÊÞÕÊ>Ài½ÌÊv>V}ÊÃÕÀ«ÀÃiÃÊvÀÊÌ
iÊ automatic statistics features, it’s important to analyze the effectiveness of statistics while diag nosing SQL Server issues. Unfortunately, if you come across an issue with the auto update statistics feature and
>ÛiÊÌÊÌÕÀÊÌÊvv]Ê>iÊÃÕÀiÊÌÊVÀi>ÌiÊ>Ê-+Ê-iÀÛiÀÊLÊÌÊÕ«`>ÌiÊÌ
iÊÃÌ>ÌÃÌVÃÊ>`ÊÃV
i`ÕiÊ ÌÊÌÊÀÕÊ>ÌÊÀi}Õ>ÀÊÌiÀÛ>ðÊÀÊ«iÀvÀ>ViÊÀi>ÃÃ]ÊiÃÕÀiÊÌ
>ÌÊÌ
iÊ-+ÊLÊÃÊÃV
i`Õi`ÊÌÊ ÀÕÊ`ÕÀ}Êvv«i>Ê
ÕÀð 9ÕÊV>ÊVÀi>ÌiÊ>Ê-+Ê-iÀÛiÀÊLÊÌÊÕ«`>ÌiÊÌ
iÊÃÌ>ÌÃÌVÃÊvÀÊ-+Ê-iÀÛiÀÊ>>}iiÌÊ-ÌÕ dio by following these simple steps: 1. Select ServerName
Ê-+Ê-iÀÛiÀÊ}iÌÊ ÊLÃ]ÊÀ}
ÌVV]Ê>`ÊÃiiVÌÊ iÜÊL°
2. "ÊÌ
iÊiiÀ>Ê«>}iÊvÊÌ
iÊ iÜÊLÊ`>}ÊLÝ]ÊiÌiÀÊÌ
iÊLÊ>iÊ>`ÊÌ
iÀÊ`iÌ>Ã]Ê>ÃÊ Ã
ÜÊÊ}ÕÀiÊÇÎ{°
Figure 7-34. Entering new job information
205
206
CHAPTER 7
STATISTICS ANALYSIS
3.
ÃiÊÌ
iÊ-Ìi«ÃÊ«>}i]ÊVVÊ iÜ]Ê>`ÊiÌiÀÊÌ
iÊ-+ÊV>`ÊvÀÊÌ
iÊÕÃiÀÊ`>Ì>L>Ãi]Ê >ÃÊÃ
ÜÊÊ}ÕÀiÊÇÎx°ÊÊÕÃi`Êol[ql`]paop]po here instead of QL@=PAOP=PEOPE?O because it’s a shortcut. I could have run QL@=PAOP=PEOPE?O against each table in the database a different way, especially if I was interested in taking advantage of all the control offered by QL@=PAOP=PEOPE?O: ATA?ol[iobkna]_dp]^ha#QL@=PAOP=PEOPE?O;=HH#
Figure 7-35. Entering the SQL command for the user database 4. Return to the New Job dialog box by clicking the OK button. 5. On the Schedules page of the New Job dialog box, click New Schedule, and enter an >««À«À>ÌiÊÃV
i`ÕiÊÌÊÀÕÊÌ
iÊ-+Ê-iÀÛiÀÊL]Ê>ÃÊÃ
ÜÊÊ}ÕÀiÊÇÎÈ°
CHAPTER 7
STATISTICS ANALYSIS
Figure 7-36. Scheduling the SQL Server job 6. Return to the New Job dialog box by clicking the OK button. 7. Once you’ve entered all the information, click OK in the New Job dialog box to create Ì
iÊ-+Ê-iÀÛiÀÊL° 8. ÃÕÀiÊÌ
>ÌÊ-+Ê-iÀÛiÀÊ}iÌÊÃÊÀÕ}ÊÃÊÌ
>ÌÊÌ
iÊ-+Ê-iÀÛiÀÊLÊÃÊÀÕÊ>ÕÌ>ÌV>ÞÊ at the set schedule.
Automatic Update Statistics Asynchronously Letting statisticsÊÕ«`>ÌiÊ>ÌÊÌ
iÊLi}}ÊvÊ>ʵÕiÀÞ]ÊÜ
V
ÊÃÊÌ
iÊ`iv>ÕÌÊLi
>ÛÀ]ÊÜÊLiÊÕÃÌÊ fine in most cases. In the very rare circumstances where the statistics update or the execu tion plan recompiles resulting from that update are very expensive (more expensive than the VÃÌÊvÊÕÌv`>ÌiÊÃÌ>ÌÃÌVî]ÊÌ
iÊÞÕÊV>ÊÌÕÀÊÊÌ
iÊ>ÃÞV
ÀÕÃÊÕ«`>ÌiÊvÊÃÌ>ÌÃÌVðÊÕÃÌÊ Õ`iÀÃÌ>`ÊÌ
>ÌÊÌÊ>ÞÊi>ÊÌ
>ÌÊ«ÀVi`ÕÀiÃÊÌ
>ÌÊÜÕ`ÊLiivÌÊvÀÊÀiÊÕ«Ì`>ÌiÊÃÌ>ÌÃ tics will suffer until the next time they are run. Don’t forget—you do need automatic update of statistics enabled in order to enable the asynchronous updates.
207
208
C HAPTER 7
ST A TIS TIC S A NA L YS IS
Amount of Sampling to Collect Statistics It is generally recommended that you use the default Ã>«}ÊÀ>Ìi°Ê/
ÃÊÀ>ÌiÊÃÊ`iV`i`ÊLÞÊ>Ê ivvViÌÊ>}ÀÌ
ÊL>Ãi`ÊÊÌ
iÊ`>Ì>ÊÃâiÊ>`ÊÕLiÀÊvÊ`vV>ÌðÊÌ
Õ}
ÊÌ
iÊ`iv>ÕÌÊ sampling rate turns out to be best in most cases, if for a particular query you find that the sta tistics are not very accurate, then you can manually update them with BQHHO?=J. vÊÌ
ÃÊÃÊÀiµÕÀi`ÊÀi«i>Ìi`Þ]ÊÌ
iÊÞÕÊV>Ê>``Ê>Ê-+Ê-iÀÛiÀÊLÊÌÊÌ>iÊV>ÀiÊvÊÌ°ÊÀÊ «iÀvÀ>ViÊÀi>ÃÃ]ÊiÃÕÀiÊÌ
>ÌÊÌ
iÊ-+ÊLÊÃÊÃV
i`Õi`ÊÌÊÀÕÊ`ÕÀ}Êvv«i>Ê
ÕÀðÊ/Ê identify cases in which the default sampling rate doesn’t turn out to be the best, analyze the statistics effectiveness for costly queries while troubleshooting the database performance. Remember that BQHHO?=J is expensive, so you should run it only on those tables or indexes that you’ve determined will really benefit from it.
Summary ÃÊ`ÃVÕÃÃi`ÊÊÌ
ÃÊV
>«ÌiÀ]Ê-+Ê-iÀÛiÀ½ÃÊVÃÌL>Ãi`Ê«ÌâiÀÊÀiµÕÀiÃÊ>VVÕÀ>ÌiÊÃÌ>ÌÃÌVÃÊÊ VÕÃÊÕÃi`ÊÊvÌiÀÊ>`ÊÊVÀÌiÀ>ÊÌÊ`iÌiÀiÊ>ÊivvViÌÊ«ÀViÃÃ}ÊÃÌÀ>Ìi}Þ°Ê-Ì>ÌÃÌVÃÊ on an index key are always created during the creation of the index, and by default, SQL Server also keeps the statistics on indexed and nonindexed columns updated as the data changes. /
ÃÊi>LiÃÊÌÊÌÊ`iÌiÀiÊÌ
iÊLiÃÌÊ«ÀViÃÃ}ÊÃÌÀ>Ìi}iÃÊ>««V>LiÊÌÊÌ
iÊVÕÀÀiÌÊ`>Ì>Ê distribution.
ÛiÊÌ
Õ}
ÊÞÕÊV>Ê`Ã>LiÊLÌ
ÊÌ
iÊ>ÕÌÊVÀi>ÌiÊÃÌ>ÌÃÌVÃÊ>`Ê>ÕÌ update statistics fea tures, it is recommended that you leave these features on, since their benefit to the optimizer is almost always more than their overhead cost. For a costly query, analyze the statistics to iÃÕÀiÊÌ
>ÌÊÌ
iÊ>ÕÌ>ÌVÊÃÌ>ÌÃÌVÃÊ>Ìi>ViÊÛiÃÊÕ«ÊÌÊÌÃÊ«ÀÃi°Ê/
iÊLiÃÌÊiÜÃÊÃÊÌ
>ÌÊ ÞÕÊV>ÊÀiÃÌÊi>ÃÞÊÜÌ
Ê>ÊÌÌiÊÛ}>Vi]ÊÃViÊ>ÕÌ>ÌVÊÃÌ>ÌÃÌVÃÊ`ÊÌ
iÀÊLÊÜiÊÃÌÊvÊÌ
iÊ Ìi°ÊvÊ>Õ>ÊÃÌ>ÌÃÌVÃÊ>Ìi>ViÊ«ÀVi`ÕÀiÃÊ>ÀiÊÕÃi`]ÊÌ
iÊÞÕÊV>ÊÕÃiÊ-+Ê-iÀÛiÀÊLÃÊ to automate these procedures.
ÛiÊÜÌ
Ê«À«iÀÊ`iÝiÃÊ>`ÊÃÌ>ÌÃÌVÃÊÊ«>Vi]Ê>Ê
i>ÛÞÊvÀ>}iÌi`Ê`>Ì>L>ÃiÊÜÊVÕÀÊ >ÊVÀi>Ãi`Ê`>Ì>ÀiÌÀiÛ>ÊVÃÌ°ÊÊÌ
iÊiÝÌÊV
>«ÌiÀ]ÊÞÕÊÜÊÃiiÊ
ÜÊvÀ>}iÌ>ÌÊÊ>Ê index can affect query performance, and you’ll learn how to analyze and resolve fragmentation.
CHAPT ER
8
Fragmentation Analysis A
s explained in Chapter 4, index column values are stored in the leaf pages of an index’s B-tree structure. When you create an index (clustered or nonclustered) on a table, the cost of data retrieval is reduced by properly ordering the leaf pages of the index and the rows within the leaf pages. In an OLTP database, data changes continually, causing fragmentation of the indexes. As a result, the number of reads required to return the same number of rows increases over time. In this chapter, I cover the following topics: Ê
UÊ /
iÊV>ÕÃiÃÊvÊ`iÝÊvÀ>}iÌ>Ì]ÊVÕ`}Ê>Ê>>ÞÃÃÊvÊ«>}iÊëÌÃÊV>ÕÃi`ÊLÞÊ EJOANP and QL@=PA statements
Ê
UÊ /
iÊÛiÀ
i>`ÊVÃÌÃÊ>ÃÃV>Ìi`ÊÜÌ
ÊvÀ>}iÌ>Ì
Ê
UÊ ÜÊÌÊ>>ÞâiÊÌ
iÊ>ÕÌÊvÊvÀ>}iÌ>Ì
Ê
UÊ /iV
µÕiÃÊÕÃi`ÊÌÊÀiÃÛiÊvÀ>}iÌ>Ì
Ê
UÊ /
iÊÃ}vV>ViÊvÊÌ
iÊvÊv>VÌÀÊÊ
i«}ÊÌÊVÌÀÊvÀ>}iÌ>Ì
Ê
UÊ ÜÊÌÊ>ÕÌ>ÌiÊÌ
iÊvÀ>}iÌ>ÌÊ>>ÞÃÃÊ«ÀViÃÃ
Causes of Fragmentation Fragmentation occurs when data is modified in a table. When you insert or update data in a table (via EJOANP or QL@=PA), the table’s corresponding clustered indexes and the affected nonclustered indexes are modified. This can cause an index leaf page split if the modification to an index can’t be accommodated in the same page. A new leaf page will then be added that contains part of the original page and maintains the logical order of the rows in the index key. Although the new leaf page maintains the logical order of the data rows in the original page, this new page usually won’t be physically adjacent to the original page on the disk. Or, put a slightly different way, the logical key order of the index doesn’t match the physical order within the file.
209
210
C HAPTER 8
F RA G MENTA TION A NA L YS IS
ÀÊiÝ>«i]ÊÃÕ««ÃiÊ>Ê`iÝÊ
>ÃÊiÊiÞÊÛ>ÕiÃÊÀÊ`iÝÊÀÜîÊ>`ÊÌ
iÊ>ÛiÀ>}iÊÃâiÊ of the index rows allows a maximum of four index rows in a leaf page. As explained in Chapter 4, the 8KB leaf pages are connected to the previous and next leaf pages to maintain the logical order of the index. Figure 8-1 illustrates the layout of the leaf pages for the index.
Figure 8-1. Leaf pages layout Since the index key values in the leaf pages are always sorted, a new index row with a key value of 25 has to occupy a place between the existing key values 20 and 30. Because the leaf page containing these existing index key values is full with the four index rows, the new index row will cause the corresponding leaf page to split. A new leaf page will be assigned to the index, and part of the first leaf page will be moved to this new leaf page so that the new index key can be inserted in the correct logical order. The links between the index pages will also be updated so that the pages are logically connected in the order of the index. As shown in Figure 8-2, the new leaf page, even though linked to the other pages in the correct logical order, can be physically out of order.
Figure 8-2. Out-of-order leaf pages The pages are grouped together in bigger units called extents, which can contain eight pages. SQL Server uses an extent as a physical unit of allocation on the disk. Ideally, the physical order of the extents containing the leaf pages of an index should be the same as the logical order of the index. This reduces the number of switches required between extents when ÀiÌÀiÛ}Ê>ÊÀ>}iÊvÊ`iÝÊÀÜðÊÜiÛiÀ]Êpage splits can physically disorder the pages within the extents, and they can also physically disorder the extents themselves. For example, suppose the first two leaf pages of the index are in extent 1, and say the third leaf page is in extent 2. If extent 2 contains free space, then the new leaf page allocated to the index because of the page split will be in extent 2, as shown in Figure 8-3.
CHAPTER 8
F R A G M E N T A T I O N A N A LY S I S
Figure 8-3. Out-of-order leaf pages distributed across extents With the leaf pages distributed between two extents, ideally you expect to read a range of `iÝÊÀÜÃÊÜÌ
Ê>Ê>ÝÕÊvÊiÊÃÜÌV
ÊLiÌÜiiÊÌ
iÊÌÜÊiÝÌiÌðÊÜiÛiÀ]ÊÌ
iÊ`ÃÀ}>â>ÌÊvÊ«>}iÃÊLiÌÜiiÊÌ
iÊiÝÌiÌÃÊV>ÊV>ÕÃiÊÀiÊÌ
>ÊiÊiÝÌiÌÊÃÜÌV
ÊÜ
iÊÀiÌÀiÛ}Ê>Ê range of index rows. For example, to retrieve a range of index rows between 25 and 90, you will need three extent switches between the two extents, as follows: Ê
UÊ ÀÃÌÊiÝÌiÌÊÃÜÌV
ÊÌÊÀiÌÀiÛiÊÌ
iÊiÞÊÛ>ÕiÊÎäÊ>vÌiÀÊÌ
iÊiÞÊÛ>ÕiÊÓx
Ê
UÊ -iV`ÊiÝÌiÌÊÃÜÌV
ÊÌÊÀiÌÀiÛiÊÌ
iÊiÞÊÛ>ÕiÊxäÊ>vÌiÀÊÌ
iÊiÞÊÛ>ÕiÊ{ä
Ê
UÊ /
À`ÊiÝÌiÌÊÃÜÌV
ÊÌÊÀiÌÀiÛiÊÌ
iÊiÞÊÛ>ÕiÊäÊ>vÌiÀÊÌ
iÊiÞÊÛ>ÕiÊnä
This type of fragmentation is called external fragmentation. External fragmentation is always undesirable. Fragmentation can also happen within an index page. If an EJOANP or QL@=PA operation creates a page split, then free space will be left behind in the original leaf page. Free space can also be caused by a @AHAPA operation. The net effect is to reduce the number of rows included in a leaf page. For example, in Figure 8-3, the page split caused by the EJOANP operation has created an empty space within the first leaf page. This is known as internal fragmentation. For a highly transactional database, it is desirable to deliberately leave some free space ÜÌ
ÊÞÕÀÊi>vÊ«>}iÃÊÃÊÌ
>ÌÊÞÕÊV>Ê>``ÊiÜÊÀÜÃ]ÊÀÊV
>}iÊÌ
iÊÃâiÊvÊiÝÃÌ}ÊÀÜÃ]ÊÜÌ
out causing a page split. In Figure 8-3, the free space within the first leaf page allows an index key value of 26 to be added to the leaf page without causing a page split.
Note Note that this index fragmentation is different from disk fragmentation. The index fragmentation cannot be fixed simply by running the disk defragmentation tool, because the order of pages within a SQL Server file is understood only by SQL Server, not by the operating system.
SQL Server 2008 exposes the leaf pages and other data through a dynamic management view called ouo*`i[`^[ej`at[lduoe_]h[op]po. It storesÊLÌ
ÊÌ
iÊ`iÝÊÃâiÊ>`ÊÌ
iÊvÀ>}iÌ>tion. I’ll cover it in more detail in the next section. The DMV is much easier to work with than @>??ODKS?KJPEC. Let’s now take a look at the mechanics of fragmentation.
211
212
C HAPTER 8
F RA G MENTA TION A NA L YS IS
Page Split by an UPDATE Statement To show what happens when a page split is caused by an QL@=PA statement, I’ll use a constructed table for an example. This small test table will have a clustered index, which orders the rows within one leaf (or data) page as follows (you’ll find this code in _na]pa[p-*omh in the code download): EB$OAHA?PK>FA?P[E@$#p-#%%EOJKPJQHH @NKLP=>HApCK ?NA=PAP=>HAp-$_-EJP(_.?D=N$555%(_/R=N?D=N$-,%% EJOANPEJPKp-R=HQAO$-,,(#_.#(##% EJOANPEJPKp-R=HQAO$.,,(#_.#(##% EJOANPEJPKp-R=HQAO$/,,(#_.#(##% EJOANPEJPKp-R=HQAO$0,,(#_.#(##% EJOANPEJPKp-R=HQAO$1,,(#_.#(##% EJOANPEJPKp-R=HQAO$2,,(#_.#(##% EJOANPEJPKp-R=HQAO$3,,(#_.#(##% EJOANPEJPKp-R=HQAO$4,,(#_.#(##% ?NA=PA?HQOPANA@EJ@ATe-KJp-$_-% TheÊ>ÛiÀ>}iÊÃâiÊvÊ>ÊÀÜÊÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊi>vÊ«>}iÊiÝVÕ`}ÊÌiÀ>ÊÛiÀ
i>`®ÊÃÊ ÌÊÕÃÌÊÌ
iÊÃÕÊvÊÌ
iÊ>ÛiÀ>}iÊÃâiÊvÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊVÕÃÆÊ̽ÃÊÌ
iÊÃÕÊvÊÌ
iÊ>ÛiÀ>}iÊ ÃâiÊvÊ>ÊÌ
iÊVÕÃÊÊÌ
iÊÌ>Li]ÊÃViÊÌ
iÊi>vÊ«>}iÊvÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊ>`ÊÌ
iÊ`>Ì>Ê«>}iÊ vÊÌ
iÊÌ>LiÊ>ÀiÊÌ
iÊÃ>i°Ê/
iÀivÀi]ÊÌ
iÊ>ÛiÀ>}iÊÃâiÊvÊ>ÊÀÜÊÊÌ
iÊVÕÃÌiÀi`Ê`iÝÊÃÊ>ÃÊvÜÃ\
rÊÛiÀ>}iÊÃâiÊvÊQV£R®Ê³ÊÛiÀ>}iÊÃâiÊvÊQVÓR®Ê³ÊÛiÀ>}iÊÃâiÊvÊQVÎR®ÊLÞÌià rÊ-âiÊvÊ /®Ê³Ê-âiÊvÊ ,®®Ê³ÊÛiÀ>}iÊÃâiÊvÊ`>Ì>ÊÊQVÎR®ÊLÞÌià rÊ{ʳÊʳÊäÊrÊ£]ääÎÊLÞÌià /
iÊ>ÝÕÊÃâiÊvÊ>ÊÀÜÊÊ-+Ê-iÀÛiÀÊÃÊn]äÈäÊLÞÌiðÊ/
iÀivÀi]ÊvÊÌ
iÊÌiÀ>ÊÛiÀhead is not very high, all eight rows can be accommodated in a single 8KB page. To determine the number of leaf pages assigned to the e- clustered index, execute the OAHA?P statement against ouo*`i[`^[ej`at[lduoe_]h[op]po: OAHA?Po*]rc[bn]ciajp]pekj[ej[lan_ajp (o*bn]ciajp[_kqjp (o*l]ca[_kqjp (o*]rc[l]ca[ol]_a[qoa`[ej[lan_ajp (o*na_kn`[_kqjp (]rc[na_kn`[oeva[ej[^upao BNKIouo*`i[`^[ej`at[lduoe_]h[op]po$@>[E@$#=`rajpqnaSkngo.,,4#%( K>FA?P[E@$J#`^k*p-#%(JQHH(JQHH( #O]ilha`#%=Oo
CHAPTER 8
FRAGMENTATION ANALYSIS
You can see the results of this query in Figure 8-4.
Figure 8-4. Physical layout of index eFrom the l]ca[_kqjp column in this output, you can see that the number of pages assigned to the clustered index is 1. You can also see the average space used, 100, in the ]rc[ l]ca[ol]_a[qoa`[ej[lan_ajp column. From this you can infer that the page has no free space left to expand the content of _/, which is of type R=N?D=N$-,% and is currently empty.
Note I’ll analyze more of the information provided by ouo*`i[`^[ej`at[lduoe_]h[op]po in the “Analyzing the Amount of Fragmentation” section later in this chapter.
Therefore, if you attempt to expand the content of column _/ for one of the rows as follows, it should cause a page split: QL@=PApOAP_/9#=```]p]# SDANA_-9.,, Selecting the data from ouo*`i[`^[ej`at[lduoe_]h[op]po results in the information in Figure 8-5.
Figure 8-5. e- index after a data update From the output in Figure 8-5, you can see that SQL Server has added a new page to the index. On a page split, SQL Server generally moves half the total number of rows in the original page to the new page. Therefore, the rows in the two pages are distributed as shown in Figure 8-6.
213
214
C HAPTER 8
F RA G MENTA TION A NA L YS IS
Figure 8-6. Page split caused by an QL@=PA statement From the preceding tables, you can see that the page split caused by the QL@=PA statement results in an internal fragmentation of data in the leaf pages. If the new leaf page can’t be written physically next to the original leaf page, there will be external fragmentation as well. For a large table with a high amount of fragmentation, a larger number of leaf pages will be required to hold all the index rows. To confirm the resultant distribution of rows shown in the previous pages, you can add three trailing rows to the first leaf page and four trailing rows to the second page (p-[ejoanp* omh in the download):
CHAPTER 8
FRAGMENTATION ANALYSIS
EJOANPEJPKp-R=HQAO$0-,(#_.#(##% EJOANPEJPKp-R=HQAO$0.,(#_.#(##% EJOANPEJPKp-R=HQAO$0/,(#_.#(##% EJOANPEJPKp-R=HQAO$5,,(#_.#(##% EJOANPEJPKp-R=HQAO$-,,,(#_.#(##% EJOANPEJPKp-R=HQAO$--,,(#_.#(##% EJOANPEJPKp-R=HQAO$-.,,(#_.#(##% These seven new rows are accommodated in the existing two leaf pages without causing a page split. You can confirm this by querying ouo*`i[`^[ej`at[lduoe_]h[op]po again (Figure 8-7).
Figure 8-7. Pages after the addition of more rows
Page Split by an INSERT Statement To understand how a page split can be caused by an EJOANP statement, create the same test table (_na]pa[p-*omh) as you did previously, with the eight initial rows and the clustered index. Since the single index leaf page is completely filled, any attempt to add an intermediate row as follows should cause a page split in the leaf page: EJOANPEJPKp-R=HQAO$--,(#_.#(##% You can verify this by examining the output of ouo*`i[`^[ej`at[lduoe_]h[op]po (Figure 8-8).
Figure 8-8. Pages after insert As explained previously, half the rows from the original leaf page are moved to the new page. Once space is cleared in the original leaf page, the new row is added in the appropriate À`iÀÊÌÊÌ
iÊÀ}>Êi>vÊ«>}i°Ê iÊ>Ü>ÀiÊÌ
>ÌÊ>ÊÀÜÊÃÊ>ÃÃV>Ìi`ÊÜÌ
ÊÞÊiÊ«>}iÆÊÌÊV>ÌÊ span multiple pages. Figure 8-9 shows the resultant distribution of rows in the two pages.
215
216
C HAPTER 8
F RA G MENTA TION A NA L YS IS
Figure 8-9. Page split caused by an EJOANP statement From the previous index pages, you can see that the page split caused by the EJOANP statement spreads the rows sparsely across the leaf pages, causing internal fragmentation. It often causes external fragmentation also, since the new leaf page may not be physically adjacent to the original page. For a large table with a high amount of fragmentation, the page splits caused by the EJOANP statement will require a larger number of leaf pages to accommodate all the index rows. To verify the row distribution shown in the index pages, you can run p-[ejoanp*omh again, adding three more rows to the first page and four more rows to the second page: EJOANPEJPKp-R=HQAO$-.,(#_.#(##% EJOANPEJPKp-R=HQAO$-/,(#_.#(##% EJOANPEJPKp-R=HQAO$-0,(#_.#(##% EJOANPEJPKp-R=HQAO$5,,(#_.#(##% EJOANPEJPKp-R=HQAO$-,,,(#_.#(##% EJOANPEJPKp-R=HQAO$--,,(#_.#(##% EJOANPEJPKp-R=HQAO$-.,,(#_.#(##%
CHAPTER 8
F R A G M E N T A T I O N A N A LY S I S
The result is the same as for the previous example: these seven new rows can be accom`>Ìi`ÊÊÌ
iÊÌÜÊiÝÃÌ}Êi>vÊ«>}iÃÊÜÌ
ÕÌÊV>ÕÃ}Ê>ÞÊ«>}iÊÃ«Ì°Ê ÌiÊÌ
>ÌÊÊÌ
iÊvÀÃÌÊ page, new rows are added in between the other rows in the page. This won’t cause a page split, since free space is available in the page. What about when you have to add rows to the trailing end of an index? In this case, even if a new page is required, it won’t split any existing page. For example, adding a new row with _- equal to 1,300 will require a new page, but it won’t cause a page split since the row isn’t added in an intermediate position. Therefore, if new rows are added in the order of the clustered index, then the index rows will be always added at the trailing end of the index, preventing the page splits otherwise caused by the EJOANP statements. Fragmentation caused by page splits hurts data-retrieval performance, as you will see next.
Fragmentation Overhead Both internal and external fragmentations adversely affect data-retrieval performance. External fragmentation causes a noncontiguous sequence of index pages on the disk, with new leaf pages far from the original leaf pages, and their physical ordering different from their logical ordering. Consequently, a range scan on an index will need more switches between the corresponding extents than ideally required, as explained earlier in the chapter. Also, a range scan on an index will be unable to benefit from read-ahead operations performed on the disk. If the pages are arranged contiguously, then a read-ahead operation can read pages in advance without much head movement. For better performance, it is preferable to use sequential I/O, since this can read a whole extent (eight 8KB pages together) in a single disk I/O operation. By contrast, a noncontiguous layout of pages requires nonsequential or random I/O operations to retrieve the index pages from the disk, and a random I/O operation can read only 8KB of data in a single disk operation (this may be acceptable, however, if you are retrieving only one row). In the case of internal fragmentation, rows are distributed sparsely across a large number of pages, increasing the number of disk I/O operations required to read the index pages into memory and increasing the number of logical reads required to retrieve multiple index rows from memory. As mentioned earlier, even though it increases the cost of data retrieval, a little internal fragmentation can be beneficial, because it allows you to perform EJOANP and QL@=PA queries without causing page splits. For queries that don’t have to traverse a series of pages to retrieve the data, fragmentation can have minimal impact. To understand how fragmentation affects the cost of a query, create a test table with a clustered index, and insert a highly fragmented data set in the table. Since an EJOANP operation in between an ordered data set can cause a page split, you can easily create the fragmented data set by adding rows in the following order (_na]pa[p-[bn]ciajpa`*omh in the code download):
217
218
C HAPTER 8
F RA G MENTA TION A NA L YS IS
EB$OAHA?PK>FA?P[E@$#p-#% %EOJKPJQHH @NKLP=>HApCK ?NA=PAP=>HAp$_-EJP (_.EJP (_/EJP (_0?D=N$.,,,%% ?NA=PA?HQOPANA@EJ@ATe-KJp-$_-%7 SEPDJqio =O$OAHA?P-=Oj QJEKJ=HH OAHA?Pj'BNKIJqio SDANAj8.% EJOANPEJPKp-$_-(_.(_/(_0% OAHA?Pj (j (j (#]# BNKIJqio7 SEPDJqio =O$OAHA?P-=Oj QJEKJ=HH OAHA?Pj'BNKIJqio SDANAj8.% EJOANPEJPKp-$_-(_.(_/(_0% OAHA?P0-)j (j (j (#]# BNKIJqio To determine the number of logical reads required to retrieve a small result set and a large result set from this fragmented table, execute the two OAHA?P statements with OP=PEOPE?OEK and PEIA set to KJ (op]peope_o*omh in the download): OAHA?P&BNKIp-SDANA_->APSAAJ.-=J@.1))Na]`o2nkso OAHA?P&BNKIp-SDANA_->APSAAJ-=J@0,))Na]`o]hhnkso
CHAPTER 8
F R A G M E N T A T I O N A N A LY S I S
The number of logical reads performed by the individual queries is, respectively, as follows: P]^ha#p-#*O_]j_kqjp-(hkce_]hna]`o2 ?LQpeia9,io(ah]loa`peia9.4io* P]^ha#p-#*O_]j_kqjp-(hkce_]hna]`o-1 ?LQpeia9,io(ah]loa`peia9.2,io* To evaluate how the fragmented data set affects the number of logical reads, rearrange the index leaf pages physically by rebuilding the clustered index: =HPANEJ@ATe-KJ`^k*p-NA>QEH@ With the index leaf pages rearranged in the proper order, rerun op]peope_o*omh. The number of logical reads required by the preceding two OAHA?P statements reduces to 5 and 13, respectively: P]^ha#p-#*O_]j_kqjp-(hkce_]hna]`o1 ?LQpeia9,io(ah]loa`peia9/,io* P]^ha#p-#*O_]j_kqjp-(hkce_]hna]`o-/ ?LQpeia9,io(ah]loa`peia9.-5io* ÌVi]ÊÌ
Õ}
]ÊÌ
>ÌÊÌ
iÊiÝiVÕÌÊÌiÊÜ>ýÌÊÀ>`V>ÞÊÀi`ÕVi`]ÊLiV>ÕÃiÊ`À««}Ê>Ê single page from the read just isn’t likely to increase speed much. The cost overhead because of fragmentation usually increases in line with the number of rows retrieved, because this involves reading a greater number of out-of-order pages. For point queries (queries retrieving only one row), fragmentation doesn’t usually matter, since the row is retrieved from one leaf page only, but this isn’t always the case. Because of the internal structure of the index, fragmentation may increase the cost of even a point query. For instance, the following OAHA?P statement (oejchaop]p*omh in the download) performs two logical reads with the leaf pages rearranged properly, but it requires three logical reads on the fragmented data set. To see this in action, run _na]pa[p-[bn]ciajpa`*omhÊ>}>°Ê ÜÊÀÕÊÌ
ÃʵÕiÀÞÊÜÌ
ÊOP=PEOPE?OEK and PEIA enabled: OAHA?P&BNKIp-SDANA_-9-,))Na]`-nks The resulting message in the query window for this script is as follows: P]^ha#p-#*O_]j_kqjp-(hkce_]hna]`o/ ?LQpeia9,io(ah]loa`peia9,io* Once more, rebuild the index using this script: =HPANEJ@ATe-KJ`^k*p-NA>QEH@ ,Õ}ÊÌ
iÊi>ÀiÀÊOAHA?P statement again results in the following output: P]^ha#p-#*O_]j_kqjp-(hkce_]hna]`o. ?LQpeia9,io(ah]loa`peia9,io* ,iiLiÀ]ÊÌ
ÃÊÌiÃÌÊÃÊÊ>ÊÛiÀÞÊÃ>ÊÃV>i]ÊLÕÌÊÌ
iÊÕLiÀÊvÊÀi>`ÃÊÜ>ÃÊ`iVÀi>Ãi`ÊLÞÊ a third. Imagine what reducing a third of the number of reads against a table with millions of rows could accomplish.
219
220
CHAPTER 8
FRAGMENTATION ANALYSIS
Note The lesson from this section is that, for better query performance, it is important to analyze the amount of fragmentation in an index and rearrange it if required.
Analyzing the Amount of Fragmentation You canÊ>>ÞâiÊÌ
iÊfragmentation ratio of an index by using the ouo*`i[`^[ej`at[lduoe_]h[ op]po dynamic management function. For a table with a clustered index, the fragmentation of the clustered index is congruous with the fragmentation of the data pages, since the leaf pages of the clustered index and data pages are the same. ouo*`i[`^[ej`at[lduoe_]h[op]po also indicates the amount of fragmentation in a heap table (or a table with no clustered index). Since a heap table doesn’t require any row ordering, the logical order of the pages isn’t relevant for the heap table. The output of ouo*`i[`^[ej`at[lduoe_]h[op]po shows information on the pages and extents of an index (or a table). A row is returned for each level of the B-tree in the index. A single row for each allocation unit in a heap is returned. As explained earlier, in SQL Server, eight VÌ}ÕÕÃÊn Ê«>}iÃÊ>ÀiÊ}ÀÕ«i`ÊÌ}iÌ
iÀÊÊ>ÊiÝÌiÌÊÌ
>ÌÊÃÊÈ{ ÊÊÃâi°ÊÀÊÛiÀÞÊÃ>Ê tables (much less than 64KB), the pages in an extent can belong to more than one index or table—these are called mixed extents. If there are too many small tables in the database, mixed extents help SQL Server conserve disk space. As a table (or an index) grows and requests more than eight pages, SQL Server creates an extent dedicated to the table (or index) and assigns the pages from this extent. Such an extent is called a uniform extent, and it serves up to eight page requests for the same table (or index). Uniform extents help SQL Server lay out the pages of a table (or an index) contiguously. They also reduce the number of page creation requests by an eighth, since a set of eight pages is created in the form of an extent. /Ê>>ÞâiÊÌ
iÊvÀ>}iÌ>ÌÊvÊ>Ê`iÝ]Êi̽ÃÊÀiVÀi>ÌiÊÌ
iÊÌ>LiÊÜÌ
ÊÌ
iÊvÀ>}iÌi`Ê data set used in the “Fragmentation Overhead” section (_na]pa[p-[bn]ciajpa`*omh). You can obtain the fragmentation detail of the clustered index (Figure 8-10) by executing the query against the ouo*`i[`^[ej`at[lduoe_]h[op]po dynamic view used earlier: OAHA?Po*]rc[bn]ciajp]pekj[ej[lan_ajp (o*bn]ciajp[_kqjp (o*l]ca[_kqjp (o*]rc[l]ca[ol]_a[qoa`[ej[lan_ajp (o*na_kn`[_kqjp (]rc[na_kn`[oeva[ej[^upao BNKIouo*`i[`^[ej`at[lduoe_]h[op]po$@>[E@$#=`rajpqnaSkngo.,,4#%( K>FA?P[E@$J#`^k*p-#%(JQHH(JQHH( #O]ilha`#%=Oo
Figure 8-10. Fragmented statistics
CHAPTER 8
F R A G M E N T A T I O N A N A LY S I S
The dynamic management function ouo*`i[`^[ej`at[lduoe_]h[op]po scans the pages of an index to return the data. You can control the level of the scan, which affects the speed and the accuracy of the scan. To quickly check the fragmentation of an index, use the Limited option. You can obtain an increased accuracy with only a moderate decrease in speed by using the Sample option, as in the previous example, which scans 1 percent of the pages. For the most accuracy, use the Detailed scan, which hits all the pages in an index. If the index has fewer than 10,000 pages and you select the Sample mode, then the Detailed mode is used instead. This means that despite the choice made in the earlier query, the Detailed scan mode was used. By defining the different parameters, you can get fragmentation information on different sets of data. By removing the K>FA?P[E@ function in the earlier query and supplying a JQHH value, the query would return information on all indexes within the database. You can also specify the index you want information on or even the partition with a partitioned index. The output from ouo*`i[`^[ej`at[lduoe_]h[op]po includes 21 different columns. I ÃiiVÌi`ÊÌ
iÊL>ÃVÊÃiÌÊvÊVÕÃÊÕÃi`ÊÌÊ`iÌiÀiÊÌ
iÊvÀ>}iÌ>ÌÊ>`ÊÃâiÊvÊ>Ê`iÝ°Ê This output represents the following: Ê
UÊ ]rc[bn]ciajp]pekj[ej[lan_ajp: This number represents the logical average fragmentation for indexes and heaps as a percentage. If the table is a heap and the mode is Sampled, then this value will be JQHH. If average fragmentation is less than 10 to 20 percent, fragmentation is unlikely to be an issue. If the index is between 20 and 40 percent, fragmentation might be an issue, but it can generally be resolved by defragmenting the index throughÊ>Ê`iÝÊÀiÀ}>â>ÌÊÀiÊvÀ>ÌÊÊ`iÝÊÀiÀ}>â>ÌÊ and index ÀiLÕ`ÊÃÊ>Û>>LiÊÊÌ
iʺÀ>}iÌ>ÌÊ,iÃÕÌûÊÃiVÌ®°Ê>À}iÃV>iÊ fragmentation, usually greater than 40 percent, may require an index rebuild. Your system may have different requirements than these general numbers
Ê
UÊ bn]ciajp[_kqjp: This number represents the number of fragments, or separated groups of pages, that make up the index. It’s a useful number to understand how the index is distributed, especially when compared to the l]ca[_kqjp value. bn]ciajp[_kqjp is JQHH when the sampling mode is Sampled.
Ê
UÊ l]ca[_kqjp: This number is a literal count of the number of index or data pages that >iÊÕ«ÊÌ
iÊÃÌ>ÌÃÌV°Ê/
ÃÊÕLiÀÊÃÊ>Êi>ÃÕÀiÊvÊÃâiÊLÕÌÊV>Ê>ÃÊ
i«Ê`V>ÌiÊ vÀ>}iÌ>Ì°ÊvÊÞÕÊÜÊÌ
iÊÃâiÊvÊÌ
iÊ`>Ì>ÊÀÊ`iÝ]ÊÞÕÊV>ÊV>VÕ>ÌiÊ
ÜÊ>ÞÊ rows can fit on a page. If you then correlate this to the number of rows in the table, you should get a number close to the l]ca[_kqjp value. If the l]ca[_kqjp value is VÃ`iÀ>LÞÊ
}
iÀ]ÊÞÕÊ>ÞÊLiÊ}Ê>ÌÊ>ÊvÀ>}iÌ>ÌÊÃÃÕi°Ê,iviÀÊÌÊÌ
iÊ]rc[ bn]ciajp]pekj[ej[lan_ajp value for a precise measure.
Ê
UÊ ]rc[l]ca[ol]_a[qoa`[ej[lan_ajp: To get an idea of the amount of space allocated within the pages of the index, use this number. This value is JQHH when the sampling mode is Limited.
Ê
UÊ na_kn`[_kqjp: Simply put, this is the number of records represented by the statistics. For indexes, this is the number of records within the current level of the B-tree as represented from the scanning mode. (Detailed scans will show all levels of the B-tree, not simply the leaf level.) For heaps, this number represents the records present, but this number may not correlate precisely to the number of rows in the table since a heap may have two records after an update and a page split.
Ê
UÊ ]rc[na_kn`[oeva[ej[^upao: This number simply represents a useful measure for the amount of data stored within the index or heap record.
221
222
CHAPTER 8
FRAGMENTATION ANALYSIS
,Õ}Êouo*`i[`^[ej`at[lduoe_]h[op]po with a Detailed scan will return multiple rows for a given index. That is, multiple rows are displayed if that index spans more than one level. Multiple levels exist in an index when that index spans more than a single page. To see what this looks like and to observe some of the other columns of data present in the dynamic management function, run the query this way: OAHA?Po*& BNKIouo*`i[`^[ej`at[lduoe_]h[op]po$@>[E@$#=`rajpqnaSkngo.,,4#%( K>FA?P[E@$J#`^k*p-#%(JQHH(JQHH( #@ap]eha`#%=Oo To make the data readable, I’ve broken down the resulting data table into three pieces in a Ã}iÊ}À>«
VÆÊÃiiÊ}ÕÀiÊn££°
Figure 8-11. Detailed scan of fragmented index As you can see, two rows were returned, representing the leaf level of the index (ej`at[ harah = 0) and representing the first level of the B-tree (ej`at[harah = 1), which is the second row. You can see the additional information offered by ouo*`i[`^[ej`at[lduoe_]h[op]po that can provide more detailed analysis of your indexes. For example, you can see the minimum >`Ê>ÝÕÊÀiVÀ`ÊÃâiÃ]Ê>ÃÊÜiÊ>ÃÊÌ
iÊ`iÝÊ`i«Ì
ÊÌ
iÊÕLiÀÊvÊiÛiÃÊÊÌ
iÊ ÌÀii®Ê and how many records are on each level. A lot of this information will be less useful for basic fragmentation analysis, which is why I chose to limit the number of columns in the samples as well as use the Sampled scan mode.
Analyzing the Fragmentation of a Small Table Don’t be overly concerned with the output of ouo*`i[`^[ej`at[lduoe_]h[op]po for small tables. For a small table or index with fewer than eight pages, SQL Server uses mixed extents for the pages. For example, if a table (Oi]hhP]^ha- or its clustered index) contains only two pages, then SQL Server allocates the two pages from a mixed extent instead of dedicating an extent to the table. The mixed extent may contain pages of other small tables/indexes also, as shown in Figure 8-12.
CHAPTER 8
F R A G M E N T A T I O N A N A LY S I S
Figure 8-12. Mixed extent The distribution of pages across multiple mixed extents may lead you to believe that there is a high amount of external fragmentation in the table or the index, when in fact this is by design in SQL Server and is therefore perfectly acceptable. To understand how the fragmentation information of a small table or index may look, create a small table with a clustered index (_na]pa[oi]hh[p-[bn]ciajpa`*omh in the download): EB$OAHA?PK>FA?P[E@$#p-#%%EOJKPJQHH @NKLP=>HApCK ?NA=PAP=>HAp-$_-EJP(_.EJP(_/EJP(_0?D=N$.,,,%% @A?H=NAÛiÀ>}iÊÀÜÊÃâiÊÃÊ£]ä£äÊLÞÌiÃ]Ê>ÊVÕÃÌiÀi`Ê`iÝÊi>vÊ«>}iÊÀÊÌ>LiÊ`>Ì>Ê«>}i®Ê can contain a maximum of eight rows. Therefore, at least three leaf pages are required for the 24 rows. You can confirm this in the ouo*`i[`^[ej`at[lduoe_]h[op]po output shown in Figure 8-17.
Figure 8-17. Fill factor set to default value of 0 ÌiÊÌ
>ÌÊ]rc[l]ca[ol]_a[qoa`[ej[lan_ajp is 100 percent, since the default fill factor allows the maximum number of rows to be compressed in a page. Since a page cannot contain a part row to fill the page fully, ]rc[l]ca[ol]_a[qoa`[ej[lan_ajp will be often a little less than 100 percent, even with the default fill factor. To prevent page splits caused by EJOANP and QL@=PA operations, create some free space within the leaf (or data) pages by re-creating the clustered index with a fill factor as follows: =HPANEJ@ATe-KJ`^k*p-NA>QEH@ SEPD$ BEHHB=?PKN9 31% Because each page has a total space for eight rows, a fill factor of 75 percent will allow six rows per page. Thus, for 24 rows, the number of leaf pages should increase to four, as in the ouo*`i[`^[ej`at[lduoe_]h[op]po output shown in Figure 8-18.
Figure 8-18. Fill factor set to 75
231
232
CHAPTER 8
FRAGMENTATION ANALYSIS
ÌiÊÌ
>ÌÊ]rc[l]ca[ol]_a[qoa`[ej[lan_ajp is about 75 percent, as set by the fill factor. This allows two more rows to be inserted in each page without causing a page split. You can confirm this by adding two rows to the first set of six rows (_- = 100 – 600, contained in the first page): EJOANPEJPKp-R=HQAO$--,(#]#%)).1pdnks EJOANPEJPKp-R=HQAO$-.,(#]#%)).2pdnks Figure 8-19 shows the current fragmentation.
Figure 8-19. Fragmentation after new records From the output, you can see that the addition of the two rows has not added any pages to the index. Accordingly, ]rc[l]ca[ol]_a[qoa`[ej[lan_ajp increased from 74.99 percent to 81.25 percent. With the addition of two rows to the set of the first six rows, the first page should be completely full (eight rows). Any further addition of rows within the range of the first eight rows should cause a page split and thereby increase the number of index pages to five: EJOANPEJPKp-R=HQAO$-/,(#]#%)).3pdnks ÜÊouo*`i[`^[ej`at[lduoe_]h[op]po displays the difference in Figure 8-20.
Figure 8-20. Number of pages goes up ÌiÊÌ
>ÌÊiÛiÊÌ
Õ}
ÊÌ
iÊvÊv>VÌÀÊvÀÊÌ
iÊ`iÝÊÃÊÇxÊ«iÀViÌ]Ê=rc*L]ca@ajoepu$bqhh% has decreased to 67.49 percent, which can be computed as follows:
Avg. Page Density (full) = Average rows per page / Maximum rows per page = (27 / 5) / 8 = 67.5% From the preceding example, you can see that the fill factor is applied when the index is created. But later, as the data is modified, it has no significance. Irrespective of the fill factor, whenever a page splits, the rows of the original page are distributed between two pages, and ]rc[l]ca[ol]_a[qoa`[ej[lan_ajp settles accordingly. Therefore, if you use a nondefault fill factor, you should ensure that the fill factor is reapplied regularly to maintain its effect.
CHAPTER 8
F R A G M E N T A T I O N A N A LY S I S
You can reapply a fill factor by re-creating the index or by using =HPANEJ@ATNAKNC=JEVA or =HPANEJ@ATNA>QEH@, as was shown. =HPANEJ@ATNAKNC=JEVA takes the fill factor specified during the index creation into account. =HPANEJ@ATNA>QEH@ also takes the original fill factor into account, but it allows a new fill factor to be specified, if required. Without periodic maintenance of the fill factor, for both default and nondefault fill factor settings, ]rc[l]ca[ol]_a[qoa`[ej[lan_ajp for an index (or a table) eventually settles within a narrow range. Therefore, in most cases, without manual maintenance of the fill factor, the default fill factor is generally good enough. You should also consider one final aspect when deciding upon the fill factor. Even for a heavy OLTP application, the number of database reads typically outnumbers writes by a factor of 5 to 10. Specifying a fill factor other than the default can degrade read performance by an amount inversely proportional to the fill factor setting, since it spreads keys over a wider area. Before setting the fill factor at a database-wide level, use Performance Monitor to compare the OMHOanran6>qbbanI]j]canXL]cana]`o+oa_ counter to the OMHOanran6>qbbanI]j]canXL]ca snepao+oa_ counter, and use the fill factor option only if writes are a substantial fraction of reads (greater than 30 percent).
Automatic Maintenance In a database with a great deal of transactions, tables and indexes become fragmented over time. Thus, to improve performance, you should check the fragmentation of the tables and indexes regularly, and you should defragment the ones with a high amount of fragmentation. You can do this analysis for a database by following these steps: 1. `iÌvÞÊ>ÊÕÃiÀÊÌ>LiÃÊÊÌ
iÊVÕÀÀiÌÊ`>Ì>L>ÃiÊÌÊ>>ÞâiÊvÀ>}iÌ>Ì° 2. Determine fragmentation of every user table and index. 3. Determine user tables and indexes that require defragmentation by taking into account the following considerations: Ê UÊ Ê
}
ÊiÛiÊvÊvÀ>}iÌ>ÌÊÜ
iÀiÊ]rc[bn]ciajp]pekj[ej[lan_ajp is greater than 20 percent Ê UÊ
ÌÊ>ÊÛiÀÞÊÃ>ÊÌ>LiÉ`iÝpÌ
>ÌÊÃ]Êl]ca[_kqjp is greater than 8
4. Defragment tables and indexes with high fragmentation. A sample SQL stored procedure (Ej`at@abn]c*omh in the download) is included here for easy reference. It performs the following actions: Ê
UÊ 7>ÃÊ>Ê`>Ì>L>ÃiÃÊÊÌ
iÊÃÞÃÌiÊ>`Ê`iÌviÃÊ`iÝiÃÊÊÕÃiÀÊÌ>LiÃÊÊi>V
Ê`>Ì>base that meets the fragmentation criteria and saves them in a temporary table
Ê
UÊ >Ãi`ÊÊÌ
iÊiÛiÊvÊvÀ>}iÌ>Ì]ÊÀiÀ}>âiÃÊ}
ÌÞÊvÀ>}iÌi`Ê`iÝiÃÊ>`Ê rebuilds those that are highly fragmented
233
234
C HAPTER 8
F RA G MENTA TION A NA L YS IS
iÀi½ÃÊ
ÜÊÌÊ>>ÞâiÊ>`ÊÀiÃÛiÊ`>Ì>L>ÃiÊvÀ>}iÌ>ÌÊÃÌÀiÊÌ
ÃÊÜ
iÀiÊ>««À«À>ÌiÊ ÊÞÕÀÊÃÞÃÌiÆÊÜiÊ
>ÛiÊ>Ê`iÃ}>Ìi`Ê`>Ì>L>ÃiÊvÀÊiÌiÀ«ÀÃiiÛiÊÃVÀ«Ìî\ ?NA=PALNK?A@QNAEj`at@abn]c =O @A?H=NAJ]iaJR=N?D=N$.11% (J]iaJR=N?D=N$.11% (P]^haJ]iaJR=N?D=N$.11% (O_dai]J]iaJR=N?D=N$.11% (Ej`atJ]iaJR=N?D=N$.11% (=rcBn]ciajp@A?EI=H% ATA?ol[iobkna]_d`^#EJOANPEJPKBn]c$ @>J]ia( P]^haJ]ia( O_dai]J]ia( Ej`atJ]ia( =rcBn]ciajp %OAHA?P##;##=O@>J]ia (p*J]ia=OP]^haJ]ia (o_*J]ia=OO_dai]J]ia (e*j]ia=OEj`atJ]ia (o*]rc[bn]ciajp]pekj[ej[lan_ajp BNKI;*ouo*`i[`^[ej`at[lduoe_]h[op]po$@>[E@$##;##%(JQHH(JQHH( JQHH(##O]ilha`##%=Oo FKEJ;*ouo*ej`ataoe KJo*K^fa_p[E`9e*K^fa_p[e` =J@o*Ej`at[e`9e*Ej`at[e` FKEJ;*ouo*p]^haop KJe*K^fa_p[e`9p*K^fa_p[E` FKEJ;*ouo*o_dai]oo_ KJp*o_dai][e`9o_*O?DAI=[E@
CHAPTER 8
F R A G M E N T A T I O N A N A LY S I S
SDANAo*]rc[bn]ciajp]pekj[ej[lan_ajp:., =J@p*PULA9##Q## =J@o*l]ca[_kqjp:4 KN@AN>UP]^haJ]ia(Ej`atJ]ia# @A?H=NA_Heop?QNOKN BKNOAHA?P&BNKIBn]c KLAJ_Heop BAP?DJATPBNKI_Heop EJPKJ]ia(}ÊLÝÊÌÊVÀiate the SQL Server job. A SQL Server job is created that schedules the ol[Ej`at@abn]c stored procedure to run at a regular (weekly) time interval. 9. Ensure that SQL Server Agent is running so that the SQL Server job will run automatically according to the set schedule. /
iÊ-+ÊLÊÜÊ>ÕÌ>ÌV>ÞÊ>>ÞâiÊ>`Ê`ivÀ>}iÌÊÌ
iÊvÀ>}iÌ>ÌÊvÊi>V
Ê database every Sunday at 1 a.m. Figure 8-25 shows the corresponding output of the Bn]ciajp]pekjKqplqp*ptp file.
CHAPTER 8
FRAGMENTATION ANALYSIS
Figure 8-25. Bn]ciajp]pekjKqplqp*ptp file output /
iÊÕÌ«ÕÌÊÃ
ÜÃÊÌ
>ÌÊÌ
iÊLÊ>>Þâi`ÊÌ
iÊvÀ>}iÌ>ÌÊvÊÌ
iÊ`>Ì>L>ÃiÊ>`Ê`iÌvi`Ê>Ê ÃiÀiÃÊvÊ`iÝiÃÊvÀÊ`ivÀ>}iÌ>Ì]ÊëiVvV>ÞÊvÀÊÀiÀ}>â>Ì°Ê-ÕLÃiµÕiÌÞ]ÊÌÊ`ivÀ>}ments the index. The stored procedure defragmented only the database object that was highly fragmented. Thus, the next run of the SQL job generally won’t identify these same indexes for defragmentation.
Summary As you learned in this chapter, in a highly transactional database page splits caused by EJOANP and QL@=PA statements fragment the tables and indexes, increasing the cost of data retrieval. You can avoid these page splits by maintaining free spaces within the pages using the fill factor. Since the fill factor is applied only during index creation, you should reapply it at regular intervals to maintain its effectiveness. You can determine the amount of fragmentation in an index (or a table) using ouo*`i[`^[lduoe_]h[op]po. Upon determining a high amount of fragmentation, you can use either =HPANEJ@ATNA>QEH@ or =HPANEJ@ATNAKNC=JEVA, depending on the required amount of defragmentation and database concurrency. Defragmentation rearranges the data so that its physical order on the disk matches its }V>ÊÀ`iÀÊÊÌ
iÊÌ>LiÉ`iÝ]ÊÌ
ÕÃÊ«ÀÛ}ÊÌ
iÊ«iÀvÀ>ViÊvʵÕiÀiðÊÜiÛiÀ]ÊÕiÃÃÊ Ì
iÊ«ÌâiÀÊ`iV`iÃÊÕ«Ê>ÊivviVÌÛiÊiÝiVÕÌÊ«>ÊvÀÊÌ
iʵÕiÀÞ]ʵÕiÀÞÊ«iÀvÀ>ViÊiÛiÊ >vÌiÀÊ`ivÀ>}iÌ>ÌÊV>ÊÀi>Ê«À°Ê/
iÀivÀi]ÊÌÊÃÊ«ÀÌ>ÌÊÌÊ
>ÛiÊÌ
iÊ«ÌâiÀÊÕÃiÊ efficient techniques to generate cost-effective execution plans. In the next chapter, I will delve deeply into execution plan generation and the techniques Ì
iÊ«ÌâiÀÊÕÃiÃÊÌÊ`iV`iÊÕ«Ê>ÊivviVÌÛiÊiÝiVÕÌÊ«>°
239
CHAPT ER
9
Execution Plan Cache Analysis T
he performance of any query depends on the effectiveness of the execution plan decided upon by the optimizer, as you learned in previous chapters. Because the overall time required to execute a query is the sum of the time required to generate the execution plan plus the time required to execute the query based on this execution plan, it is important that the cost of generating the execution plan itself is low. The cost incurred when generating the execution plan depends on the process of generating the execution plan, the process of caching the plan, and the reusability of the plan from the plan cache. In this chapter, you will learn how an execution plan is generated and how to analyze the execution plan cache for plan reusability. In this chapter, I cover the following topics: Ê
UÊ ÝiVÕÌÊ«>Ê}iiÀ>ÌÊ>`ÊV>V
}
Ê
UÊ /
iÊ-+Ê-iÀÛiÀÊV«iÌÃÊÕÃi`ÊÌÊ}iiÀ>ÌiÊ>ÊiÝiVÕÌÊ«>
Ê
UÊ -ÌÀ>Ìi}iÃÊÌÊ«ÌâiÊÌ
iÊVÃÌÊvÊiÝiVÕÌÊ«>Ê}iiÀ>Ì
Ê
UÊ >VÌÀÃÊ>vviVÌ}Ê«>À>iÊ«>Ê}iiÀ>Ì
Ê
UÊ ÜÊÌÊ>>ÞâiÊiÝiVÕÌÊ«>ÊV>V
}
Ê
UÊ +ÕiÀÞÊ«>Ê
>Ã
Ê>`ʵÕiÀÞÊ
>Ã
Ê>ÃÊiV
>ÃÃÊvÀÊ`iÌvÞ}ʵÕiÀiÃÊÌÊÌÕi
Ê
UÊ ÝiVÕÌÊ«>ÃÊ}iÊÜÀ}Ê>`Ê«>À>iÌiÀÊÃvv}
Ê
UÊ 7>ÞÃÊÌÊ«ÀÛiÊÌ
iÊÀiÕÃ>LÌÞÊvÊiÝiVÕÌÊ«>ÊV>V
}
Execution Plan Generation As you knowÊLÞÊÜ]Ê-+Ê-iÀÛiÀÊÕÃiÃÊ>ÊVÃÌL>Ãi`Ê«Ìâ>Ì technique to determine the processing strategy of a query. The optimizer considers both the metadata of the database objects and the current distribution statistics of the columns referred to in the query when deciding which index and join strategies should be used.
241
242
C HAPTER 9
EXEC U TION P L A N C A C HE A NA L YS IS
The cost-based optimization allows a database developer to concentrate on implementing a business rule, rather than on the exact syntax of the query. At the same time, the process of determining the query processing strategy remains quite complex and can VÃÕiÊ>Êv>ÀÊ>ÕÌÊvÊÀiÃÕÀViðÊ-+Ê-iÀÛiÀÊÕÃiÃÊ>ÊÕLiÀ of techniques to optimize resource consumption: Ê
UÊ -ÞÌ>ÝL>Ãi`Ê«Ìâ>ÌÊvÊÌ
iʵÕiÀÞ
Ê
UÊ /ÀÛ>Ê«>Ê>ÌV
ÊÌÊ>Û`Ê`i«Ì
ʵÕiÀÞÊ«Ìâ>ÌÊvÀÊëiʵÕiÀiÃ
Ê
UÊ `iÝÊ>`ÊÊÃÌÀ>Ìi}iÃÊL>Ãi`ÊÊVÕÀÀiÌÊ`ÃÌÀLÕÌÊÃÌ>ÌÃÌVÃ
Ê
UÊ +ÕiÀÞÊ«Ìâ>ÌÊÊÕÌ«iÊ«
>ÃiÃÊÌÊVÌÀÊÌ
iÊVÃÌÊvÊ«Ìâ>Ì
Ê
UÊ ÝiVÕÌÊ«>ÊV>V
}ÊÌÊ>Û`ÊÌ
iÊÀi}iiÀ>ÌÊvʵÕiÀÞÊ«>à The following techniques are performed in order, as shown in the vÜV
>ÀÌÊÊ}ÕÀiÊ£\
Ê
UÊ *>ÀÃiÀ
Ê
UÊ }iLÀâiÀ
Ê
UÊ +ÕiÀÞÊ«ÌâiÀ
Ê
UÊ ÝiVÕÌÊ«>Ê}iiÀ>Ì]ÊV>V
}]Ê>`Ê
>Ã
Ê«>Ê}iiÀ>Ì
Ê
UÊ +ÕiÀÞÊiÝiVÕÌ
Figure 9-1. SQL Server techniques to optimize query execution i̽ÃÊÌ>iÊ>ÊÊ>ÌÊÌ
iÃiÊÃÌi«ÃÊÊÀiÊ`iÌ>°
CHAPTER 9
E X E C U T I O N P LA N C A C H E A N A LY S I S
Parser 7
iÊ>ʵÕiÀÞÊÃÊÃÕLÌÌi`]Ê-+Ê-iÀÛiÀÊ«>ÃÃià it to the parser within the relational engine. (This Ài>Ì>Êi}iÊÃÊiÊvÊÌ
iÊÌÜÊ>Ê«>ÀÌÃÊvÊ-+Ê-iÀÛiÀ]ÊÜÌ
ÊÌ
iÊÌ
iÀÊLi}Ê the storage engine, which is responsible for data access, modifications, and caching.) The relational engine takes care of parsing, name and type resolution (the algebrizer), and optimization. It also executes a query as per the query execution plan and requests data from the storage engine. The parser checks an incoming query, validating it for the correct syntax. The query is terminated if a syntax error is detected. If multiple queries are submitted together as a batch as follows (note the error in syntax): ?NA=PAP=>HAp-$_-EJP% EJOANPEJPKp-R=HQAO$-% ?AEHAGP&BNKIp-))Annkn6Eia]jp(OAHA?P&BNKIpCK then the parser checks the complete batch together for syntax and cancels the complete batch when it detects a syntax error. (Note that more than one syntax error may appear in a batch, but the parser goes no further than the first one.) On validating a query for correct syntax, the parser generates an internal data structure called a parse tree for the algebrizer. The parser and algebrizer taken together are called query compilation.
Algebrizer The parse tree generated by the parser is passed to the algebrizer for processing. The algebrizer resolves all the names of the different objects, meaning the tables, the columns, and so ]ÊÌ
>ÌÊ>ÀiÊLi}ÊÀiviÀiVi`ÊÊÌ
iÊ/-+°ÊÌÊ>ÃÊ`iÌviÃÊ>ÊÌ
iÊÛ>ÀÕÃÊ`>Ì>ÊÌÞ«iÃÊLi}Ê processed. It even checks for the location of aggregates (such as CNKQL>U and I=T). The output of all these verifications and resolutions is a binary set of data called a query processor tree. To see the algebrizer in action, if the following batch query (]hca^nevan[paop*omh in the download) is submitted: ?NA=PAP=>HAp-$_-EJP%7 EJOANPEJPKpR=HQAO$-%7 OAHA?P#>abknaAnnkn# (_BNKIp-=Op7 OAHA?P#annkn# (_BNKIjk[p-7))Annkn6P]^ha`kaoj#pateop OAHA?P#]bpanannkn#_BNKIp-=Op7 then the first three statements before the error statement are executed, and the errant statement and the one after it are cancelled.
243
244
C HAPTER 9
EXEC U TION P L A N C A C HE A NA L YS IS
If a query contains an implicit data conversion, then the normalization process adds an appropriate step to the query tree. The process also performs some syntax-based optimizaÌ°ÊÀÊiÝ>«i]ÊvÊÌ
iÊvÜ}ʵÕiÀÞÊoujp]t[klpeieva*omh in the download) is submitted: OAHA?P& BNKIO]hao*O]haoKn`anDa]`an=Ookd SDANAokd*O]haoKn`anE@>APSAAJ2.1,,=J@2.11, Ì
iÊÌ
iÊÃÞÌ>ÝL>Ãi`Ê«Ìâ>ÌÊÌÀ>ÃvÀÃÊÌ
iÊÃÞÌ>ÝÊvÊÌ
iʵÕiÀÞ]Ê>ÃÊÃ
ÜÊÊ}ÕÀiÊÓ]Ê where >APSAAJ becomes :9 and 89.
Figure 9-2. Syntax-based optimization ÀÊÃÌÊ >Ì>Ê ivÌÊ>}Õ>}iÊ ®ÊÃÌ>ÌiiÌÃÊÃÕV
Ê>ÃÊ?NA=PAP=>HA, ?NA=PALNK?, and so on), after passing through the algebrizer, the query is compiled directly for execution, ÃViÊÌ
iÊ«ÌâiÀÊii`ÊÌÊV
ÃiÊ>}ÊÕÌ«iÊ«ÀViÃÃ}ÊÃÌÀ>Ìi}iðÊÀÊiÊ ÊÃÌ>Ìiment in particular, ?NA=PAEJ@AT, the optimizer can determine an efficient processing strategy based on other existing indexes on the table, as explained in Chapter 4. ÀÊÌ
ÃÊÀi>Ã]ÊÞÕÊÜÊiÛiÀÊÃiiÊ>ÞÊÀiviÀiViÊÌÊ?NA=PAP=>HA in an execution plan, although you will see reference to ?NA=PAEJ@AT. If theÊÀ>âi`ʵÕiÀÞÊÃÊ>Ê >Ì>Ê>«Õ>ÌÊ>}Õ>}iÊ ®ÊÃÌ>ÌiiÌÊÃÕV
Ê>ÃÊOAHA?P, EJOANP, QL@=PA, or @AHAPA), then the query processor tree is passed to the optimizer to decide the processing strategy for the query.
Optimization Based on the complexity of a query, including the number of tables referred to and the indexes available, there may be several ways to execute the query contained in the query processor ÌÀii°Ê Ý
>ÕÃÌÛiÞÊV«>À}ÊÌ
iÊVÃÌÊvÊ>ÊÌ
iÊÜ>ÞÃÊvÊiÝiVÕÌ}Ê>ʵÕiÀÞÊV>ÊÌ>iÊ>ÊVÃ`iÀable amount of time, which may sometimes override the benefit of finding the most optimized µÕiÀÞ°Ê}ÕÀiÊÎÊÃ
ÜÃÊÌ
>Ì]ÊÌÊavoid a high optimization overhead compared to the actual execution cost of the query, the optimizer adopts different techniques, namely: Ê
UÊ /ÀÛ>Ê«>Ê>ÌV
Ê
UÊ ÕÌ«iÊ«Ìâ>ÌÊ«
>ÃiÃ
Ê
UÊ *>À>iÊ«>Ê«Ìâ>Ì
CHAPTER 9
Figure 9-3. Query optimization steps
E X E C U T I O N P LA N C A C H E A N A LY S I S
245
246
C HAPTER 9
EXEC U TION P L A N C A C HE A NA L YS IS
Trivial Plan Match -iÌiÃÊÌ
iÀiÊ}
ÌÊLiÊÞÊiÊÜ>ÞÊÌÊiÝiVÕÌiÊ>ʵÕiÀÞ°ÊÀÊiÝ>«i]Ê>Ê
i>«ÊÌ>LiÊÜÌ
ÊÊ indexes can be accessed in only one way: via a table scan. To avoid the runtime overhead of «Ìâ}ÊÃÕV
ʵÕiÀiÃ]Ê-+Ê-iÀÛiÀÊ>Ì>ÃÊ>ÊÃÌÊvÊÌÀÛ>Ê«>ÃÊÌÊV
ÃiÊvÀ°ÊvÊÌ
iÊ«Ìmizer finds a match, then a similar plan is generated for the query without any optimization.
Multiple Optimization Phases ÀÊ>ÊV«iÝʵÕiÀÞ]ÊÌ
i number of alternative processing strategies to be analyzed can be very high, and it may take a long time to evaluate each option. Therefore, instead of analyzing all the possible processing strategies together, the optimizer breaks them into multiple configurations, each consisting of different index and join techniques. The index variations consider different indexing aspects, such as single-column index, V«ÃÌiÊ`iÝ]Ê`iÝÊVÕÊÀ`iÀ]ÊVÕÊ`iÃÌÞ]Ê>`ÊÃÊvÀÌ
°Ê->ÀÞ]ÊÌ
iÊÊÛ>À>ÌÃÊVÃ`iÀÊÌ
iÊ`vviÀiÌÊÊÌiV
µÕiÃÊ>Û>>LiÊÊ-+Ê-iÀÛiÀ\ÊiÃÌi`Ê«Ê]ÊiÀ}iÊ ]Ê>`Ê
>Ã
Ê°Ê
>«ÌiÀÊÎÊVÛiÀÃÊÌ
iÃiÊÊÌiV
µÕiÃÊÊ`iÌ>°® The optimizer considers the statistics of the columns referred to in the SDANA clause to evaluate the effectiveness of the index and the join strategies. Based on the current statistics, it evaluates the cost of the configurations in multiple optimization phases. The cost includes many v>VÌÀÃ]ÊVÕ`}ÊLÕÌÊÌÊÌi`ÊÌ®ÊÕÃ>}iÊvÊ *1]ÊiÀÞ]Ê>`Ê`ÃÊÉ"ÊÀiµÕÀi`ÊÌÊiÝiVÕÌiÊ the query. After each optimization phase, the optimizer evaluates the cost of the processing strategy. If the cost is found to be cheap enough, then the optimizer stops further iteration through the optimization phases and quits the optimization process. Otherwise, it keeps iterating through the optimization phases to determine a cost-effective processing strategy. -iÌiÃÊ>ʵÕiÀÞÊV>ÊLiÊÃÊV«iÝÊÌ
>ÌÊÌ
iÊ«ÌâiÀÊii`ÃÊÌÊiÝÌiÃÛiÞÊÌiÀ>ÌiÊ Ì
ÀÕ}
ÊÌ
iÊ«Ìâ>ÌÊ«
>ÃiðÊ7
iÊ«Ìâ}ÊÌ
iʵÕiÀÞ]ÊvÊÌÊv`ÃÊÌ
>ÌÊÌ
iÊVÃÌÊvÊÌ
iÊ processing strategy is more than the cost threshold for parallelism, then it evaluates the cost vÊ«ÀViÃÃ}ÊÌ
iʵÕiÀÞÊÕÃ}ÊÕÌ«iÊ *1ðÊ"Ì
iÀÜÃi]ÊÌ
iÊ«ÌâiÀÊ«ÀVii`ÃÊÜÌ
ÊÌ
iÊ serial plan. You can find out some detail of what occurred during the multiple optimization phases via two sources. Take, for example, this query (jkj[pnere]h[mqanu*omh): OAHA?Pokd*O]haoKn`anJqi^an (ok`*Kn`anMpu (ok`*HejaPkp]h (ok`*QjepLne_a (ok`*QjepLne_a@eo_kqjp (l*WJ]iaY=OLnk`q_pJ]ia (l*Lnk`q_pJqi^an (lo*WJ]iaY=OLnk`q_pOq^?]packnuJ]ia (l_*WJ]iaY=OLnk`q_p?]packnuJ]ia BNKIO]hao*O]haoKn`anDa]`an=Ookd FKEJO]hao*O]haoKn`an@ap]eh=Ook` KJokd*O]haoKn`anE@9ok`*O]haoKn`anE@ FKEJLnk`q_pekj*Lnk`q_p=Ol KJok`*Lnk`q_pE@9l*Lnk`q_pE@ FKEJLnk`q_pekj*Lnk`q_pIk`ah=Oli KJl*Lnk`q_pIk`ahE@9li*Lnk`q_pIk`ahE@
CHAPTER 9
E X E C U T I O N P LA N C A C H E A N A LY S I S
FKEJLnk`q_pekj*Lnk`q_pOq^_]packnu=Olo KJl*Lnk`q_pOq^_]packnuE@9lo*Lnk`q_pOq^_]packnuE@ FKEJLnk`q_pekj*Lnk`q_p?]packnu=Ol_ KJlo*Lnk`q_p?]packnuE@9l_*Lnk`q_p?]packnuE@ SDANAokd*?qopkianE@9.5214 7
iÊÌ
ÃʵÕiÀÞÊÃÊÀÕ]ÊÌ
iÊiÝiVÕÌÊ«>ÊÊ}ÕÀiÊ{]Ê>ÊÌÀÛ>Ê«>ÊvÀÊÃÕÀi]ÊÃÊ returned.
Figure 9-4. Nontrivial execution plan I realize that this execution plan is a little hard to read. The important point to take away is that it involves quite a few tables, each with indexes and statistics that all had to be taken into account to arrive at this execution plan. The first place you can go to look for information >LÕÌÊÌ
iÊ«ÌâiÀ½ÃÊÜÀÊÊÌ
ÃÊiÝiVÕÌÊ«>ÊÃÊÌ
iÊ«À«iÀÌÞÊÃ
iiÌÊvÊÌ
iÊ/-+ÊOAHA?P «iÀ>ÌÀÊ>ÌÊÌ
iÊv>ÀÊivÌÊvÊÌ
iÊiÝiVÕÌÊ«>°Ê}ÕÀiÊxÊÃ
ÜÃÊÌ
iÊ«À«iÀÌÞÊÃ
iiÌ°
Figure 9-5. OAHA?P operator property sheet
247
248
C HAPTER 9
EXEC U TION P L A N C A C HE A NA L YS IS
-Ì>ÀÌ}Ê>ÌÊÌ
iÊÌ«]ÊÞÕÊV>ÊÃiiÊvÀ>ÌÊ`ÀiVÌÞÊÀi>Ìi`ÊÌÊÌ
iÊVÀi>ÌÊ>`Ê«Ìâ>tion of this execution plan: Ê
UÊ /
iÊÃâiÊvÊÌ
iÊV>V
i`Ê«>]ÊÜ
V
ÊÃÊ{äÊLÞÌiÃ
Ê
UÊ /
iÊÕLiÀÊvÊ *1ÊVÞViÃÊÕÃi`ÊÌÊV«iÊÌ
iÊ«>]ÊÜ
V
ÊÃÊÓ£ÊÃ
Ê
UÊ /
iÊ>ÕÌÊvÊiÀÞÊÕÃi`]ÊÜ
V
ÊÃÊÇ{{
Ê
UÊ /
iÊV«iÊÌi]ÊÜ
V
ÊÃÊÓ£ÊÃ
The "«Ìâ>ÌÊiÛiÊ«À«iÀÌÞÊOp]paiajpKlpiHarahÊÊÌ
iÊ8Ê«>®ÊÃ
ÜÃÊÜ
>ÌÊÌÞ«iÊ vÊ«ÀViÃÃ}ÊVVÕÀÀi`ÊÜÌ
ÊÌ
iÊ«ÌâiÀ°ÊÊÌ
ÃÊV>Ãi]Ê1Êi>ÃÊÌ
>ÌÊÌ
iÊ«ÌâiÀÊ``Ê a full optimization. This is further displayed in the property ,i>ÃÊvÀÊ >ÀÞÊ/iÀ>ÌÊvÊ -Ì>ÌiiÌ]ÊÜ
V
ÊÃÊ`Ê Õ}
Ê*>ÊÕ`°Ê-]ÊÌ
iÊ«ÌâiÀÊÌÊÓ£ÊÃÊÌÊÌÀ>VÊ`ÜÊ>Ê plan that it deemed good enough in this situation. You can also see the +ÕiÀÞ*>>Ã
ÊÛ>Õi]Ê also known as the fingerprint, for the execution plan (you can find more details on this in the ÃiVÌʺ+ÕiÀÞÊ*>Ê>Ã
Ê>`Ê+ÕiÀÞÊ>Ã
»®° The second source for optimizer information is the dynamic management view ouo* `i[ata_[mqanu[klpeievan[ejbk. ThisÊ 6ÊÃÊ>Ê>}}Ài}>ÌÊvÊÌ
iÊ«Ìâ>ÌÊiÛiÌÃÊÛiÀÊ Ìi°ÊÌÊܽÌÊÃ
ÜÊÌ
iÊ`Û`Õ>Ê«Ìâ>ÌÃÊvÀÊ>Ê}ÛiʵÕiÀÞ]ÊLÕÌÊÌÊÜÊÌÀ>VÊÌ
iÊ«Ìâ>ÌÃÊ«iÀvÀi`°Ê/
ÃÊýÌÊ>ÃÊi`>ÌiÞÊ
>`ÞÊvÀÊÌÕ}Ê>Ê`Û`Õ>ʵÕiÀÞ]ÊLÕÌÊvÊÞÕÊ are working on reducing the costs of a workload over time, being able to track this information can help you determine whether your query tuning is making a positive difference, at i>ÃÌÊÊÌiÀÃÊvÊ«Ìâ>ÌÊÌi°Ê-iÊvÊÌ
iÊ`>Ì>ÊÀiÌÕÀi`ÊÃÊvÀÊÌiÀ>Ê-+Ê-iÀÛiÀÊÕÃiÊ Þ°Ê}ÕÀiÊÈÊÃ
ÜÃÊ>ÊÌÀÕV>Ìi`ÊiÝ>«iÊvÊÌ
iÊÕÃivÕÊ`>Ì>ÊÀiÌÕÀi`ÊÊÌ
iÊÀiÃÕÌÃÊvÀÊÌ
iÊ following query: OAHA?P?kqjpan (K__qnnaj_a (R]hqa BNKIouo*`i[ata_[mqanu[klpeievan[ejbk
Figure 9-6. Output from ouo*`i[ata_[mqanu[klpeievan[ejbk Running this query before and after a set of optimizations can show you the changes that have occurred in the number and type of optimizations completed.
Parallel Plan Optimization The optimizer considers various factors while evaluating the cost of processing a query using a «>À>iÊ«>°Ê-iÊvÊÌ
iÃiÊv>VÌÀÃÊ>ÀiÊ>ÃÊvÜÃ\
CHAPTER 9
Ê
UÊ
Ê
UÊ -+Ê-iÀÛiÀÊi`Ì
Ê
UÊ Û>>LiÊiÀÞ
Ê
UÊ /Þ«iÊvʵÕiÀÞÊLi}ÊiÝiVÕÌi`
Ê
UÊ
ÕLiÀÊvÊÀÜÃÊÌÊLiÊ«ÀViÃÃi`ÊÊ>Ê}ÛiÊÃÌÀi>
Ê
UÊ
ÕLiÀÊvÊ>VÌÛiÊVVÕÀÀiÌÊViVÌÃ
E X E C U T I O N P LA N C A C H E A N A LY S I S
ÕLiÀÊvÊ *1ÃÊ>Û>>LiÊÌÊ-+Ê-iÀÛiÀ
If only oneÊ *1ÊÃÊ>Û>>LiÊÌÊ-+Ê-iÀÛiÀ]ÊÌ
iÊÌ
iÊ«ÌâiÀÊܽÌÊVÃ`iÀÊ>Ê«>À>iÊ «>°Ê/
iÊÕLiÀÊvÊ *1ÃÊ>Û>>LiÊÌÊ-+Ê-iÀÛiÀÊV>ÊLiÊÀiÃÌÀVÌi`ÊÕÃ}ÊÌ
iÊaffinity mask ÃiÌÌ}ÊvÊÌ
iÊ-+Ê-iÀÛiÀÊVv}ÕÀ>Ì°Ê/
iÊ>vvÌÞÊ>ÃÊÛ>ÕiÊÃÊ>ÊLÌ>«ÊÊÌ
>ÌÊ>ÊLÌÊÀi«ÀiÃiÌÃÊ>Ê *1]ÊÜÌ
ÊÌ
iÊÀ}
ÌÃÌÊLÌÊ«ÃÌÊÀi«ÀiÃiÌ}Ê *1ä°ÊÀÊiÝ>«i]ÊÌÊ>ÜÊ-+Ê -iÀÛiÀÊÌÊÕÃiÊÞÊ *1äÊÌÊ *1ÎÊÊ>Êi}
ÌÜ>ÞÊLÝ]ÊiÝiVÕÌiÊÌ
iÃiÊÃÌ>ÌiiÌÃÊ]bbejepu[ i]og*omh in the download): QOAi]opan ATA?ol[_kjbecqna#odks]`r]j_a`klpekj#(#-# NA?KJBECQNA ATA?ol[_kjbecqna#]bbejepui]og#(-1))>epi]l6,,,,---NA?KJBECQNA This configuration takes effect immediately. ]bbejepui]og is a special setting, and I recommend that you use it onlyÊÊÃÌ>ViÃÊÜ
iÀiÊÌ>}ÊVÌÀÊ>Ü>ÞÊvÀÊ-+Ê-iÀÛiÀÊ >iÃÊÃiÃi]ÊÃÕV
Ê>ÃÊÜ
iÊÞÕÊ
>ÛiÊÕÌ«iÊÃÌ>ViÃÊvÊ-+Ê-iÀÛiÀÊÀÕ}ÊÊÌ
iÊÃ>iÊ >V
iÊ>`ÊÞÕÊÜ>ÌÊÌÊÃ>ÌiÊÌ
iÊvÀÊi>V
ÊÌ
iÀ°Ê/ÊÃiÌÊ>vvÌÞÊ«>ÃÌÊÎÓÊ«ÀViÃÃÀÃ]Ê you have to use the ]bbejepu20i]ogÊ«ÌÊÌ
>ÌÊÃÊ>Û>>LiÊÞÊÊÌ
iÊÈ{LÌÊÛiÀÃÊvÊ-+Ê -iÀÛiÀ°Ê9ÕÊV>Ê>ÃÊL`ÊÉ"ÊÌÊ>ÊëiVvVÊÃiÌÊvÊ«ÀViÃÃÀÃÊÕÃ}ÊÌ
iÊ>vvÌÞÊ>ÃÊÉ"Ê«ÌÊ in the same way.
ÛiÊvÊÕÌ«iÊ *1ÃÊ>ÀiÊ>Û>>LiÊÌÊ-+Ê-iÀÛiÀ]ÊvÊ>Ê`Û`Õ>ʵÕiÀÞÊÃÊÌÊ>Üi`ÊÌÊ ÕÃiÊÀiÊÌ
>ÊiÊ *1ÊvÀÊiÝiVÕÌ]ÊÌ
iÊÌ
iÊ«ÌâiÀÊ`ÃV>À`ÃÊÌ
iÊ«>À>iÊ«>Ê«Ì°Ê /
iÊ>ÝÕÊÕLiÀÊvÊ *1ÃÊÌ
>ÌÊV>ÊLiÊÕÃi`ÊvÀÊ>Ê«>À>iʵÕiÀÞÊÃÊ}ÛiÀi`ÊLÞÊÌ
iÊi]t `acnaakbl]n]hhaheoiÊÃiÌÌ}ÊvÊÌ
iÊ-+Ê-iÀÛiÀÊVv}ÕÀ>Ì°Ê/
iÊ`iv>ÕÌÊÛ>ÕiÊÃÊä]ÊÜ
V
Ê >ÜÃÊ>ÊÌ
iÊ *1ÃÊ>Û>i`ÊLÞÊÌ
iÊ]bbejepui]og setting) to be used for a parallel query. If you Ü>ÌÊÌÊ>ÜÊ«>À>iʵÕiÀiÃÊÌÊÕÃiÊÊÀiÊÌ
>ÊÌÜÊ *1ÃÊÕÌÊvÊ *1äÊÌÊ *1Î]ÊÌi`ÊLÞÊ the preceding ]bbejepui]og setting, execute the following statements (l]n]hhaheoi*omh in the download): QOAi]opan ATA?ol[_kjbecqna#odks]`r]j_a`klpekj#(#-# NA?KJBECQNA ATA?ol[_kjbecqna#i]t`acnaakbl]n]hhaheoi#(. NA?KJBECQNA This change takes effect immediately, without any restart. The i]t`acnaakbl]n]hhaheoi setting can also be controlled at a query level using the I=T@KL query hint: OAHA?P&BNKIp-SDANA_-9-KLPEKJ$I=T@KL.%
249
250
C HAPTER 9
EXEC U TION P L A N C A C HE A NA L YS IS
Changing the i]t`acnaakbl]n]hhaheoi setting is best determined by the needs of your system. To limit contention with the operating system, I will usually set the i]t`acnaakb l]n]hhaheoiÊÃiÌÌ}ÊÌÊiÊiÃÃÊÌ
>ÊÌ
iÊÕLiÀÊÊÌ
iÊÃiÀÛiÀ]Ê>`ʽÊÃiÌÊ>Ê>vvÌÞÊ>ÃÊÌÊ Ì
ÃiÊ *1ÃÊ>ÃÊÜi° -ViÊ«>À>iʵÕiÀiÃÊÀiµÕÀiÊÀiÊiÀÞ]ÊÌ
iÊ«ÌâiÀÊ`iÌiÀiÃÊÌ
iÊ>ÕÌÊvÊ memory available before choosing a parallel plan. The amount of memory required increases with the degree of parallelism. If the memory requirement of the parallel plan for a given `i}ÀiiÊvÊ«>À>iÃÊV>ÌÊLiÊÃ>ÌÃvi`]ÊÌ
iÊ-+Ê-iÀÛiÀÊ`iVÀi>ÃiÃÊÌ
iÊ`i}ÀiiÊvÊ«>À>iÃÊ automatically or completely abandons the parallel plan for the query in the given workload context. +ÕiÀiÃÊÜÌ
Ê>ÊÛiÀÞÊ
}
Ê *1ÊÛiÀ
i>`Ê>ÀiÊÌ
iÊLiÃÌÊV>``>ÌiÃÊvÀÊ>Ê«>À>iÊ«>°Ê
Ý>«iÃÊVÕ`iÊ}Ê>À}iÊÌ>LiÃ]Ê«iÀvÀ}ÊÃÕLÃÌ>Ì>Ê>}}Ài}>ÌÃ]Ê>`ÊÃÀÌ}Ê>À}iÊ ÀiÃÕÌÊÃiÌÃ]Ê>ÊVÊ«iÀ>ÌÃÊÊÀi«ÀÌ}ÊÃÞÃÌiÃÊiÃÃÊÃÊÊ"/*ÊÃÞÃÌiî°ÊÀÊëiÊ queries usually found in transaction-processing applications, the additional coordination required to initialize, synchronize, and terminate a parallel plan outweighs the potential performance benefit. 7
iÌ
iÀÊÀÊÌÊ>ʵÕiÀÞÊÃÊëiÊÃÊ`iÌiÀi`ÊLÞÊV«>À}ÊÌ
iÊiÃÌ>Ìi`ÊiÝiVÕÌÊÌiÊ of the query with a cost threshold. This cost threshold is controlled by the _koppdnaodkh` bknl]n]hhaheoiÊÃiÌÌ}ÊvÊÌ
iÊ-+Ê-iÀÛiÀÊVv}ÕÀ>Ì°Ê ÞÊ`iv>ÕÌ]ÊÌ
ÃÊÃiÌÌ}½ÃÊÛ>ÕiÊÃÊx]Ê Ü
V
Êi>ÃÊÌ
>ÌÊvÊÌ
iÊiÃÌ>Ìi`ÊiÝiVÕÌÊÌiÊvÊÌ
iÊÃiÀ>Ê«>ÊÃÊÀiÊÌ
>ÊxÊÃiV`Ã]ÊÌ
iÊ Ì
iÊ«ÌâiÀÊVÃ`iÀÃÊ>Ê«>À>iÊ«>ÊvÀÊÌ
iʵÕiÀÞ°ÊÀÊiÝ>«i]ÊÌÊ`vÞÊÌ
iÊVÃÌÊÌ
ÀiÃ
`Ê ÌÊÈÊÃiV`Ã]ÊiÝiVÕÌiÊÌ
iÊvÜ}ÊÃÌ>ÌiiÌÃÊl]n]hhaheoi[pdnaodkh`*omh in the download): QOAi]opan ATA?ol[_kjbecqna#odks]`r]j_a`klpekj#(#-# NA?KJBECQNA ATA?ol[_kjbecqna#_koppdnaodkh`bknl]n]hhaheoi#(2 NA?KJBECQNA /
ÃÊV
>}iÊÌ>iÃÊivviVÌÊi`>ÌiÞ]ÊÜÌ
ÕÌÊ>ÞÊÀiÃÌ>ÀÌ°ÊvÊÞÊiÊ *1ÊÃÊ>Û>>LiÊ ÌÊ-+Ê-iÀÛiÀ]ÊÌ
iÊÌ
ÃÊÃiÌÌ}ÊÃÊ}Ài`°Ê½ÛiÊvÕ`ÊÌ
>ÌÊ"/*ÊÃÞÃÌiÃÊÃÕvviÀÊÜ
iÊÌ
iÊVÃÌÊ Ì
ÀiÃ
`ÊvÀÊ«>À>iÃÊÃÊÃiÌÊÌ
ÃÊÜ°Ê1ÃÕ>ÞÊVÀi>Ã}ÊÌ
iÊÛ>ÕiÊÌÊÃiÜ
iÀiÊLiÌÜiiÊ£xÊ >`ÊÓxÊÜÊLiÊLiivV>° The Ê>VÌʵÕiÀiÃÊEJOANP, QL@=PA, and @AHAPA®Ê>ÀiÊiÝiVÕÌi`ÊÃiÀ>Þ°ÊÜiÛiÀ]ÊÌ
iÊ OAHA?P portion of an EJOANP statement and the SDANA clause of an QL@=PA or a @AHAPA statement can be executed in parallel. The actual data changes are applied serially to the database. Also, if the optimizer determines that the number of rows affected is too low, it does not introduce parallel operators. Note that, even at execution time, -+Ê-iÀÛiÀÊ`iÌiÀiÃÊÜ
iÌ
iÀÊÌ
iÊVÕÀÀiÌÊÃÞÃÌiÊ workload and configuration information allow for parallel query execution. If parallel query iÝiVÕÌÊÃÊ>Üi`]Ê-+Ê-iÀÛiÀÊ`iÌiÀiÃÊÌ
iÊ«Ì>ÊÕLiÀÊvÊÌ
Ài>`ÃÊ>`ÊëÀi>`ÃÊÌ
iÊ iÝiVÕÌÊvÊÌ
iʵÕiÀÞÊ>VÀÃÃÊÌ
ÃiÊÌ
Ài>`ðÊ7
iÊ>ʵÕiÀÞÊÃÌ>ÀÌÃÊ>Ê«>À>iÊiÝiVÕÌ]ÊÌÊÕÃiÃÊ Ì
iÊÃ>iÊÕLiÀÊvÊÌ
Ài>`ÃÊÕÌÊV«iÌ°Ê-+Ê-iÀÛiÀÊÀiiÝ>iÃÊÌ
iÊ«Ì>ÊÕLiÀÊvÊ threads before executing the parallel query the next time. Once the processing strategy is finalized by using either a serial plan or a parallel plan, the optimizer generates the execution plan for the query. The execution plan contains the detailed processing strategy decided by the optimizer to execute the query. This includes steps such as data retrieval, result set joins, result set ordering, and so on. A detailed explanation of how
CHAPTER 9
E X E C U T I O N P LA N C A C H E A N A LY S I S
ÌÊ>>ÞâiÊÌ
iÊ«ÀViÃÃ}ÊÃÌi«ÃÊVÕ`i`ÊÊ>ÊiÝiVÕÌÊ«>ÊÃÊ«ÀiÃiÌi`ÊÊ
>«ÌiÀÊΰÊ/
iÊ execution plan generated for the query is saved in the plan cache for future reuse.
Execution Plan Caching The execution planÊvÊ>ʵÕiÀÞÊ}iiÀ>Ìi`ÊLÞÊÌ
iÊ«ÌâiÀÊÃÊÃ>Ûi`ÊÊ>ÊëiV>Ê«>ÀÌÊvÊ-+Ê-iÀÛiÀ½ÃÊiÀÞÊ«ÊV>i`ÊÌ
iÊplan cache or procedure cache. (The procedure cache is part of the -+Ê-iÀÛiÀÊLÕvviÀÊV>V
iÊ>`ÊÃÊiÝ«>i`ÊÊ
>«ÌiÀÊÓ°®Ê->Û}ÊÌ
iÊ«>ÊÊ>ÊV>V
iÊ>ÜÃÊ-+Ê -iÀÛiÀÊÌÊ>Û`ÊÀÕ}ÊÌ
ÀÕ}
ÊÌ
iÊÜ
iʵÕiÀÞÊ«Ìâ>ÌÊ«ÀViÃÃÊ>}>ÊÜ
iÊÌ
iÊÃ>iÊ µÕiÀÞÊÃÊÀiÃÕLÌÌi`°Ê-+Ê-iÀÛiÀÊÃÕ««ÀÌÃÊ`vviÀiÌÊÌiV
µÕiÃÊÃÕV
Ê>ÃÊplan cache aging and plan cache types to increase the reusability of the cached plans. It also stores two binary values called the query hash and the query plan hash.
Note I discuss the techniques supported by SQL Server for improving the effectiveness of execution plan reuse later in this chapter.
Components of the Execution Plan The execution plan generated by the optimizer contains two components: Ê
UÊ Query plan: This represents the commands that specify all the physical operations required to execute a query.
Ê
UÊ Execution context: This maintains the variable parts of a query within the context of a given user. I will cover these components in more detail in the next sections.
Query Plan The query plan is a reentrant, read-only data structure, with commands that specify all the physical operations required to execute the query. The reentrant property allows the query plan to be accessed concurrently by multiple connections. The physical operations include specifications on which tables and indexes to access, how and in what order they should be accessed, the type of join operations to be performed between multiple tables, and so forth. ÊÕÃiÀÊVÌiÝÌÊÃÊÃÌÀi`ÊÊÌ
iʵÕiÀÞÊ«>°ÊÀÊ>ÊÃ}iʵÕiÀÞ]ÊÌ
iÀiÊV>ÊLiÊÌÜÊV«iÃÊvÊÌ
iÊ query plan: the serial plan and the parallel plan (regardless of the degree of parallelism).
Execution Context The execution context is another data structure that maintains the variable part of the query. Although the server keeps track of the execution plans in the procedure cache, these plans are context neutral. Therefore, each user executing the query will have a separate execution context that holds data specific to their execution, such as parameter values and connection details.
251
252
C HAPTER 9
EXEC U TION P L A N C A C HE A NA L YS IS
Aging of the Execution Plan The procedure cache isÊ«>ÀÌÊvÊ-+Ê-iÀÛiÀ½ÃÊLÕvviÀÊV>V
i]ÊÜ
V
Ê>ÃÊ
`ÃÊ`>Ì>Ê«>}iðÊÃÊ new execution plans are added to the procedure cache, the size of the procedure cache keeps }ÀÜ}]Ê>vviVÌ}ÊÌ
iÊÀiÌiÌÊvÊÕÃivÕÊ`>Ì>Ê«>}iÃÊÊiÀÞ°Ê/Ê>Û`ÊÌ
Ã]Ê-+Ê-iÀÛiÀÊ dynamically controls the retention of the execution plans in the procedure cache, retaining the frequently used execution plans and discarding plans that are not used for a certain period of time. -+Ê-iÀÛiÀÊii«ÃÊÌÀ>VÊvÊÌ
i frequency of an execution plan's reuse by associating an age vi`ÊÌÊÌ°Ê7
iÊ>ÊiÝiVÕÌÊ«>ÊÃÊ}iiÀ>Ìi`]ÊÌ
iÊ>}iÊvi`ÊÃÊ««Õ>Ìi`ÊÜÌ
ÊÌ
iÊVÃÌÊvÊ}ierating the plan. A complex query requiring extensive optimization will have an age field value higher than that for a simpler query. At regular intervals, the age fields of all the execution plans in the procedure cache are `iVÀiiÌi`ÊLÞÊ-+Ê-iÀÛiÀ½ÃÊlazy writer process (which manages most of the background «ÀViÃÃiÃÊÊ-+Ê-iÀÛiÀ®°ÊvÊ>ÊiÝiVÕÌÊ«>ÊÃÊÌÊÀiÕÃi`ÊvÀÊ>Ê}ÊÌi]ÊÌ
iÊÌ
iÊ>}iÊvi`Ê ÜÊiÛiÌÕ>ÞÊLiÊÀi`ÕVi`ÊÌÊä°Ê/
iÊV
i>«iÀÊÌ
iÊiÝiVÕÌÊ«>ÊÜ>ÃÊÌÊ}iiÀ>Ìi]ÊÌ
iÊÃiÀÊÌÃÊ >}iÊvi`ÊÜÊLiÊÀi`ÕVi`ÊÌÊä°Ê"ViÊ>ÊiÝiVÕÌÊ«>½ÃÊ>}iÊvi`ÊÀi>V
iÃÊä]ÊÌ
iÊ«>ÊLiViÃÊ >ÊV>``>ÌiÊvÀÊÀiÛ>ÊvÀÊiÀÞ°Ê-+Ê-iÀÛiÀÊÀiÛiÃÊ>Ê«>ÃÊÜÌ
Ê>Ê>}iÊvi`ÊvÊäÊ from the procedure cache when memory pressure increases to such an extent that there is no }iÀÊiÕ}
ÊvÀiiÊiÀÞÊÌÊÃiÀÛiÊiÜÊÀiµÕiÃÌðÊÜiÛiÀ]ÊvÊ>ÊÃÞÃÌiÊ
>ÃÊiÕ}
ÊiÀÞÊ and free memory pages are available to serve new requests, execution plans with an age field vÊäÊV>ÊÀi>ÊÊÌ
iÊ«ÀVi`ÕÀiÊV>V
iÊvÀÊ>Ê}ÊÌiÊÃÊÌ
>ÌÊÌ
iÞÊV>ÊLiÊÀiÕÃi`Ê>ÌiÀ]ÊvÊ required. As well as aging, execution plans can also find their age field incremented by the cost vÊ}iiÀ>Ì}ÊÌ
iÊ«>ÊiÛiÀÞÊÌiÊÌ
iÊ«>ÊÃÊÀiÕÃi`°ÊÀÊiÝ>«i]ÊÃÕ««ÃiÊÞÕÊ
>ÛiÊÌÜÊ iÝiVÕÌÊ«>ÃÊÜÌ
Ê}iiÀ>ÌÊVÃÌÃÊiµÕ>ÊÌÊ£ääÊ>`Ê£ä°Ê/
iÀÊÃÌ>ÀÌ}Ê>}iÊvi`ÊÛ>ÕiÃÊÜÊ Ì
iÀivÀiÊLiÊ£ääÊ>`Ê£ä]ÊÀiëiVÌÛiÞ°ÊvÊLÌ
ÊiÝiVÕÌÊ«>ÃÊ>ÀiÊÀiÕÃi`Êi`>ÌiÞ]ÊÌ
iÀÊ >}iÊvi`ÃÊÜÊLiÊVÀiiÌi`ÊÌÊÓääÊ>`ÊÓä]ÊÀiëiVÌÛiÞ°Ê7Ì
ÊÌ
iÃiÊ>}iÊvi`ÊÛ>ÕiÃ]ÊÌ
iÊ>âÞÊ ÜÀÌiÀÊÜÊLÀ}Ê`ÜÊÌ
iÊVÃÌÊvÊÌ
iÊÃiV`Ê«>ÊÌÊäÊÕV
Êi>ÀiÀÊÌ
>ÊÌ
>ÌÊvÊÌ
iÊvÀÃÌÊi]Ê unless the second plan is reused more often. Therefore, even if a costly plan is reused less frequently than a cheaper plan, because of the effect of the cost on the age field, the costly plan can remain at a nonzero age value for a longer period of time.
Analyzing the Execution Plan Cache You can obtain a lot of information about the execution plans in the procedure cache by accessing the dynamic management view ouo*`i[ata_[_]_da`[lh]jo: OAHA?P&BNKIouo*`i[ata_[_]_da`[lh]jo />LiÊ£ÊÃ
ÜÃÊÃiÊvÊÌ
iÊÕÃivÕÊvÀ>ÌÊ«ÀÛ`i`ÊLÞÊouo*`i[ata_[_]_da`[lh]jo Ì
ÃÊÃÊi>ÃiÀÊÌÊÀi>`ÊÊÀ`ÊÛiÜ®°
CHAPTER 9
E X E C U T I O N P LA N C A C H E A N A LY S I S
Table 9-1. ouo*`i[ata_[_]_da`[lh]jo
Column Name
Description
nab_kqjpo
Represents the number of other objects in the cache referencing this plan.
qoa_kqjpo
The number of times this object has been used since it was added to the cache.
oeva[ej[^upao
The size of the plan stored in the cache.
_]_dak^fpula
7
>Ì type of plan this is. There are several, but of particular interest are these: Compiled plan: A completed execution plan Compiled plan stub: A marker used for ad hoc queries (you can find more `iÌ>ÃÊÊÌ
iʺ`ÊVÊ7À>`»ÊÃiVÌÊvÊÌ
ÃÊV
>«ÌiÀ® *>ÀÃiÊÌÀii\ÊÊ«>ÊÃÌÀi`ÊvÀÊ>VViÃÃ}Ê>ÊÛiÜ
K^fpula
The type of object that generated the plan. Again, there are several but these are of particular interest: *ÀV *Ài«>Ài` Ad hoc 6iÜ
Lh]j[d]j`ha
The identifier for this plan in memory. It is used to retrieve query text and execution plans.
1Ã}ÊÌ
iÊ 6Êouo*`i[ata_[_]_da`[lh]jo all by itself gets you only part of the informaÌ°Ê/
iÊiÝÌÊÌÜÊ«>ÀÌÃÊV>ÊLiÊÕÃÌÊ>ÃÊ«ÀÌ>Ì°Ê1Ã}ÊÌ
iÊ`Þ>VÊ>>}iiÌÊvÕVÌÊ ouo*`i[ata_[mqanu[lh]j$lh]j[d]j`ha% in combination with ouo*`i[ata_[_]_da`[lh]jo will also bring back the 8ÊiÝiVÕÌÊ«>ÊÌÃivÊÃÊÌ
>ÌÊÞÕÊV>Ê`ë>ÞÊÌÊ>`ÊÜÀÊÜÌ
ÊÌ°ÊvÊÞÕÊ then bring in ouo*`i[ata_[omh[patp$lh]j[d]j`ha%, you can also retrieve the original query ÌiÝÌ°Ê/
ÃÊ>ÞÊÌÊÃiiÊÕÃivÕÊÜ
iÊÞÕ½ÀiÊÀÕ}ÊÜʵÕiÀiÃÊvÀÊÌ
iÊiÝ>«iÃÊ
iÀi]ÊLÕÌÊ when you go to your production system and begin to pull in execution plans from the cache, it might be handy to have the original query. To get detailed performance metrics about the cached plan, you can use ouo*`i[ata_[mqanu[op]po to return that data. Among other pieces vÊ`>Ì>]ÊÌ
iʵÕiÀÞÊ
>Ã
Ê>`ʵÕiÀÞÊ«>Ê
>Ã
Ê>ÀiÊÃÌÀi`ÊÊÌ
ÃÊ ° ÊÌ
iÊvÜ}ÊÃiVÌÃ]ʽÊiÝ«ÀiÊ
ÜÊÌ
iÊ«>ÊV>V
iÊÜÀÃÊÜÌ
Ê>VÌÕ>ʵÕiÀiÃÊÕÃ}Ê ouo*`i[ata_[_]_da`[lh]jo andÊÌ
iÊÌ
iÀÊ 6ÃÊ>`Ê Ã°
Execution Plan Reuse 7
iÊ>ʵÕiÀÞÊÃÊÃÕLÌÌi`]Ê-+Ê-iÀÛiÀÊV
iVÃ the procedure cache for a matching execution «>°ÊvÊiÊÃÊÌÊvÕ`]ÊÌ
iÊ-+Ê-iÀÛiÀÊ«iÀvÀÃÊÌ
iʵÕiÀÞÊV«>ÌÊ>`Ê«Ìâ>ÌÊÌÊ }iiÀ>ÌiÊ>ÊiÜÊiÝiVÕÌÊ«>°ÊÜiÛiÀ]ÊvÊÌ
iÊ«>ÊiÝÃÌÃÊÊÌ
iÊ«ÀVi`ÕÀiÊV>V
i]ÊÌÊÃÊÀiÕÃi`Ê with the private execution context. This saves theÊ *1ÊVÞViÃÊÌ
>ÌÊÌ
iÀÜÃiÊÜÕ`Ê
>ÛiÊLii spent on the plan generation. +ÕiÀiÃÊ>ÀiÊÃÕLÌÌi`ÊÌÊ-+Ê-iÀÛiÀÊÜÌ
Êfilter criteria to limit the size of the result set. /
iÊÃ>iʵÕiÀiÃÊ>ÀiÊvÌiÊÀiÃÕLÌÌi`ÊÜÌ
Ê`vviÀiÌÊÛ>ÕiÃÊvÀÊÌ
iÊvÌiÀÊVÀÌiÀ>°ÊÀÊiÝ>ple, consider the following query:
253
254
C HAPTER 9
EXEC U TION P L A N C A C HE A NA L YS IS
OAHA?Pokd*O]haoKn`anJqi^an (okd*Kn`an@]pa (ok`*Kn`anMpu (ok`*HejaPkp]h BNKIO]hao*O]haoKn`anDa]`an=Ookd FKEJO]hao*O]haoKn`an@ap]eh=Ook` KJokd*O]haoKn`anE@9ok`*O]haoKn`anE@ SDANAokd*?qopkianE@9.525, =J@ok`*lnk`q_pe`93-7
iÊÌ
ÃʵÕiÀÞÊÃÊÃÕLÌÌi`]ÊÌ
iÊ«ÌâiÀÊVÀi>ÌiÃÊ>ÊiÝiVÕÌÊ«>Ê>`ÊÃ>ÛiÃÊÌÊÊÌ
iÊ procedure cache to reuse in the future. If this query is resubmitted with a different filter criterion value—for example, okd*?qopkianE@9.51,,—it will be beneficial to reuse the existing execution plan for the previously supplied filter criterion value. But whether the execution plan created for one filter criterion value can be reused for another filter criterion value `i«i`ÃÊÊ
ÜÊÌ
iʵÕiÀÞÊÃÊÃÕLÌÌi`ÊÌÊ-+Ê-iÀÛiÀ° /
iʵÕiÀiÃÊÀÊÜÀ>`®ÊÃÕLÌÌi`ÊÌÊ-+Ê-iÀÛiÀÊV>ÊLiÊLÀ>`ÞÊV>ÃÃvi`ÊÕ`iÀÊÌÜÊ categories that determine whether the execution plan will be reusable as the value of the variable parts of the query changes: Ê
UÊ `Ê
V
Ê
UÊ *Ài«>Ài`
Tip To test the output of ouo*`i[ata_[_]_da`[lh]jo for this chapter, it will be necessary to remove the plans from cache on occasion by executing @>??BNAALNK??=?DA. Do not run this on your production server since flushing the cache will require all execution plans to be rebuilt as they are executed, placing a serious strain on your production system for no good reason.
Ad Hoc Workload +ÕiÀiÃÊV>ÊLiÊÃÕLÌÌi`ÊÌÊ-+Ê-iÀÛiÀÊÜÌ
ÕÌÊiÝ«VÌÞÊÃ>Ì}ÊÌ
iÊÛ>À>LiÃÊvÀÊÌ
iÊ query. These types of queries executed without explicitly converting the variable parts of the query into parameters are referred to as ad hoc workloads ÀʵÕiÀiî°ÊÀÊiÝ>«i]ÊVÃ`iÀÊ this query (]`dk_*omh in the download): OAHA?Pokd*O]haoKn`anJqi^an (okd*Kn`an@]pa (ok`*Kn`anMpu (ok`*HejaPkp]h BNKIO]hao*O]haoKn`anDa]`an=Ookd FKEJO]hao*O]haoKn`an@ap]eh=Ook` KJokd*O]haoKn`anE@9ok`*O]haoKn`anE@ SDANAokd*?qopkianE@9.525, =J@ok`*lnk`q_pe`93--
CHAPTER 9
E X E C U T I O N P LA N C A C H E A N A LY S I S
If the query is submitted as is, without explicitly converting either of the variable values to a parameter (that can be supplied to the query when executed), then the query is an ad hoc query. In this query, the filter criterion value is embedded in the query itself and is not explicitly parameterized to isolate it from the query. This means that you cannot reuse the execution «>ÊvÀÊÌ
ÃʵÕiÀÞÊÕiÃÃÊÞÕÊÕÃiÊÌ
iÊÃ>iÊÛ>À>Li°ÊÜiÛiÀ]ÊÌ
iÊÛ>À>LiÊ«>ÀÌÃÊvÊÌ
iʵÕiÀiÃÊ can be explicitly parameterized in three different ways that are jointly categorized as a prepared workload.
Prepared Workload Prepared workloads (or queries) explicitly parameterize the variable parts of the query so that Ì
iʵÕiÀÞÊ«>ÊýÌÊÌi`ÊÌÊÌ
iÊÛ>ÕiÊvÊÌ
iÊÛ>À>LiÊ«>ÀÌðÊÊ-+Ê-iÀÛiÀ]ʵÕiÀiÃÊV>ÊLiÊÃÕLmitted as prepared workloads using the following three methods: Ê
UÊ Stored procedures: AllowsÊÃ>Û}Ê>ÊViVÌÊvÊ-+ÊÃÌ>ÌiiÌÃÊÌ
>ÌÊV>Ê>VVi«ÌÊ>`Ê return user-supplied parameters
Ê
UÊ ol[ata_qpaomh: AllowsÊiÝiVÕÌ}Ê>Ê-+ÊÃÌ>ÌiiÌÊÀÊ>Ê-+ÊL>ÌV
ÊÌ
>ÌÊ>ÞÊVÌ>Ê ÕÃiÀÃÕ««i`Ê«>À>iÌiÀÃ]ÊÜÌ
ÕÌÊÃ>Û}ÊÌ
iÊ-+ÊÃÌ>ÌiiÌÊÀÊL>ÌV
Ê
UÊ Prepare/execute model: AllowsÊ>Ê-+ÊViÌÊÌÊÀiµÕiÃÌÊÌ
iÊ}iiÀ>ÌÊvÊ>ʵÕiÀÞÊ«>Ê that can be reused during subsequent executions of the query with different parameter Û>ÕiÃ]ÊÜÌ
ÕÌÊÃ>Û}ÊÌ
iÊ-+ÊÃÌ>ÌiiÌîÊÊ-+Ê-iÀÛiÀ
ÀÊiÝ>«i]ÊÌ
iÊOAHA?P statement shown previously can be explicitly parameterized using a stored procedure as follows (ol>]oe_O]haoEjbk*omh in the download): EB$OAHA?PK>FA?P[E@$#ol>]oe_O]haoEjbk#%%EOJKPJQHH @NKLLNK?`^k*ol>]oe_O]haoEjbk CK ?NA=PALNK?`^k*ol>]oe_O]haoEjbk