详解SQL Server中SCAN和SEEK的区别

openkk 9年前
   <p>        SQL SERVER 使用扫描(scan)和查找(seek)这两种算法从数据表和索引中读取数据。这两种算法构成了查询的基础,几乎无处不在。Scan 会扫描并且返回整个表或整个索引。 而 seek 则更有效率,根据谓词(predicate),只返索引内的一个或多个范围内的数据。下面将以如下的查询语句作为例子来分析 scan 和 seek:</p>    <pre>    <span style="color:#0000ff;">select</span> OrderDate <span style="color:#0000ff;">from</span> Orders <span style="color:#0000ff;">where</span> OrderKey <span style="color:#808080;">=</span> <span style="color:#800000;font-weight:bold;">2</span></pre>    <p style="text-align:center;"><img title="SQL Server 中 SCAN 和 SEEK 的区别" border="0" alt="详解SQL Server中SCAN和SEEK的区别" src="https://simg.open-open.com/show/cae5569377dd40410f88e3fb838278b3.jpg" width="466" height="383" /></p>    <p>        <strong>Scan</strong></p>    <p>        使用 Scan 的方式,SQL Server 会去读取 Orders 表中的每一行数据,读取的时候评估是否满足谓词 “where order=2”。如果满足(数据行符合条件),则返回该行。这个例子里,我们将这个谓词称作“residual predicate”。为了得到最优的性能,SQL 会尽可能地在扫描中使用“residual predicate”。但如果 residual predicate 的开销过于昂贵,SQL Server 可能会使用单独的“filter iterator”. “residual predicate”以 where 关键字的形式出现在文本格式的 plan 中。对 XML 格式的 plan,则是    <predicate>     标记的形式。    </predicate></p>    <p>        下面这个扫描的文本格式的 plan 的结果:</p>    <p>        –Table Scan (OBJECT:([ORDERS]), WHERE:([ORDERKEY]=(2)))</p>    <p>        下图说明了扫描的方式:</p>    <p style="text-align:center;"><a href="https://simg.open-open.com/show/de6df17b8074b29ac29eb01467ee3ef9.jpg"><img border="0" alt="详解SQL Server中SCAN和SEEK的区别" src="https://simg.open-open.com/show/95af09cab334be3b57ea985d3514dcf9.jpg" width="550" height="123" /></a></p>    <p>        无论数据行是否满足条件,扫描的读取方式都会访问表中的每一个数据,所以 scan 的成本和表的数据总量是成比例的。 因此,如果表很小或者表内的大多数数据多满足谓词,scan 是一种有效率的读取方式。然而如果表很大或者绝大多数的数据并不满足谓词, 那么这种方式会让我们访问到太多不需要的数据页面,并执行更多的额外的 IO 操作。</p>    <p>        <strong>Seek</strong></p>    <p>        继续以上面的查询为例子,如果在 orderkey 列上有一个索引,那么 seek 可能会是一个好的选择。使用 seek 的访问方式,SQL Server 会使用索引直接导向到满足谓词条件的数据行。 这个例子里,我们将这个谓词称为“seek predicate”。 大多数情况下,SQL Server 不必将“seek predicate”重新评估为“residual predicate”。 索引会保证“seek”只返回符合条件的数据行。“seek predicate”以 seek 关键字的形式出现在文本格式的 plan 中。 对于 xml 格式的 plan,则以    <seekpredicates>     标记出现。    </seekpredicates></p>    <p>        下面是使用 seek 的文本格式的 plan 的结果:</p>    <p>        –Index Seek (OBJECT:([ORDERS].[OKEY_IDX]), SEEK:([ORDERKEY]=(2)) ORDERED FORWARD)</p>    <p style="text-align:center;"><a href="https://simg.open-open.com/show/c2eba8f2716a3d167b46bd8eec15c2dc.jpg"><img border="0" alt="详解SQL Server中SCAN和SEEK的区别" src="https://simg.open-open.com/show/ea09768a7e6380005697d1e20411ad89.jpg" width="550" height="288" /></a></p>    <p>        使用 seek 时,SQL Server 只会直接访问到满足条件的数据行和数据页,因此它的成本只跟满足条件的数据行的及其相应的数据页面数量成比例, 和基表的数据量完全没有关系。因此,如果 对于一个选择性很高(通过这个谓词,可以筛选掉表中的大部分数据)的谓词条件,seek 是非常高效的。</p>    <p>        下面的表格列出了 seek 和 scan 这两种查找方式和堆表,聚簇索引和非聚簇索引的各种组合:</p>    <table border="1">     <tbody>      <tr>       <td> </td>       <td>Scan</td>       <td>Seek</td>      </tr>      <tr>       <td>Heap</td>       <td>Table Scan</td>       <td> </td>      </tr>      <tr>       <td>Clustered Index</td>       <td>Clustered Index Scan</td>       <td>Clustered Index Seek</td>      </tr>      <tr>       <td>Non-Clustered Index</td>       <td>Index Scan</td>       <td>Index Seek</td>      </tr>     </tbody>    </table>    <div id="come_from">    <br /> 来自:     <a id="link_source2" href="/misc/goto?guid=4958521641566342621" target="_blank">blogs.msdn.com</a>    </div>