UML软件工程组织

数据仓库的 RDBMS 性能优化指南
来源:Microsoft Tech Net
简介
性能优化的基本原则
优化磁盘 I/O 性能
使用分区来提高性能
查找更多信息

本性能优化指南旨在帮助数据库管理员和开发人员配置 Microsoft® SQL Server 2000,以获得最佳的性能,并帮助找出造成关系数据库(包括用于数据仓库的数据库)性能低下的原因。本指南还就如何装载、索引和编写查询以访问 SQL Server 中存储的数据提供了指导原则和最佳做法。另外还介绍了多种可用于分析性能特征的 SQL Server 工具。

简介

Microsoft SQL Server 7.0 中引入了一项重大改进:一个在很大程度上可以自行配置、自行优化和自行管理的数据库引擎。在 SQL Server 7.0 面世之前,大多数数据库服务器都会耗费数据库管理员大量的时间和精力,他们必须手动优化服务器配置以获得最佳性能。实际上,很多竞争性数据库产品现在仍要求管理员手动配置和优化他们的数据库服务器。这是许多客户改用 SQL Server 的主要原因。SQL Server 2000 是在 SQL Server 7.0 奠定的坚实基础上更上层楼的产品。SQL Server 的目标是:通过实现数据库引擎自行优化并允许 DBA 自动完成管理任务,使 DBA 不必手动配置和不断优化数据库服务器。

尽管现在仍可以手动配置和调整一些 sp_configure 选项,但建议数据库管理员尽量不要这样做,而是让 SQL Server 自动配置和优化。对于这种调整能力,SQL Server 7.0 具有已被广泛认可且经过实践证明的成绩记录,SQL Server 2000 对此方案进行了显著的改进。环境中不断变化的条件可能会对数据库性能产生负面影响,所以,让 SQL Server 自行优化,就可以使数据库服务器进行动态调整以适应这些变化。

性能优化的基本原则

您可以采取许多措施来管理数据库的性能。SQL Server 2000 提供了几种工具来帮助您完成这些任务。

管理性能

  • 让 SQL Server 完成大多数优化任务。
    SQL Server 2000 经过了重大改进,可以创建基本上能够进行自动配置和自行优化的数据库服务器。利用 SQL Server 自动优化设置这一优点,可帮助 SQL Server 即使在用户负荷和查询随时间不断变化的情况下也能以最高性能运行。

  • 管理 RAM 缓存。
    RAM 是一种有限的资源。任何数据库服务器环境的一个主要部分就是管理随机存取存储器 (RAM) 缓冲区缓存。访问 RAM 缓存中的数据比访问磁盘中同样的信息要快得多。但是,RAM 资源是有限的。如果可以将数据库 I/O(针对物理磁盘子系统的输入/输出操作)减少到最低所需的数据集和索引页,这些页将在 RAM 中停留更长时间。如果流入缓冲区缓存的不必要的数据和索引信息过多,会将有价值的页很快排挤出去。性能优化的主要目标是减少 I/O,以使缓冲区缓存得到最充分的利用。

  • 创建和维护合适的索引。
    对于所有的数据库查询,维护最小 I/O 的一个关键因素是确保创建和维护合适的索引。

  • 对大数据集和索引进行分区。
    要减少总体 I/O 争用现象并改善并行操作,请考虑对表数据和索引进行分区。本章介绍了使用 SQL Server 2000 实现和管理分区的多种方法。

  • 监视磁盘 I/O 子系统的性能。
    物理磁盘子系统必须为数据库服务器提供足够的 I/O 处理能力,以使数据库服务器在运行时不会出现磁盘排队现象。磁盘排队现象会导致性能不佳。本文介绍了如何检测磁盘 I/O 问题以及如何解决这些问题。

  • 优化应用程序和查询。
    当数据库服务器通过给定的应用程序为来自成百上千个连接的请求提供服务时,这种优化变得尤为重要。由于应用程序通常会确定要在数据库服务器上执行的 SQL 查询,所以应用程序开发人员一定要了解 SQL Server 的结构基础知识以及如何充分利用 SQL Server 的索引来最小化 I/O。

  • 优化活动数据。
    在许多商业智能数据库中,绝大多数数据库活动都会涉及最近一个月或一个季度的数据(多达 80% 的数据库活动都可能是因最近装载的数据而发生的)。要保持良好的数据库总体性能,在对这些数据进行装载、索引和分区时,所采取的方式一定要能够为这些数据提供最佳的数据访问性能。

利用 SQL Server 性能工具

  • SQL 事件探查器和索引优化向导
    SQL 事件探查器可用于监视和记录 SQL Server 的工作负荷。然后,可以将记录下来的工作负荷可以提交到索引优化向导,这样,在必要的时候就可以更改索引来帮助提高性能。SQL 事件探查器和索引优化向导可帮助管理员实现优化的索引。定期使用这些工具可使 SQL Server 保持良好的性能,即使查询工作负荷随时间而变化也是如此。

  • SQL 查询分析器和图形执行计划
    在 SQL Server 2000 中,SQL 查询分析器提供了“图形执行计划”,使用这种方法可以方便地分析有问题的 SQL 查询。“统计 I/O”是 SQL 查询分析器的另一个重要功能,本章后面将会进行介绍。

  • 系统监视器对象
    SQL Server 提供了一组系统监视器对象和计数器,可以为监视和分析 SQL Server 的运行状况提供信息。本章介绍了要监视的关键计数器。

影响性能的配置选项

最大异步 IO

SQL Server 7.0 中的手动配置选项最大异步 I/O 在 SQL Server 2000 中已经实现了自动化。以前,最大异步 I/O 用于指定在一次检查点操作过程中,SQL Server 7.0 可以向 Microsoft Windows NT® 4.0 和 Windows® 2000 同时提交的磁盘 I/O 请求的数量。Windows 接下来又将这些请求提交到物理磁盘子系统。此配置设置实现自动化后,SQL Server 2000 就能够以动态方式自动维护最佳的 I/O 吞吐量。

注意 Windows 98 不支持异步 I/O,因此在该平台上不支持最大异步 I/O 选项。

数据库恢复模型

SQL Server 2000 引入了在数据库级别对事务记录方式进行配置的功能。选定的模型会对性能产生很大的影响,尤其是在数据装载过程中。恢复模型有三种:“完全”、“大容量日志记录的”和“简单”。新数据库的恢复模型是在新数据库创建时从模型数据库继承的。在创建数据库之后,可以更改它的模型。

  • “完全恢复”可为将数据库恢复到以前的时间点提供最大的灵活性。
  • “大容量日志记录的恢复”可为某些大规模操作(例如,创建索引或大容量复制)提供较高的性能并占用较少的日志空间。这种恢复的缺点是与时间点恢复相比不够灵活。
  • “简单恢复”提供的性能最高,占用的日志空间最少,但在系统发生故障时丢失数据的风险很大。在使用“简单恢复”模型时,数据只能恢复到上次(最近一次)进行完全数据库或差异备份时的状态。在这一模型中,由于事务在日志中会从检查点处截断,因此不能使用事务日志备份来恢复事务。这会产生丢失数据的可能。在不再需要日志空间以从服务器故障(活动事务)进行恢复之后,日志空间就会被截断并重新使用。

经验丰富的管理员可以使用这个恢复模型功能来大大加快数据装载和大容量操作的速度。不过,根据所选模型不同,丢失数据的可能性也各不相同。

重要说明 在选择某种恢复模型之前,必须仔细考虑会遇到的风险。

每种恢复模型致力于满足不同的需要。请根据所选的模型权衡利弊。权衡的结果需对性能、空间利用率(磁盘或磁带)和防范数据丢失的保护措施等方面加以综合考虑。当您选择恢复模型时,需要结合以下几个方面的业务需求作出决定:

  • 大规模操作(例如,创建索引或大容量装载)的性能
  • 数据丢失的可能性(例如,丢失已提交的事务)
  • 事务日志空间占用大小
  • 备份和恢复过程的难易

根据所执行的操作不同,一种模型可能会比另一种模型更适合。在选择一种恢复模型之前,请考虑它将带来的影响。下表提供了一些帮助性信息。

恢复模型 优点 丢失所做工作的可能性 是否恢复到时间点?
简单 可高性能地完成大容量复制操作。
可回收日志空间,使空间要求保持较低的水平。
自最近的数据库备份或差异备份以来所做的更改必须重做 可以恢复到任何备份的结束点。此后的更改必须重做。
完全 不会因数据文件丢失或损坏而丢失所做的工作。
可恢复到任意时间点(例如,发生应用程序或用户错误之前的那一刻)。
通常没有风险。
如果日志受到损坏,则必须重做自最近的日志备份以来所做的更改。
可恢复到任意时间点。
大容量日志记录的 可高性能地完成大容量复制操作。
大容量操作使用最小的日志空间。
如果日志受到损坏,或者自最近的日志备份以来出现过大容量操作,则必须重做自上次备份以来所做的更改。
除此之外,不会丢失所做的任何工作。
可以恢复到任何备份的结束点。此后所做的更改必须重做。

有关多实例的注意事项

SQL Server 2000 中还引入了在一台计算机上运行 SQL Server 的多个实例的功能。默认情况下,SQL Server 的每个实例会动态地获取和释放内存,以针对实例的工作负荷的变化进行调整。当 SQL Server 2000 有多个实例,而每个实例都独立地自动调整内存使用量时,性能优化会变得很复杂。大多数高端的业务智能客户通常只在每台计算机上安装一个 SQL Server 实例,因此对于他们来说,通常不需要考虑这项功能。但是,随着计算机个体变得越来越大(Windows 2000 Datacenter Server 最多支持 64 GB RAM 和 32 个 CPU),在有些生产环境中,可能会出现对多个实例的需求。那些利用扩展内存支持的实例需要特别关注。

扩展内存支持

一般情况下,SQL Server 2000 会根据需要动态地获取和释放内存,所以管理员通常不需要指定应该为 SQL Server 分配多少内存。但是,SQL Server 2000 企业版和 SQL Server 2000 开发人员版引入了对使用 Microsoft Windows 2000 Address Windowing Extensions (AWE) 的支持。这样,SQL Server 2000 就可以对更多的内存进行寻址(对于 Windows 2000 Advanced Server 最多约 8 GB;对于 Windows 2000 Datacenter Server 最多约 64 GB)。在配置了扩展内存的情况下,必须将访问扩展内存的每个实例配置为静态分配它将使用的内存。

注意 只有当您运行 Windows 2000 Advanced Server 或 Windows 2000 Datacenter Server 时,才可使用这项功能。

使用 Windows 2000 的注意事项

要利用 AWE 内存,必须使用已分配了 Windows 2000 的“内存中锁定页”特权的 Windows 2000 帐户运行 SQL Server 2000 数据库引擎。SQL Server 安装程序将自动授权 MSSQLServer 服务帐户使用内存中锁定页选项。如果您从命令提示符使用 Sqlservr.exe 来启动 SQL Server 2000 的实例,必须使用 Windows 2000 的“组策略”实用工具 (Gpedit.msc) 将这一权限手动分配给交互操作的用户帐户,否则的话,如果 SQL Server 不作为服务运行就将无法使用 AWE 内存。

启用“内存中锁定页”选项

  1. 开始菜单上,单击运行,然后在打开框中输入 gpedit.msc
  2. 组策略树窗格中,展开计算机配置,然后展开 Windows 设置
  3. 展开安全性设置,然后展开本地策略
  4. 选择用户权限分配文件夹。
  5. 策略将会显示在详细信息窗格中。
  6. 在详细信息窗格中,双击内存中锁定页
  7. 本地安全性策略设置对话框中,单击添加
  8. 选择用户或组对话框中,添加一个有权运行 Sqlservr.exe 的帐户。

要使 Windows 2000 Advanced Server 或 Windows 2000 Datacenter Server 能够支持 4 GB 以上的物理内存,必须将 /pae 参数添加到 Boot.ini 文件中。

对于内存不超过 16 GB 的计算机,您可以在 Boot.ini 文件中使用 /3gb 参数。这就使 Windows 2000 Advanced Server 和 Windows 2000 Datacenter Server 能够允许用户应用程序通过 3 GB 的虚拟内存来对扩展内存进行寻址,并且为操作系统本身保留 1 GB 虚拟内存。

如果计算机上的物理内存超过 16 GB,则 Windows 2000 操作系统本身会需要 2 GB 虚拟内存地址空间用于系统开销。因此,它只能支持将 2 GB 虚拟地址空间用于应用程序开销。对于物理内存超过 16 GB 的系统,一定要在 Boot.ini 文件中使用 /2gb 参数。

注意 如果您意外使用了 /3gb 参数,Windows 2000 将无法对 16 GB 以上的任何内存进行寻址。

使用 SQL Server 2000 的注意事项

要使 SQL Server 2000 的实例能够使用 AWE 内存,请使用 sp_configure 来设置启用 awe 选项。然后,重新启动 SQL Server 以激活 AWE。由于 AWE 支持会在 SQL Server 启动过程中启用,并在 SQL Server 关闭前一直保持启用状态,所以,在 AWE 处于使用状态时,SQL Server 会通过向 SQL Server 错误日志发送“已启用 Address Windowing Extension”消息来通知用户。

当您启用 AWE 内存时,SQL Server 2000 的实例不会动态管理地址空间的大小。因此,当您启用 AWE 内存并启动 SQL Server 2000 的实例时,根据最大服务器内存的设置方式不同,会出现下列情况之一。

  • 如果已经设置了最大服务器内存并且计算机上至少有 3 GB 可用内存,则该实例获取在最大服务器内存中指定的内存量。如果计算机上可用的内存量小于最大服务器内存(但是大于 3 GB),则该实例获取几乎所有的可用内存,并且可能最多只留下 128 MB 可用内存。
  • 如果尚未设置最大服务器内存并且计算机上至少有 3 GB 可用内存,则该实例几乎获取所有的可用内存,并且可能最多只留下 128 MB 可用内存。
  • 如果计算机上的可用内存不足 3 GB 而且内存是动态分配的,则无论为启用 awe 设置了什么参数,SQL Server 都将以非 AWE 模式运行。

当在 32 GB 系统上分配 SQL Server AWE 内存时,Windows 2000 可能至少需要 1 GB 可用内存来管理 AWE。因此,如果在启动 SQL Server 的实例时已启用了 AWE,建议您不要使用默认的最大服务器内存设置,而应将它限制在 31 GB 或更小。

有关故障转移群集和多实例的注意事项

如果您在使用 AWE 内存的同时使用 SQL Server 2000 故障转移群集或者运行多个实例,则必须确保正在运行的所有 SQL Server 实例的最大服务器内存设置的值的总和小于可用的物理 RAM 量。对于故障转移,您必须考虑任何候选存活节点上物理 RAM 的最小量。如果故障转移节点上的物理内存比初始节点上的少,则 SQL Server 2000 的实例可能无法启动或者启动时用的内存比其在初始节点上的少。

sp_configure 选项

“并行度的成本阈值”选项

使用并行度的成本阈值选项可指定 SQL Server 创建和执行并行计划所用的阈值。只有当为同一个查询执行串行计划的预计成本高于在并行度的成本阈值中设置的值时,SQL Server 才为查询创建和执行并行计划。成本是指对特定的硬件配置执行串行计划时预计需要的时间(以秒为单位)。只针对对称多处理器 (SMP) 设置并行度的成本阈值

通常,并行计划对较长的查询有利;性能上的优势可以补偿初始化、同步以及终止计划所需的额外时间。在混合执行短查询和长查询时,通常会使用并行度的成本阈值选项。短查询执行串行计划,而长查询使用并行计划。并行度的成本阈值的值决定哪些查询被视为短查询,从而只执行串行计划。

在有些情况下,即使查询的成本计划小于当前的并行度的成本阈值值,也可以选择并行计划。这是因为就并行度的成本阈值而言,使用并行计划还是串行计划要根据完全优化完成之前提供的预计成本来决定。

并行度的成本阈值选项可以设置为从 0 到 32767 的任何值。默认值是 5(以毫秒为单位)。如果计算机只有一个处理器,或者如果由于关系掩码配置选项的值而使得 SQL Server 只能使用一个 CPU,或者如果最大并行度选项设置为 1,那么,SQL Server 会忽略并行度的成本阈值

“最大并行度”选项

最大并行度选项用于限制在执行并行计划时使用的处理器数(最多 32 个)。默认值是 0,此时使用可用的实际数量的 CPU。将最大并行度选项设置为 1 可强制取消生成并行计划。如果将该值设置为大于 1 的数字,则可以限制在执行单个查询时使用的最大处理器数。如果将该值指定为大于可用 CPU 数量的数字,则使用可用的实际数量的 CPU。

注意 如果未将关系掩码选项设成默认值,则在对称多处理器 (SMP) 系统上可供 SQL Server 使用的 CPU 数可能会受到限制。

对于在 SMP 计算机上运行的服务器,很少改变最大并行度。如果计算机只有一个处理器,则会忽略最大并行度值。

“优先级提升”选项

优先级提升选项用于指定 SQL Server 是否应当以高于同一台计算机上其他进程的调度优先级运行。如果将该选项设置为 1,SQL Server 将在 Windows 调度程序中按照 13 的优先级基数运行。默认值是 0,表示优先级基数 7。优先级提升选项只应当用在 SQL Server 专用且具有 SMP 配置的计算机上。

注意 如果将优先级提升得太高,则可能会使基本操作系统和网络功能的资源不足,从而造成关闭 SQL Server 或使用服务器上的其他 Windows 任务等问题。

在某些情况下,如果将优先级提升设置为默认值以外的任何值,可能会导致在 SQL Server 错误日志中记录以下通讯错误:

Error: 17824, Severity: 10, State: 0 Unable to write to ListenOn
connection '<servername>', loginname '<login ID>', hostname '<hostname>'
OS Error: 64, The specified network name is no longer available.

错误 17824 指出在尝试写入客户端时 SQL Server 遇到连接问题。如果客户端已停止响应,或者客户端已经重新启动,则这些通讯问题可能是由网络问题引起的。但是,错误 17824 不一定表示网络问题,而可能只是设置优先级提升选项的结果。

“设置工作集大小”选项

设置工作集大小选项用于为 SQL Server 保留等于服务器内存设置的物理内存空间。服务器内存设置由 SQL Server 根据工作负荷和可用资源自动配置。它将在最小服务器内存最大服务器内存之间显著变化。设置设置工作集大小的意思是:即使在 SQL Server 空闲时,另一个进程可以更容易地使用 SQL Server 页,操作系统也不尝试换出这些页。

如果您要允许 SQL Server 动态使用内存,请不要设置设置工作集大小。在将设置工作集大小设置为 1 之前,请将最小服务器内存最大服务器内存设置为同一个值(希望 SQL Server 使用的内存量)。

轻量池关系掩码选项将在本章后面的“要监视的关键性能计数器”一节讨论。

优化磁盘 I/O 性能

如果您配置的 SQL Server 将只包含几 GB 数据,而且不负担繁重的读写活动,可以不必考虑磁盘 I/O 以及通过平衡硬盘驱动器间的 SQL Server I/O 活动来实现最大性能等事项。但是,要创建大型 SQL Server 数据库来包含数百 GB 甚至 TB 的数据,并且/或者要能够负担繁重的读/写活动,则有必要进行相应的配置,以平衡多个硬盘驱动器间的负载,从而最大程度地提高 SQL Server 的磁盘 I/O 性能。

优化传输速度

对于数据库性能优化来说,最重要的方面之一就是优化 I/O 性能。当然,SQL Server 也不例外。除非运行 SQL Server 的计算机有足够的 RAM 来容纳整个数据库,否则 I/O 性能将由磁盘 I/O 子系统处理 SQL Server 读写数据的速度来确定。

因为传输速度、I/O 吞吐量和可能影响 I/O 性能的其他因素不断改善,所以我们将不针对应当从存储系统中期望看到哪种速度给出具体数字。为了更好地理解可期望获得的性能,建议您与首选的硬件供应商协作确定期望的最优性能。

我们必须要强调的是顺序 I/O 操作(通常又称为“序列”或“按磁盘顺序”)与非顺序 I/O 操作之间的差异。我们还希望大家注意预读可能对 I/O 操作产生的显著影响。

顺序和非顺序磁盘 I/O 操作

有必要解释以下这些术语相对于磁盘驱动器的含义。通常,一个硬盘驱动器由一组驱动器盘片组成。每个盘片都提供用于读取/写入操作的表面。一组带有读取/写入磁头的臂用于在这些盘片之间移动,并且从盘片的表面读取数据或者向其中写入数据。就 SQL Server 而言,有关硬盘驱动器的以下两点很重要,需要记住。

第一,读取/写入磁头和相关的磁盘臂需要移动,以定位到 SQL Server 请求的硬盘驱动器盘片上的位置并针对其进行操作。如果数据不按位置顺序分布到硬盘驱动器盘片上,则硬盘驱动器需要花更多的时间来移动磁盘臂(寻道时间)和旋转读/写头(旋转滞后时间)来找到数据。这与按位置顺序分布时的情形完全不同,在该情况下,所需的全部数据都位于硬盘驱动器盘片的一个连续物理扇区上,因此磁盘臂和读取/写入磁头在执行所需的磁盘 I/O 时移动量很少。

非顺序和顺序情形的时间相差很大:每次非顺序寻道大约花费 50 毫秒,而顺序寻道则大约需要两三毫秒。请注意,这些时间是大致估计值,并且将根据以下因素而有所变化:非顺序数据在磁盘上的分布距离、硬盘盘片可以旋转的速度 (RPM) 以及硬盘驱动器的其他物理属性。主要的一点是顺序 I/O 有助于提高性能,而非顺序 I/O 会降低性能。

第二,一定要记住,读写 8 KB 与读写 64 KB 所需的时间几乎一样多。在 8 KB 到大约 64 KB 的范围内,磁盘臂以及读/写头的移动(寻道时间和旋转滞后时间)仍然占一次磁盘 I/O 传输操作所需时间的大部分。因此,从数学角度讲,在需要传输 64 KB 以上的 SQL Server 数据时,因为 64 KB 与 8 KB 的传输速度基本上一样,但每次传输所处理的 SQL Server 数据却是后者的 8 倍,所以最好尝试尽可能多地执行 64 KB 磁盘传输操作。请记住,预读管理器在 64 KB 区块(称作 SQL Server 扩展盘区)中执行它的磁盘操作。日志管理器也以较大的 I/O 大小执行顺序写入。要记住的主要一点是,如果充分利用预读管理器并将 SQL Server 日志文件与不按顺序访问的其他文件分开,会改善 SQL Server 性能。

根据经验,大多数硬盘驱动器处理顺序 I/O 操作时所提供的性能是处理非顺序 I/O 操作时的 2 倍。即,需要非顺序 I/O 的操作所花费的时间是执行顺序 I/O 操作的两倍。因此,要尽可能避免可能导致数据库中出现随机 I/O 的情况。虽然应当尽量按顺序执行 I/O 操作,但是像页拆分或者数据无序这样的情形还是可能会导致出现非顺序 I/O。

为了促使执行顺序 I/O,一定要避免出现导致页拆分的情形。设计一个精心安排的数据装载策略也会有所帮助。您可以通过利用可分隔数据和索引的分区策略来促使在磁盘上按顺序分布数据。一定要设置作业以定期检查数据和索引中是否有碎片,并且在数据碎片太多时,使用 SQL Server 随付的实用工具来对数据重新排序。有关执行这些操作的更多信息将在本章的稍后部分介绍。

注意 因为事务日志数据总是以不超过 32 KB 的大小按顺序写入日志文件中,所以日志通常不是主要的考虑事项。

RAID

RAID(廉价磁盘冗余阵列)是一种存储技术,通常用于大于几 GB 的数据库。RAID 既具有性能优点又具有容错优点。多个 RAID 控制器和磁盘配置会在成本、性能和容错之间提供平衡。本主题简单介绍了将 RAID 技术用于 SQL Server 数据库的情况,并讨论了各种配置以及平衡方案。

  • 性能。硬件 RAID 控制器将从 Windows NT 4.0 与 Windows 2000 和应用程序(例如 SQL Server)中读取/写入的所有数据划分为很多切片(通常是 16–128 KB),这些切片随后会分布到所有参与 RAID 阵列的磁盘上。按这种方式将数据拆散到各个物理驱动器上会有这样的效果:读取/写入 I/O 工作负荷会平均分布到所有参与 RAID 阵列的物理硬盘驱动器上。这样就会提高磁盘 I/O 性能,因为参与 RAID 阵列的硬盘在总体上保持相同程度的闲忙,而不会因 I/O 请求分布不均而导致某些磁盘成为瓶颈。
  • 容错。 RAID 还通过使用镜像和奇偶校验这两种方法来防止硬盘出现故障并因此造成数据丢失。

镜像通过将信息写入另一组(镜像)驱动器上来实现。如果在有镜像时丢失了驱动器,则可以通过更换有故障的驱动器并重建镜像集来重建丢失驱动器上的数据。大多数 RAID 控制器都会提供在 Windows NT 4.0 和 Windows 2000 以及 SQL Server 联机的情况下更换故障驱动器并重新镜像的功能。这样的 RAID 系统通常被称作能够“热插拔”的驱动器。

镜像有一个优点:如果需要容错,它所实现的性能是 RAID 选项中最佳的。请切记,SQL Server 每次写入镜像集时,都会执行两个磁盘 I/O 操作,对于镜像集的每一面各执行一个这样的操作。另一个优点是:进行镜像比实现奇偶校验 RAID 提供的容错更多。镜像能够使系统在至少一个驱动器发生故障后继续运行,而且,在镜像集中多达半数的驱动器都有故障的情况下,也许还能够支持系统,而不是强制系统管理员关闭服务器并从文件备份中恢复。

镜像的缺点是成本高。镜像的磁盘成本是:每个需要用来存放数据的驱动器都额外需要一个驱动器。这实际上就会使存储成本增加一倍,对于数据仓库来说,存储器通常是所需的最昂贵的组件之一。RAID 1 及其混合 RAID 0+1(有时称作 RAID 10 或 0/1)都是通过镜像实现的。

奇偶校验是这样实现的:计算有关写入磁盘的数据的恢复信息,然后将该奇偶校验信息写入构成 RAID 阵列的其他驱动器。如果某个驱动器发生故障,一个新驱动器就会插入 RAID 阵列,并通过提取写入其他驱动器上的恢复信息(奇偶校验),使用这些信息重新生成故障驱动器上的数据,从而恢复故障驱动器上的数据。RAID 5 及其混合通过奇偶校验实现。

奇偶校验的优点是成本低。要用 RAID 5 保护任意数量的驱动器,只需要另外增加一个驱动器。奇偶校验信息会均匀分布在参与 RAID 5 阵列的所有驱动器上。奇偶校验的缺点是性能和容错能力。由于在计算和写入奇偶校验时会额外带来成本,因此 RAID 5 对于每次写入都需要四个磁盘 I/O 操作,而镜像只需两个磁盘 I/O 操作。镜像和奇偶校验的读取 I/O 操作成本是相同的。但是,读取操作通常会发生在有故障的驱动器上,此后,必须将阵列脱机,而且必须从备份介质中执行恢复,以恢复数据。

一般经验:一定要在所需的任意多个磁盘上进行条带化,以实现可靠的磁盘 I/O 性能。系统监视器将会指出在特定的 RAID 阵列上是否存在磁盘 I/O 瓶颈。请准备根据需要添加磁盘,并将数据重新分布到 RAID 阵列和/或小型计算机系统接口 (SCSI) 通道中,以平衡磁盘 I/O 并最大限度地提高性能。

硬件 RAID 控制器板载缓存的效果

许多硬件 RAID 控制器都有某种形式的读取和/或写入缓存。对 SQL Server 来说,这种可用的缓存功能可以显著增强磁盘子系统高效处理 I/O 的能力。这些基于控制器的缓存机制的原理是:收集来自主机服务器 (SQL Server) 的较小的并且有可能是非顺序的 I/O 请求,然后尝试在几毫秒内将它们与其他 I/O 请求合成一批,这样,成批的 I/O 就可以形成较大 (32–128 KB) 并且有可能是顺序 I/O 请求,以便发送到硬盘驱动器。

按顺序的和较大的 I/O 请求有利于提高性能,请遵循这一原则,因为在硬盘驱动器能够向 RAID 控制器提供固定数量 I/O 的情况下,这样有助于产生更大的磁盘 I/O 吞吐量。硬盘每秒能够处理更多的 I/O,并不是 RAID 控制器的缓存功能有多么神奇,而是因为 RAID 控制器缓存使用了某种组织方式来排列传入的 I/O 请求,从而能够尽可能充分地利用好基础硬盘固定的 I/O 处理能力。

这些 RAID 控制器通常用某种形式的后备电源来保护它们的缓存机制。这种后备电源可以帮助在断电时,将写入缓存中的数据保留一段时间(可能是几天)。如果数据库服务器也由不间断电源 (UPS) 支持,在断电时,RAID 控制器会有更多的时间和机会将数据刷新到磁盘中。虽然服务器的 UPS 不直接影响性能,但它的确可以为 RAID 控制器缓存所提供的性能改进提供保护。

RAID 级别

如上所述,在 RAID 的各个级别中,RAID 1 和 RAID 0+1 提供最佳的数据保护和最佳性能,但是就所需的磁盘而言会需要更多的成本。当硬盘成本不是限制因素时,就兼顾性能和容错而言,RAID 1 或 RAID 0+1 是最佳选择。

RAID 5 的成本比 RAID 1 或 RAID 0+1 低,但是它提供的容错和写入性能较差。RAID 5 的写入性能大约只是 RAID 1 或 RAID 0+1 的一半,这是因为 RAID 5 读取和写入奇偶校验信息需要额外的 I/O。

使用 RAID 0 可实现最佳磁盘 I/O 性能(磁盘条带化没有容错保护)。因为 RAID 0 不提供容错保护,所以决不应当将它用在生产环境中,也不建议将它用在开发环境中。RAID 0 通常只用于基准检验或测试。

许多 RAID 阵列控制器都通过物理硬盘驱动器提供 RAID 0+1(又称作 RAID 1/0 和 RAID 10)选项。RAID 0+1 是一种混合 RAID 解决方案。在较低级别,该控制器像普通的 RAID 1 那样镜像所有的数据。在较高级别,它(像 RAID 0 一样)将数据条带化到所有的驱动器上。因此,RAID 0+1 提供最大的保护(镜像)和高性能(条带化)。因为这些条带化和镜像操作由 RAID 控制器进行管理,所以它们对于 Windows 和 SQL Server 是透明的。RAID 1 和 RAID 0+1 之间的区别在硬件控制器级别上。对于给定的存储量,RAID 1 和 RAID 0+1 需要相同数量的驱动器。有关以 RAID 0+1 方式实现特定 RAID 控制器的更多信息,请与生产该控制器的硬件供应商联系。

下面的插图显示了 RAID 0、RAID 1、RAID 5 和 RAID 0+1 之间的区别。


如果您的浏览器不支持嵌入式框架,请单击此处在单独的页中查看。

注意 在上面的插图中,为了容纳相当于四个磁盘的数据,RAID 1(和 RAID 0+1)需要八个磁盘,而 Raid 5 只需要五个磁盘。一定要咨询您的存储器供应商,以了解有关他们特定的 RAID 实现的更多信息。

0 级

因为该级别使用名为条带集的磁盘文件系统,所以又将它称作磁盘条带。数据被划分成多个块并按固定顺序分布到阵列中的所有磁盘上。RAID 0 将多个操作分布到多个磁盘上,以便可以同时独立地执行这些操作,从而改善了读取/写入性能。RAID 0 类似于 RAID 5,但是 RAID 5 还提供容错功能。

下面的插图显示的是 RAID 0。

raid0

1 级

因为该级别使用名为镜像集的磁盘文件系统,所以又将它称作磁盘镜像。磁盘镜像可提供一个与所选磁盘完全相同的冗余副本。写入主磁盘的所有数据都会写入镜像磁盘。RAID 1 提供了容错功能,而且通常可以改进读取性能(但是可能会降低写入性能)。下面的插图显示的是 RAID 1。

raid1

2 级

该级别通过使用将奇偶校验分布到所有磁盘上的纠错方法来添加冗余。它还利用磁盘条带策略将一个文件分成多个字节并将该文件分布到多个磁盘上。与镜像 (RAID 1) 相比,该策略在磁盘利用率和读取/写入性能方面只带来了很小的改进。RAID 2 不如其他 RAID 级别效率高,通常不使用它。

3 级

该级别使用与 RAID 2 相同的条带化方法,但是纠错方法只需一个磁盘用于奇偶校验数据。磁盘空间的使用情况因数据磁盘的数量而异。RAID 3 在读取/写入性能方面提供一些改进。RAID 3 也极少使用。

4 级

该级别使用的条带数据块或段比 RAID 2 或 RAID 3 大得多。与 RAID 3 一样,纠错方法只需一个磁盘用于奇偶校验数据。它将用户数据与纠错数据分开。RAID 4 不如其他 RAID 级别效率高,通常不使用。

5 级

该级别又称作具有奇偶校验的条带化,它是新设计中最常用的策略。与 RAID 4 相似,它将数据以大块形式条带化到阵列中的磁盘上。不同之处在于它在所有磁盘之间写入奇偶校验的方式。数据冗余通过奇偶校验信息提供。数据和奇偶校验信息会在磁盘阵列上排列,所以这两种信息总是位于不同的磁盘上。与磁盘镜像 (RAID 1) 相比,具有奇偶校验的条带化可提供更好的性能。但是,当条带成员丢失时(例如,当磁盘发生故障时),读取性能会下降。RAID 5 是最常用的 RAID 配置之一。下面的插图显示的是 RAID 5。

raid5

Level 10 (1+0)

该级别又称作具有条带化的镜像。该级别使用条带化的磁盘阵列,而该阵列又镜像到另一组相同的条带化磁盘。例如,可使用四个磁盘创建一个条带化的阵列。然后,条带化的磁盘阵列使用另一组(四个)条带化的磁盘进行镜像。RAID 10 提供磁盘条带化带来的性能益处以及镜像带来的磁盘冗余。在所有的 RAID 级别中,RAID 10 提供的读取/写入性能最高,代价是使用的磁盘数量是其他级别的两倍。下面的插图显示的是 RAID 10。


如果您的浏览器不支持嵌入式框架,请单击此处在单独的页中查看。

联机 RAID 扩展

使用该功能可以在 SQL Server 保持联机的情况下动态地给物理 RAID 阵列添加磁盘。增加的磁盘驱动器会自动集成到 RAID 存储器中。添加磁盘驱动器的方法是:将它们安装到被称为热插拔驱动器插槽或热插拔插槽的物理位置。许多硬件供应商都提供了能够实现该功能的硬件 RAID 控制器。数据会均匀地在所有驱动器(包括新添加的驱动器)之间均匀地重新进行条带化,而且不需要关闭 SQL Server 或 Windows。

您可以通过在磁盘阵列盒中的热插拔插槽中保留空位来利用该功能。如果 SQL Server 经常因 I/O 请求而使 RAID 阵列负担过重(这可以由与该 RAID 阵列相关联的 Windows 逻辑驱动器盘符的磁盘队列长度来指示),则可能需要在 SQL Server 仍在运行时,将一个或多个新的硬盘驱动器安装到热插拔插槽中。RAID 控制器会将一些现有的 SQL Server 数据移到这些新驱动器上,以便数据均匀地分布到 RAID 阵列中的所有驱动器上。然后,新驱动器的 I/O 处理能力(每个驱动器每秒 75 个非顺序/150 个顺序 I/O)会添加到 RAID 阵列的总体 I/O 处理能力中。

系统监视器和 RAID

在系统监视器(在 Microsoft Windows NT® 4.0 中为性能监视器)中,可以获取逻辑磁盘驱动器和物理磁盘驱动器的信息。逻辑磁盘和物理磁盘的区别在于,在系统监视器中,逻辑磁盘与 Windows 读作逻辑驱动器盘符的内容相关联,物理磁盘与 Windows 读作一个物理硬盘的内容相关联。

在 Windows NT 4.0 中,默认情况下,性能监视器的所有磁盘计数器都是处于关闭状态的,因为它们可能会对性能略有影响。在 Windows 2000 中,默认情况下,物理磁盘计数器处于打开状态,逻辑磁盘计数器处于关闭状态。Diskperf.exe 是一个 Windows 命令,它控制可在系统监视器中查看的计数器的类型。

在 Windows 2000 中,要获取逻辑驱动器或存储卷的性能计数器数据,您必须在命令提示符下键入 diskperf -yv,然后按 Enter 键。这会导致用于收集磁盘性能数据的磁盘性能统计驱动程序报告逻辑驱动器或存储卷的数据。在默认情况下,操作系统使用 diskperf -yd 命令来获取物理驱动器的数据。

在 Windows 2000 中,Diskperf.exe 的语法如下所示:

diskperf [-y[d|v] | -n[d|v]] [\\computername]

参数

(none)

报告磁盘性能计数器是否处于启用状态并标识启用的计数器。

-y

将系统设置为在计算机重新启动时启动所有的磁盘性能计数器。

-yd

在计算机重新启动时启用物理驱动器的磁盘性能计数器。

-yv

在计算机重新启动时启用逻辑驱动器或存储卷的磁盘性能计数器。

-n

将系统设置为在计算机重新启动时禁用所有的磁盘性能计数器。

-nd

禁用物理驱动器的磁盘性能计数器。

-nv

禁用逻辑驱动器的磁盘性能计数器。

\\computername

指定要查看或设置要使用的磁盘性能计数器的计算机。

在 Windows NT 4.0 和更低版本中,diskperf –y 用于监视不使用 Windows NT 软件 RAID 的硬盘驱动器或者硬盘驱动器与 RAID 控制器的集和。在使用 Windows 软件 RAID 时,请使用 diskperf –ye,以便系统监视器将在 Windows NT 条带集之间正确地报告物理计数器。当结合使用 diskperf –ye 和 Windows NT 条带集时,逻辑计数器所报告的信息将不正确并且应当被忽略。如果必须将逻辑磁盘计数器信息与 Windows NT 条带集结合使用,请使用 diskperf –y。

在使用 diskperf –y 时,逻辑磁盘计数器会被正确地报告给 Windows NT 条带集,但是物理磁盘计数器所报告的信息将不正确并且应当被忽略。

注意 diskperf 命令要在重新启动了 Windows 之后才会起作用(Windows 2000 和 Windows NT 4.0 及更低版本均是如此)。

有关监视硬件 RAID 的注意事项

因为 RAID 控制器将多个物理硬盘驱动器作为一个 RAID 镜像集或条带集提供给 Windows,所以 Windows 就会像读取一个物理磁盘那样读取该分组。实际底层硬盘驱动器活动的最终抽象视图会导致性能计数器报告可能会起误导作用的信息。

从优化性能的角度看,知道一个 RAID 阵列关联了多少物理硬盘驱动器是很重要的。在确定 Windows 和 SQL Server 发送给每个物理硬盘驱动器的磁盘 I/O 请求数时,将需要此信息。将系统监视器报告为与某个硬盘驱动器相关联的磁盘 I/O 请求数除以该 RAID 阵列中已知的实际物理硬盘驱动器数。

要粗略估计 RAID 阵列中每个硬盘驱动器的 I/O 活动,一定还要将系统监视器报告的磁盘写入 I/O 数乘以 2(RAID 1 和 0+1)或 4 (RAID 5)。这将更精确地给出发送到物理硬盘驱动器的实际 I/O 请求数,因为正是在这个物理级别应用硬盘驱动器的 I/O 能力。但是,当硬盘 RAID 控制器使用缓存功能时,此方法无法精确计算硬盘驱动器 I/O,因为缓存功能会极大地影响对硬盘驱动器进行的直接 I/O。

在监视磁盘活动时,最好将重点关注磁盘队列,而不是每个磁盘的实际 I/O。磁盘 I/O 的速度取决于驱动器的传输速率,而这种速率是无法调整的。除了购买更快或更多的驱动器外,您没有什么其他措施,所以,关心实际发生的 I/O 量没有什么意义。但是,您又希望避免出现过多的磁盘队列。大量的磁盘队列表明您的 I/O 有问题。因为 Windows 不能读取 RAID 阵列中物理驱动器的数量,所以很难精确估计每个物理磁盘的磁盘队列。通过将磁盘队列长度除以参与所观察的逻辑驱动器的硬件 RAID 磁盘阵列的物理驱动器数,可以确定大致的近似值。对于 SQL Server 文件所在的硬盘驱动器,努力使磁盘队列数少于两个,是最理想的。

软件 RAID

Windows 2000 支持软件 RAID,在不使用硬件 RAID 控制器时,软件 RAID 通过操作系统来提供镜像集和条带集(具有或不具有容错功能),从而提供容错功能。您可以使用操作系统过程来设置 RAID 0、RAID 1 或 RAID 5 功能。多数大型数据仓库都使用硬件 RAID,但是,如果您的安装规模相对较小,或者您选择不实现硬件 RAID,那么,软件 RAID 可以带来一些数据访问和容错方面的优点。

软件 RAID 确实会占用一些 CPU 资源,因为 Windows 必须管理通常由硬件 RAID 控制器为您管理的 RAID 操作。因此,在磁盘驱动器数相同的情况下,Windows 软件 RAID 提供的性能会比硬件 RAID 低几个百分点,尤其是当系统处理器的使用率因其他目的而接近 100% 时。通过降低 I/O 瓶颈的可能性,与没有软件 RAID 时相比,Windows 软件 RAID 通常将帮助一组驱动器为 SQL Server I/O 提供更好的服务。如果使用软件 RAID,SQL Server 应该能够更好地利用 CPU,因为服务器通常等待 I/O 请求完成的时间会减少。

磁盘 I/O 并行度

为了改善存储在多个磁盘驱动器上的大型 SQL Server 数据库的性能,一个有效的方法是创建磁盘 I/O 并行机制,该机制同时对多个磁盘驱动器进行读写操作。RAID 通过硬件和软件实现磁盘 I/O 并行度。下一个主题讨论使用分区来组织 SQL Server 数据以进一步增加磁盘 I/O 并行度。

使用分区来提高性能

对于存储在多个磁盘驱动器上的 SQL Server 数据库,可通过对数据进行分区以增加磁盘 I/O 并行度来改善性能。

可使用多种方法来进行分区。分区的创建和管理方法包括配置存储子系统(磁盘、RAID 分区)和在 SQL Server 中应用各种数据配置机制(例如,文件、文件组、表和视图)。虽然本节重点介绍一些与性能相关的分区功能,但是第 18 章“在 SQL Server 2000 数据仓库中使用分区”也特别介绍了分区主题。

创建磁盘 I/O 并行度的最简单方法是,使用硬件分区并创建一个为所有的 SQL Server 数据库文件(事务日志文件除外,它们总是应当存储在从物理上分开且仅专用于日志文件的磁盘驱动器上)提供服务的驱动器池。驱动器池可以是一个 RAID 阵列,它在 Windows 中呈现为一个物理驱动器。可以使用多个 RAID 阵列和 SQL Server 文件/文件组来设置较大的池。可以将一个 SQL Server 文件与每个 RAID 阵列相关联,并将这些文件组合成一个 SQL Server 文件组。然后,可基于该文件组构建一个数据库,以便将数据均匀地分布到所有的驱动器和 RAID 控制器上。驱动器池方法依赖 RAID 在所有的物理驱动器之间划分数据,这样有助于确保在数据库服务器操作过程中对该数据进行并行访问。

该驱动器池方法简化了 SQL Server I/O 的性能优化,因为数据库管理员知道只有一个物理位置可供创建数据库对象。可监视单个驱动器池的磁盘队列情况,必要时可向该池中添加更多的硬盘驱动器以防出现磁盘排队现象。一般情况下,无法确定数据库哪些部分的利用率最高,此时使用该方法有助于优化性能。最好不要只是因为 SQL Server 可能要用 5% 的时间来对另一磁盘分区进行 I/O 而将总体可用 I/O 能力的一部分隔离到该磁盘分区上。“单个驱动器池”方法有助于使所有可用的 I/O 能力对于 SQL Server 操作“始终”可用。它还允许 I/O 操作分布到最大数量的可用磁盘上。

SQL Server 日志文件始终 都应该从物理上分散到不同的硬盘驱动器,与所有其他 SQL Server 数据库文件分开。对于管理多个繁忙数据库的极其繁忙的 SQL Server 来说,每个数据库的事务日志文件应当在物理上互相分离,以减少争用现象。

由于事务日志记录主要是顺序写入 I/O,所以将日志文件分开往往会显著提高 I/O 性能。包含日志文件的磁盘驱动器可以非常高效地执行这些顺序写入操作,但前提是这些操作不被其他 I/O 请求中断。有时,将需要在 SQL Server 操作(例如,复制、回滚和延迟更新)过程中读取事务日志。有些实现通过将新数据几乎实时地装载到数据仓库中,将复制用作其数据转换实用工具的前端。参与复制的 SQL Server 的管理员需要确保所有用于事务日志文件的磁盘都有足够的 I/O 处理能力,以便处理除正常日志事务写入之外需要发生的读取操作。

物理上分割的文件和文件组需要额外的管理工作。事实证明,为了隔离和改善对非常活动的表或索引的访问而进行分割时,这些额外的工作是值得的。下面列出了一些益处:

  • 对于特定对象的 I/O 需求,可以进行更准确的评估,而如果所有数据库对象都放在一个大驱动器池中,进行这种评估就不那么容易了。
  • 使用文件和文件组对数据和索引进行分区,可以增强管理员创建粒度更细的备份和恢复策略的能力。
  • 文件和文件组可用于维护数据在磁盘上的顺序放置,从而减少或消除非顺序的 I/O 活动。如果数据装载到数据仓库的可用时间窗口要求并行执行处理以满足最终期限,则该功能就变得尤其重要。
  • 在数据库开发和基准检验阶段,可能适于对文件和文件组进行物理分割,这样可收集数据库 I/O 信息并将其应用于生产数据库服务器环境的容量计划。

有关对象分区的注意事项

可以在不同的硬盘驱动器、RAID 控制器和 PCI 通道(或者三者的组合)之间分隔以下方面的 SQL Server 活动:

  • 事务日志
  • tempdb
  • 数据库
  • 非聚集索引

注意 在 SQL Server 2000 中,Microsoft 增强了分布式分区视图,使用这种视图可以创建联合数据库(通常称作扩展),这种数据库会将资源负荷和 I/O 活动分布到多个服务器上。联合数据库适于某些高端联机分析处理 (OLTP) 应用程序,但是建议不要使用该方法来解决数据仓库的需求。

使用硬件 RAID 控制器、RAID 热插拔驱动器和联机 RAID 扩展功能可以轻松实现对 SQL Server I/O 活动的物理分割。最灵活的方法是排列 RAID 控制器,让单独的 RAID 通道与上述不同活动方面相关联。同样,应当将每个 RAID 通道连接到一个单独的 RAID 热插拔机柜,以便充分利用联机 RAID 扩展功能(如果可通过 RAID 控制器使用该功能)。随后,Windows 逻辑驱动器盘符将会与每个 RAID 阵列相关联,并且 SQL Server 文件会基于已知的 I/O 使用模式在不同的 RAID 阵列之间被分隔开。

使用这种配置,有可能将与每个活动相关联的磁盘队列重新与一个不同的 RAID 通道及其驱动器机柜相关联。如果某个 RAID 控制器及其驱动器阵列机柜均支持联机 RAID 扩展功能,而且机柜中有热插拔硬盘驱动器的插槽,则只需向 RAID 阵列中添加更多的驱动器,直到系统监视器报告该 RAID 阵列的磁盘队列已经达到可接受的程度(对于 SQL Server 文件最好少于两个),即可解决该 RAID 阵列的磁盘队列问题。这可以在 SQL Server 联机时完成。

分离事务日志

维护事务日志文件的存储设备应该在物理上与数据文件所在的设备分开。根据您的数据库恢复模型设置不同,大多数更新活动既产生数据设备活动又产生日志活动。如果将这两个活动设置为共享同一个设备,则要执行的操作将争用同一个有限资源。大多数安装都受益于将这些竞争 I/O 活动分开。

分离 tempdb

SQL Server 会在每个服务器实例上创建一个名为 tempdb 的数据库,以供服务器用作各种不同活动的共享工作区,这些活动包括:临时表、排序、处理子查询、生成聚合以支持 GROUP BY 或 ORDER BY 子句、使用 DISTINCT 的查询(必须创建临时工作表才能删除重复行)、游标,以及哈希联接。通过将 tempdb 分割到其自己的 RAID 通道上,我们使 tempdb I/O 操作能够与它们的相关事务的 I/O 操作并行发生。由于 tempdb 实际上是一个草稿区域,而且更新频繁,所以 RAID 5 对于 tempdb 并不是好的选择,而 RAID 1 或 0+1 提供的性能更好。虽然 Raid 0 不提供容错功能,但可以考虑将它用于 tempdb,因为每次重新启动数据库服务器时都会重新生成 tempdb。RAID 0 使用最少的物理驱动器为 tempdb 带来了最佳的 RAID 性能,但在生产环境中将 RAID 0 用于 tempdb 时主要的顾虑是:如果有物理驱动器(包括用于 tempdb 的驱动器)出现故障,就可能影响到 SQL Server 的可用性。如果将 tempdb 放在具备容错能力的 RAID 配置上,就可以避免这一点。

要移动 tempdb 数据库,请使用 ALTER DATABASE 命令更改与 tempdb 相关联的 SQL Server 逻辑文件名的物理文件位置。例如,要将 tempdb 以及与之相关联的日志移到新文件位置 E:\mssql7 和 C:\temp,请使用以下命令:

alter database tempdb modify file (name='tempdev',filename=
'e:\mssql7\tempnew_location.mDF')
alter database tempdb modify file (name='templog',filename=
'c:\temp\tempnew_loglocation.mDF')

与用户数据库相比,master 数据库 msdb 和model 数据库在生产过程中很少使用,因此,在考虑优化 I/O 性能时,通常不必考虑它们。master 数据库通常只用于添加新登录、数据库、设备和其他系统对象。

数据库分区

可以使用文件和/或文件组对数据库进行分区。文件组只是为管理目的而将多个单独的文件组合在一起的命名集合。一个文件不能是多个文件组的成员。表、索引、textntextimage 数据都可以与一个特定的文件组相关联。这就是说,它们所有的页都是从该文件组中的文件中分配而来的。下面介绍三种类型的文件组。

主文件组

该文件组包含主数据文件以及未放到另一个文件组中的所有其他文件。系统表的所有页都是从主文件组分配的。

用户定义的文件组

该文件组是使用 CREATE DATABASE 或 ALTER DATABASEfilegroup 语句中的 FILEGROUP 关键字或者在 SQL Server 企业管理器中的属性对话框上指定的任何文件组。

默认文件组

默认文件组包含在创建时未指定文件组的所有表和索引的页。在每个数据库中,每次只能有一个文件组是默认文件组。如果未指定默认文件组,则主文件组就是默认文件组。

文件和文件组对于控制数据和索引的位置以及消除设备争用现象很有用。有相当一部分安装还将文件和文件组用作一种比数据库粒度更细的机制,以便对它们的数据库备份/恢复策略进行更多的控制。

水平分区(表)

水平分区将一个表分割成多个表,每个表都包含相同的列数,但是行数会减少。怎样对表进行水平分区要根据分析数据的方式而定。根据一般经验,在对表进行分区时,应当使查询引用的表尽可能少。否则,用于在查询时按逻辑合并表的 UNION 查询就会过多,从而会影响性能。

例如,假定企业要求规定:我们要将十年来不断滚动的事务数据存储到我们数据仓库的中央事实表中。我们公司十年来的事务数据意味着数据会超过十亿行。数量达到十亿的任何内容管理起来都会很困难。现在,请考虑每年我们都必须除去第十年的数据,然后装载最新一年的数据。

管理员通常采用的方法是:创建十个独立但结构相同的表,每个表中存放一年的数据。然后,管理员在这十个表的基础上定义一个联合视图,以便让最终用户看到所有数据都放在一个表中。实际上并非如此。针对该视图执行的任何查询都被优化成只搜索指定的年份(和相应的表)。不过,管理员确实获得了管理能力。现在,管理员能够以粒度方式单独管理每年的数据。每年的数据都可以单独装载、索引或维护。添加新年份就是这样简单:除去该视图,除去包含第十年数据的表,装载和索引新年份的数据,然后重新定义新视图以包括新年份的数据。

当您在多个表或多个服务器之间对数据进行分区时,只访问部分数据的查询运行得更快,因为要扫描的数据比较少。如果这些表位于不同的服务器上,或者在一台具有多个处理器的计算机上,还可以并行扫描查询所涉及的每个表,从而改善查询性能。另外,维护任务(例如,重建索引或备份表)的执行速度会更快。

通过使用分区视图,数据仍显示为一个表,而且在查询数据时可以不必手动引用相应的基础表。如果满足下列任一条件,分区视图就可以进行更新。有关分区视图及其限制的详细信息,请参阅“SQL Server 联机丛书”。

  • 在该视图上用可支持 INSERT、UPDATE 和 DELETE 语句的逻辑定义了 INSTEAD OF 触发器。
  • 该视图以及 INSERT、UPDATE 和 DELETE 语句遵循为可更新的分区视图定义的规则。

分离非聚集索引

索引驻留在 B 型树结构中,通过使用 ALTER DATABASE 命令来设置一个不同的文件组,这些索引可以与它们的相关数据库表分开(聚集索引除外)。在下面的示例中,第一个 ALTER DATABASE 创建一个文件组。第二个 ALTER DATABASE 向新创建的文件组中添加一个文件。

alter database testdb add filegroup testgroup1
alter database testdb add file (name = 'testfile',
filename = 'e:\mssql7\test1.ndf') to filegroup testgroup1

在创建了文件组及其关联的文件之后,可以在创建索引时指定该文件组,从而使用该文件组来存储索引。

create table test1(col1 char(8))
create index index1 on test1(col1) on testgroup1

SP_HELPFILE 会将有关给定数据库中文件和文件组的信息反馈回来。SP_HELP <表名> 的输出结果中有一节,该节提供有关表的索引及其文件组关系的信息。

sp_helpfile
sp_help test1

并行数据检索

SQL Server 在具有多个处理器的计算机上运行时可以并行扫描数据。如果一个表在包含多个文件的文件组中,则可以对该表执行多个并行扫描。只要按顺序访问某个表,就会创建一个独立线程来并行读取每个文件。例如,如果完全扫描在包含四个文件的文件组上创建的表,将会使用四个独立线程来并行读取数据。因此,为每个文件组创建多个文件会有助于提高性能,因为这样会使用独立的线程来并行扫描每个文件。同样,当某个查询联接着不同文件组上的表时,可以并行读取每个表,从而改进查询性能。

另外,表中的任何 textntextimage 列都可以在除基表所在文件组以外的文件组上创建。

最终,文件过多会导致并行线程过多,进而导致磁盘 I/O 子系统中出现瓶颈,这时就会达到饱和点。通过使用系统监视器来监视 PhysicalDisk 对象和磁盘队列长度计数器,可以确定这些瓶颈。如果磁盘队列长度计数器大于 3,请考虑减少文件数量。

为了通过使用多个文件并行访问数据来提高吞吐量,将尽可能多的数据分布到尽可能多的物理驱动器上是很有益处的。要将数据均匀地分布到所有磁盘上,请首先设置基于硬件的磁盘条带化,然后根据需要使用文件组将数据分布到多个硬件条带集上。

并行查询建议

SQL Server 可自动以并行方式执行查询。这样就会对在多处理器计算机上执行查询进行优化。工作会细分为多个线程(受线程和内存的可用性影响),而不是一个查询用一个操作系统线程执行,这样,完成复杂查询时就会速度更快,效率更高。

SQL Server 中的优化器会为查询生成计划并确定将在何时并行执行查询。确定时会依据以下条件:

  • 计算机是否有多个处理器?
  • 是否有足够的内存来并行执行查询?
  • 服务器上的 CPU 负荷是多少?
  • 正在运行哪种类型的查询?

如果允许 SQL Server 以并行方式运行并行操作(例如 DBCC 和创建索引),对服务器资源的压力就会变重,而且在执行繁重的并行操作任务时,您可能会看到警告信息。如果服务器错误日志中经常出现有关资源不足的警告消息,请考虑使用系统监视器来调查哪些资源(例如,内存、CPU 使用率和 I/O 使用率)可用。

当服务器上有活动用户时,请不要并行运行大量查询。请尝试在没有负载的时间段中执行维护作业(例如,DBCC 和创建索引)。这些作业可以并行执行。监视磁盘 I/O 性能。观察系统监视器(在 Windows NT 4.0 中为性能监视器)中的磁盘队列长度,确定是升级硬盘还是将数据库重新分布到不同的磁盘上。如果 CPU 的使用率非常高,请升级或添加更多的处理器。

下列服务器配置选项可能会影响查询的并行执行:

  • 并行度的成本阈值
  • 最大并行度
  • 最大工作线程
  • 查询调控器成本限制

优化数据负荷

在加速数据装载活动时,一定要记住多种提示和方法。根据您执行的是初始数据装载还是增量数据装载,这些方法可能会有所不同。一般来说,增量装载更复杂且限制性更强。您选择的方法可能还基于您无法控制的因素。处理窗口要求、所选存储配置、服务器硬件的限制等都会影响可供您使用的选项。

在执行初始数据装载和增量数据装载时,有一些共同的要点需要记住。下面将详细讨论以下主题:

  • 选择适当的数据库恢复模型
  • 使用 bcp、BULK INSERT 或大容量复制 API
  • 控制锁定行为
  • 并行装载数据
  • 杂项,其中包括:
    • 绕过引用完整性检查(约束和触发器)
    • 装载预先排序的数据
    • 删除索引带来的影响

选择适当的数据库恢复模型

我们已在“影响性能的配置选项”一节中讨论了数据库恢复模型。一定要记住所选恢复模型对执行数据装载所需的时间可能会有很大的影响。这些恢复模型主要控制将写出到事务日志中的数据量。因为对事务日志执行写入操作基本上会使工作负荷加倍,所以这非常重要。

日志记录和最小日志记录大容量复制操作

在使用完全恢复模型时,由某个大容量数据装载机制(将在下面讨论)执行的所有插入行操作都记录到事务日志中。对于大型数据装载,这可能会导致快速填充事务日志。为了帮助防止事务日志的空间不足,可执行最小日志记录大容量复制操作。是以日志记录还是以无日志记录形式执行大容量复制不作为大容量复制操作的一部分来指定;它取决于大容量复制中涉及到的数据库和表的状态。如果符合以下所有的条件,将进行无日志记录的大容量复制:

  • 恢复模型是“简单”或“大容量日志记录的”,或者数据库选项 select into/bulkcopy 设置为真。
  • 目标表未在进行复制。
  • 目标表没有索引,或者如果目标表有索引,在开始大容量复制时它也是空的。
  • TABLOCK 提示是在将 eOption 设置为 BCPHINTS 的情况下使用 bcp_control 指定的。

任何不满足上述条件的向 SQL Server 实例中进行的大容量复制将完全记录下来。

在执行初始数据装载时,应当总是在“大容量日志记录的”或“简单”恢复模型下运行。对于增量数据装载,只要数据丢失的可能性很低,就考虑使用“大容量日志记录的”模型。因为许多数据仓库基本上都是只读的或者事务活动的数量很少,所以将数据库恢复模型设置为“大容量日志记录的”不会产生任何问题。

使用 bcp、BULK INSERT 或大容量复制 API

SQL Server 内部存在两个机制,用来解决大容量移动数据的需求。第一个机制是 bcp 实用工具。第二个机制是 BULK INSERT 语句。bcp 是一个命令提示符实用工具,它既将数据复制到 SQL Server 中又从其中复制数据。在 SQL Server 2000 中,bcp 实用工具是用 ODBC 大容量复制应用程序编程接口 (API) 重新编写的。bcp 实用工具的早期版本是使用 DB-Library 大容量复制 API 编写的。

BULK INSERT 是 SQL Server 附带的 Transact-SQL 语句,该语句可从数据库环境内执行。与 bcp 不同的是,BULK INSERT 只能将数据拉入 SQL Server 中。它不能将数据推出。使用 BULK INSERT 的一个好处在于,它可以使用 Transact-SQL 语句将数据复制到 SQL Server 的实例中,而不必退出解释器转到命令提示符中。

第三个选项是大容量复制 API,程序员通常对该选项很感兴趣。有了这些 API,程序员就能够使用 ODBC、OLE DB、SQL-DMO 或者甚至基于 DB 库的应用程序将数据移入或移出 SQL Server。

所有这些选项都使您能够对批处理大小进行控制。除非您使用的是小容量数据,否则最好习惯于指定批处理大小以进行恢复。如果未指定批处理大小,则 SQL Server 将所有要装载的行作为一批提交。例如,您尝试将 1,000,000 行新数据装载到某个表中。服务器在处理完第 999,999 行后突然断电。当服务器恢复时,将需要从数据库中回滚处理完的 999,999 行,然后再尝试重新装载数据。您可以通过将批处理大小指定为 10,000 来大大节省自己的恢复时间,这是由于您已经将 1 到 990,000 行提交到数据库中,因此将只需回滚 9,999 行(而不是 999,999 行)。同样,如果未指定批处理大小,则将必须从第 1 行重新启动装载处理才能重新装载数据。如果将批处理大小指定为 10,000 行,则只需从第 990,001 行重新启动装载处理,这样就高效地绕过了已经提交的 990,000 行。

控制锁定行为

bcp 实用工具和 BULK INSERT 语句接受 TABLOCK 提示,该提示允许用户指定要使用的锁定行为。TABLOCK 指定在大容量复制操作过程中将采用大容量更新表级锁。使用 TABLOCK 可以减少表上对锁的争用,从而改进大容量复制操作的性能。当针对单个表处理并行装载时,该设置有非常重要的含义(将在下一节讨论)。

例如,要将 Authors.txt 数据文件中的数据大容量复制到 pubs 数据库中的 authors2 表中,请指定表级锁,并从以下命令行提示符执行:

bcp pubs..authors2 in authors.txt -c -t, -Sservername -Usa -Ppassword -h "TABLOCK"

或者,您可以从查询工具(如 SQL 查询分析器),使用 BULK INSERT 语句来大容量复制数据,如下例所示:

BULK INSERT pubs..authors2 FROM 'c:\authors.txt'
WITH (
DATAFILETYPE = 'char',
FIELDTERMINATOR = ',',
TABLOCK
)

如果未指定 TABLOCK,除非对于表将 table lock on bulk load 选项设置为 on,否则默认锁定会使用行级锁。将 table lock on bulk load 选项与 sp_tableoption 命令一起使用,也可以设置大容量装载操作过程中表的锁定行为。

Table lock on bulk load 表的锁定行为
Off 使用行级锁
On 使用表级锁

注意 如果指定了 TABLOCK 提示,则在大容量装载过程中,它将替代使用 sp_tableoption 声明的设置。

并行装载数据

并行装载 — 非分区表

使用 SQL Server 中的任一大容量数据装载机制,都可以将数据并行装载到一个非分区表中。这是通过同时运行多个数据装载来完成的。在开始装载之前,需要将要并行装载的数据拆分成多个独立文件(大容量插入 API 的数据源)。然后,可同时启动所有的独立装载操作,以便并行装载数据。

例如,假设您需要为在全球四个地区运作的服务公司装载合并的数据库,每个地区每个月都报告寄给客户的帐单上的报告时间(小时)。对于大型服务组织,这可能表示需要合并大量事务数据。如果这四个报告地区都分别提供独立文件,则可以使用上面介绍的方法将这四个文件同时装载到一个表中。

注意 并行处理的并行线程(装载)的数量不应超过 SQL Server 的可用处理器数。

下面的插图说明了对非分区表进行的并行装载。


如果您的浏览器不支持嵌入式框架,请单击此处在单独的页中查看。

并行装载:水平分区(表)

本节重点介绍如何使用水平分区表来提高数据装载的速度。在上一节中,我们讨论了将数据从多个文件装载到一个(非分区)表中。如果对表进行水平分区,则可以减少设备争用现象,从而有机会改善数据的连续性并加速装载过程。虽然上图显示的是数据装载到了表的不同部分中,但这样的表述可能不准确。如果上述装载中的所有三个线程是同时处理的,为该表提取的扩展盘区最后就可能会是混合状态。在数据混合后,可能导致在检索数据时无法实现最佳性能。这是由于数据不是按物理上连续的顺序存储的,从而可能导致系统使用不连续的 I/O 访问它。

在该表基础上生成聚集索引将解决上述问题,因为数据会按连续顺序被读入、按键顺序排序,并被回写。但是,读取、排序、删除旧数据以及将新排序的数据回写可能是一项非常耗时的任务(请参阅下面的装载预先排序的数据)。为避免出现这种混合的情形,请考虑使用文件组在可以存储大表的位置保留多块连续空间。许多安装还使用文件组将索引数据与表数据分开。

为便于阐述,假定有一个数据仓库分配在一个大型物理分区上。任何对该数据库并行执行的装载操作都有可能导致以非连续(混合)状态存储受影响的数据/索引页。将执行哪种操作?任何对数据进行修改的操作都将导致数据变得不连续。为了满足处理窗口的要求,用户可能会尝试并行执行初始数据装载、增量数据装载、索引创建、索引维护、插入、更新、删除等活动。

下面的插图显示的是跨多个文件组对表分区。


如果您的浏览器不支持嵌入式框架,请单击此处在单独的页中查看。

装载预先排序的数据

SQL Server 的早期版本提供了一个选项,您可以在创建索引时用它来指定 SORTED_DATA 选项。SQL Server 2000 取消了这个选项。在早期版本中,将该选项指定为 CREATE INDEX 语句的一部分是:它能够让您在索引创建过程中避免排序的步骤。默认情况下,在 SQL Server 中创建聚集索引时,表中数据会在处理过程中排序。要在 SQL Server 2000 中获得同样的效果,请考虑在大容量装载数据之前创建聚集索引。SQL Server 2000 中的大容量操作使用了增强的索引维护策略,这样,对于已具有聚集索引的表,可以改进数据导入性能,而且在导入之后不需要对数据重新排序。

FILLFACTOR 和 PAD_INDEX 对数据装载的影响

FILLFACTOR 和 PAD_INDEX 将在题为“索引和索引维护”的一节中更完整地介绍。对于 FILLFACTOR 和 PAD_INDEX,都需要记住关键的一点:创建索引时,如果将它们保留为默认值设置,可能会导致 SQL Server 为存储数据而执行比必需数量多的写入和读取 I/O 操作。如果数据仓库中没有发生多少写入活动但发生了大量读取活动,则更是如此。要让 SQL Server 在一页数据页或索引页中写入更多的数据,您可以在创建索引时指定特定的 FILLFACTOR。最好在提供覆盖 FILLFACTOR 值时指定 PAD_INDEX。

初始数据装载的一般准则

在装载数据时

  • 删除索引(唯一的例外可能是在装载预先排序的数据时,请参阅上文)
  • 使用 BULK INSERT、bcp 或大容量复制 API
  • 使用分区数据文件并行装载到分区表中
  • 对于每个可用的 CPU 运行一个装载流
  • 设置“大容量日志记录的”或“简单”恢复模型
  • 使用 TABLOCK 选项

在装载数据之后

  • 创建索引
  • 切换到相应的恢复模型
  • 执行备份

增量数据装载的一般准则

  • 用索引将数据装载到适当位置。
  • 应当根据性能和并发要求来确定锁定粒度 (sp_indexoption)
  • 除非特别需要保留时点恢复(例如,联机用户在大容量装载过程中修改数据库),否则请将恢复模型从“完全”更改为“大容量日志记录的”。读取操作不应当影响大容量装载。

索引和索引维护

前面已经讨论了服务器硬件设备的 I/O 特征。现在,我们将讨论 SQL Server 数据和索引结构在物理上是如何放置在磁盘驱动器上的。如果要在设计完成之后改善性能,则索引位置有可能是影响数据仓库的一个最大因素。

SQL Server 中的索引类型

虽然 SQL Server 2000 引入了几种新索引类型,但它们全部都基于两个核心窗体。这两个核心窗体的格式是聚集索引或非聚集索引。在 SQL Server 中,数据库设计人员可以使用以下两种主要类型的索引:

  • 聚集索引。
  • 非聚集索引。

这两个主要类型的其他变体包括:

  • 唯一索引。
  • 计算列的索引。
  • 索引视图。
  • 全文索引。

以下各节将详细介绍上面提到的每种索引(全文索引除外)。全文索引是一种特殊情况,它与其他数据库索引不同,本章不对它进行介绍。索引视图是 SQL Server 2000 中新引入的一种索引,它应该会引起数据仓库用户的特别关注。SQL Server 2000 中引入的另一项新功能是按升序或降序创建索引。

索引的工作原理

数据库中的索引类似于图书中的索引。在一本书中,您使用索引可以迅速找到信息,而不必读完全书。在一个数据库中,数据库程序使用索引可以找到表中的数据,而不必扫描整个表。书中的索引是一个字词以及各字词所在页码的列表。数据库中的索引是表中的值以及各值存储位置(在表中所在的行)的列表。

索引可以针对表中的一列或一组列创建,并以 B 树的形式实现。索引包含一个条目以及一个或多个对应于表中每一行的列(搜索键)。B 树根据搜索键的排序次序按升序或者降序(视创建索引时所选选项而定)存储,利用该搜索键的任何前导子集,可以高效地搜索到 B 树。例如,利用以下组合,可以高效地搜索 ABC 列的索引:AABABC

当您创建数据库并优化其性能时,应当为查询中使用的列创建用来查找数据的索引。在 SQL Server 附带的 pubs 示例数据库中,在 employee 表的 emp_id 列有一个索引。当用户执行的语句根据指定的 emp_id 值在 employee 表中查找数据时,SQL Server 查询处理器识别 emp_id 列的索引并使用该索引来查找数据。下面的插图说明了该索引如何存储每个 emp_id 值并指向表中具有相应值的数据所在的行。


如果您的浏览器不支持嵌入式框架,请单击此处在单独的页中查看。

但是,带索引的表需要在数据库中占用更多的存储空间。同样,用来插入、更新或删除数据的命令的运行时间以及维护索引所需的处理时间会更长。您在设计和创建索引时,一定要注意:性能益处比存储空间和处理资源所导致的额外成本更加重要。

索引交集

SQL Server 查询处理器中有一项独特的功能:执行索引交集。这是一种特殊形式的索引覆盖,我们将在以后详述,但是现在因以下两个原因而需要提及索引交集。第一,它是一种可能会影响您的索引设计策略的技术。

第二,该技术可能会减少您需要的索引数,从而可以大大节省大型数据库占用的磁盘空间。

索引交集允许查询处理器使用多个索引来解决查询。大多数数据库查询处理器在尝试解决查询时都只使用一个索引。SQL Server 可以组合给定表或视图中的多个索引,基于这些索引生成哈希表并利用哈希表来减少给定查询的 I/O 操作。就本质而言,从索引交集中生成的哈希表变成了覆盖索引,而且,它提供的 I/O 性能与覆盖索引提供的相同。在数据库用户环境中,很难预先确定将针对该数据库运行的所有查询,而索引交集为这种环境提供了更大的灵活性。在这种情况下,较好的策略是针对所有经常会被查询的列定义单列的非群集索引,并让索引交集处理来需要覆盖索引的情形。

下面的示例使用了索引交集:

Create index Indexname1 on Table1(col2)
Create index Indexname2 on Table1(col3)
Select col3 from table1 where col2 = 'value'

在执行上面的查询时,可以通过组合这些索引来快速高效地解决该查询。

SQL Server 中的索引结构

SQL Server 中所有的索引在物理上都是基于存储在 8 KB 索引页上的 B 树索引结构构建的。每个索引页都有一个页头,页头后面是索引行。每个索引行都包含一个键值和一个指向行级索引页或实际数据行的指针。索引中的每一页又被称作一个索引节点。B 树的顶层节点被称作根节点。索引中的底层节点被称作叶节点。根和叶之间的任何索引层都统称为中级层或节点。每层索引中的页都在双向链接列表中链接在一起。

SQL Server 数据页和索引页的大小均为 8 KB。SQL Server 数据页包含所有与表中某行关联的数据(文本和图像数据可能除外)。就文本和图像数据而言,在默认情况下,包含与该文本或图像列关联的行的 SQL Server 数据页将包含一个指针,该指针指向一个或多个包含该文本或图像数据的 8 KB 页的二进制树(或 B 树)结构。SQL Server 2000 中的一个新功能是能够将小型文本和图像值存储在行中,这意味着小型文本或图像列将存储在数据页上。因为可以避免提取相应的图像或文本数据所必需的额外 I/O,所以该功能可以减少 I/O 操作。有关如何将表设置为在行中存储文本或图像的信息,请参阅“SQL Server 联机从书”。

聚集索引

聚集索引对于从表中检索一定范围的数据值非常有用。非聚集索引最适于检索特定行,而聚集索引最适于检索一定范围的行。但是,由于每个表只允许使用一个聚集索引,因此按照这个简单的逻辑来确定要创建哪种类型的索引并不总能成功。对于该问题有一个简单的物理原因。对于聚集索引 B 树结构的上部(非叶层),如果像对它们的非聚集索引部分那样组织,则聚集索引的底层由表的实际 8 KB 数据页组成。但这种情况有一个例外,那就是在视图的基础上创建聚集索引时。因为将在下面介绍索引视图,所以我们将讨论针对实际表创建的聚集索引。在针对表创建聚集索引时,会按与索引搜索键相同的顺序读取与该表关联的数据、对这些数据进行排序,并会在物理上将它们存回数据库。因为该表的数据只能按照一种顺序保存到存储器中,不会导致重复,所以符合一个聚集的限制。

下图描述了聚集索引的存储器。


如果您的浏览器不支持嵌入式框架,请单击此处在单独的页中查看。

聚集索引和性能

聚集索引有一些会影响性能的固有特征。

在使用聚集索引根据搜索键来检索 SQL Server 数据时,不需要指针跳转(会导致硬盘上的位置可能不按顺序更改)来检索关联的数据页。这是由于聚集索引的叶层实际上就是关联的数据页。

如前所述,叶层(当然也包括表或索引视图的数据)在物理上会按照与搜索键相同的顺序进行排序和存储。因为聚集索引的叶层包含表的实际 8 KB 数据页,所以整个表的行数据会按照由聚集索引确定的顺序以物理方式排列在磁盘驱动器上。这就会在根据聚集索引的值从该表中提取大量行(至少大于 64 KB)时带来潜在的 I/O 性能优势,因为使用的是顺序磁盘 I/O(除非该表上发生了页拆分,这种情况将在题为“FILLFACTOR 和 PAD_INDEX”的一节中讨论)。正因为如此,所以在检索大量行时,一定要根据将用于执行范围扫描的列来对表选取聚集索引。

表中与聚集索引相关联的行必须按照与索引搜索键相同的顺序排序和存储,这一点具有以下意义:

  • 在您创建聚集索引时,表会被复制,表中的数据会被排序,然后,原来的表会被删除。所以,数据库中必须有足够的空闲空间来存放数据的副本。
  • 在默认情况下,会在创建索引时对表中的数据进行排序。但是,如果数据已按正确顺序排过序,则会自动跳过排序操作。这样就可以显著加快索引创建过程。
  • 将数据装载到表中时的顺序应尽可能与您计划用于生成聚集索引的搜索键的顺序相同。对于大表(例如那些通常会成为数据仓库特征的表),该方法将大大加速索引创建过程,从而缩短您处理初始数据装载所需的时间。只要表中的行仍保持未创建聚集索引时所排的顺序,就可以在除去和重建聚集索引时可以使用该方法。任何行排序有误,操作都会被取消,会出现相应的错误信息,而且不会创建索引。
  • 同样,针对排过序的数据生成聚集索引时所需要的 I/O 也少得多,这是因为不必复制数据、对数据进行排序、将数据存回数据库,然后删除旧表数据,而是会将数据留在原来分配给它的扩展盘区中。索引扩展盘区只是添加到数据库中来存储顶层节点和中间节点。

注意 针对大表生成索引的首选方法是:先生成聚集索引,然后生成非聚集索引。这样,就不会因为数据移动而需要重新生成非聚集索引。在除去所有索引时,首先会除去非聚集索引,最后除去聚集索引。这样,就不需要重新生成索引。

非聚集索引

非聚集索引最适于根据特定的键值,从大型 SQL Server 表中提取少数几个具有良好选择性的行。如前所述,非聚集索引是由 8 KB 索引页形成的二进制树。索引页二进制树的底层或叶层包含组成该索引的列中的所有数据。在使用非聚集索引根据键值的匹配项从表中检索信息时,会遍历索引的 B 树,直到在索引的叶层找到键的匹配项。如果需要表中不构成索引的列,指针就会跳转。这种指针跳转将有可能需要针对磁盘执行非顺序 I/O 操作。它甚至可能需要从另一磁盘中读取数据,尤其是在表及其伴随的索引 B 树很大时。如果多个指针指向同一个 8 KB 数据页,对 I/O 性能的影响就会比较小,因为只需将该页读入数据缓存一次。如果 SQL 查询涉及到用非聚集索引进行搜索,则对于对该查询返回的每一行,至少需要一次指针跳转。

注意 由于指针每次跳转都会带来与之相关的开销,因此非聚集索引更适于处理从表中只返回一行或几行的查询。聚集索引更适于处理需要一系列行的查询。

下图说明了非聚集索引的存储。请注意,添加的叶层指向对应的数据页。在使用非聚集索引而不是聚集索引来访问表数据时,添加的指针跳转就会在那里起作用。有关非聚集索引的更多信息,请参阅“SQL Server 联机丛书”。


如果您的浏览器不支持嵌入式框架,请单击此处在单独的页中查看。

唯一索引

聚集索引和非聚集索引均可用于强制表内的唯一性,方法是在现有表上创建索引时指定 UNIQUE 关键字。确保表内唯一性的另一种方法是使用 UNIQUE 约束。如同唯一索引,UNIQUE 约束强制一组列中各值的唯一性。实际上,UNIQUE 约束的赋值自动创建基础唯一索引,以利于强制该约束。由于唯一性可以作为 CREATE TABLE 语句的一部分来加以定义和记录,因此,UNIQUE 约束通常优先于单独唯一索引的创建。

计算列上的索引

SQL Server 2000 引入了在计算列上创建索引的功能。如果查询是以一般方式提交的,而且会例行提供计算列,但管理员不愿意只是为了允许创建索引而在实际的表列中持续存放数据,在这样的情况下,使用这项功能就会很方便。在此情况下,只要计算列满足索引所需的全部条件,就可以通过引用计算列来创建索引。其他限制包括,计算列表达式必须有确定性、精确,并且不得取值为 textntextimage 数据类型。

确定性

如果要在视图或计算列上创建索引,视图和计算列均无法唤醒调用没有确定性的用户定义函数。所有函数要么有确定性,要么没有确定性:

  • 无论何时使用一组特定的输入值调用有确定性的函数,这些函数总是会返回相同的结果。
  • 每次用特定的一组输入值调用没有确定性的函数时,这些函数返回的结果可能各不相同。

例如,DATEADD 内置函数有确定性,因为对于通过该函数的三个输入参数传入的一组给定参数值,它始终返回可预测的结果。GETDATE 没有确定性。虽然始终用相同的参数值唤醒调用 GETDATE 函数,但每次执行调用返回的值各不相同。

精确

如果满足下列条件,说明计算列表达式是精确的:

  • 它不是 float 数据类型的表达式。
  • 它不在自己的定义中使用 float 数据类型。例如,在下面的语句中,列 yint 并且有确定性,但不精确。

    CREATE TABLE t2 (a int, b int, c int, x float,
    y AS CASE x
    WHEN 0 THEN a
    WHEN 1 THEN b
    ELSE c
    END)

COLUMNPROPERTY 函数的 IsPrecise 属性报告 computed_column_expression 是否精确。

注意 任何 float 表达式均被视为不精确,不能作为索引的键;float 表达式可以在索引视图中使用,但不能用作键。这一规则同样适用于计算列。任何函数、表达式、用户定义函数或视图定义,只要包含任何 float 表达式,包括逻辑表达式(比较),均被视为没有确定性。

如果在计算列或视图上创建索引,先前能够正确执行的 INSERT 或 UPDATE 操作现在可能无法执行。在计算列导致算术错误时,可能会发生此类无法执行的情况。例如,虽然下表中的计算列 c 导致算术错误,但 INSERT 语句会起作用:

CREATE TABLE t1 (a int, b int, c AS a/b)
GO
INSERT INTO t1 VALUES ('1', '0')
GO

如果改为在创建该表之后在计算列 c 上创建索引,相同的 INSERT 语句将会失败。

CREATE TABLE t1 (a int, b int, c AS a/b)
GO
CREATE UNIQUE CLUSTERED INDEX Idx1 ON t1.c
GO
INSERT INTO t1 VALUES ('1', '0')
GO

索引视图

索引视图是为了实现快速访问而将其结果持续存放于数据库内并创建索引的视图。与任何其他视图一样,索引视图也依靠基表来提供视图数据。此类相关性意味着,如果更改为索引视图提供数据的基表,索引视图可能变得无效。例如,重命名为视图提供数据的列会使该视图无效。为了避免此类问题,SQL Server 支持创建具有架构绑定的视图。架构绑定禁止对表或列进行任何会使视图无效的修改。使用视图设计器创建的索引视图自动获得架构绑定,因为 SQL Server 要求该索引视图具有架构绑定。架构绑定并不是说您不能修改视图;它的意思是您不能按更改视图结果集的方式来修改基础表或视图。另外,就像计算列上的索引一样,索引视图也必须有确定性、精确,且不得包含 textntextimage 等列。

索引视图在基础数据不经常更新的情况下效果最佳。维护索引视图的成本可能高于维护表索引的成本。如果基础数据更新频繁,索引视图数据的维护成本就可能超过使用索引视图带来的性能收益。

索引视图改进了以下几类查询的性能:

  • 处理多行的联接和聚合。
  • 许多查询经常执行的联接和聚合操作。

    例如,在某个记录清单的 OLTP 数据库中,预计许多查询要联接 PartsPartSupplierSuppliers 表。虽然执行这一联接的每个查询不一定都会处理许多行,但成千上万个查询的联合处理加在一起仍然是非常庞大的操作。因为不太可能经常更新这些关系,所以通过定义存储联接结果的索引视图,即可改进整个系统的总体性能。

  • 决策支持工作负荷。
  • 分析系统的特点是存储不经常更新的概要数据、聚合数据。许多决策支持查询的特点是进一步聚合数据和联接许多行。

索引视图通常不会改进以下几类查询的性能:

  • 经常写入的 OLTP 系统。
  • 经常更新的数据库。
  • 不涉及聚合或联接的查询。
  • 键基数程度高的数据聚合。基数程度高意味着该键包含许多不同的值。唯一键具有基数可能的最高程度,因为每个键的值各不相同。索引视图通过减少查询必须访问的行的数量来改进性能。如果视图结果集的行数量几乎与基表的行数量相同,那么使用该视图几乎就无任何性能收益可言。例如,对于具有 1,000 行的表考虑使用此查询:

    SELECT PriKey, SUM(SalesCol)
    FROM ExampleTable
    GROUP BY PriKey

    如果表键的基数为 100,使用此查询的结果生成的索引视图就只有 100 行。使用该视图的查询平均需要的读取次数为读取基表次数的十分之一。如果该键是一个唯一的键,而该键的基数是 1000,视图结果集将返回 1000 行。使用该索引视图,而不直接读取基表,查询不会带来任何性能改进。

  • 展开联接,这些联接是结果集大于基表内原始数据的视图。

您设计的索引视图应能满足多个操作。因为,即使未在 FROM 子句中指定索引视图,优化程序也能使用索引视图,所以设计完好的索引视图可以加快许多查询的处理速度。例如,考虑在以下视图上创建索引:

CREATE VIEW ExampleView (PriKey, SumColx, CountColx)
AS
SELECT PriKey, SUM(Colx), COUNT_BIG(Colx)
FROM MyTable
GROUP BY PriKey

该视图不仅满足直接引用视图列的查询,而且可以用来满足查询基础基表并且包含 SUM(Colx)、COUNT_BIG(Colx)、COUNT(Colx) 和 AVG(Colx) 等表达式的查询。所有此类查询的速度都会更快,因为它们只需检索视图中的少数几列,而不必读取基表中的所有列。

在视图上创建的第一个索引必须是唯一的聚集索引。创建了唯一的聚集索引之后,您可以创建其他非聚集索引。视图上的索引命名规则与表上的索引命名规则相同。唯一不同的是表名会替换为视图名。

如果除去视图,视图上的所有索引也将被除去。如果除去聚集索引,视图上的所有非聚集索引也将被除去。非聚集索引可被单独除去。除去视图上的聚集索引会删除存储的结果集,优化程序会恢复以标准视图的处理方式来处理该视图。

虽然在 CREATE UNIQUE CLUSTERED INDEX 语句中仅指定构成聚集索引键的列,但视图的完整结果集存储于数据库中。就像在基表的聚集索引中一样,聚集索引的 B 树结构只包含键列,但数据行包含视图结果集中的所有列。

注意 SQL Server 2000 的任何版本均可创建索引视图。在 SQL Server 2000 企业版中,索引视图将由查询优化程序自动考虑。要在所有其他版本中使用索引视图,必须使用 NOEXPAND 提示。

覆盖索引

覆盖索引是根据满足 SQL 查询(在选择条件和 WHERE 谓词两方面满足)所需的所有列建立的非聚集索引。覆盖索引可以节省大量 I/O,从而极大地提升了查询的性能。但是,您需要平衡考虑新索引的创建成本(及其相关的 B 树索引结构维护成本)与覆盖索引带来的 I/O 性能收益。如果覆盖索引将会极大地提升某个查询或某组查询的性能,而这些查询经常在 SQL Server 上运行,那么就值得创建覆盖索引。

下面的示例说明如何使用覆盖索引交集:

Create index indexname1 on table1(col2,col1,col3)
Select col3 from table1 where col2 = 'value'

执行上述查询时,只会读取较少的索引页,所以能够迅速从基础表中检索到所需的值,从而非常高效地解析该查询。通常,如果覆盖索引较小(就索引中所有列的字节数与该表的单行内的字节数相比较而言),并且使用覆盖索引的查询确实会经常执行,那么适合使用覆盖索引。

索引选择

对索引的选择会极大地影响生成的磁盘 I/O 数量,进而极大地影响性能。非聚集索引适合于检索少量的行,而聚集索引适合于区间扫描。下列原则有助于选择要使用的索引类型:

  • 尽量精简索引(行数和字节数保持最小)。该原则尤其适用于聚集索引,因为非聚集索引将使用聚集索引作为定位行数据的方法。
  • 在非聚集索引中,选择性非常重要。如果在只有几个唯一值的大表上创建非聚集索引,使用该非聚集索引不会在数据检索过程中节省大量 I/O。实际上,使用该索引生成的 I/O 很可能远远多于只是执行连续表扫描生成的 I/O。适合使用非聚集索引的对象包括发票号、唯一的客户号、社会保障号和电话号码。
  • 对于涉及到区间扫描的查询,或在经常使用列联接其他表时,聚集索引比非聚集索引的效果好。原因是聚集索引对表数据进行物理排序,允许键值上存在连续的 64 KB I/O。适合使用聚集索引的对象包括省、公司分支机构、销售日期、邮政编码和客户地区。

    针对一个表只能创建一个聚集索引;如果典型查询经常从表的某一列提取大量连续区间,而表的其他列包含唯一值,则在第一列上使用聚集索引,在包含唯一值的列上使用非聚集索引。在每个表上尝试选择用来创建聚集索引的最佳列时,要问的关键问题是:“是否会有大量查询需要根据该列的顺序来提取大量的行?”。答案视每个用户环境的具体情况而定。某个公司可能根据日期区间进行大量查询,而另一个公司可能根据银行分支机构的区间进行大量查询。

索引的创建和并行操作

在 SQL Server 2000 企业版和 Developer Edition 中,针对创建索引而建立的查询计划允许在配有多个微处理器的计算机上进行并行、多线程的索引创建操作。

SQL Server 在为创建索引操作确定并行度(同时运行的单个线程的总数)时使用的算法与为其他 Transact-SQL 语句确定并行度时使用的算法相同。唯一不同是创建索引的 CREATE INDEX、CREATE TABLE 或 ALTER TABLE 语句不支持 MAXDOP 查询提示。索引创建的最大并行度取决于最大并行度服务器配置选项,但您不能为各索引创建操作设置不同的 MAXDOP 值。

在 SQL Server 建立创建索引查询计划时,并行操作的数量以下面的最小值为准:

  • 计算机中微处理器或 CPU 的数量。
  • 最大并行度服务器配置选项中指定的数量。
  • 尚未超过 SQL Server 线程工作阈值的 CPU 数量。

例如,某台计算机配有八个 CPU,但最大并行度选项设置为 6,那么为创建索引生成的并行线程不会超过六个。如果在建立索引创建执行计划时,计算机中的五个 CPU 超过 SQL Server 工作的阈值,执行计划将只指定三个并行线程。

并行索引创建的主要阶段包括:

  • 协调线程对表进行快速随机的扫描,以估计索引键的分发情况。协调线程建立键边界,键边界的作用是创建多个键区间,键区间的数量与并行操作度相等,预计每个键区间内包含的行数大致相同。例如,如果表中有四百万行,而最大并行度选项设置为 4,协调线程将确定分隔四个行集的键值,每个行集包含一百万行。
  • 协调线程根据并行操作度分派同等数量的多个线程,然后等待这些线程完成工作。每个线程使用筛选器扫描基表,筛选器只在分配给线程的区间内检索具有键值的行。每个线程为其键区间内的行建立索引结构。

在所有并行线程完成工作后,协调线程将多个索引子单元连接到单个索引中。单独的 CREATE TABLE 或 ALTER TABLE 语句可以具有多个需要创建索引的约束。虽然可以在配有多个 CPU 的计算机上并行执行每个索引创建操作,但此处所述的多个索引创建操作仍然按序执行。

索引维护

在数据库中创建索引时,查询使用的索引信息存储在索引页中。连续索引页之间通过指针一页一页相互链接在一起。对影响索引的数据进行更改时,数据库中的索引信息会被拆散。重建索引将重新组织索引数据的存储(如果为聚集索引,还将重新组织表数据的存储),以便删除碎片。这样可以减少为获得请求数据所需的页读取的数目,从而改进磁盘性能。

插入活动或更新会修改聚集索引的搜索键值,在执行大量的插入活动或更新时,即会出现碎片。因此,为了防止拆分索引页和数据页,您应该尝试在索引页和数据页上保留一定的开放空间,这一点很重要。如果索引页或数据页不能再存放任何新行,而且由于该页中定义的数据的逻辑排序,需要将某一行插入该页,则会拆分该页。出现此情况时,SQL Server 需要对一整页的数据进行分割,将大约一半的数据移到新页上,这样,新旧两页都能保留一定的开放空间。因为这样会耗用系统资源和时间,所以建议不要经常这样做。

最初建立索引时,SQL Server 尝试将索引的 B 树结构放在物理上连续的页上;这样就能在使用连续 I/O 扫描索引页时优化 I/O 性能。在发生拆分页和需要将新页插入索引的逻辑 B 树结构时,SQL Server 必须分配新的 8 KB 索引页。如果在硬盘的其他位置发生此情况,将会破坏索引页的物理连续特性。这样会导致 I/O 操作的执行从连续切换为不连续,而且还会极大地降低性能。通过重建索引来恢复索引页的物理连续顺序,应该能解决页拆分过多的问题。同样的行为还可能在聚集索引的叶级别上发生,从而影响表的数据页。

在系统监视器中,尤其要注意“SQL Server:访问方法 – 页拆分/秒”。该计数器的非零值表示正在进行页拆分,应使用 DBCC SHOWCONTIG 作进一步分析。

DBCC SHOWCONTIG 命令也可用来揭示表上是否已进行了过多的页拆分。扫描密度是 DBCC SHOWCONTIG 提供的关键指标。该值应尽量接近 100%,越接近越好。如果该值大大低于 100%,请考虑对出现问题的索引运行维护。

DBCC INDEXDEFRAG

一个索引维护选项要使用 SQL Server 2000 中引入的新语句 (DBCC INDEXDEFRAG)。DBCC INDEXDEFRAG 可以为表和视图上的聚集索引和非聚集索引整理碎片。DBCC INDEXDEFRAG 在索引的叶级别整理碎片,因此各页的物理顺序与叶节点从左至右的逻辑顺序一致,从而改进了索引扫描性能。

DBCC INDEXDEFRAG 还压缩索引的各页,它会考虑在创建索引时指定的 FILLFACTOR。由压缩创建的空页将被删除。

如果索引跨越多个文件,DBCC INDEXDEFRAG 一次为一个文件整理碎片。索引页不会在文件之间迁移。DBCC INDEXDEFRAG 每隔五分钟向用户报告一次预计已完成的百分比。在执行过程中,您随时都可以终止 DBCC INDEXDEFRAG,已完成的所有工作会被保留。

与 DBCC DBREINDEX(或一般的索引建立操作)不同,DBCC INDEXDEFRAG 是联机操作。它不会长期保持锁定,因而不会阻止运行查询或更新。为相对而言没有碎片的索引整理碎片可以比建立新索引快,因为整理碎片所需的时间与碎片量相关。为非常零碎的索引整理碎片的时间可能比重建索引的时间要长得多。此外,不论数据库恢复模型设置如何,始终完全记录碎片整理情况(请参阅 ALTER DATABASE)。为非常零碎的索引整理碎片所生成的日志甚至可能比记录整个索引创建过程所生成的日志还多。不过,由于碎片整理是作为一系列小事务执行的,因此,如果经常进行日志备份,或者恢复模型设置为 SIMPLE,则不需要大日志。

另外,如果两个索引在磁盘上交错存放,则不适合使用 DBCC INDEXDEFRAG,因为 INDEXDEFRAG 会打乱索引页的位置。要改进索引页的聚集,请重建索引。出于相同的原因,DBCC INDEXDEFRAG 无法更正页拆分。对于已按反映搜索键的连续顺序分配的索引页,它实质上会进行重新排序。索引页的次序可能因多种原因而变得不正确,这些原因包括:无序数据装载、过多的插入、更新、删除活动,等等。

“SQL Server 联机丛书”中提供了一段示例代码,您只需对该代码稍加修改,即可使用它来自动执行各种索引维护任务。该示例说明如何用一种简单的方法来对数据库中碎片量大于声明阈值的所有索引进行碎片整理。有关更多信息,请参阅“SQL Server 联机丛书”中的主题“DBCC SHOWCONTIG”。

DBCC DBREINDEX

根据所用语法不同,DBCC DBREINDEX 可以只重建表的某一个指定索引,或者也可以重建表的所有索引。与除去并重新创建各个索引时采用的方法类似,DBCC DBREINDEX 语句也具备能够在一条语句中重建表的所有索引这一优点。这样比编写单独的 DROP INDEX 和 CREATE INDEX 语句更方便,并且,在重建表的一个或多个索引时,不必知道表结构或任何指定的约束条件。另外,DBCC REINDEX 语句固有原子性。如果要在编写单独的 DROP INDEX 和 CREATE INDEX 语句时获得相同的原子性,必须将多个单独的命令包含在一个事务内。

与单独的 DROP INDEX 和 CREATE INDEX 语句相比,DBCC DBREINDEX 会自动利用更多优化方案,在多个非聚集索引引用具有聚集索引的表时尤其如此。DBCC DBREINDEX 也可用于重建强制 PRIMARY KEY 或 UNIQUE 约束的索引,而不必删除和重新创建约束(因为,如果不先删除约束,则无法删除为了强制 PRIMARY KEY 或 UNIQUE 约束而创建的索引)。例如,您可能希望通过在 PRIMARY KEY 约束上重建索引来为索引重新建立给定的填充因子。

DROP_EXISTING

重建索引或整理索引碎片的另一种方法是:除去索引后再重新创建索引。通过删除旧索引,然后再重新创建相同的索引来重建聚集索引,这种方法很昂贵,因为所有二级索引都依赖指向数据行的聚集键。如果只删除聚集索引,然后再重新创建索引,您可能会不慎导致所有引用非聚集索引被删除和重新创建两次。在除去聚集索引时进行第一次除去/重新创建。在重新创建聚集索引时进行第二次除去/重新创建。

为了避免这一开销,使用 CREATE_INDEX 的 DROP_EXISTING 子句就可以一步完成这一重新创建的过程。采用一个步骤重新创建索引会告诉 SQL Server 您要重新组织现有索引,并避免删除和重新建相关非聚集索引等不必要的工作。这种方法还有一点明显的好处:可以使用现有索引中已预先排序的数据,因而不需要执行数据排序。这样就可以明显减少重新创建聚集索引的时间和成本。

DROP INDEX / CREATE INDEX

维护索引的最后一种方法是:直接除去索引,然后再重新创建索引。此选项仍在广泛使用,并且可能是以下人员的首选:熟悉此选项的人员、其处理窗口能够容纳表上所有索引完整的重新创建的人员。使用此方法的缺点是必须手动控制事件,以使事件按照适当的顺序发生。在手动除去和重新创建索引时,一定要在除去和重新创建聚集索引之前,除去所有非聚集索引。否则,在创建聚集索引时,将自动创建所有非聚集索引。

手动创建非聚集索引有一个优点:各个非聚集索引可以同时重新创建。不过,您的分区策略可能会影响所生成的索引的物理布局。如果同时在同一个文件(文件组)上重建两个非聚集索引,这两个索引的索引页可能在磁盘上交错在一起。这可能会打乱数据的存储顺序。如果多个文件(文件组)位于不同的磁盘上,您可以指定单独的文件(文件组)在创建索引之后保存索引,从而保持了索引页的顺序连续性。

前面提及的有关在预先排序的数据上建立索引的问题在此处同样适用。在已排序的数据上建立的聚集索引不必执行额外的排序步骤,从而可以极大地减少建立索引所需的时间和处理资源。

FILLFACTOR 和 PAD_INDEX

FILLFACTOR 选项提供了一种方法,用于指定在索引页和数据页上保留的开放空间的百分比。CREATE INDEX 的 PAD_INDEX 选项会在非叶级别的索引页上应用 FILLFACTOR 的设置。如果没有 PAD_INDEX 选项,FILLFACTOR 主要影响聚集索引的叶级别索引页。最好同时使用 PAD_INDEX 选项和 FILLFACTOR 选项。

PAD_INDEX 和 FILLFACTOR 用于控制页拆分。为 FILLFACTOR 指定的最佳值取决于在给定时间段内插入 8 KB 索引页和数据页的新数据量。请记住,通常,SQL Server 索引页包含的行数远远多于数据页包含的行数,因为索引页只包含与该索引相关的列数据,而数据页包含整行的数据,这一点很重要。

另外,请记住维护窗口的出现频率,维护窗口允许重建索引,以便更正即将发生的页拆分。请尝试只在大多数索引页和数据页已填满数据时再重建索引。如果表的聚集索引选择得当,则不会经常需要重建索引。如果聚集索引均匀地分布数据,从而所有与表相关的数据页上都会在该表中插入新行,那么,数据页将会均匀填充。总体说来,这将在开始发生页拆分且有必要重建聚集索引之前提供更多的时间。

为了确定用于 PAD_INDEX 和 FILLFACTOR 的适当的值,您需要发出判断请求。在作决定之前,您应该考虑两方面:一是在页上保留大量开放空间,二是可能发生的拆分页的数量,这两方面要保持性能上的平衡。如果为 FILLFACTOR 指定的百分比很小,它将在索引页和数据页上保留大量开放空间,这样,为了回答查询,SQL Server 就需要读取大量部分填充的页。对于大量读取操作而言,如果索引页和数据页上的压缩数据越多,SQL Server 的处理速度会明显加快。指定过高的 FILLFACTOR 会使各页上保留的开放空间过少,这样,各页很快就会溢出,从而导致页拆分。

在确定 FILLFACTOR 或 PAD_INDEX 值之前,请记住,在许多数据仓库环境中,读取操作的数量往往比写入操作的数量多得多。不过,如果定期装载数据,可能就不是这种情况了。许多数据仓库管理员尝试对表/索引进行分区和组织,以便容纳预计会出现的定期数据装载。

根据一般经验,如果预计的写入量相当于读取量的一大部分,最佳方法是按可行情况尽高地指定 FILLFACTOR,同时在每个 8 KB 页上保留足够的可用空间,以避免经常发生页拆分,至少要让 SQL Server 能够到达重新创建索引所需的下一个可用时间窗。该策略均衡了 I/O 性能(尽量填满各页),并且避免了页拆分(不让各页溢出)。如果不写入 SQL Server 数据库,FILLFACTOR 应设置为 100%,以便填满所有索引页和数据页,获得最佳 I/O 性能。

用于分析和优化的 SQL Server 工具

本节提供在表中装载数据的示例代码,稍后再用该段代码说明如何使用 SQL 事件探查器和 SQL 查询分析器分析和优化性能。

样本数据和工作负荷

下面的示例说明如何使用 SQL Server 性能工具。首先构造下表:

create table testtable
(nkey1 int identity,
col2 char(300) default 'abc',
ckey1 char(1))

然后,在该表中装载 20,000 行测试数据。装载到 nkey1 列的数据适用于非聚集索引。ckey1 列中的数据适用于聚集索引,col2 中的数据只是为了将每行的大小增加 300 个字节而填入的数据。

declare @counter int
set @counter = 1
while (@counter <= 4000)
begin
insert testtable (ckey1) values ('a')
insert testtable (ckey1) values ('b')
insert testtable (ckey1) values ('c')
insert testtable (ckey1) values ('d')
insert testtable (ckey1) values ('e')
set @counter = @counter + 1
end

下列查询构成了数据库服务器工作负荷:

select ckey1 from testtable where ckey1 = 'a'
select nkey1 from testtable where nkey1 = 5000
select ckey1,col2 from testtable where ckey1 = 'a'
select nkey1,col2 from testtable where nkey1 = 5000

SQL 事件探查器

优化性能的常用方法通常称为标记和度量。要验证为改进性能所做的更改是否确实改进了性能,首先需要建立现有不良性能情况的基线或标记度量 指建立一些可量化的方法,用来证明性能正在得到改进。

SQL 事件探查器是用来进行标记和度量的工具。它不仅可以捕获服务器内发生的活动,供您进行性能分析;而且可以稍后再回放该活动。SQL Server 中的回放功能提供了一种有用的回归测试工具。使用回放功能,您可以方便地确定目前为了改进性能而采取的操作是否能达到预期效果。

回放功能还可以模拟负载或压力测试。您可以设置多个事件探查器客户端会话,让它们同时回放。例如,利用这一功能,管理员可以轻松地捕获五个并发用户的活动,然后同时启动十个回放,模拟有 50 个并发用户时的系统性能。您还可以跟踪数据库活动,然后在正进行修改的数据库中回放该活动,或在正进行测试的新硬件配置中回放该活动。

请记住,您可以利用 SQL 事件探查器记录 SQL Server 数据库中发生的活动。可以对 SQL 事件探查器进行配置,让它监视和记录对 SQL Server 执行查询的一个或多个用户。除了 SQL 语句外,使用该工具还能捕获各种各样的性能信息。使用 SQL 事件探查器记录的某些性能信息包括:I/O 统计信息、CPU 统计信息、锁定请求、Transact-SQL 和 RPC 统计信息、索引和表扫描、引发的警告和错误、数据库对象的创建/除去、建立连接/断开连接、存储过程操作、游标操作,等等。

捕获供索引优化向导使用的事件探查器信息

SQL 事件探查器和索引优化向导的结合使用,形成了一个功能非常强大的工具组合,它可以帮助数据库管理员确保在表和视图上放置正确的索引。SQL 事件探查器可以将查询的资源耗用情况记录到三个位置上。可以将输出定向到 .trc 文件、SQL Server 表或监视器。之后,索引优化向导从 .trc 文件或 SQL Server 表读取捕获的数据。索引优化向导对捕获的工作负荷中的信息和有关表结构的信息进行分析,然后针对改进性能提出应该创建哪些索引的建议。有了索引优化向导,

您就能自动完成以下任务:为数据库创建正确的索引、调度稍后进行的索引创建、生成可以手动检查和执行的 Transact-SQL 脚本。

分析查询负载需要完成以下步骤:

设置 SQL 事件探查器

  1. 工具菜单上选择 SQL 事件探查器,从 SQL Server 企业管理器中启动 SQL 事件探查器。
  2. 按 CTRL+N 键新建一个 SQL 事件探查器跟踪。在连接到 SQL Server 对话框中,选择要连接到的服务器。
  3. 从下拉列表框中选择 SQLProfilerTuning 模板。
  4. 选中另存为文件另存为表复选框。另存为表选项将打开连接对话框,在该对话框中,您可以将跟踪信息保存到探查查询的服务器以外的其他服务器。如果要将跟踪的活动同时另存为文件和表,请选中这两个复选框。如果要另存为 .trc 文件,请指向有效的目标和文件名。如果以前已运行过跟踪,现在再次运行同一跟踪,请指向现有的跟踪表;如果这是第一次将跟踪活动捕获到表中,您也可以提供新的表名。单击确定
  5. 单击运行

运行工作负荷若干 (3-4) 次

  1. 从 SQL Server 企业管理器或开始菜单启动 SQL 查询分析器。
  2. 连接到 SQL Server,然后将当前数据库设为在其中创建测试表的数据库。
  3. 在 SQL 查询分析器的查询窗口中输入下列查询:

    select ckey1 from testtable where ckey1 = 'a'
    select nkey1 from testtable where nkey1 = 5000
    select ckey1,col2 from testtable where ckey1 = 'a'
    select nkey1,col2 from testtable where nkey1 = 5000

  4. 按 CTRL+E 键执行查询。反复执行该步骤三至四次,生成工作负荷样本。

停止 SQL 事件探查器

  • 在“SQL 事件探查器”窗口中,单击红色方块,停止该事件探查器跟踪。

将跟踪文件或表装载到索引优化向导中

  1. SQL 事件探查器中,选择工具菜单上的索引优化向导,以启动该向导。单击下一步
  2. 选择要分析的数据库。单击下一步
  3. 选择是否要保留现有索引的选项,或者添加索引视图。
  4. 选择一种优化模式(快速适中彻底)。在“快速”优化模式下,索引优化向导执行分析所需的时间较少,但分析不够彻底,在“彻底”模式下生成的分析最彻底,但所需的分析时间最长。
  5. 要查找用 SQL 事件探查器创建的跟踪文件/表,请选择我的工作负荷文件SQL Server 跟踪表。单击下一步
  6. 选择要优化的表对话框中,选择要分析的表,然后单击下一步
  7. 索引优化向导将分析跟踪的工作负荷和表结构,然后在索引建议对话框中确定应创建的正确索引。单击下一步
  8. 该向导提供了几个选项:立即创建索引、安排索引创建的时间(之后会自动执行的任务),或者创建包含用于创建索引的命令的 Transact-SQL 脚本。选择首选项,然后单击下一步
  9. 单击完成

索引优化向导针对样本数据库和工作负荷生成的 Transact-SQL

/* Created by: Index Tuning Wizard */
/* Date: 9/6/2000 */
/* Time: 4:44:34 PM */
/* Server Name: JHMILLER-AS2 */
/* Database Name: TraceDB */
/* Workload File Name: C:\Documents and Settings\jhmiller\My Documents\trace.trc */
USE [TraceDB]
go
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
SET NUMERIC_ROUNDABORT OFF
go
DECLARE @bErrors as bit

BEGIN TRANSACTION
SET @bErrors = 0

CREATE CLUSTERED INDEX [testtable1] ON [dbo].[testtable] ([ckey1] ASC )
IF( @@error <> 0 ) SET @bErrors = 1

CREATE NONCLUSTERED INDEX [testtable2] ON [dbo].[testtable] ([nkey1] ASC )
IF( @@error <> 0 ) SET @bErrors = 1

IF( @bErrors = 0 )
COMMIT TRANSACTION
ELSE
ROLLBACK TRANSACTION

索引优化向导为样本表和数据建议的索引正是我们所需要的:在 ckey1 上创建聚集索引,在 nkey1 上创建非聚集索引。ckey1 只有五个唯一值,每个值有 4000 行。假定其中一个样本查询 (select ckey1, col2 from testtable where ckey1 = 'a') 需要根据 ckey1 中的某个值来检索表,则适合在 ckey1 列上创建聚集索引。第二个查询 (select nkey1, col2 from testtable where nkey1 = 5000) 根据 nkey1 列的值提取一行。因为 nkey1 是唯一的,并且有 20,000 行,所以适合在该列上创建非聚集索引。

在使用了很多表并且要处理很多查询的实际数据库服务器环境中,将 SQL 事件探查器和索引优化向导组合使用,功能会非常强大。在数据库服务器处理典型的一组查询时,使用 SQL 事件探查器记录 .trc 文件或跟踪表。随后,将跟踪装载到索引优化向导中,以确定要建立的正确索引。按照索引优化向导中的提示执行操作,以自动生成索引,或安排索引创建作业在非高峰时间运行。您可能希望定期运行 SQL 事件探查器和索引优化向导的组合(也许每周一次或每月一次),以查看目前在数据库服务器上执行的查询是否发生了重大变化,这样就有可能会需要不同的索引。定期组合使用 SQL 事件探查器和索引优化向导,有助于数据库管理员在查询工作负荷不断变化和数据库日渐增大的情况下,仍保持 SQL Server 处于最佳运行状态。

使用 SQL 查询分析器分析事件探查器中记录的信息

在信息记录到 SQL Server 表中之后,可以使用 SQL 查询分析器来确定系统中哪些查询消耗资源最多。这样,数据库管理员就能集中精力改进那些最需要帮助的查询。如果将跟踪数据存储在表中,您就能方便地对跟踪数据的子集进行选择和筛选,从而为优化性能标识出性能最差的查询。例如,在上面的示例中,Duration 列是您使用 SQLProfiler Tuning 模板自动捕获的列,它可以用来标识需要最长执行时间(以毫秒计)的查询。要查找前 10% 的运行时间最长的查询,您可以运行下面这样的查询:

SELECT TOP 10 PERCENT *
FROM [TraceDB].[dbo].[Trace]
ORDER BY Duration DESC

要查找运行时间最长的前五个查询,您可以运行类似下面的查询:

SELECT TOP 5 *
FROM [TraceDB].[dbo].[Trace]
ORDER BY Duration DESC

要只将希望用于优化的行放在单独的表中,请考虑使用下面的 SELECT/INTO 语句:

SELECT TOP 10 PERCENT *
INTO TuningTable
FROM [TraceDB].[dbo].[Trace]
ORDER BY Duration DESC

前面提到的 SQLProfiler Tuning 模板只是针对优化建议的一组预选列和筛选器设置。您可能会发现,您需要捕获更多的信息。当然,您完全可以创建自己的自定义优化模板,方法是:只需打开预先提供的一个模板,然后用不同的名称保存即可。许多事件都可被捕获,包括 I/O 统计信息、锁定信息,等等。

SQL 查询分析器

SQL 查询分析器用于优化查询。该工具提供了多种类似“统计信息 I/O”的机制和用来解决查询问题的执行计划。

统计信息 I/O

SQL 查询分析器提供了一个选项,利用该选项,您能够获得在 SQL 查询分析器中执行的查询在 I/O 消耗方面的相关信息。要设置该选项,请在 SQL 查询分析器的查询菜单上,选择当前连接属性,以显示当前连接属性对话框。选中设置 statistics I/O 复选框,然后关闭该对话框。然后,执行查询并在结果窗格中选择消息选项卡,查看 I/O 统计信息。

例如,在选中设置 statistics IO 选项时,对前面的“SQL 事件探查器”一节中创建的样本数据进行以下查询,将在消息选项卡上返回以下 I/O 信息:

select ckey1, col2 from testtable where ckey1 = 'a'
Table 'testtable'.Scan count 1, logical reads 800, physical reads 62, read-ahead reads
760.

使用统计信息 I/O 是监视查询优化效果的一种好方法。例如,创建索引优化向导为样本数据建议的索引,然后再次运行该查询。

select ckey1, col2 from testtable where ckey1 = 'a'
Table 'testtable'.Scan count 1, logical reads 164, physical reads 4, read-ahead reads
162.

请注意,在可以使用索引时,逻辑读取和物理读取的数量会明显降低。

执行计划

使用图形化执行计划可以显示有关查询优化程序所做操作的详细信息,从而让您着重关注有问题的 SQL 查询。

查询的预计执行计划可以显示在 SQL 查询分析器的“结果”窗格中,方法是:用 CTRL+L 键执行 SQL 查询,或在查询菜单上选择显示预计的执行计划。各图标表明了查询优化程序如果执行了查询后会执行哪些操作。各箭头表示查询的数据流方向。将鼠标指针悬停于操作图标上方,即可显示有关每个操作的详细信息。各个操作图标下方还注明了每个操作步骤的大致成本。通过此标签,您可以迅速判断出查询中哪项操作是最昂贵的。

您也可以查看查询的实际执行计划,方法是在查询菜单上选择显示执行计划,然后执行查询。与显示预计的执行计划选项相比,显示执行计划先执行查询,然后才显示用于该查询的实际执行计划。

您可以创建执行计划的文本版本,方法是在查询菜单上选择当前连接属性,然后在该对话框中选中设置 showplan_text 选项。执行查询时,执行计划将在结果选项卡中显示为文本。

您还可以在查询内设置执行计划选项,方法是执行以下任一命令:

set showplan_all on
go
set showplan_text on
go

SET SHOWPLAN_ALL 供读取其输出的应用程序使用。使用 SET SHOWPLAN_TEXT 返回 Microsoft MS-DOS® 应用程序(如 osql 实用工具)可读取的输出。

SET SHOWPLAN_TEXT 和 SET SHOWPLAN_ALL 以一组文本行的形式返回信息,这些文本行所形成的分层树表示 SQL Server 查询处理器在执行每个语句时所采取的步骤。输出中反映的每个语句包含一个语句文本行,后面紧接若干行分别描述执行步骤的详细信息。

执行计划输出示例

这些结果是使用前面定义的查询示例和在 SQL 查询分析器中执行的“set showplan_text on”得出的。

查询 1

select ckey1,col2 from testtable where ckey1 = 'a'

基于文本的执行计划输出

|--Clustered Index Seek (OBJECT:([TraceDB].[dbo].[testtable].[testtable1]),
SEEK:([testtable].[ckey1]='a') ORDERED FORWARD)

同等的图形化执行计划输出

下图显示查询 1 的图形化执行计划。


如果您的浏览器不支持嵌入式框架,请单击此处在单独的页中查看。

执行计划利用 ckey1 列上的聚集索引来解析查询,正如聚集索引查找所示。

如果从表中删除了聚集索引,并且再次执行相同的查询,查询将恢复使用表扫描。下面的图形化执行计划表明该行为变化。

基于文本的执行计划输出

|--Table Scan(OBJECT:([TraceDB].[dbo].[testtable]), WHERE:([testtable].[ckey1]=[@1]))

同等的图形化执行计划输出

下图显示查询 1 的图形化执行计划。


如果您的浏览器不支持嵌入式框架,请单击此处在单独的页中查看。

该执行计划使用表扫描来解析查询 1。要从小表检索信息,最有效的方法是使用表扫描。但在大表上,由执行计划指明的表扫描实际是一种警告,它说明表需要最好的索引,或者现有索引的统计信息需要更新。您可以使用 UPDATE STATISTICS 命令在表或索引上更新统计信息。如果启发式页与基础索引值的同步差异过大,SQL Server 将自动更新索引。例如,如果您从 testtable 中删除了所有包含 ckey1 值等于“b”的行,然后,没有先更新统计信息就运行查询。最好让 SQL Server 自动维护索引统计信息,因为它有助于确保查询始终能够使用完好的索引统计信息。如果使用 ALTER DATABASE 语句将 AUTO_UPDATE_STATISTICS 数据库选项设为 OFF,则 SQL Server 不会自动更新统计信息。

查询 2

select nkey1,col2 from testtable where nkey1 = 5000

基于文本的执行计划输出

--Bookmark Lookup(BOOKMARK:([Bmk1000]),
OBJECT:([TraceDB].[dbo].[testtable]))
|--Index Seek(OBJECT:([TraceDB].[dbo].[testtable].[testtable2]),
SEEK:([testtable].[nkey1]=Convert([@1])) ORDERED FORWARD)

同等的图形化执行计划输出

下面两图显示查询 2 的图形化执行计划。


如果您的浏览器不支持嵌入式框架,请单击此处在单独的页中查看。


如果您的浏览器不支持嵌入式框架,请单击此处在单独的页中查看。

查询 2 的执行计划在 nkey1 列上使用非聚集索引。这是由 nkey1 列上的 Index Seek 操作指明的。Bookmark Lookup 操作指明 SQL Server 需要执行指针跳转,从表的索引页跳转到数据页,以检索请求的数据。需要执行指针跳转的原因是查询要求查找 col2 列,而非聚集索引内不包含该列。

查询 3

select nkey1 from testtable where nkey1 = 5000

基于文本的执行计划输出

|--Index Seek(OBJECT:([TraceDB].[dbo].[testtable].[testtable2]),
SEEK:([testtable].[nkey1]=Convert([@1])) ORDERED FORWARD)

同等的图形化执行计划输出

下图显示查询 3 的图形化执行计划。


如果您的浏览器不支持嵌入式框架,请单击此处在单独的页中查看。

查询 3 的执行计划使用 nkey1 上的非聚集索引作为覆盖索引。请注意,该查询不需要执行 Bookmark Lookup 操作。原因是该查询(SELECT 和 WHERE 子句)所需的全部信息都由非聚集索引提供。这就是说,非聚集索引页中不需要有指向数据页的指针跳转。与需要书签查找的情况相比,I/O 有所减少。

系统监视

系统监视器提供大量的有关数据库服务器执行期间所发生的 Windows 和 SQL Server 操作的信息。

在系统监视器的图形模式下,请注意最大最小值。因为过大和过小的数据点都会使平均值失真,所以对过于强调平均值的情况一定要小心。研究图形的形状并与最小最大值比较,以便准确地理解行为。使用 BACKSPACE 键,用一条白线突出显示计数器。

您可以使用系统监视器在日志文件中记录所有可用的 Windows 和 SQL Server 系统监视器对象/计数器,而同时以交互方式查看系统监视器(图表模式)。采样间隔的设置决定了日志文件增大的速度。日志文件可能很快变大(例如,如果打开所有计数器,采样间隔设为 15 秒,日志文件在 1 小时内就能达到 100 兆字节)。测试服务器上最好有足够的空闲千兆字节来存储这些类型的文件。不过,如果保留空间对您很重要,请尝试采用较长的日志间隔,以免系统监视器过于频繁地对系统采样。请尝试 30 或 60 秒。这样,系统监视器会以合理的频率对所有计数器重新采样,同时又能保持较小的日志文件大小。

系统监视器也会耗用少量 CPU 资源和磁盘 I/O 资源。如果系统没有多余的备用磁盘 I/O 和/或 CPU,请考虑从另一台计算机运行系统监视器,然后通过网络监视 SQL Server。在通过网络监视时,请只使用图形模式。与通过局域网发送信息相比,在 SQL Server 本地记录性能监视信息的效率往往会更高。如果您必须通过网络记录日志信息,可以只记录最重要的计数器信息。

在性能测试运行期间,将所有可用计数器的信息记录到某个文件中供以后分析,这不失为一个好做法。这样,对于任何计数器,以后都可以再作进一步检查。您可以配置系统监视器将所有计数器记录到日志文件中,与此同时,在其他某种模式(如图形模式)下监视最感兴趣的计数器。这样,在性能运行期间,所有信息都会被记录下来,但您最关注的计数器会以清晰整洁的系统监视器图形显示出来。

设置要记录的系统监视器会话

  1. 从 Windows 2000 开始菜单中,指向程序管理工具,然后单击性能,打开系统监视器。
  2. 双击性能日志和警报,然后单击计数器日志
  3. 现有的日志都会在详细信息窗格中列出。绿色图标表示日志正在运行;红色图标表示日志已被停止。
  4. 右键单击详细信息窗格的空白区域,然后单击新日志设置
  5. 名称中键入日志的名称,然后单击确定
  6. 常规选项卡上,单击添加。选择要记录的计数器。您在此处确定要在会话期间监视的 SQL Server 计数器。
  7. 如果要更改默认文件,请在日志文件选项卡上进行更改。
  8. 记录的会话可以设置为按预定义的时间段自动运行。为此,请在调度选项卡上修改调度信息。

注意 要保存日志文件的计数器设置,请用右键单击详细信息窗格中的文件,然后单击将设置另存为。然后,指定用来保存这些设置的 .htm 文件。要在新日志中重用已保存的设置,请用右键单击详细信息窗格,然后单击新日志设置来自

启动已记录的监视会话

  1. 从 Windows 2000 开始菜单中,指向程序管理工具,然后选择性能,打开系统监视器。
  2. 双击性能日志和警报,然后单击计数器日志
  3. 右键单击要运行的计数器日志,然后选择启动。
  4. 现有的日志都会在详细信息窗格中列出。绿色图标表示日志正在运行;红色图标表示日志已被停止。

停止已记录的监视会话

  1. 从 Windows 2000 开始菜单中,指向程序管理工具,然后选择性能,打开系统监视器。
  2. 双击性能日志和警报,然后单击计数器日志
  3. 右键单击要运行的计数器日志,然后选择停止。

从已记录的监视会话向系统监视器装载数据供分析使用

  1. 从 Windows 2000 开始菜单中,指向程序管理工具,然后选择性能,打开系统监视器。
  2. 单击系统监视器
  3. 右键单击系统监视器的详细信息窗格,然后单击属性
  4. 单击选项卡。
  5. 数据源下,单击日志文件,然后键入文件路径,或单击浏览,查找所需的日志文件。
  6. 单击时间区间。要在日志文件中指定希望查看的时间区间,请拖动滑动条或滑动条柄,设置相应的开始和结束时间。
  7. 单击数据选项卡,然后单击添加,打开添加计数器对话框。您在日志配置期间选择的计数器会显示出来。您可以在图形中包括所有这些计数器或其中一部分。

如何使系统监视器记录的事件与过去的某个时点相关

  • 从系统监视器会话中,右键单击系统监视器的详细信息窗格,然后单击属性。时间区间和滑动条允许您设定要在图形中查看的开始、当前和结束时间。

需要监视的关键性能计数器

有几个性能计数器提供了有关以下重要方面的信息:内存、分页、处理器、I/O 和磁盘活动。

监视内存

默认情况下,SQL Server 会根据可用系统资源动态更改其内存需求。如果 SQL Server 需要更多内存,它会查询操作系统,以确定是否有可用的空闲物理内存,并使用可用内存。如果 SQL Server 当前不需要分配给它的内存,它将向操作系统释放内存。不过,动态使用内存的选项会被服务器配置选项替代,这些选项是最小服务器内存最大服务器内存设置工作集大小。有关更多信息,请参阅“SQL Server 联机丛书”。

要监视由 SQL Server 使用的内存量,请检查下列性能计数器:

  • 进程:工作集
  • SQL Server:缓冲管理器:缓存命中率
  • SQL Server:缓冲管理器:全部页
  • SQL Server:内存管理器:总的服务器内存 (KB)

工作集计数器显示由进程使用的内存量。如果该数字一直低于 SQL Server 配置使用的内存量(由服务器选项最小服务器内存最大服务器内存设置),说明为 SQL Server 配置的内存比它实际需要的内存多。否则,使用设置工作集大小服务器选项调整工作集的大小。

缓存命中率计数器是特定于应用程序的;不过,该比率达到或超过 90% 比较理想。请增多内存,直到该值稳定地达到 90% 以上,这样就表明数据缓存满足了 90% 以上的数据请求。

如果与计算机中的物理内存量相比,总的服务器内存 (KB) 计数器值一直较高,说明需要更多内存。

强制分页

如果内存:页/秒大于零或内存:页读取/秒大于五,说明 Windows 正在使用磁盘来解决内存引用(强制分页错误)。它耗用磁盘 I/O + CPU 资源。内存:页/秒清楚地指明了 Windows 正在执行的分页量,以及数据库服务器当前的 RAM 配置是否够用。系统监视器中的强制分页信息有一个子集记录的是 Windows 为了解决内存引用而必须读取分页文件的次数/秒,它由内存:页读取/秒表示。如果内存:页读取/秒大于 5,则对性能不利。

为了避免分页,SQL Server 自动内存优化将尝试动态调整 SQL Server 对内存的使用。每秒读取少量页是正常的,但如果分页过多,则需采取更正措施。

如果 SQL Server 自动优化内存,您可以选择添加更多 RAM 或从数据库服务器中删除其他应用程序,以帮助内存:页/秒达到合理水平。

如果 SQL Server 内存是在数据库服务器上手动配置的,则需要减少指定给 SQL Server 的内存,从数据库服务器中删除其他应用程序,或向数据库服务器添加更多 RAM。

保持内存:页/秒为零或接近零,对数据库服务器性能有利。这就是说,Windows 及其所有应用程序(包括 SQL Server)不会为满足内存请求中的任何数据而转到分页文件,所以服务器上的 RAM 是充足的。页/秒略大于零尚可接受,但请记住,每次从分页文件(而非 RAM)检索数据时,都将遭受相对较高的性能惩罚(磁盘 I/O)。

对与 Windows 分页文件相关的所有驱动器间的内存:页输入/秒逻辑磁盘:磁盘读取/秒以及内存:页输出/秒逻辑磁盘:磁盘写入/秒进行比较是很有用的,因为通过它们可以知道真正与分页而非其他应用程序(即 SQL Server)相关的磁盘 I/O 量。隔离分页文件 I/O 活动的另一种便捷方式是确保分页文件与所有其他 SQL Server 文件不在同一组驱动器上。将分页文件与 SQL Server 文件隔开也对磁盘 I/O 性能有利,因为它允许与分页相关的磁盘 I/O 和与 SQL Server 相关的磁盘 I/O 并行执行。

软分页

如果内存:分页错误/秒大于零,说明 Windows 正在分页,但计数器中既有强制分页,也有软分页。我们已在上一节讨论过强制分页。软分页表示数据库服务器上的应用程序正在请求的内存页仍然位于 RAM 以内,但已位于 Windows 工作集之外。内存:分页错误/秒有助于获得正在发生的软分页量。系统中没有称为“软分页错误/秒”的计数器。您可以使用此公式计算每秒钟发生的软分页错误数:内存:分页错误/秒 - 内存:页输入/秒 = 软分页错误/秒

要确定导致过多分页的是否是 SQL Server 而不是其他进程,请监视 SQL Server 进程的进程:分页错误/秒计数器,并注意相关 Sqlservr.exe 实例的分页错误数/秒是否接近内存:页/秒的数值。

与硬分页错误相比,软分页错误对性能的不利影响通常要小一些,因为它们耗用 CPU 资源。硬分页错误耗用磁盘 I/O 资源。获得良好性能的最佳环境是杜绝任何类型的错误。

注意 在 SQL Server 第一次访问它所有的数据缓存页时,对每一页的第一次访问均会导致软分页错误。在 SQL Server 第一次启动和第一次使用数据缓存时,不必担心最初的软分页错误。

监视处理器

您的目标应该是:尽可能充分地利用所有分配给服务器的处理器,以获得最佳性能,而同时又避免因过于繁忙而出现处理器瓶颈。性能优化所面临的挑战是:如果 CPU 不是瓶颈,总有其他东西是瓶颈(很可能是磁盘子系统),因而浪费了 CPU 容量。通常,CPU 是最难扩展的资源(某些配置特定的级别除外,例如,许多最新系统上的 4 CPU 或 8 CPU),因此,如果繁忙系统上的 CPU 使用率超过 95%,说明系统运行良好。同时,您应该监视事务的响应时间,确保响应时间合理;如果响应时间不合理,而 CPU 使用率超过 95%,则可能说明可用 CPU 资源承担的工作负荷过多,您要么增加 CPU 资源,要么减少或优化工作负荷。

请查看系统监视器计数器处理器:% 处理器时间,确保每个 CPU 上的处理器使用率一直低于 95%。系统:处理器队列长度是 Windows 系统上所有 CPU 的处理器队列。如果每个 CPU 的系统:处理器队列长度大于二,则说明出现了 CPU 瓶颈。在检测到 CPU 瓶颈时,您需要向服务器添加处理器,或减少系统上的工作负荷。减少工作负荷的方法是:通过优化查询或改进索引来减少 I/O,从而减少 CPU 使用率。

在怀疑出现 CPU 瓶颈时需要监视的另一个系统监视器计数器是系统:上下文切换/秒,因为它指明了 Windows 和 SQL Server 必须从在一个线程上执行切换到在另一个线程上拖动的频率(次数/秒)。它耗用 CPU 资源。上下文切换是多线程、多处理器环境的正常组件,但过多的上下文切换会降低系统性能。应对方法是在有处理器队列时,只关注上下文切换。

如果观察处理器队列,则将上下文切换级别用作 SQL Server 性能优化的尺度。如果看起来是上下文切换导致出现瓶颈,您可以考虑两种方法:使用关系掩码选项,使用基于纤程的调度。

使用关系掩码选项可以提高重负荷下运行的对称多处理器 (SMP) 系统(微处理器数量超过四个)的性能。您可以使线程与特定处理器相关,并指定 SQL Server 将使用的处理器。您还可以使用关系掩码选项设置来阻止 SQL Server 活动使用某些处理器。在更改关系掩码的设置之前,请记住,Windows 会将与 NIC 的相关延迟进程调用 (DPC) 活动分配给系统中编号最高的处理器。在安装并激活了多个 NIC 的系统中,另外每增加一个卡的活动,就会分配给下一个编号最高的处理器。例如,安装了两个 NIC 的八处理器系统将每个 NIC 的 DPC 分配给处理器 7 和处理器 6(从 0 开始计数)。在使用轻量池选项时,SQL Server 切换到基于纤程的调度模式,而不是默认的基于线程的调度模式。纤程本质上是轻量线程。使用命令 sp_configure 'lightweight pooling',1 可启用基于纤程的调度。

通过监视处理器队列和上下文切换,您可以监视设置关系掩码轻量池的值之后产生的效果。某些情况下,这些设置非但不会改进性能,反而会使性能下降。另外,除非系统中有四个或更多个处理器,否则它们一般不会带来很大的收益。DBCC SQLPERF (THREADS) 提供了映射回 SPID 的有关 I/O、内存以及 CPU 使用率的更多信息。执行下面的 SQL 查询可调查当前最耗用 CPU 时间的使用者:

select * from master.sysprocesses order by cpu desc

监视处理器队列长度

如果系统:处理器队列长度大于二,说明服务器的处理器收到的工作请求多于它们能够以一个组的方式集体处理的请求。因此,Windows 需要将这些请求放在队列中。

某些处理器队列说明 SQL Server 的总体 I/O 性能良好。如果没有处理器队列,并且 CPU 使用率低,说明系统某处可能出现了性能瓶颈,最有可能的地方就是磁盘子系统。处理器队列中留有合理的工作请求量,这说明 CPU 并不空闲,系统的其余部分也与 CPU 保持同步。

根据一般经验,理想的处理器队列数是数据库服务器中的 CPU 数乘以二。

如果处理器队列数明显高于该值,可能表明服务器遇到了 CPU 瓶颈,您需要进行调查。过多的处理器队列会耗用查询执行时间。多个不同活动可能导致出现处理器队列。消除强制分页和软分页有助于节省 CPU 资源。其他有助于减少处理器队列的方法包括:优化 SQL 查询、挑选更好的索引以减少磁盘 I/O(从而减少 CPU 使用量)、在系统中添加更多 CPU(处理器)。

监视 I/O

磁盘写入字节/秒磁盘读取字节/秒计数器表明磁盘的数据吞吐量,以每个逻辑驱动器或物理驱动器每秒的字节数计。请仔细地将这些数字与磁盘读取/秒磁盘写入/秒均衡比较。不要看到较低的字节数/秒,就相信磁盘 I/O 子系统不忙。

监视与 SQL Server 文件相关的所有驱动器的磁盘队列长度,然后确定哪些文件与过多的磁盘队列相关。

如果系统监视器表明某些驱动器不如其他驱动器繁忙,则可将 SQL Server 文件从出现瓶颈的驱动器移到不太繁忙的驱动器。这样有助于将磁盘 I/O 活动更均匀地分布到各个硬盘。如果将一个大型驱动器池用于 SQL Server 文件,磁盘队列的解决方法是在池中添加更多的物理驱动器,从而增大驱动器池的 I/O 容量。

出现磁盘队列可能表明某个 SCSI 通道中的 I/O 请求数量已达到饱和。系统监视器无法直接确定该情况是否属实。存储器供应商通常会另外提供工具,以帮助监视 RAID 控制器所服务的 I/O 数量,以及控制器是否在对 I/O 请求进行排队。如果 SCSI 通道上连接了许多磁盘驱动器(十个或更多个),并且所有驱动器都以全速执行 I/O,则更有可能发生该情况。应对此情况的解决方案是:将一半磁盘驱动器连接到另一个 SCSI 通道或 RAID 控制器,以平衡该 I/O。通常,在 SCSI 通道之间重新平衡驱动器需要重建 RAID 阵列以及完全备份/还原 SQL Server 数据库文件。

磁盘时间百分比

在系统监视器中,物理磁盘:% 磁盘时间逻辑磁盘:% 磁盘时间计数器监视磁盘因读取/写入活动而处于繁忙状态的时间百分比。如果 % 磁盘时间计数器很高(超过 90%),请检查当前磁盘队列长度计数器,以查看有多少系统请求正在等待磁盘访问。等待 I/O 的请求数量应该始终不超过构成物理磁盘的轴数量的 1.5 至 2 倍。大多数磁盘只有一个轴,然而不昂贵的磁盘冗余阵列 (RAID) 设备通常有多个轴。硬件 RAID 设备在系统监视器中显示为一个物理磁盘;通过软件创建的 RAID 设备显示为多个实例。

磁盘队列长度

监视过长的磁盘队列是一项重要任务。

要监视磁盘队列长度,您需要观察多个系统监视器磁盘计数器。要启用这些计数器,请从 Windows 2000 或 Windows NT 命令窗口运行 diskperf –y 命令,然后重新启动计算机。

出现磁盘队列的物理硬盘驱动器将在弥补 I/O 处理的同时阻止磁盘 I/O 请求。这些驱动器上的 SQL Server 响应时间也不如从前。此操作会耗用查询执行时间。

如果使用 RAID,为了计算每个物理驱动器的磁盘队列,您需要了解有多少个物理硬盘驱动器与每个 Windows 视为单个物理驱动器的驱动器阵列相关。为了了解每个物理驱动器保存 SQL Server 数据的具体方式,以及每个 SCSI 通道上分发的 SQL Server 数据量,请向硬件专家咨询,让他们来解释 SCSI 通道和物理驱动器分发。

通过系统监视器查看磁盘队列的选择有多种。逻辑磁盘计数器与通过磁盘管理器分配的逻辑驱动器盘符相关,而物理磁盘计数器与磁盘管理器视为一个物理磁盘设备的内容相关。请注意,磁盘管理器视为一个物理设备的驱动器可能是一个硬盘驱动器,也可能是一个包含多个硬盘驱动器的 RAID 阵列。当前磁盘队列长度是对磁盘队列的即时度量,而平均磁盘队列长度是采样期间磁盘队列度量的平均值。如果指明出现以下任一情况,请加以注意:

  • 逻辑磁盘:平均磁盘队列长度 > 2
  • 物理磁盘:平均磁盘队列长度 > 2
  • 逻辑磁盘:当前磁盘队列长度 > 2
  • 物理磁盘:当前磁盘队列长度 > 2

这些建议的度量适于每个物理硬盘驱动器。如果 RAID 阵列与磁盘队列度量相关,则需要用该度量除以 RAID 阵列中的物理硬盘驱动器的数量,以确定每个物理硬盘驱动器的磁盘队列。

注意 在保存 SQL Server 日志文件的物理硬盘驱动器或 RAID 阵列上,磁盘队列不是有用的度量方法,因为日志管理器不会对多个针对 SQL Server 日志文件的 I/O 请求进行排队。

了解 SQL Server 技术内幕

了解 SQL Server 2000 的一些技术内幕有助于您管理数据库的性能。

工作线程

SQL Server 维护着一个 Windows 线程池,这些线程的作用是为成批提交到数据库服务器的 SQL Server 命令提供服务。sp_configure 选项最大工作线程的设置规定了可以为所有传入的命令批提供服务的线程(在 SQL Server 术语中称为工作线程)的总数。如果主动提交命令批的连接数大于指定的最大工作线程数,将在主动提交命令批的连接之间共享工作线程。许多安装都适合使用默认值 255。请注意,大部分连接大多数的时间都在等待从客户端接收命令批。

从 SQL Server 缓冲区缓存中写出 8 KB 脏页的任务主要由工作线程来完成。为了获得最佳性能,工作线程会异步调度它们的 I/O 操作。

惰性写入器

惰性写入器是在缓冲管理器内运行的 SQL Server 系统进程。惰性写入器刷新脏的旧缓冲(必须先将这些缓冲内所含的更改写入磁盘,随后才能将缓冲重新用于其他不同的页)批,然后将它们提供给用户进程。该活动有助于生成和维护可用的空闲缓冲,它们是大小为 8 KB,不含任何数据,可以重新使用的数据缓存页。在惰性写入器将每个 8 KB 缓存缓冲区刷新到磁盘上时,缓存页的标识会被初始化,这样,其他数据就可以写入空闲的缓冲区。惰性写入器在磁盘 I/O 量少时工作,从而将该活动对其他 SQL Server 操作的影响减到最小。

SQL Server 自动配置和管理空闲缓冲水平。性能计数器 SQL Server:缓冲管理器:惰性写入/秒指明了物理写出到磁盘的 8 KB 页的数量。请监视 SQL Server:缓冲管理器:可用页,查看该值是否下降。最佳状态是:惰性写入器使该计数器在所有 SQL Server 操作之间保持水平,这意味着惰性写入器与用户对空闲缓冲的需求保持同步。如果系统监视器对象 SQL Server:缓冲管理器:可用页的值达到零,说明用户负载有时需要较高水平的空闲缓冲,而惰性写入器无法提供这一水平的空闲缓冲。

如果惰性写入器难以使空闲缓冲保持稳定或至少保持在零以上,说明磁盘子系统可能无法提供足够的磁盘 I/O 性能。要证明是否确实如此,请将空闲缓冲水平的下降与磁盘队列作比较。解决办法是向数据库服务器磁盘子系统添加更多物理磁盘驱动器,以提高磁盘 I/O 处理能力。

在系统监视器中监视当前的磁盘队列水平,方法是查看逻辑磁盘或物理磁盘的性能计数器平均磁盘队列长度当前磁盘队列长度,确保与任何 SQL Server 活动相关的每个物理驱动器的磁盘队列小于 2。对于使用硬件 RAID 控制器和磁盘阵列的数据库服务器,记住用“逻辑/物理磁盘”计数器报告的数字除以与该逻辑驱动器盘符或物理硬盘驱动器盘符(依据磁盘管理器的报告)相关的实际硬盘数量,因为 Windows 和 SQL Server 不知道与 RAID 控制器相连的物理硬盘驱动器的实际数量。为了正确地解释系统监视器报告的磁盘队列数量,一定要知道与 RAID 阵列控制器相关的驱动器数量。

有关更多信息,请参阅“SQL Server 联机丛书”。

检查点

SQL Server 的每个实例需要定期确保将所有脏日志和数据页刷新到磁盘。这称为检查点。在重新启动 SQL Server 的实例时,使用检查点可以减少从故障中恢复所需的时间和资源。在检查点期间,脏页(进入缓冲区缓存后已经过修改的缓冲区缓存页)会被写入 SQL Server 数据文件。在检查点处写入磁盘的缓冲仍然包含数据页,用户可以读取或更新该页,而不必从磁盘重新读取,这一点与惰性写入器创建的空闲缓冲不同。

检查点逻辑尝试让工作线程和惰性写入器负责大部分的脏页写出工作。为此,如有可能,检查点逻辑在写出脏页之前,尝试额外多等待一个检查点。这样,工作线程和惰性写入器就有更多的时间来写出脏页。某些情况下,检查点逻辑在写出脏页之前需要额外多等一段时间,有关这些情况的详细信息,请参阅“SQL Server 联机丛书”中的主题“检查点和日志的活动部分”。要请住的重点是,检查点逻辑会尝试通过等待额外的检查点在更长的时间段内均衡 SQL Server 磁盘 I/O 活动。

在有大量的数据页需要从缓存刷新到磁盘上时,为了使检查点操作更有效,SQL Server 将要刷新的数据页按照它们在磁盘上出现的顺序进行排序。这有助于尽量减少磁盘在缓存刷新过程中的来回移动,并在可能的情况下使用连续磁盘 I/O。检查点进程也向磁盘子系统异步提交 8 KB 磁盘 I/O 请求。这样,SQL Server 就能更快地完成对所需磁盘 I/O 请求的提交,因为检查点进程不必等待磁盘子系统发回指明已将数据实际写入磁盘的报告。

重要的一点是要监视与 SQL Server 数据文件相关的硬盘驱动器上的磁盘队列,确定 SQL Server 目前发送的 I/O 请求是否超过磁盘的实际处理能力;如果情况属实,必须提高磁盘子系统的磁盘 I/O 能力,使它能够处理负载。

日志管理器

像所有其他主流 RDBMS 产品一样,SQL Server 也可以确保在发生中断 SQL Server 联机状态的事件(例如,断电、磁盘驱动器有故障、数据中心起火,等等)时,数据库上执行的所有写入活动(插入、更新和删除)不会丢失。SQL Server 日志记录进程有助于确保可恢复性。在完成任何隐式(单个 SQL 查询)或显式事务(所定义的发出 BEGIN TRAN/COMMIT 或 ROLLBACK 命令序列的事务)之前,日志管理器必须从磁盘子系统收到信号,表明与该事务相关的所有数据更改均已成功写入相关的日志文件。这一规则可以确保:如果 SQL Server 因某种原因而突然关机,而检查点和惰性写入器尚未将写入数据缓存的事务刷新到数据文件,那么,SQL Server 可以在启动后读取和重新应用事务日志。恢复是指服务器停机之后读取事务日志以及向 SQL Server 数据应用事务。

由于在每个事务完成时,SQL Server 必须等待磁盘子系统完成对 SQL Server 日志文件的 I/O,所以包含 SQL Server 日志文件的磁盘要有足够的磁盘 I/O 处理能力来承受预期的事务负载,这一点很重要。

SQL Server 日志文件的相关磁盘队列的监视方法与 SQL Server 数据库文件的相关磁盘队列的监视方法不同。请使用系统监视器计数器 SQL Server:数据库 <数据库实例>:日志刷新等待时间SQL Server:数据库 <数据库实例>:日志刷新等待/秒来查看磁盘子系统上是否有处于等待完成状态的日志写入器请求。

具备缓存功能的控制器性能最高,但除非该控制器能够确保将它负责的数据最终写入磁盘,甚至在电源故障时也能最终写入磁盘,否则不得将它用于包含日志文件的磁盘。有关具备缓存功能的控制器的更多信息,请参阅本章的“硬件 RAID 控制器板载缓存的效果”一节。

预读管理

SQL Server 2000 为大规模连续读取表扫描等活动提供了自动管理功能。预读管理完全自行配置和自行优化,并且与

SQL Server 查询处理器的操作紧密地结合在一起。预读管理用于大表扫描、大索引区间扫描、探测聚集索引和非聚集索引二进制树,以及其他情况。原因是预读采用的是 64 KB I/O,与 8 KB I/O 相比,64 KB I/O 能使磁盘子系统达到更大的磁盘吞吐量。如果需要检索大量数据,SQL Server 就使用预读来获得最大的吞吐量。

SQL Server 使用简单有效的索引分配映射表 (IAM) 存储结构,该结构支持预读管理。IAM 是 SQL Server 用于记录扩展盘区位置的机制,即每 64 KB 扩展盘区包含八页数据或索引信息。每个 IAM 页是包含紧密打包(位映射)信息的 8 KB 页,这些信息指明哪些扩展盘区包含所需的数据。IAM 页的压缩特性加快了它们的读取速度,经常使用的 IAM 页还可以保留在缓冲区缓存中。

预读管理可以将来自查询处理器的查询信息与需要从 IAM 页读取的所有扩展分区的位置信息组合在一起,从而构成多个连续的读取请求。连续的 64 KB 磁盘读取提供优异的磁盘 I/O 性能。SQL Server:缓冲管理器:预读页/秒性能计数器提供有关预读管理的有效性及效率的信息。

SQL Server 2000 企业版根据现有内存量动态调整预读页的最大数量。在 SQL Server 2000 的所有其他版本中,该值固定不变。SQL Server 2000 企业版的另一改进之处是通常所说的“旋转木马式扫描”,它允许多个任务共享整个表扫描。如果 SQL 语句的执行计划要求扫描表中的数据页,并且关系数据库引擎检测到已经为另一个执行计划扫描过该表,那么数据库引擎在第二次扫描的当前位置将第二次扫描加入到第一次扫描中。数据库引擎每次读取一页,并将每一页的所有行同时传递到这两个执行计划。此操作会一直进行,直至到达表的结尾。此时,第一个执行计划具有完整的扫描结果,但第二个执行计划仍必须检索在它加入正在进行的扫描之前所发生的数据页。然后,为第二个执行计划执行的扫描会折返回表的第一个数据页,并且向前扫描至它加入第一次扫描的位置。用这种方式可以组合任意数量的扫描;数据库引擎将一直在所有数据页之间循环,直到完成所有扫描。

有关预读管理需要注意一点,那就是过多的预读会对总体性能不利,因为它在缓存内填入不需要的数据页,占用本应用于其他用途的 I/O 和 CPU。对于这一点,只能通过一般的性能优化来解决:优化所有 SQL 查询,尽量减少进入缓冲区缓存的页数量。它包括确保具备正确的索引并在使用这些索引。使用聚集索引可以获得有效的区间扫描,定义非聚集索引有助于快速定位单行或更小的行集。例如,如果您准备在表中只创建一个索引,并且该索引将用于提取单行或更小的行集,该索引应为聚集索引。从表面上看,聚集索引比非聚集索引的速度快。

其他性能主题

使用星型架构和雪花形架构的数据库设计

数据仓库使用维度建模来组织数据,以便进行分析。维度建模会生成星型架构和雪花架构,这样也就为数据仓库中经常执行的大量数据读取操作带来了性能效率。大量的数据(通常成千上万行)存储在事实数据表中,表内各行都很短,这就使存储需求和查询时间减到最少。业务事实数据的属性会非正常化为维度表,以最大程度地减少检索数据时的表联接数量。

有关数据仓库的数据库设计的讨论,请参阅第 17 章“数据仓库设计注意事项”。

在 Transact-SQL 查询中使用等价运算符

在 SQL 查询中使用非等价运算符将强制数据库使用表扫描来对非等价对象取值。如果经常对非常大的表运行这些查询,将会生成高 I/O。包含“NOT”运算符(!=、<>、!<、!>)的 WHERE 子句(如 WHERE <column_name> != some_value)将生成高 I/O。

如果需要运行此类查询,请尝试更改查询的结构,从其中消除 NOT 关键字。例如:

不使用:

select * from tableA where col1 != "value"

尝试使用:

select * from tableA where col1 < "value" and col1 > "value"

减少行集大小和通讯开销

使用 Microsoft ActiveX® 数据对象 (ADO)、远程数据对象 (RDO) 和数据访问对象 (DAO) 数据库 API 等易用界面的 SQL 数据库程序员需要考虑他们生成的结果集。

ADO、RDO 和 DAO 为程序员提供了极好的数据库开发界面,程序员即使没有太多的 SQL 编程经验也能实现丰富的 SQL 行集功能。如果程序员仔细考虑他们的应用程序返回到客户端的数据量,并且跟踪 SQL Server 索引的位置以及 SQL Server 数据的安排方式,就能避免性能问题。SQL 事件探查器、索引优化向导和图形化的执行计划都是非常有用的工具,它们可以帮助程序员精确定位和修复出现问题的查询。

在使用游标逻辑时,请选择最适合您的处理类型的游标。不同类型的游标开销也不同。您应该了解所要执行的是何种类型的操作(只读、只向前处理,等等),然后选择相应的游标类型。

寻找各种机会来减少返回的结果集的大小,方法包括在选择列表中消除不需要返回的列、只返回所需的行。这有助于减少 I/O 和 CPU 消耗。

使用多个语句

通过在数据库上执行处理,您可以减少结果集的大小,并避免在客户端和数据库服务器之间进行不必要的网络通讯。为了执行无法用单个 Transact-SQL 语句执行的处理,SQL Server 允许您将多个 Transact-SQL 语句以下列方式组合在一起。

分组方法 说明
批处理 批处理是以一个单元的形式从应用程序发送到服务器的一组 Transact-SQL 语句,其中可包含一条或多条语句。SQL Server 在执行每个批处理时将其视为单个的可执行单元。
存储过程 存储过程是已在服务器上预定义和预编译的一组 Transact-SQL 语句。存储过程可以接受参数、返回结果集、返回代码,还可以将参数输出到调用应用程序。
触发器 触发器是一类特殊的存储过程。它不由应用程序直接调用,而是每当用户对表执行指定修改(INSERT、UPDATE 或 DELETE)时执行。
脚本 脚本是存储在文件中的一组 Transact-SQL 语句。该文件可以用作 osql 实用工具或 SQL 查询分析器的输入。然后,这些实用工具执行存储于该文件中的 Transact-SQL 语句。

下面的 SQL Server 功能使您可以对同时使用多个 Transact-SQL 语句的情况进行控制。

功能 说明
控制流语句 允许您包含条件逻辑。例如,如果国家为加拿大,则执行某一组的 Transact-SQL 语句。如果国家为英国,则执行另一组的 Transact-SQL 语句。
变量 允许您存储数据,在稍后的 Transact-SQL 语句中用作输入。例如,您可以编写这样一个查询:每次执行该查询时,都需要在 WHERE 子句中指定不同的数据值。您可以在编写该查询时在 WHERE 子句中使用变量,并编写相应的逻辑,以使用正确数据填充该变量。存储过程的参数是一类特殊变量。
错误处理 允许您自定义 SQL Server 响应问题的方式。您可以指定在发生错误时采取的相应的操作,或显示对用户来说比一般的 SQL Server 错误信息更有用的自定义错误信息。

重用执行计划

如果 SQL Server 能够利用先前查询的现有执行计划,则可以提高性能。要促使 SQL Server 重用执行计划,开发人员可以做的工作有很多。Transact-SQL 语句应根据以下原则编写。

  • 使用对象的完全限定名,例如表和视图。

    例如,请不要这样编写 SELECT 语句:

    SELECT * FROM Shippers WHERE ShipperID = 3

    而应使用 SQLBindParameter ODBC 函数(以使用 ODBC 为例):

    SELECT * FROM Northwind.dbo.Shippers WHERE ShipperID = 3

  • 使用参数化查询,并提供参数值,而不要指定存储过程参数值或直接在搜索条件谓词中指定值。使用 sp_executesql 中的参数替代,或使用 ADO、OLE DB、ODBC 和 DB-Library API 的参数绑定。

    例如,请不要这样编写 SELECT 语句:

    SELECT * FROM Northwind.dbo.Shippers WHERE ShipperID = 3

    而应使用 SQLBindParameter ODBC 函数(以使用 ODBC 为例),将参数标记 (?) 绑定到程序变量,并按下面这样编写 SELECT 语句:

    SELECT * FROM Northwind.dbo.Shippers WHERE ShipperID = ?

  • 在 Transact-SQL 脚本、存储过程或触发器中,使用 sp_executesql 执行 SELECT 语句:

    DECLARE @IntVariable INT
    DECLARE @SQLString NVARCHAR(500)
    DECLARE @ParmDefinition NVARCHAR(500)
    /* Build the SQL string. */
    SET @SQLString =
    N'SELECT * FROM Northwind.dbo.Shippers WHERE ShipperID = @ShipID'
    /* Specify the parameter format once. */
    SET @ParmDefinition = N'@ShipID int'
    /* Execute the string. */
    SET @IntVariable = 3
    EXECUTE sp_executesql @SQLString, @ParmDefinition,
    @ShipID = @IntVariable

    如果要避免创建和维护单独的存储过程的开销,则可使用 sp_executesql

对多个批处理重用执行计划

如果多个并发应用程序将用一组已知参数执行同一个批处理,请将该批处理实现为将由这些应用程序调用的存储过程。

在 ADO、OLE DB 或 ODBC 应用程序将会多次执行同一个批处理时,请使用执行该批处理的 PREPARE/EXECUTE 模型。使用绑定到程序变量的参数标记来提供所需的全部输入值,例如,在 UPDATE VALUES 子句或搜索条件谓词中使用的表达式。

维护列中的统计信息

SQL Server 允许创建与某个列中值的分布有关的统计信息,即使该列不是索引的一部分也不成问题。查询处理器可以使用该统计信息来确定评估查询的最佳策略。在您创建索引时,SQL Server 会自动存储与索引列中的值的分布有关的统计信息。除索引列外,如果 AUTO_CREATE_STATISTICS 数据库选项设置为 ON(默认设置),只要在谓词中使用了某个列,即使该列不在索引中,SQL Server 也会自动创建该列的统计信息。

随着列中数据的更改,索引和列的统计信息将会过时,从而导致查询优化程序所做的有关如何处理查询的决策也不如以前理想。随着表中数据的更改,SQL Server 会定期地自动更新此统计信息。采样是在数据页中随机进行的,而且是从统计信息所需的表中或列上最小的非聚集索引中采样。在从磁盘读取了数据页之后,该数据页上的所有行都会用来更新统计信息。更新统计信息的频率由列或索引中的数据量以及发生更改的数据量决定。

例如,某个表中包含 10,000 行,如果其中的 1,000 个索引值发生了更改,这时就可能需要更新该表的统计信息,因为这 1,000 个值可能代表了表中很大一部分数据。但是,对于包含 1000 万个索引条目的表而言,其中 1000 个索引值发生了更改就没有太大关系,因此可能不会自动更新统计信息。不过,SQL Server 始终会确保对最小数量的行进行采样;始终会对小于 8 MB 的表通过完全扫描来收集统计信息。

注意 使用 SQL 查询分析器以图形方式显示查询的执行计划时,将以示警的形式(表名用红色文字显示)指出统计信息过时或缺少统计信息。另外,使用 SQL 事件探查器监视缺少的列统计信息事件类,可以发现什么时候缺少统计信息。

通过使用 sp_createstats 系统存储过程,您可以使用单个语句,在当前数据库内的所有用户表中的所有适合的列上很轻松地创建统计信息。不适于创建统计信息的列包括:不确定的或不精确的计算列,或是数据类型为 imagetextntext 的列。

如果手动创建统计信息,则您可以创建包含多个列密度(列组合重复的平均数)的统计信息。例如,某个查询包含以下子句:

WHERE a = 7 and b = 9

同时在两列(ab)上创建手动统计信息可以使 SQL Server 更好地预估查询,因为统计信息也包含 ab 列组合的非重复值的平均数。这样,SQL Server 就可以利用 col1 上建立的索引(此情况下最好为聚集索引),而不必进行表扫描。有关如何创建列统计信息的信息,请参阅“SQL Server 联机丛书”中的主题“创建统计信息”。

查找更多信息
  • “SQL Server 联机丛书”提供了有关 SQL Server 结构和数据库优化的信息,同时还提供了完整的命令语法和管理的文档资料。“SQL Server 联机丛书”可以从 SQL Server 安装介质安装到任何 SQL Server 客户端或服务器计算机上。
  • 有关 Microsoft SQL Server 的最新信息,包括有关 SQL Server 的技术论文,请访问下面的 Microsoft SQL Server Web 站点:
  • http://www.sqlmag.com 中有以期刊的形式提供信息的外部资源。您可以在其中找到许多优化和调整提示、代码示例、概述 SQL Server 内部工作原理的见解深刻的文章,以及其他有价值的信息。
  • Delaney、Kalen 和 Soukup, Ron 合著。Inside Microsoft SQL Server 2000(Microsoft SQL Server 2000 技术内幕),Microsoft Press,2001

    该书是对上一版本(Inside Microsoft SQL Server 7.0 (Microsoft SQL Server 7.0 技术内幕))的更新,其中纳入了 SQL Server 2000 的信息。该书深入探讨了 SQL Server 的许多内部概念。

  • Kimball, Ralph 著。The Data Warehouse Lifecycle Toolkit(数据仓库生存期工具箱),John Wiley 和 Sons 合著,1998 年。

    该书深入探讨数据仓库的数据库设计,并解释了维度建模概念

 

版权所有:UML软件工程组织