Query performance optimization for SQL Server: bookmarkcheck

Source: Internet
Author: User

SmallProgramGrowth of SQL Server Cognition

1. I don't have to graduate or work for a long time. I only know the relationship between SQL and SQL Server Oracle and MySQL. I usually think that SQL is SQL Server.

2. after several years of work, I have also written a lot of SQL statements, but I don't know what the index is. I only know that the database has an index, so I cannot tell the clustered index and non-clustered index, I only know that it is faster to create an index after the query is slow. In the end, the index has been created a lot and the query is indeed fast. I accidentally asked: why is the index type created? Answer :...

3. Finally, I was excited and started to work hard. I finally found out that the original index was divided into clustered index and non-clustered index, and burst into tears. I finally knew what the index was.

4. Further study: clustered indexes are physical indexes, non-clustered indexes are logical indexes, and clustered indexes are the storage sequence of data. Non-clustered indexes are logical indexes that are used for clustered indexes.

5. then I learned how to view the execution plan. I finally got a rough idea about the query process through the query plan. I also learned that clustered index scan and table scan did not use indexes, when you see the clustering index and index search, you can see the RID and key search secretly. You can see that the key search is definitely a keyword search. If you use the index, the efficiency must be high, so every time I write an SQL statement, I have to watch its execution plan, and I don't want to scan tables. I only need to search indexes and keys.

6. confident and carefree little day, suddenly lost one day, why did I clearly set up an index on this field, and her sister's old show me clustered index scanning, is the query optimizer having a fever? In actual execution, I find that the actual execution plan is still a table scan, which is totally confusing. Maybe it is a problem that the query optimizer shows.

7. further in-depth study, I finally found that the database is too deep to understand. I think about the evolution from ape to man, now it's a new kind of programme, and it's a great programmer.

En, run the question and enter our topic: Search for database bookmarks

Search for bookmarks

The word booksearch may be unfamiliar to many developers. Many people have encountered this word, but they have not paid enough attention to it, so they have always ignored its existence.

Definition: When the query optimizer uses a non-clustered index for search, if the selected column or the column in the query condition is partially included in the used non-clustered index and clustered index, you need a lookup to retrieve other fields to meet the request. For a table with clustered indexes, it is a key lookup, and for a heap table it is an RID lookup ), this kind of search is bookmark lookup ). To put it simply, when the SQL query conditions you use and the columns returned by the SELECT statement are not completely included in the index column, a bookmarked query will occur.

Importance of bookmarks

1. bookmarked Search Condition: only when a non-clustered index is used for data search will a bookmarked search be generated. Clustered index search, clustered index scan, and table scan will not perform bookmarked search.

2. bookmarks' search frequency: The bookmarks' search frequency is very high. You can even say that most queries have bookmarks' searches. We know that only one clustered index can be created for a table, therefore, more queries use non-clustered indexes. Non-clustered indexes cannot overwrite all query columns. Therefore, bookmarks are generated frequently.

3. The impact of bookmarked search: one of the main causes of index failure. The bookmarked query reads data from the table based on the row Locator of the index. In addition to the logic reading of the index page, it also needs to read the logic of the data page, if a large amount of data is returned from the query results, resulting in a large number of logical reads or indexes being invalid, this is why we sometimes create an index on the query column when viewing the query plan, the reason why the query optimizer still uses table scanning.

4. How to remove bookmarks:
1. using clustered index search, the leaf node of the clustered index is the data row itself, so there is no bookmarked search.
2. Clustered index scanning and table scanning. To put it bluntly, no index is built and the whole table is scanned directly. It is certainly not a bookmarked query, but is it efficient...
3. key columns that use non-clustered indexes contain all the queried or returned columns. This is unreliable. the maximum number of key columns for non-clustered indexes is 16, and the maximum index key size is 900 bytes, even if you have the courage to create an index on all 16 columns, what if the number of columns in the table exceeds 16, and the length of the index Column cannot exceed 900 bytes, therefore, it is impossible for a non-clustered index to contain all columns. The more columns involved in the index, the higher the index maintenance overhead.
4. use include. Well, this is a good thing. indexes can only contain 16 columns and cannot exceed 900 bytes. Include is not restricted. You can use a maximum of 1023 columns, and there is no limit on the length. You can include nvarchar (max) columns as you like. Of course, do not consider the text stream.
5. I don't know anything about other things. I guess it should be, maybe, or maybe I have it. If any of you know me, let me know.

It may be a little abstract. Let's take a look at the specific example.

Generally, our databases will be built with clustered indexes (generally, you prefer to use an auto-incrementing ID column as the primary key when creating a table, this primary key SQL Server creates a clustered index by default.) Therefore, we assume that a clustered index has been created on the table, regardless of the heap table (that is, a table without a clustered index)

1. Create a table users, insert some sample data, and create a clustered index pk_userid. The non-clustered index ix_username

 
 
 
-- Too lazy fat rabbit http ://Http://www.cnblogs.com/lzrabbit/
--  Create a table users  Create   Table  Users (userid  Int   Identity  , Username  Nvarchar ( 50  ), Age  Int  , Gender  Bit  , Createtime  Datetime )  --  Create a clustered index pk_userid In the userid Column  Create   Unique   Clustered   Index Pk_userid On  Users (userid)  --  Create a non-clustered index ix_username in username  Create   Index Ix_username On  Users (username)  -- Insert sample data  Insert   Into  Users (username, age, gender, createtime)  Select N '  Bob  ' , 20 , 1 , '  2012-5-1  '  Union   All Select N '  Jack  ' , 23 , 0 , '  2012-5-2  '  Union   All  Select N '  Robert  ' ,28 , 1 , '  2012-5-3  '  Union   All  Select N '  Janet  ' , 40 , 0 , '   '  Union   All  Select N '  Michael  ' , 22 , 1 , '  2012-5-2  '  Union   All  Select N ' Laura  ' , 16 , 1 , '  2012-5-1  '  Union   All  Select N '  Anne  ' , 36 , 1 ,'    ' 

 

2. Execute the following query and view the query plan. You can see that the first SQL statement performs a clustered index scan, and the second SQL statement does not use a clustered index search.

 
 
 
-- Too lazy fat rabbit http ://Http://www.cnblogs.com/lzrabbit/
 
Select * FromUsersSelect * FromUsersWhereUserid=4

3. Compare the following query SQL statements, observe the query plan, and find out why bookmarks occur.

-- Query 1:  Use the index ix_username to select the userid and username columns. The query condition column is username.  Select Userid, username From Users With ( Index (Ix_username )) Where Username =  '  Robert  '  -- Query 2:  Use the index ix_username, select the column userid, username, age, and query condition column username.  Select Userid, username, age From Users With ( Index (Ix_username )) Where Username =  '  Robert  '  -- Query 3:  Use the index ix_username to select the column userid and username. The query condition column is username and age.  Select Userid, username From Users With (Index (Ix_username )) Where Username =  '  Robert  '   And Age =  28  -- Query 4:  Use the index ix_username to select all columns and the query condition column is username.  Select   *   From Users With (Index (Ix_username )) Where Username =  '  Robert  ' 

Analysis:

Query 1: The selected column userid is the key column of the clustered index pk_userid, and username is the key column of the index ix_username. The query condition column is username. Because the index ix_username contains all the columns used for the query, therefore, you only need to scan the index to return the query results. You do not need to go to the data page to obtain data, so no bookmarked search will occur.

Query 2: select a column age that is not included in the clustered index pk_userid and ix_username. Therefore, you need to perform additional bookmarked search.

Query 3: The age column of the query condition is not included in the pk_userid and ix_username of the clustered index. Therefore, you need to perform additional bookmarked search.

Query 4: contains all columns. The age, gender, and createtime columns are not included in the clustered index pk_userid and ix_username. Therefore, you need to bookmark them to find the data.

The query overhead is basically the same for the columns used in the query, regardless of whether one or more columns are not covered by the index. Each record only needs one bookmark query overhead, it is not said that query 3 has only one age column, and query 4 has three age, gender, and createtime columns not covered by the index, resulting in additional overhead.

How does bookmarks look up?

Like many people who see the binary tree index structure they have drawn, they have a big head and are confused. So here we take the table users as an example to describe the clustered index (pk_userid) and non-clustered index (ix_username) can be simply expressed

First, let's look at the clustered index pk_userid. For clustered indexes, the data row is its leaf node, therefore, after a specific key value is found during the clustered index search, you can directly obtain all required data from the leaf node without additional logical reads, for example, select * from users where userid = 2. Find the value of userid 2 in the index pk_userid Based on Value 2, and then go to the leaf node to get the required data, and then return the query result.

Next, let's look at the non-clustered index ix_username. As we have mentioned above, the columns covered by the non-clustered index include the key columns of the non-clustered index + the key columns of the clustered index, for ix_username, the key column username is stored in the index's Binary Tree node, and the clustered index column is included in its leaf node, which forms a pair of columns (username, userid) for query 1 (select userid, username from users with (index (ix_username) where username = 'Robert '), only the username and userid columns are used for query, in this way, you only need to scan the index ix_username to get all the data and then return the result. For query 2 and query 3, the age column is required, and the index ix_username does not contain the age column, in this case, you need a bookmark lookup to locate the specific data row based on the rowid in the leaf node to obtain the age column value. For the example query, locate the row where Robert is located based on the index ix_username, then obtain the age value from the data table based on rowid = 3 and complete the query. For query 4, more columns (age, gender, createtime) are required ), similarly, we locate row rowid = 3 where Robert is located, get the age, gender, and createtime data from the data table at one time, and then return the data. In this way, the bookmarked search is formed (shownKey searchOrRid Lookup)

Effect of bookmarked search on query performance

 

-- This is the index we are currently using
Create IndexIx_usernameOnUsers (username)

 

Open IO statistics and execute the following two queries

--Set statistics Io onSelect * FromUsersWhereUsernameLike 'Ja %'Select * FromUsersWith(Index(Ix_username ))WhereUsernameLike 'Ja %'

 

 

Two data records are returned for both queries. The clustered index scan only performs two logical reads and the ix_username index is used for six logical reads.

The data volume in our example is small, so it is not obvious, but we also see that we have created an index ix_username In the username column, by default, the query optimizer does not use our indexes. Instead, it chooses table scanning and obtains the required data only after two logical reads, after we use the index prompt to force the query optimizer to use the index ix_username, two data records are also returned, and the number of logical reads reaches 6 times, the query plan uses ix_username, and a bookmarked query occurs. This overhead is mainly caused by bookmarked search. As the returned data volume increases, the logical read caused by bookmarked search will go up in a straight line. The result is that the query overhead is much larger than the full table scan, leading to index failure.

Use overwriting indexes to avoid searching bookmarks

Covering indexes refers to columns (key columns + including columns) + clustered indexes on non-clustered indexes that contain all columns used in the query, for the index ix_username, the index overwrite column is (username, userid ). If only the columns covered by the index are used in the query, you only need to scan the index to complete the query. If the columns not covered by the index are used, you need to bookmark the query to obtain the data, when a large number of such queries occur, the index becomes invalid, resulting in Table scanning. Because the query optimizer is an overhead-based optimizer, when it finds that the bookmarked search overhead caused by the use of non-clustered indexes is larger than the overhead of table scanning, it will discard the use of indexes and turn to table scanning.

1. re-create the index ix_username on the username and age columns, and change the overwrite column to (username, age, userid) for the index ix_username. Execute the preceding query SQL statement again to find that the query plan has changed.

 
 
-- Too lazy fat rabbit http ://Http://www.cnblogs.com/lzrabbit/
 
Drop IndexIx_usernameOnUsersCreate IndexIx_usernameOnUsers (username, age)

We can see that the bookmarked search for query 2 and query 3 has disappeared, because the index ix_username contains all columns (userid, username, age) used in the query ), query 4 because we choose to return all the columns. Our index does not contain the Gender and createtime columns, we still perform the bookmarkdonetsearch.

The index ix_username structure is as follows:


It can be seen that for query 2 and query 3, only the index ix_username can be used to obtain the required column username, age, and userid. For query 4, the indexes are not completely overwritten, and you still need to perform a bookmark search.

2. modify our index ix_username and use include to include non-key columns (key columns are the columns on the index, and non-key columns are the columns outside the index, for include, columns are stored on non-clustered index leaf nodes, and clustered index columns are also placed on non-clustered index leaf nodes)

-- Too lazy fat rabbit http ://Http://www.cnblogs.com/lzrabbit/
Drop IndexIx_usernameOnUsersCreate IndexIx_usernameOnUsers (username, age) include (gender, createtime)

We can see that after modifying the index to include contains gender and createtime, the index ix_username overwrites all columns of users in the data table, at this time, there is no doubt that the query 2 and query 3 do not see the bookmarksearch, and the query 4 bookmarks also disappear.

The index ix_username structure is as follows:

The index ix_username has reached the full coverage of the users table. For our query 2, query 3, and query 4, the query can only be completed by indexing ix_username, without the need to perform a bookmarked search.

Now let's take a look at the overhead and query plan of these two queries. We can see that we do not need to prompt the index. The query optimizer has automatically selected our index, logical reads also dropped to 2 times

Select * FromUsersWhereUsernameLike 'Ja %'Select * FromUsersWith(Index(Ix_username ))WhereUsernameLike 'Ja %'

For more information about include, see the include charm in SQL Server indexes (indexes with contained columns)

It is explained that the bookmarked search has a great impact on the query performance and is basically inevitable. This does not mean that the bookmarked search is a great beast. We didn't know what a bookmarked search is, the query performance is the same, right. The bookmarked query also explains why we do not recommend using select * when writing SQL statements. It also explains why our indexes sometimes fail and can be used as an aspect to Optimize Query performance, when designing tables and indexes, we try to avoid the negative impact of bookmarked search. For example, if non-clustered indexes are used as highly selective columns as possible, we will return as few rows as possible, clustering indexes should be used whenever possible for querying large volumes of data.

This article demonstrates how to use tables with only a few pieces of data, and index prompts are used in queries to use indexes. Please do not use index prompts during actual development, in most cases, the query optimizer generates an execution plan that is optimal (the optimum does not mean the minimum overhead. If the overhead is small enough, the execution plan is considered optimal, the rowid used in the index structure is only used to demonstrate the fiction. We only need to think that it is a identifier of the Data row.

This article aims to let us know the meaning of the bookmarked search and to have a clear understanding of the causes of index failure and to better understand the query plan.

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.