创建索引时可以指定 WITH SORT_IN_TEMPDB 选项,该选项指示数据引擎使用 tempdb 存储用于生成索引的中间排序结果。虽然该选项会增加用于创建索引的磁盘空间量,但是当 tempdb 与用户数据库位于不同的磁盘集上时,该选项可减少创建索引所需的时间。
数据库引擎在生成索引时经历下面两个阶段:
首先,数据库引擎扫描数据页以检索键值,并为每个数据行生成索引叶行。当内部排序缓冲区被叶索引项填满时,这些项被排序并作为中间排序进程写入磁盘。然后,数据库引擎继续扫描数据页,直到排序缓冲区再次被填满。这种先扫描多个数据页,然后排序并写入排序进程的模式继续进行,直到处理完基表中的所有行。在聚集索引中,索引的叶行是表的数据行,因此中间排序进程包含所有的数据行。在非聚集索引中,叶行不包含非键列的值,因此通常较小。然而,如果索引键很大,则非聚集的排序进程也可能很大。
数据库引擎将排序的索引叶行进程合并为单个的排序流。引擎的排序合并组件从每个排序进程的第一页开始,在所有的页中找出最小的键,并将那个叶行传递到索引创建组件。然后处理下一个最小的键,随后再处理下一个,依此类推。当将最后一个叶索引行从排序进程页中析取出来时,该进程从此排序进程切换到下一页。当处理完某个排序进程扩展盘区中的所有页时,释放该扩展盘区。每个叶索引行在传递到索引创建组件时,均被放置在缓冲区的叶索引页中。每个叶页在填充时被写入。当写入叶页时,数据库引擎还会生成该索引的上级。每个上级索引页在填充时被写入。
如果在已经有非聚集索引的表上创建聚集索引,一般过程是:
重新分配非聚集索引,但保留索引的定义。空间直到包含 CREATE INDEX 语句的事务结束时才可用,因而,如果在事务回滚期间必须恢复旧索引页,则这些旧索引页仍是可用的。
创建聚集索引。
重新创建非聚集索引。
如果未指定 SORT_IN_TEMPDB,则排序进程将存储在目的文件组中。在创建索引的第一阶段,基表页的交替读取和排序进程的写入使读写磁头从磁盘的一个区域移动到另一个区域。当扫描数据页时,磁头位于数据页区域。当填充排序缓冲区并且当前的排序进程必须写入磁盘时,读写磁头移动到某个可用空间区域,然后当继续扫描表页时移回数据页区域。在第二阶段,读写磁头的移动频率较高。这时,排序进程一般正在从各排序进程区域交替读取。目的文件组中将生成排序进程和新的索引页,这意味着数据库引擎在各排序进程间分布读取的同时,还必须定期跳到索引扩展盘区,以便在填充索引页时写入新的索引页。
如果指定了 SORT_IN_TEMPDB 选项并且 tempdb 与目的文件组位于不同的磁盘集上,那么在第一阶段,对数据页的读取与对 tempdb 中排序工作区的写入会发生在不同的磁盘上。这意味着对数据键的磁盘读取在整个磁盘上会趋于更加连续,并且对 tempdb 磁盘的写入也趋于连续,正如生成最终索引时的写入操作一样。即使其他用户正在使用数据库并且正在访问不同的磁盘地址,指定 SORT_IN_TEMPDB 选项时的总体读写模式的效率也比没有指定时要高。
SORT_IN_TEMPDB 选项可能会提高索引扩展盘区的邻接,尤其当不是并行处理 CREATE INDEX 时。排序工作区扩展盘区在数据库中的释放位置方面有些随机。如果排序工作区包含在目的文件组中,则释放排序工作区扩展盘区时,可通过请求来获取它们,以使扩展盘区在生成时容纳索引结构。这在某种程度上使索引扩展盘区的位置随机化。如果在 tempdb 中单独容纳排序扩展盘区,则它们的释放顺序与索引扩展盘区的位置无关。另外,当中间排序进程存储在 tempdb 中而不是目的文件组中时,目的文件组中将有更多的可用空间,从而增加了索引扩展盘区邻接的机会。
SORT_IN_TEMPDB 选项只影响当前的语句。没有任何元数据记录索引是否存储在 tempdb 中。例如,如果使用 SORT_IN_TEMPDB 选项创建了某个非聚集索引,后来在没有指定该选项的情况下创建了某个聚集索引,则当数据库引擎重新创建那个非聚集索引时,将不使用该选项。
可用空间要求
如果指定了 SORT_IN_TEMPDB 选项,则 tempdb 中必须有足够的可用空间容纳中间排序进程,而且目的文件组中必须有足够的可用空间容纳新的索引。如果没有足够的可用空间,并且由于某种原因数据库不能自动增长以获得更多的空间(如磁盘上没有剩余空间或关闭了自动增长功能),CREATE INDEX 语句将失败。
如果没有指定 SORT_IN_TEMPDB,目的文件组中的可用空间必须大约等于最终索引的大小。在第一阶段,生成排序进程并要求可用空间量大约等于最终索引的大小。在第二阶段,处理每个排序进程扩展盘区后将其释放。这意味着释放排序进程扩展盘区的速度与获取扩展盘区以容纳最终索引页的速度差不多相同,因此总的空间要求并没有显著超过最终索引的大小。这样的一个副作用就是如果可用空间量非常接近最终索引的大小,则数据库引擎倾向于在排序进程扩展盘区释放后立即重新使用它们。因为排序进程扩展盘区的释放方式有些随机,所以在这种情形中将降低索引扩展盘区的连续性。如果没有指定 SORT_IN_TEMPDB,那么如果目的文件组中有足够的可用空间,则可以从邻接的池而不是从刚刚重新分配的排序进程扩展盘区分配索引扩展盘区,这将提高索引扩展盘区的连续性。
执行 CREATE INDEX 语句时,必须有足够的可用空间:
创建非聚集索引时:
如果指定了 SORT_IN_TEMPDB,则 tempdb 中必须有足够的可用空间存储排序进程,而且目的文件组中必须有足够的可用空间存储最终索引结构。排序进程包含索引的叶行。
如果没有指定 SORT_IN_TEMPDB,目的文件组中必须有足够的可用空间存储最终索引结构。如果有更多的可用空间,则可以提高索引扩展盘区的连续性。
在没有非聚集索引的表上创建聚集索引时:
如果指定了 SORT_IN_TEMPDB,则 tempdb 中必须有足够的可用空间存储排序进程,包括表的数据行。目的文件组中必须有足够的可用空间存储最终的索引结构,包括表的数据行和索引 B 树。粗略的估计值是原始表大小的 1.2 倍,但可能需要根据不同的因素调整估计值,如键的大小很大或填充因子的值很低。
如果没有指定 SORT_IN_TEMPDB,则目的文件组中必须有足够的可用空间存储最终表,包括索引结构。如果有更多的可用空间,则可以提高表和索引扩展盘区的连续性。
在有非聚集索引的表上创建聚集索引时:
如果指定了 SORT_IN_TEMPDB,则 tempdb 中必须有足够的可用空间存储最大索引(一般为聚集索引)的排序进程集合,而且目的文件组中必须有足够的空间存储所有索引的最终结构,包括表的数据行所在的聚集索引。
如果没有指定 SORT_IN_TEMPDB,则目的文件组中必须有足够的可用空间存储最终表,包括所有索引结构。如果有更多的可用空间,则可以提高表和索引扩展盘区的连续性。