查询优化器内核剖析第八篇:执行引擎之数据访问操作---Seek(上)
查询优化器内核剖析第八篇:执行引擎之数据访问操作---Seek(上)
系列文章索引:
查询优化器内核剖析第二篇:产生候选执行计划&执行计划成本估算
查询优化器内核剖析第三篇:查询的执行与计划的缓存 & Hint提示
查询优化器内核剖析第六篇:谈谈Join的顺序问题,纠正江湖偏方
查询优化器内核剖析第七篇:执行引擎之数据访问操作---Scan
查询优化器内核剖析第八篇:执行引擎之数据访问操作---Seek与Lookup
前言:自从本系列文章开始以后,收到了一些朋友的来信,也有很多的朋友对我说,写内核分析的文章不受欢迎,因为没有多少人会关注这个东西。确实,每每在分析一些底层机制或者内核的时候,不断是书还是博客,很少能够特别“火”的。从自己的经验看来,作为一个技术人员,如果想要走得更远,更高,这些东西是很有必要的!也许大家还记得当我们遇到问题时候,无法下手的囧况,也许还记得,当我们做了一个修改之后,只能给出“可能会搞定”这样不确定的答案。为什么?因为东西掌握的不够深入,全面,导致我们顾此失彼,以至于心虚!
曾经有朋友说:你是搞开发的,需要懂把数据库搞的那么深吗?我曾经也认为:确实没有必要,不是有DBA吗?后来,我发现我错了:第一,没有几个公司有很明确的DBA,很多时候开发人员做着DBA的事情;第二,数据库是一个应用的关键,性能优化,很大的程度上,都需要关注数据库;第三,如果想以后为项目做架构,做设计,不懂数据库,不考虑它的存储架构,设计,维护,性能,甚至是灾难恢复,如何敢说自己会做架构,如何扛起这么大一个责任。朋友们可能就说了:这么多,搞的完吗?看需要,需要啥,就去学习啥,开始的时候,为了应急,可以懂个大概,等时间有了,再深入!
不得不承认我们技术界有些浮躁的气氛,存在着很多的“潜规则论“,”关系论“,”忽悠论“等,但是不管怎么样,作为一个技术人员,要过活下去,要有点竞争力,技术这一关是要扎扎实实过的!我们分析内核,不是单从理论上讲述,而是从实用的角度,我也不想浪费大家的时间去看毫无用处的文章,也不想浪费自己的时间写没有营养的东西。
如果大家认为说的还有点道理,就接着往下看技术的部分;如果不赞同,大家可以选择不看下面的文章,避免浪费大家的 时间!
在上一篇文章中介绍了Scan的操作,因为我每次都只是介绍一小点,所以,朋友们可能阅读起来没有感觉,一是因为这些东西确实不好讲,二是,我希望每次写一点,利于大家消化,等到整个系列文章完整之后,大家就会全面掌握这些知识的每一个细节,自己在啃这些东西的时候,也是一点点的消化的。
说了这么多,我们言归正传,对于Seek,这个操作主要是发生在聚集索引和非聚集索引上面。
为了讲明这个问题,我们依然采用的是数据库AdventureWorks,看看下面的一个例子,看到下面的查询:
我们运行这个查询并且查看它的执行计划,如下图:
很显然,这个查询最后在执行的时候,采用了Seek操作。
下面,我们就来具体的看看,为什么?
首先,我们讲解一下与数据库索引存储相关的知识。
我们知道,聚集索引是按 B 树结构进行组织的,既然是B树组织,那么就有叶子结点和非叶子节点之分。聚集索引B 树的顶端节点称为根节点;聚集索引中的底层节点称为叶节点。在根节点与叶节点之间的任何索引级别统称为中间级。
在聚集索引中,叶节点包含基础表的数据页。根节点和中间级节点包含存有索引行的索引页。每个索引行包含一个键值和一个指针,该指针指向 B 树上的某一中间级页或叶级索引中的某个数据行。每级索引中的页都处于链接在双向链接所在列表中。
可以这么说,聚集索引的叶子结点存储的是按聚集索引顺序排列的数据本身,而中间结点和根节点则在维护索引和其层级。如图:
对于每个聚集索引,在系统表 sys.system_internals_allocation_units 中的 root_page 列指向该聚集索引某个特定分区的顶部。下图就是sys.system_internals_allocation_units的数据:
SQL Server 将从索引中向下移动以查找与某个聚集索引键对应的行。
为了查找键的范围,SQL Server 将在索引中移动以查找该范围的起始键值,然后用向前或向后指针在数据页中进行扫描。
为了查找数据页链的首页,SQL Server 将从索引的根节点沿最左边的指针进行扫描。
看完了上面的介绍之后,大家基本对聚集索引的存储有了大致的理解。
下面我们首先来看看Person.Address表的聚集索引的定义:
大家看到上面的这个图,这个表的聚集索引就是AddressID。当查询在执行的时候,就会使用AddressID作为索引键通过B树一下子就找到了这个键对应的数据页,然后读数据!