本文属于SQL Server T-SQL执行内幕系列
在执行树的叶子端(通常就是图形化执行计划每个分支的最右端),一般是实际访问数据的操作符。当调用这些操作符上的next()方法时,会返回表或者索引上的实际数据。数据访问通常有三类可能的操作符:
- Scan:
各类扫描,扫描操作会在数据上循环访问所有的行。它永远不会定位一个特定的行,取而代之的是扫描整个数据集。在执行计划中常见的扫描操作符有:聚集索引扫描(clustered_index_scan)、非聚集索引扫描(nonclustered_index_scan)、表扫描(table_scan)、远程索引扫描(remote_index_scan)和远程扫描(remote_scan)。由于应用在不同的数据源(表、索引、远程链接服务器),所以这些扫描不是相同的操作符,但是都有相同的端对端扫描行为。同时由于需要读取整个数据集,所以往往都是高开销操作,理想情况下,仅适合数据仓库查询。
- Seek:
查找操作,通过键直接定位一行数据,因此只能应用在B-Tree结构的数据源中,也就是聚集索引和非聚集索引上。如果一个索引是复合索引(多列),那么查找操作仅对索引键的最左边那列进行操作,比如一个索引有且顺序为ABC三列,而语句中出现如where A=’a’ AND B=’b’ AND C=’c’或Where A=’a’ and B=’b’或者Where A=’a’这种都可以使用到该索引进行查找操作,但是如果Where只有B=’b’或者C=’c’等没有A列出现,则索引不能进行查找操作,通常就是使用扫描。注意从SQL 2005开始Where条件的列顺序已经无关紧要。
另外,查找操作也适用于范围查询,比如上面的索引,如果Where A >’a’ and A<’z’是可以用到索引查找,但是如果WhereA=’a’ and B>’b’ and B<’z’这种,对B的范围查找并不会用到索引查找,仅会对A=’a’进行查找。
执行计划中可能出现Clustered_Index_Seek或者Remote_Index_Seek这类操作符,他们同样由于不同的数据源所以是不同的操作类型,但是依旧可以通过键值来高效定位某个数据或者某个范围值。
前面提到过,查找操作仅能发生在B-Treee结构上,所以不存在堆查找。对于绝大部分OLTP系统而言,应该广泛使用这种方式查询数据。
- Bookmark Lookup:
书签查找,一种特殊的数据访问操作符,可以基于特定类型的值(称为书签)高效定位一行数据。但是仅能由数据库引擎执行,用户无法通过语句或者hints来进行强制。同时跟查找和扫描不同的是它永远不是主要的数据访问操作符,只是用来查找之前一个查找或扫描操作访问过的数据。简单来说就是协助查找或扫描操作进行额外的数据获取。书签查找可以应用在任何数据结构,包括堆和B-Tree。分别称为RID Lookup(RID查找)和Key Lookup(键值查找)。注意从SQL 2008开始,书签查找这个名字已经被RID 查找和键值查找所替代,书签查找仅作为这两个查找的统称。
从性能优化方面来说,出现书签查找意味着你的非聚集索引无法覆盖特定查询所用到的列,对于大部分情况,可以通过修改现有非聚集索引来去除书签查找,毕竟它往往意味着引入了额外的I/O。不过也有一些情况,比如SELECT *,非聚集索引往往不会也不应该覆盖所有的列,所以这个时候,可能书签查找甚至聚集索引操作反而更高效。
严格来说,所有用于增、删、改数据的操作符同样都是数据访问操作符。在触发器中,inserted和deleted分别使用InsertedScan和Deleted Scan操作符。Log RowScan是一个神秘的数据访问操作符(从log中读取数据而不是从数据表中读取)。
在很多文献、文章中还会看到范围扫描(range scan),这意味着一个查找操作符快速定位一个基于键值的行然后从当前位置继续向前循环遍历行,有时候止于特定的第二个键值。查找操作基于开始和结束的键定义进行扫描,因此叫做范围扫描。
回到执行计划,当查询树根节点对操作符调用next()方法时,会逐级调用其子操作符直到到达数据访问操作符为止。这些数据访问操作符通过读取第一条符合要求的实际数据并返回来实现next()方法。然后记录位置之后,接下来再次调用next()方法读取下一行并返回,直到全部读取完毕。数据访问操作符没有更多的子操作符,它们就位于查询树的叶子端(图形计划的最右端)。在数据访问操作符的上级通常是实现某些功能的操作符,如过滤(filter)、表关联(join)、排序(sort)、聚合(computing aggregate)等。
总结
简单来说,对于各种操作符可以归纳以下行为:
- 扫描操作读取所有数据。
- 查找操作读取最小所需数据。
- 堆只支持扫描和书签查找(RID查找)。
- B-Tree结构仅在索引键最左列被指定时才进行查找操作。