Pau*_*dal
21
哈 - 我被问到的(非)最喜欢的问题(当我写 DBCC CHECKDB 时)。
干得好:
只有一次您应该尝试计算 CHECKDB 需要多长时间 - 当您计划定期数据库维护时。如果您面临一个损坏的(或疑似损坏的)数据库,并且您才刚刚开始考虑 CHECKDB 需要多长时间 - 您在规划灾难恢复策略时犯了一个错误。您总是需要知道 CHECKDB 为您的数据库运行(平均)需要多长时间,因此:
- 您可以判断特定运行的 CHECKDB 是否比平时花费的时间更长 - 这表明它发现了一些损坏
- 您知道在灾难恢复情况下需要多长时间才能获得结果
在我参加的每个会议上,有人问我在他们的数据库上运行 CHECKDB 需要多长时间。我可以通过几种方式来回答这个问题:
- 无益的答案 - 我不知道。
- 几乎有用的答案 - 上次运行需要多长时间,条件是否完全相同?
- 我通常给出的答案 - 这取决于。
现在,许多人会认为第三个答案与第一个答案有些等价 - 没有帮助。问题是有很多因素会影响 CHECKDB 的运行时间。让我解释十个最重要的因素,以便您了解为什么这实际上是一个有用的答案。这些没有任何特定的重要性顺序。
- 数据库的大小
很明显... CHECKDB 必须读取数据库中每个分配的页面,因此它越大,读取所有页面所需的时间就越长。
- 服务器上的并发IO负载
最简单的层面,CHECKDB要做什么?它读取数据库中每个分配的页面。这是很多IO。CHECKDB 非常努力地执行它所能做的最有效的 IO,并通过大量预读以物理顺序读取数据库页面,以便磁盘磁头在磁盘之间平滑移动(而不是随机跳跃并导致磁盘磁头寻道延迟)。如果服务器上没有并发 IO 负载,那么 IO 的效率将与 CHECKDB 一样有效。然而,从 SQL Server 引入任何额外的 IO 意味着磁盘磁头会四处跳跃 - 减慢 CHECKDB IO。如果 IO 子系统已经满足 CHECKDB 的 IO 需求,任何额外的 IO 都会减少 CHECKDB 可用的 IO 带宽 - 减慢它的速度。
- 服务器上的并发 CPU 活动
在下一个简单级别,CHECKDB 将以某种方式处理它读取的每个页面。根据您指定的各种选项和数据库架构(详情如下),这将使用大量 CPU - 当 CHECKDB 运行时,服务器可能会被固定在 100% CPU。如果服务器上有任何额外的工作负载,这将占用 CHECKDB 的 CPU 周期,并且会减慢它的速度。基本上,#2 和 #3 的意思是 CHECKDB 非常占用资源!这可能是您可以要求 SQL Server 执行的资源最密集的事情之一,因此在工作负载高峰期不要运行它通常是个好主意,因为您不仅会导致 CHECKDB 运行时间更长,还会减慢并发工作量,可能是不可接受的。
- 数据库上的并发更新活动
这与 SQL 2000 和 SQL 2005 都相关,但原因不同。在 SQL 2000 中,CHECKDB 从并发 DML 事务的事务日志分析中获取其一致的数据库视图(有关详细信息,请参见此处)。CHECKDB 运行时并发 DML 越多,生成的事务日志就越多 - 因此 CHECKDB 分析该事务日志所需的时间就越长。在具有大量并发 DML 和 CHECKDB 且仅限于单个 CPU 的大型多 CPU 机器上,CHECKDB 的这一阶段可能比读取和处理数据库页面所需的时间长几倍!(我在现实生活中多次看到这种情况。)在 SQL 2005 中,CHECKDB 从数据库快照中获取数据库的一致视图,该快照与数据库本身存储在相同的磁盘卷上。如果在 CHECKDB 运行时数据库中有大量更改,则更改的页面会推送到快照以保持一致。由于快照文件与数据库文件存储在同一位置,因此每次将页面推送到快照时,磁盘磁头都必须移动,这会中断 #2 中描述的高效 IO。此外,每当 CHECKDB 读取页面并且它需要从快照文件而不是数据库文件读取页面时,这是另一个磁盘磁头移动和另一个有效的 IO 中断。对数据库的并发更改越多,对高效 IO 的中断越多,CHECKDB 运行的速度就越慢。由于快照文件与数据库文件存储在同一位置,因此每次将页面推送到快照时,磁盘磁头都必须移动,这会中断 #2 中描述的高效 IO。此外,每当 CHECKDB 读取页面时,它需要从快照文件而不是数据库文件中读取页面,这是另一个磁盘磁头移动和另一个有效的 IO 中断。对数据库的并发更改越多,对高效 IO 的中断越多,CHECKDB 运行的速度就越慢。由于快照文件与数据库文件存储在同一位置,因此每次将页面推送到快照时,磁盘磁头都必须移动,这会中断 #2 中描述的高效 IO。此外,每当 CHECKDB 读取页面时,它需要从快照文件而不是数据库文件中读取页面,这是另一个磁盘磁头移动和另一个有效的 IO 中断。对数据库的并发更改越多,对高效 IO 的中断越多,CHECKDB 运行的速度就越慢。
- IO 子系统的吞吐量能力
这个很简单。根据指定的选项和数据库架构,CHECKDB 将执行大量 IO,它甚至可能最终受 IO 限制(意味着 CPU 处于空闲状态,定期等待 IO 完成)。这意味着 IO 子系统的吞吐量将对 CHECKDB 的运行时间产生直接影响。所以,如果你有一个 1TB 的数据库,而 IO 子系统只能管理 100MB/秒,那么读取数据库需要将近 3 个小时(1TB/100MB/3600 秒),除了升级IO子系统。我已经数不清我的次数了
- 盒子上的 CPU(处理核心)数量
这也确实包含正在运行的 SQL Server 版本。在企业版中,CHECKDB 可以在机箱中的所有 CPU 上并行运行(或与查询处理器决定在编译 CHECKDB 内部查询时并行运行的数量一样多)。并行运行可以显着提高 CHECKDB 的性能并缩短运行时间,只要数据库也分布在多个文件上(因此 IO 可以并行化)。使用了一个漂亮的算法,它允许 CHECKDB 并行运行,我将在以后的文章中详细解释。另一方面,CHECKDB 可以在企业版中并行运行的事实对于某些场景可能是不利的,因此一些 DBA 选择强制 CHECKDB 为单线程。SAP 通常建议这样做以帮助提高用户查询的可预测性。
- 放置 tempdb 的磁盘的速度
针对 VLDB 运行 CHECKDB 使用大量内存用于内部状态,对于 VLDB,内存需求通常超过 SQL Server 可用的内存量。在这种情况下,状态被假脱机到 tempdb,因此 tempdb 的性能可能是 CHECKDB 性能的关键因素。有关这方面的更多详细信息以及如果 tempdb 太小 CHECKDB 如何耗尽磁盘空间,请参阅此帖子。
- 数据库模式的复杂性
这会对 CHECKDB 的运行时间产生非常大的影响,因为它会影响 CHECKDB 所需的 CPU 数量。例如,CHECKDB 所做的最昂贵的检查是针对非聚集索引。它需要检查非聚集索引中的每一行是否正好映射到表的堆或聚集索引中的一行,并且每个堆/聚集索引行在每个非聚集索引中都恰好有一个匹配行。尽管有一种高效的算法可以做到这一点,但它仍然占用 CHECKDB 使用的总 CPU 的 30% 左右!还有很多其他检查只有在数据库中使用了这些功能时才进行 - 例如计算列评估、行外 LOB 值之间的链接、服务代理、XML 索引、索引视图 - 所以你可以看到经验因素沿着是'
- 指定了哪些选项
这与 #7 几乎相同,因为通过指定各种选项,您可以限制 CHECKDB 实际执行的检查。例如,使用 WITH NOINDEX 选项将关闭我在 #7 中描述的非聚集索引检查,使用 WITH PHYSICAL_ONLY 选项将关闭所有逻辑检查,大大减少 CHECKDB 的运行时间并使其几乎总是 IO -bound 而不是 CPU-bound(事实上,这是 VLDB 的 DBA 用来使 CHECKDB 的运行时间易于管理的最常见选项)。需要注意的一件事 - 如果您指定任何修复选项,CHECKDB 始终以单线程运行,即使在企业版的多进程机器上也是如此。
- 数据库中存在的损坏的数量和类型
同样,这与 #7 和 #8 类似。如果存在任何损坏,则可能会触发额外的检查以尝试找出损坏的更多详细信息。例如,对于非聚集索引检查,算法针对不存在损坏的情况进行了大量调整(绝大多数情况下,CHECKDB 每天在世界各地运行数百万次)。当检测到非聚集索引损坏时,必须使用更深入的算法来确定损坏的确切位置,这涉及重新扫描大量数据,因此需要更多时间。还有一些其他类似的算法。
现在要记住的另一件事是,使用 REPAIR_ALLOW_DATA_LOSS 使检查运行单线程,因此可以正确订购维修 - 这使其运行时间更长。查看 2005 SP2+ 上的错误日志中的 5268 消息 - 正如我上面提到的,它表示深入探讨。
总结
所以你可以看到没有简单的答案。希望这可以帮助!
PS 忘了说在 SQL 2005 中我向 DBCC CHECKDB 添加了进度报告。您可以查询sys.dm_exec_requestsDMV 并查找该percent_complete列。