SQL Server: non-clustered index scans you must know

Original source:  First-line code farmers  

http://www.open-open.com/lib/view/open1427267261293.html

Non-clustered index, this is a thing that everyone is very familiar with, sometimes we write SQL very complicated due to business reasons, need to join a lot of tables, and then burst into tears. . . At this time, there will be a DBA or senior developer to show you this tedious SQL, through an analysis of the execution plan. . . Maybe you can see the table scan that you shouldn't have. . . Root of all evil. . . Then add the non-clustered index to the key fields. . . Only to find that the speed is faster than Aston Martin. . . Then a question arises, why non-clustered indexes can be so fast. . . How did you do it? ? ? Is it very curious? ? ?

In this article, let's unravel the mystery.

One: phenomenon

Let us first glance at the true content of non-clustered indexes and see what it looks like in the execution plan. . . I have a product table here, filled with more than 8w data, and then established a non-clustered index on the Name column, just like the following figure:

SQL Server Journey (4): Non-clustered index scans you must know

I saw two interesting things from the picture above, one is the "index search [nonclustered]" that I want to see, this is very familiar to everyone, and this is what I want to say, and then we also saw a "RID search", At first glance, what a dick thing this is. . . What is the relationship between the non-clustered index and it? ? ?

Two: what is RID

Through the previous articles, I think everyone knows how to find the records in the data page? The secret is determined by the offset in the slot, then the question is coming, if it rises to the data page level, I only need (pageID: slotID) to find the record, right? If I raise the file level, do I just need to know (fileID: pageID: slotID) to find the record in the data page? In fact, the RID here is to find the table record through (fileID: pageID: slotID) at the height of the file. . . Both RID = RowID = (fileID: pageID: slotID), if you must see

To be honest, l really provides such a function (sys.fn_PhysLocFormatter (%% physloc %%)) in sq, we look at the following figure:SQL Server Journey (4): Non-clustered index scans you must know

Looking at the above picture, I am very excited, at a glance, for example, the record productID = 18088, but it is in file 1, 34941 data page, slot 0, productID = 18089

It's in slot 1, okay, when you know what RID is, I think you are not far from a thorough understanding of non-clustered indexes. . .

Three: non-clustered index

One thing we must know is that non-clustered indexes can speed up the search. If the speed of the table scan is the same, then the purpose of the index is lost. Since it can be accelerated, it is because it is played at the bottom layer like the clustered index. A B-tree is created, first we insert some sample data.

DROP TABLE dbo.Person

CREATE TABLE Person(ID INT IDENTITY,NAME CHAR(900))
CREATE  INDEX idx_Person_Name ON dbo.Person(Name)

DECLARE @ch AS INT=97

WHILE @ch<=122
BEGIN
    INSERT INTO dbo.Person VALUES(REPLICATE(CHAR(@ch),5))
    SET @ch=@ch+1
END

In the above sql, I intentionally set the Name column to 900 chars, which is also the upper limit of the index. In this case, my DBCC can export many data pages and index pages.

SQL Server Journey (4): Non-clustered index scans you must know

As you can see, when I dbcc ind, I found that there are already 4 data pages and 5 index pages in the Person table. Among them, the data page 151 is the table tracking page, and the number 174 is the index tracking page, which means that when I After creating the index, the engine assigned us a special index page to store the Name index we created. Then the next step is to take a look at what is stored in these indexes. This is what I am very concerned about. Index page.

DBCC PAGE(Ctrip,1,173,1)

SQL Server Journey (4): Non-clustered index scans you must knowSQL Server Journey (4): Non-clustered index scans you must know

Slot 0, Offset 0x60, Length 912, DumpStyle BYTE

Record Type = INDEX_RECORD           Record Attributes =  NULL_BITMAP     Record Size = 912

Memory Dump @0x000000000EF1C060

0000000000000000: 16616161 61612020 20202020 20202020 † .aaaaa           
0000000000000010:   20202020 20202020 20202020 20202020 †                 
0000000000000020:   20202020 20202020 20202020 20202020 †                 
0000000000000030:   20202020 20202020 20202020 20202020 †                 
0000000000000040:   20202020 20202020 20202020 20202020 †                 
0000000000000050:   20202020 20202020 20202020 20202020 †                 
0000000000000060:   20202020 20202020 20202020 20202020 †                 
0000000000000070:   20202020 20202020 20202020 20202020 †                 
0000000000000080:   20202020 20202020 20202020 20202020 †                 
0000000000000090:   20202020 20202020 20202020 20202020 †                 
00000000000000A0:   20202020 20202020 20202020 20202020 †                 
00000000000000B0:   20202020 20202020 20202020 20202020 †                 
00000000000000C0:   20202020 20202020 20202020 20202020 †                 
00000000000000D0:   20202020 20202020 20202020 20202020 †                 
00000000000000E0:   20202020 20202020 20202020 20202020 †                 
00000000000000F0:   20202020 20202020 20202020 20202020 †                 
0000000000000100:   20202020 20202020 20202020 20202020 †                 
0000000000000110:   20202020 20202020 20202020 20202020 †                 
0000000000000120:   20202020 20202020 20202020 20202020 †                 
0000000000000130:   20202020 20202020 20202020 20202020 †                 
0000000000000140:   20202020 20202020 20202020 20202020 †                 
0000000000000150:   20202020 20202020 20202020 20202020 †                 
0000000000000160:   20202020 20202020 20202020 20202020 †                 
0000000000000170:   20202020 20202020 20202020 20202020 †                 
0000000000000180:   20202020 20202020 20202020 20202020 †                 
0000000000000190:   20202020 20202020 20202020 20202020 †                 
00000000000001A0:   20202020 20202020 20202020 20202020 †                 
00000000000001B0:   20202020 20202020 20202020 20202020 †                 
00000000000001C0:   20202020 20202020 20202020 20202020 †                 
00000000000001D0:   20202020 20202020 20202020 20202020 †                 
00000000000001E0:   20202020 20202020 20202020 20202020 †                 
00000000000001F0:   20202020 20202020 20202020 20202020 †                 
0000000000000200:   20202020 20202020 20202020 20202020 †                 
0000000000000210:   20202020 20202020 20202020 20202020 †                 
0000000000000220:   20202020 20202020 20202020 20202020 †                 
0000000000000230:   20202020 20202020 20202020 20202020 †                 
0000000000000240:   20202020 20202020 20202020 20202020 †                 
0000000000000250:   20202020 20202020 20202020 20202020 †                 
0000000000000260:   20202020 20202020 20202020 20202020 †                 
0000000000000270:   20202020 20202020 20202020 20202020 †                 
0000000000000280:   20202020 20202020 20202020 20202020 †                 
0000000000000290:   20202020 20202020 20202020 20202020 †                 
00000000000002A0:   20202020 20202020 20202020 20202020 †                 
00000000000002B0:   20202020 20202020 20202020 20202020 †                 
00000000000002C0:   20202020 20202020 20202020 20202020 †                 
00000000000002D0:   20202020 20202020 20202020 20202020 †                 
00000000000002E0:   20202020 20202020 20202020 20202020 †                 
00000000000002F0:   20202020 20202020 20202020 20202020 †                 
0000000000000300:   20202020 20202020 20202020 20202020 †                 
0000000000000310:   20202020 20202020 20202020 20202020 †                 
0000000000000320:   20202020 20202020 20202020 20202020 †                 
0000000000000330:   20202020 20202020 20202020 20202020 †                 
0000000000000340:   20202020 20202020 20202020 20202020 †                 
0000000000000350:   20202020 20202020 20202020 20202020 †                 
0000000000000360:   20202020 20202020 20202020 20202020 †                 
0000000000000370:   20202020 20202020 20202020 20202020 †                 
0000000000000380:   20202020 20940000 00010000 00020000 †     ...........
Row - Offset                         
7 (0x7) - 6480 (0x1950)              
6 (0x6) - 5568 (0x15c0)              
5 (0x5) - 4656 (0x1230)              
4 (0x4) - 3744 (0xea0)               
3 (0x3) - 2832 (0xb10)               
2 (0x2) - 1920 (0x780)               
1 (0x1) - 1008 (0x3f0)               
0 (0x0) - 96 (0x60)

从上面至少可以发现三个有趣的现象:

<1>:173号索引页中slot0和slot1槽位指向记录的内容已经有序了,比如:aaaaa,bbbbb。。。。这样。。。。原来非聚集索引也是有序呀。。。

<2>:6161616161就是16进制的aaaaa。

9400000001000000 :这几个数字非常重要,因为是16进制表示,所以2位16进制表示一个字节,所以可以这么解释,前面4个字节表示

pageID,中间2个字节表示fileID,后面2个字节表示slot,看到这里你是不是想起了RID。。。因为RID就是这三样的组合。。。原来非聚集索引的记录存放的就是“key+RowID”呀。。。。

<3>:通过最后的槽位列表,可以得知173号索引页上存放着8条索引记录。

好了,看完了叶子节点,我们再看分支节点,也就是IndexLevel=1的那条索引数据页,也就是78号。ok,dbcc看看吧。

SQL Server Journey (4): Non-clustered index scans you must know

当看到这个列表的时候,不知道你脑子里面是不是有一幅图出来了,就像上一篇看到聚集索引一样,因为它的结构和聚集索引非常像,只不过非聚集索引这里多了一个RID而已。。。最后我也把图贡献一下。

SQL Server Journey (4): Non-clustered index scans you must know

To sum up: when walking a non-clustered index, for example, if your condition is where name = 'jjjjj', its logic is like this, according to the range of the key of the 78th index data page, then go to the 79th index by rowid The data page, and then successfully found jjjjj in the 79th index data page, at this time, you can take out the rowid of jjjjj to directly locate the record in the table data page, and finally output. . . . . This is why the RID search appears at the beginning of the blog. . .

Published 22 original articles · praised 7 · 100,000+ views

Guess you like

Origin blog.csdn.net/qyx0714/article/details/78026718