接下来的文章是记录自己曾经的盲点,同时也透漏了自己的发展历程(可能发展也算不上,只能说是瞎混)。当然,一些盲点也在工作和探究过程中慢慢有些眉目,现在也愿意发扬博客园的奉献精神,拿出来和大家分享一下。
在刚开始工作时候,总以自己有个“高科技”的工作,而感到特别神气,经常在其他人面前说一些让别人觉得高大上的措辞,到后来会在学妹面前炫耀的讲一下SQL Server的执行计划,这个时候别说执行计划了,就是SQL的优化对我来说还是个新鲜的事物,总是以自己能正确查出结果而沾沾自喜。然而,当真有不经世事的学妹会问我,执行计划是什么,我的回答:“就是......”,说的什么,我自己都不知道。
开门见山,直接入题
在查看SQL Sever执行计划的过程中,你是否曾经也和我一样,看到索引查找就欣喜若狂,万事大吉了呢,从来不会理会RID查找,键查找为何物,更不会管他们究竟是什么。但是,作为程序猿的我,为了能进化为人,我不得不去搞明白书签(RID,键值)为何物。
书签查找出现的情况:书签查找总是伴随着非聚集索引的查找而出现,但并不是所有的非聚集索引都会引起书签查找。在表以堆(不含聚集索引)的形式存储的时候,书签查找为RID查找,当表以树(含聚集索引)的形式存储时,书签查找为键查找。
书签查找为何物:当SQL Server查询优化器使用非聚集索引查找时,当该索引没有完全覆盖查询列和返回列时,就会引起书签查找。
RID是什么:当表以堆的形式存储的时候,数据行的物理位置是不会(严谨的说是很少)变动的,正是因为很少变动,所以,可以通过一个地址来唯一确定一行,又因为数据库的每一行一定存储在某一页上,而某一页又一定在某一数据库文件中,所以就可以通过文件号:页号:行号(如:1:22:14,指的就是第1个数据库文件的第22页中的第14行)。
键值书签是什么:当表以树的形式存储时,也就是说,当表含有聚集索引时,表中的数据行是会移动的,所以,RID是不能一直定位在同一行的。这个时候就需要通过聚集索引键来定位一行,这个聚集索引键就是键值书签。
下面我们可以通过例子来认识下书签查找,以及它们对性能的影响:
我们还用 SQL Server执行计划那些事儿(1)——哈希、合并、嵌套联接的选择中的例子(删除所有索引)。
1.我们先建立非聚集索引
create nonclustered index non_index_headers_buyDate on headers(BuyDate)
然后执行
select ID,BuyDate from Headers where BuyDate>‘2008-12-28‘
注:该例子仅仅为了说明情况,不含实际意义。
分析:在不含聚集索引的表中,因为这个非聚集索引中没有覆盖ID列,所以要通过RID进行查找。
2.建立聚集索引
create clustered index index_headers_ID on headers(ID)
然后执行上面查询语句
select ID,BuyDate from Headers where BuyDate>‘2008-12-28‘
分析:是不是觉得很奇怪?不奇怪,因为非聚集索引的覆盖列包含,非聚集索引的键列、非聚集索引包含的列以及聚集索引的键列。因此,ID和BuyDate为非聚集索引覆盖列,所以仅通过索引页就可以查出结果,没必要再通过任何书签查找。
3.执行下面SQL语句
select ID,BuyDate,Discount from Headers where BuyDate>‘2008-12-28‘
分析:因为此时的表中有聚集索引,又因为非聚集索引没有覆盖Discount列,所以会引起键查找。
4.书签查找对查询性能的影响到底有多大呢 ?
创建非聚集索引,让其覆盖Discount列
create nonclustered index non_index_headers_buyDate_include on headers(BuyDate) include(Discount)
然后执行例子3中的查询语句
select ID,BuyDate,Discount from Headers where BuyDate>‘2008-12-28‘
分析:因为此时非聚集索引已经覆盖了,查询列和结果列,故没有书签查找。另外,此时逻辑读取次数已经明显比之前小了很多,可见书签查找是很耗资源的,甚至会使索引失效,从而引起全表扫描。
总结
1.只有在用到非聚集索引查找时,并且查找列和返回列没有完全被索引覆盖时,才会引起书签查找。
2.书签查找是非常耗费资源的,甚至会使索引失效。
3.可以通过让非聚集索引覆盖查询列和返回列来消除书签查找。