SQL Server 诊断检测到由于读取过时或写入丢失而导致的 I/O 未报告问题

本文介绍了 SQL Server 诊断如何帮助检测由于读取过时或写入丢失而导致的未报告输入或输出问题。

原始产品版本:SQL Server
原始 KB 数: 826433

现象

如果操作系统、驱动程序或硬件问题导致 I/O 路径中的写入或过时读取条件丢失,你可能会在 SQL Server 中看到与数据完整性相关的错误消息,例如错误 605、823、3448 和 3456。 可能会收到类似于以下示例的错误消息:

2003-07-24 16:43:04.57 spid63 Getpage: bstat=0x9, sstat=0x800, cache
2003-07-24 16:43:04.57 spid63 pageno is/should be: objid is/should be:
2003-07-24 16:43:04.57 spid63 (1:7040966)/(1:7040966) 2093354622/2039782424
2003-07-24 16:43:04.57 spid63 ... IAM indicates that page is allocated to this object
2003-07-24 16:52:37.67 spid63 Error: 605, Severity: 21, State: 1
2003-07-24 16:52:37.67 spid63 Attempt to fetch logical page (1:7040966) in database 'pubs' belongs to object 'authors', not to object 'titles'..
2003-07-24 16:52:40.99 spid63 Error: 3448, Severity: 21, State: 1
2003-07-24 16:52:40.99 spid63 Could not undo log record (63361:16876:181), for transaction ID (0:159696956), on page (1:7040977), database 'pubs' (database ID 12). Page information: LSN = (63192:958360:10), type = 2. Log information: OpCode = 2, context 1..
2003-07-09 14:31:35.92 spid66 Error: 823, Severity: 24, State: 2
2003-07-09 14:31:35.92 spid66 I/O error (bad page ID) detected during read at offset 0x00000016774000 in file 'h:\sql\MSSQL\data\tempdb.mdf'..
2010-02-06 15:57:24.14 spid17s Error: 3456, Severity: 21, State: 1.
2010-02-06 15:57:24.14 spid17s Could not redo log record (58997:5252:28), for transaction ID (0:109000187), on page (1:480946), database 'MyDatabase' (database ID 17). Page: LSN = (58997:5234:17), type = 3. Log: OpCode = 2, context 5, PrevPageLSN: (58997:5243:17). Restore from a backup of the database, or repair the database.

SQL Server 中的新 I/O 诊断功能

SQL Server 从 SQL Server 2000 Service Pack 4 开始引入了新的 I/O 诊断功能,自那时以来,这些诊断一直是产品的一部分。 这些功能旨在帮助检测外部 I/O 相关问题,并排查“症状部分中所述的错误消息。

如果收到“症状”部分中列出的任何错误消息,并且它们不会由物理驱动器故障等事件解释,请查看 SQL Server、操作系统、驱动程序和硬件的任何已知问题。 诊断尝试提供有关以下两个条件的信息:

  • 丢失写入:成功调用 WriteFile API,但操作系统、驱动程序或缓存控制器无法正确将数据刷新到物理媒体,即使 SQL Server 通知写入成功。

  • 过时读取:成功调用 ReadFile API,但操作系统、驱动程序或缓存控制器错误地返回旧版数据。

为了说明,Microsoft已确认了 WriteFile API 调用返回成功状态的情况,但立即成功读取相同的数据块会返回较旧的数据,包括可能存储在硬件读取缓存中的数据。 有时,此问题因读取缓存问题而发生。 在其他情况下,写入数据永远不会写入物理磁盘。

如何启用诊断

在 SQL Server 2017 及更高版本中,默认启用此诊断功能。 在 SQL Server 2016 和早期版本中,这些诊断只能通过使用跟踪标志 818 来启用。 可以将跟踪标志 818 指定为 SQL Server 实例的启动参数 -T818,也可以运行以下 T-SQL 语句,以便在运行时启用它们:

DBCC TRACEON(818, -1)

跟踪标志 818 启用内存中环缓冲区,用于跟踪运行 SQL Server 的计算机执行的最后 2,048 次成功写入操作,不包括排序和工作文件 I/O。 当出现 605、823 或 3448 等错误时,传入缓冲区的日志序列号 (LSN) 值与最近的写入列表进行比较。 如果在读取操作期间检索的 LSN 早于写入操作中使用的 LSN,则会在 SQL Server 错误日志中记录新的错误消息。 大多数 SQL Server 写入操作都作为检查点或延迟写入进行(延迟写入是使用异步 I/O 的后台任务)。 环形缓冲区的实现是轻量级的,对系统的性能影响是微不足道的。

有关错误日志中消息的详细信息

以下消息不显示来自 WriteFile API 或 ReadFile API 调用 SQL Server 的任何显式错误。 相反,它显示一个逻辑 I/O 错误,该错误在查看 LSN 时导致,其预期值不正确:

从 SQL Server 2005 开始,显示的错误消息为:

SQL Server 检测到基于逻辑一致性的 I/O 错误:已过时读取。 它在文件<FILE NAME>偏移<PHYSICAL OFFSET>量的数据库 ID <DBID> 中的页面<PAGEID>期间<Read/Write>发生。 SQL Server 错误日志或系统事件日志中的其他消息可能提供了更详细信息。 这是一个威胁数据库完整性的严重错误条件,必须立即纠正。 完成完整的数据库一致性检查 (DBCC CHECKDB)。 此错误可能是由许多因素引起的。 有关详细信息,请参阅 SQL Server 联机丛书。

有关错误 824 的详细信息,请参阅 MSSQLSERVER_824

此时或报告此错误时,读取缓存包含较旧版本的页面,或者数据未正确写入物理磁盘。 在任一情况下(写入丢失或已过时读取),SQL Server 报告操作系统、驱动程序或硬件层的外部问题。

如果尝试回滚出现错误 605 或 823 的事务时出现错误 3448,则 SQL Server 实例会自动关闭数据库并尝试打开并恢复它。 遇到错误 605 或 823 的第一页被视为错误的页面,并且页面 ID 由运行 SQL Server 的计算机保留。 在恢复(重做阶段之前)读取错误页 ID 时,有关页面标头的主要详细信息将记录在 SQL Server 错误日志中。 此操作非常重要,因为它有助于区分丢失的写入和过时读取方案。

使用过时读取和丢失写入观察到的行为

在过时的读取方案中,你可能会看到以下两种常见行为:

  • 如果数据库文件已关闭,然后打开,则恢复期间将返回正确的和最近写入的数据。

  • 发出检查点并运行 DBCC DROPCLEANBUFFERS 语句(要从内存中删除所有数据库页),然后对数据库运行 DBCC CHECKDB 该语句时,将返回最近写入的数据。

上一段中提到的行为指示读取缓存问题,并且通过禁用读取缓存经常解决这些行为。 前面段落中概述的操作通常强制缓存失效,并且发生的成功读取表明物理媒体已正确更新。 当读取回的页面仍然是旧版数据时,即使强制刷新缓存机制后,也会发生丢失的写入行为。

有时,问题可能不特定于硬件缓存。 筛选器驱动程序可能存在问题。 在这种情况下,请查看软件,包括备份实用工具和防病毒软件,然后查看筛选器驱动程序是否存在问题。

各种过时读取和丢失写入方案的说明

Microsoft还指出了不符合错误 605 或 823 条件的条件,但是由相同的过时读取或丢失写入活动引起的。 在某些情况下,页面似乎更新了两次,但具有相同的 LSN 值。 如果对象 ID 和页 ID 正确(已分配给该对象的页面),并且对页面进行了更改并刷新到磁盘,则可能会发生此行为。 下一页检索将返回较旧的图像,然后进行第二次更改。 SQL Server 事务日志显示页面更新了两次,其 LSN 值相同。 尝试还原事务日志序列或出现数据一致性问题(例如外键失败或缺少数据条目)时,此操作就成了问题。 以下错误消息说明了此条件的一个示例:

错误:3456,严重性:21,状态:1 无法重做日志记录(276666:1664:19),事务 ID(0:825853240),页(1:1787100),数据库“作者”(7)。 页面:LSN = (276658:4501:9),类型 = 1。 日志:OpCode = 4,上下文 2,PrevPageLSN:(275565:3959:31)。

以下列表中更详细地概述了某些方案:

LSN SequenceAction
1   Checkpoint
2   Begin Transaction
3   Table created or truncated
4   Inserts (Pages allocated)
5   Newly allocated page written to disk by Lazy Writer
6   Select from table - Scans IAM chain, newly allocated page read back from disk (LRU | HASHED = 0x9 in getpage message), encounters Error 605 - Invalid Object ID
7   Rollback of transaction initiated
LSN SequenceAction
1   Checkpoint
2   Begin Transaction
3   Page Modification
4   Page written to disk by Lazy Writer
5   Page read in for another modification (stale image returned)
6   Page Modified for a second time but because of stale image does not see first modification 
7   Rollback - Fails - Transaction Log shows two different log records with the same PREV LSN for the page

SQL Server sort 运算符执行 I/O 活动,通常在 tempdb 数据库中。 这些 I/O 操作类似于缓冲区 I/O 操作;但是,它们已设计为使用读取重试逻辑来尝试解决类似的问题。 本文中介绍的其他诊断不适用于这些 I/O 操作。

Microsoft指出,以下排序读取失败的根本原因通常是过时的读取或丢失的写入:

2003-04-01 20:13:31.38 spid122 SQL Server Assertion: File: <p:\sql\ntdbms\storeng\drs\include\record.inl>, line=1447 Failed Assertion = 'm_SizeRec > 0 && m_SizeRec <= MAXDATAROW'.
2003-03-29 09:51:41.12 spid57 Sort read failure (bad page ID). pageid = (0x1:0x13e9), dbid = 2, file = e:\program files\Microsoft SQL Server\mssql\data\tempdb.mdf. Retrying.
2003-03-29 09:51:41.13 spid57 Error: 823, Severity: 24, State: 7
2003-03-29 09:51:41.13 spid57 I/O error (bad page ID) detected during read at offset 0x000000027d2000 in file 'e:\program files\Microsoft SQL Server\mssql\data\tempdb.mdf'..
* 00931097 Module(sqlservr+00531097) (utassert_fail+000002E3)
* 005B1DA8 Module(sqlservr+001B1DA8) (RecBase::Resize+00000091)
* 00407EE7 Module(sqlservr+00007EE7) (RecBase::LocateColumn+00000012)
* 00852520 Module(sqlservr+00452520) (mergerow+000000A4)
* 008522B3 Module(sqlservr+004522B3) (merge_getnext+00000285)
* 0085207D Module(sqlservr+0045207D) (mergenext+0000000D)
* 004FC5FB Module(sqlservr+000FC5FB) (getsorted+00000021)

由于读取过时或写入丢失会导致数据存储不预期,因此可能会发生各种行为。 它可能显示为缺失数据,但缺失数据的一些较常见效果显示为索引损坏,例如错误 644 或 625:

错误 644 严重级别 21 消息文本在索引页 %S_PGID、索引 ID %d、数据库 '%.*ls' 中找不到 RID '%.*hs' 的索引项。

错误 625 严重级别 21 消息文本无法从 RID 页 %S_PGID检索行,因为 slotid (%d) 无效。

某些客户在执行行计数活动后报告缺少的行。 由于写入丢失,因此会出现此问题。 也许,页面应该链接到聚集索引页链。 如果写入在物理上丢失,则数据也会丢失。

重要

如果遇到任何行为,或者怀疑类似问题以及禁用缓存机制,Microsoft强烈建议你获取 SQL Server 的最新更新。 Microsoft还强烈建议你对操作系统及其关联的配置进行严格审查。

请注意,Microsoft已确认,在罕见且繁重的 I/O 负载下,某些硬件平台可能会返回过时的读取。 如果扩展诊断指示可能过时的读取或写入条件丢失,请联系硬件供应商,立即跟进 并使用 SQLIOSim 实用工具进行测试。

SQL Server 要求系统支持按 SQL Server I/O 可靠性计划要求中所述的保证传送到稳定媒体。 有关 SQL Server 数据库引擎的输入和输出要求的详细信息,请参阅Microsoft SQL Server 数据库引擎输入/输出要求