视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
SqlServer中的非聚集索引详细介
2020-11-09 07:03:37 责编:小采
文档


  非聚集索引,这个是大家都非常熟悉的一个东西,有时候我们由于业务原因,sql写的非常复杂,需要join很多张表,然后就泪流满面了。。这时候就有DBA或者资深的开发给你看这个猥琐的sql,通过执行计划一分析。。或许就看出了不该有的表扫描。。万恶之源。。然后给你在关键的字段加上非聚集索引后。。才发现提速比阿斯顿马丁还要快。。那么一个问题来了,为什么非聚集索引能提速这么快。。怎么做到的???是不是非常的好奇???

这篇我们来解开神秘面纱。

 一:现象

      先让我们一睹非聚集索引的真容,看看到底在执行计划看来是个什么玩意。。我这里有个product表,里面灌了8w多数据,然后在Name列上建立

一个非聚集索引,就像下图一样:

  

从上图中看到了两个好玩的东西,一个就是我想看到的“索引查找[nonclustered]”,这个大家很熟悉,也是这篇要说的,然后我们还看到了一个“RID查找”,乍一看这是什么鸡巴玩意。。非聚集索引跟它扯上什么关系了???

二:什么是RID

  通过前面几篇,我想大家都知道了数据页中的记录是如何寻找的?秘密就是通过slot槽位中的偏移量决定的,那问题来了,如果上升到数据页层面,我只需要(pageID:slotID)就可以找到记录了,对不对?那如果我上升了文件层面,那是不是只需要知道(fileID:pageID:slotID)就可以找到数据页中的 记录了?其实这里的RID就是站在文件的高度通过(fileID:pageID:slotID)找到表记录的。。既RID=RowID=(fileID:pageID:slotID),如果你非要眼见实的话,在sq中l还真提供了这么个函数(sys.fn_PhysLocFormatter(%%physloc%%)),我们看下图:

看了上面的图,是不是很兴奋,一目了然,比如productID=18088这条记录,然来是在1号文件,34941号数据页,0号槽位上,productID=180是在1号槽位上,好了,当你知道RID是个什么东西的时候,我想你已经离彻底理解非聚集索引不远啦。。

 三:非聚集索引

  有一点我们肯定知道,就是非聚集索引是可以加速查找的,要是跟表扫描那样的龟速,那也就失去了索引的目的,既然能加速,是因为它和聚集索引一样,在底层都玩起了B树,首先我们插入一些样例数据。

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

上面的sql,我故意在Name列设置为900个char,这也是索引的上限值,这样的话,我DBCC就可以导出很多数据页和索引页了。

可以看到,当我dbcc ind 的时候,发现Person表中已经有4个数据页,5个索引页,其中151号数据页是表跟踪页,174号为索引跟踪页,这也就说明当我建立索引后,引擎给我们分配了专门的索引页来存放我们建立的Name索引,那下一步就是我们来看看这些索引中都存放着什么,这也是我非常关心的,接下来我导出173号索引页。

代码如下:
 DBCC PAGE(Ctrip,1,173,1)

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) - 80 (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号索引页上存放着索引记录。

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

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

总结一下:在走非聚集索引的时候,比如你的条件是where name='jjjjj' 时,它的逻辑是这样的,根据78号索引数据页的key的范围,然后通过rowid走到了79号索引数据页,然后在79号索引数据页中顺利的找到了jjjjj,这时候就可以拿出jjjjj的rowid去表数据页中直接定位记录,最后输出。。。这个也就是博客开头的地方为什么会出现RID的查找。。

您可能感兴趣的文章:

  • mssql 建立索引
  • SQL2000 全文索引完全图解
  • MSSQL 大量数据时,建立索引或添加字段后保存更改提示超时的解决方法
  • 关于重新组织和重新生成索引sp_RefreshIndex的介绍
  • SQL2005CLR函数扩展 - 关于山寨索引
  • MSSQL自动重建出现碎片的索引的方法分享
  • 理解Sql Server中的聚集索引
  • 在SQL SERVER中导致索引查找变成索引扫描的问题分析
  • 详解sqlserver查询表索引
  • SQL2005重新生成索引的的存储过程 sp_rebuild_index
  • 下载本文
    显示全文
    专题