随机错误 824 - 所有环境

Dav*_*e C 5 sql-server corruption errors sql-server-2017

我们在 DEV/QA/PROD SQL 2017 Enterprise 服务器上遇到了看似随机的错误 824。服务器运行几乎相同的代码,通过 ETL 流程将相同的日常文件摄取到我们的数据仓库中。这些错误是在 2022 年 5 月左右首次发现的,但由于日志清理,我们无法确定(供应商提供的)ETL 流程是否正在捕获这些错误、记录警告并继续处理而不是失败!

DEV/QA 已修补到 CU30(最新的 CU)——这种情况仍然存在。CU22 的生产落后了几个补丁,计划在未来几周内进行修补。

例子:

SQL Server 检测到基于逻辑不一致的 I/O 错误:校验和不正确(预期为 0xc30164e7;实际为 0x9f2bc675c)。它发生在读取文件“H:\tempdb_mssql_6.ndf”中偏移量 0x0000027de40000 处的数据库 ID 2 中的页 (7:1306400) 期间。

如前所述,这种情况在我们所有的环境中都是随机发生的。所有服务器都是虚拟化的。DEV/QA 都使用相同的 SAN。生产位于不同数据中心的单独 SAN 上。我没有 SAN 设备品牌/型号的详细信息。

在大多数情况下,当发生这种情况时,它似乎主要在 tempdb 中(但并非总是如此)。此外,suspect_pages 通常是空的。这种情况似乎在周六发生得更频繁,因为我们连续发生了 3-4 次。

另请注意,错误中列出的预期/实际值通常是相同的 - 但并非总是如此。

还注意到,特定的存储过程似乎更容易引发此错误,但是,它已经发生在 ETL 作业中的多个其他位置,再次影响不同的数据库。似乎触发此错误的存储过程最常添加一个 PERSISTED 计算列,然后添加一个基于该计算列的 ROW_NUMBER() - 到 5 个表,大小范围从 200K 到 750 万行。我们昨天(在 QA 中)修改了此过程,以限制使用 ROW_NUMBER() 值更新的行数(仅当 rownum=1 时),并将更新从一次性全部更改为 25K 批量方法。今天在 QA 中再次发生该错误 - 因此我们删除了计算列上的 PERSISTED 选项。我们实际上正在尝试在质量检查中阻止这种情况,因为它似乎受到的影响最大。

DBCC CHECKDB 每天在除一个数据库之外的所有生产数据库上运行。一个跳过的数据库相当大,接近 4TB,DBCC CHECKDB 需要大约 12 小时才能完成。DBCC CHECKDB 已在这个大型数据库的 QA 中的最近生产备份上运行,并且它是干净的。

到目前为止,只有 tempdb 受到生产影响。DEV/QA 已经看到其他几个数据库受到影响,但 DBCC CHECKDB 没有产生任何结果,即使当 Suspect_pages 包含条目时也是如此。为了安全起见,我们已将生产数据库恢复到 DEV/QA(当它不是 tempdb 时)。

当 tempdb 受到影响时,我们一直在停止 SQL,并删除有问题的 tempdb 文件并重新启动服务。

值得注意的是,H: 在生产中使用率为 100%,因为 mdf/ndf 文件占用了整个 150GB 驱动器,但文件内有充足的可用空间。DEV/QA 未达到 100%,并且有大约 30-40GB 的可用空间,并且都显示 tempdb 受到影响。所以我认为这不是驱动器空间问题。

我已经联系了我们的DBA,他认为问题是由数据本身引起的。虽然我不同意数据有问题,但我们确实在 2022 年 4 月升级了生成 ETL 作业读取的 CSV 文件的系统。源系统现在基于 Linux,而不是基于 Windows。ETL 过程无需任何更改即可读取新文件。文件中的数据在结构上发生了一些变化,但它们基本上是相同的,并且 CLR 过程可以根据每天发送的架构布局文件来适应结构变化。这些文件足够大(每天 55+GB),可以填充 300 多个表,每个表都包含大量 nvarchar(max) 字段。

我已联系我们的基础设施团队来检查 VM/SAN 运行状况 - 虽然生产的 VM 在文件摄取阶段报告了高 IO(时间戳完美排列) - 但没有报告任何错误。我们的基础设施团队在调查期间将生产虚拟机迁移到更快的主机,并将文件迁移到新的相对未使用的较新的 SAN。

7 月 26 日,DEV/QA SAN 发生驱动器故障,导致更换并重建两个驱动器。自从驱动器故障以来,我们与 Microsoft 合作时没有发生任何事件,但是上周六和上周一 - QA 出现了更多与 tempdb 相关的错误。

我们已经向微软开了一张票,经过几周/电话后,我们被告知他们是错误的团队(他们只帮助修复腐败),我们需要一张首要票。因此,我们目前正在向 Microsoft 寻求报价,以获取优先票以帮助进行根本原因分析。

与此同时,我希望生产不会出现进一步的问题,我希望有人能够提供一些进一步的见解或指导,告诉我们如何分析这个问题,或者限制它的发生,直到我们能够得到微软适当参与。

编辑 1a - 完整@@VERSION 输出

DEV: Microsoft SQL Server 2017 (RTM-CU30) (KB5013756) - 14.0.3451.2 (X64)   Jun 22 2022 18:20:15   Copyright (C) 2017 Microsoft Corporation  Enterprise Edition: Core-based Licensing (64-bit) on Windows Server 2012 R2 Datacenter 6.3 <X64> (Build 9600: ) (Hypervisor) 
QA: Microsoft SQL Server 2017 (RTM-CU30) (KB5013756) - 14.0.3451.2 (X64)   Jun 22 2022 18:20:15   Copyright (C) 2017 Microsoft Corporation  Enterprise Edition: Core-based Licensing (64-bit) on Windows Server 2012 R2 Datacenter 6.3 <X64> (Build 9600: ) (Hypervisor) 
PROD: Microsoft SQL Server 2017 (RTM-CU22-GDR) (KB4583457) - 14.0.3370.1 (X64)   Nov  6 2020 18:19:52   Copyright (C) 2017 Microsoft Corporation  Enterprise Edition: Core-based Licensing (64-bit) on Windows Server 2012 R2 Datacenter 6.3 <X64> (Build 9600: ) (Hypervisor) 
Run Code Online (Sandbox Code Playgroud)

编辑 1b - 注释后的代码中出现可疑过程失败:BATCH UPDATE RN - CURRENT/PAST ROWS

SET NOCOUNT ON;
DECLARE @schema SYSNAME, @table SYSNAME, @RNCurrDepth SMALLINT, @RNFutureDepth SMALLINT, @SQL NVARCHAR(MAX);

BEGIN TRY
    DECLARE IDComp3 CURSOR LOCAL
    FOR
    SELECT DISTINCT S.name, T.name, P.RNCurrentDescDepth, P.RNFutureAscDepth
    FROM sys.columns C
    JOIN sys.tables T ON C.object_id=T.object_id
    JOIN sys.schemas S ON T.schema_id=S.schema_id
    JOIN dbo.ParseIDComp3Control P ON S.name=P.TableSchema AND T.name=TableName /* Control Table to limit the tables parsed, and how many ROW_NUMBERS to update */
    WHERE C.name='ID_COMP_3'
    AND P.Active=1
    ORDER BY T.name;
    OPEN IDComp3;
    FETCH NEXT FROM IDComp3 INTO @schema, @table, @RNCurrDepth, @RNFutureDepth;
    WHILE @@FETCH_STATUS=0
    BEGIN
        --RAISERROR(@TABLE,0,1) WITH NOWAIT;

        DROP TABLE IF EXISTS #TMP;
        CREATE TABLE #TMP ([@ID] NVARCHAR(150)
                          ,LEAD_CO_MNE NVARCHAR(50)
                          ,RNCURRENTDESC INT
                          ,RNFUTUREASC INT);

        DROP TABLE IF EXISTS #TMP_BATCH;
        CREATE TABLE #TMP_BATCH ([@ID] NVARCHAR(150)
                                ,LEAD_CO_MNE NVARCHAR(50)
                                ,RNCURRENTDESC INT
                                ,RNFUTUREASC INT);

        /* DROP ID_COMP_3 COLUMNS IF EXISTS, TO ENSURE WE HAVE NO VALUES */
        SET @SQL='';
        SELECT @SQL+='ALTER TABLE '+QUOTENAME(S.name)+'.'+QUOTENAME(T.name)+' DROP COLUMN '+QUOTENAME(C.name)+';'+CHAR(10)
            FROM sys.columns C
            JOIN sys.tables T ON C.object_id=T.object_id
            JOIN sys.schemas S ON T.schema_id=S.schema_id
            WHERE C.name IN ('ID_COMP_3_DATE','ID_COMP_3_ID')
            AND S.name=@schema
            AND T.name=@table
        EXEC SP_EXECUTESQL @SQL;

        /* ADD COMPUTED COLUMNS IF NOT EXISTS */
        IF NOT EXISTS (SELECT 1
                        FROM sys.columns C
                        JOIN sys.tables T ON C.object_id=T.object_id
                        JOIN sys.schemas S ON T.schema_id=S.schema_id
                        WHERE C.name='ID_COMP_3_DATE'
                        AND S.name=@schema
                        AND T.name=@table)
        BEGIN   
            SET @SQL = 'ALTER TABLE '+QUOTENAME(@schema)+'.'+QUOTENAME(@table)+'ADD ID_COMP_3_DATE AS TRY_CONVERT(DATE,LEFT(ID_COMP_3,8),112),
                                                                                    ID_COMP_3_ID AS TRY_CONVERT(INT,RIGHT(ID_COMP_3,LEN(ID_COMP_3)-CHARINDEX(''.'',ID_COMP_3)));'
            EXEC SP_EXECUTESQL @SQL;
        END

        /* DROP INDEXES IF EXISTS (SO WE CAN DROP COLUMNS) */
        SET @SQL='';
        SELECT @SQL+='DROP INDEX ' + QUOTENAME(I.name) + ' ON ' + QUOTENAME(S.name)+'.'+QUOTENAME(T.name)+CHAR(10)
            FROM sys.indexes I
            JOIN sys.tables T ON I.object_id=T.object_id
            JOIN sys.schemas S ON T.schema_id=S.schema_id
            WHERE T.name=@table
            AND S.name=@schema
            AND I.name IN ('IX_'+t.name+'_RN_Current_Desc', 'IX_'+t.name+'_RN_Future_Asc')
        EXEC SP_EXECUTESQL @SQL;

        /* DROP RN COLUMNS IF EXISTS, TO ENSURE WE HAVE NO VALUES */
        SET @SQL='';
        SELECT @SQL+='ALTER TABLE '+QUOTENAME(S.name)+'.'+QUOTENAME(T.name)+' DROP COLUMN '+QUOTENAME(C.name)+';'+CHAR(10)
            FROM sys.columns C
            JOIN sys.tables T ON C.object_id=T.object_id
            JOIN sys.schemas S ON T.schema_id=S.schema_id
            WHERE C.name IN ('RN_Current_Desc','RN_Future_Asc')
            AND S.name=@schema
            AND T.name=@table
        EXEC SP_EXECUTESQL @SQL;

        /* ADD RN COLUMNS */
        IF NOT EXISTS (SELECT 1
                        FROM sys.columns C
                        JOIN sys.tables T ON C.object_id=T.object_id
                        JOIN sys.schemas S ON T.schema_id=S.schema_id
                        WHERE C.name='RN_Current_Desc'
                        AND S.name=@schema
                        AND T.name=@table)
        BEGIN   
            SET @SQL = 'ALTER TABLE '+QUOTENAME(@schema)+'.'+QUOTENAME(@table)+'ADD RN_Current_Desc SMALLINT,
                                                                                    RN_Future_Asc SMALLINT;'
            EXEC SP_EXECUTESQL @SQL;
        END

        /* ADD INDEX TO @ID/RNS */
        SET @SQL = 'CREATE NONCLUSTERED INDEX [IX_'+@table+'_RN_Current_Desc] ON '+QUOTENAME(@schema)+'.'+QUOTENAME(@table)+' ([@ID], LEAD_CO_MNE, RN_Current_Desc ASC) WHERE (RN_Current_Desc <= '+CONVERT(VARCHAR(6),@RNCurrDepth)+');
                    CREATE NONCLUSTERED INDEX [IX_'+@table+'_RN_Future_Asc] ON '+QUOTENAME(@schema)+'.'+QUOTENAME(@table)+' ([@ID], LEAD_CO_MNE, RN_Future_Asc DESC)  WHERE (RN_Current_Desc <= '+CONVERT(VARCHAR(6),@RNFutureDepth)+');
                    CREATE NONCLUSTERED INDEX [IX_TMP_Current_Desc] ON #TMP ([@ID], LEAD_CO_MNE) INCLUDE(RNCURRENTDESC, RNFUTUREASC);
                    CREATE NONCLUSTERED INDEX [IX_TMPBATCH_Current_Desc] ON #TMP_BATCH ([@ID], LEAD_CO_MNE) INCLUDE(RNCURRENTDESC, RNFUTUREASC);';
        EXEC SP_EXECUTESQL @SQL;

        /* BATCH UPDATE RN - CURRENT/PAST ROWS */
        SET @SQL = ';WITH X AS
                    (
                    SELECT [@ID]
                          ,LEAD_CO_MNE
                          ,ROW_NUMBER() OVER(PARTITION BY ID_COMP_1, LEAD_CO_MNE ORDER BY ID_COMP_3_DATE DESC, ID_COMP_3_ID DESC) AS RNCURRENTDESC
                    FROM '+QUOTENAME(@schema)+'.'+QUOTENAME(@table)+'
                    WHERE ID_COMP_3_DATE <= MIS_DATE
                    )
                    INSERT INTO #TMP ([@ID],LEAD_CO_MNE,RNCURRENTDESC)
                    SELECT [@ID], LEAD_CO_MNE, RNCURRENTDESC
                    FROM X
                    WHERE RNCURRENTDESC <= @RNCurrDepth;
                    
                    DECLARE @BATCH INT = 25000;
                    WHILE @BATCH > 0
                    BEGIN
                        UPDATE TOP(@BATCH) X
                        SET RN_Current_Desc = T.RNCURRENTDESC
                        FROM '+QUOTENAME(@schema)+'.'+QUOTENAME(@table)+' X
                        JOIN #TMP T ON X.[@ID]=T.[@ID] AND X.LEAD_CO_MNE=T.LEAD_CO_MNE
                        WHERE X.RN_Current_Desc IS NULL;

                        SET @BATCH=@@ROWCOUNT;
                    END'
        EXEC SP_EXECUTESQL @SQL, N'@RNCurrDepth SMALLINT', @RNCurrDepth=@RNCurrDepth;

        TRUNCATE TABLE #TMP;

        /* RN - FUTURE ROWS */
        SET @SQL = ';WITH X AS
                    (
                    SELECT [@ID]
                          ,LEAD_CO_MNE
                          ,ROW_NUMBER() OVER(PARTITION BY ID_COMP_1, LEAD_CO_MNE ORDER BY ID_COMP_3_DATE ASC, ID_COMP_3_ID DESC) AS RNFUTUREASC
                    FROM '+QUOTENAME(@schema)+'.'+QUOTENAME(@table)+'
                    WHERE ID_COMP_3_DATE > MIS_DATE
                    )
                    INSERT INTO #TMP ([@ID],LEAD_CO_MNE,RNFUTUREASC)
                    SELECT [@ID],LEAD_CO_MNE,RNFUTUREASC
                    FROM X
                    WHERE RNFUTUREASC <= @RNFutureDepth

                    DECLARE @BATCH INT = 25000;
                    WHILE @BATCH > 0
                    BEGIN
                        UPDATE TOP(@BATCH) X
                        SET RN_Future_Asc = T.RNFUTUREASC
                        FROM '+QUOTENAME(@schema)+'.'+QUOTENAME(@table)+' X
                        JOIN #TMP T ON X.[@ID]=T.[@ID] AND X.LEAD_CO_MNE=T.LEAD_CO_MNE
                        WHERE X.RN_Future_Asc IS NULL;

                        SET @BATCH=@@ROWCOUNT;
                    END'
        EXEC SP_EXECUTESQL @SQL, N'@RNFutureDepth SMALLINT', @RNFutureDepth=@RNFutureDepth;

        FETCH NEXT FROM IDComp3 INTO @schema, @table, @RNCurrDepth, @RNFutureDepth;
    END
    CLOSE IDComp3
    DEALLOCATE IDComp3
END TRY
BEGIN CATCH

    DECLARE @Message NVARCHAR(MAX) = ERROR_MESSAGE(), 
            @Severity INT = ERROR_SEVERITY(), 
            @State SMALLINT =ERROR_STATE()
    RAISERROR(@Message, @Severity, @State);
    RETURN(1);
END CATCH
    
RETURN(0)
Run Code Online (Sandbox Code Playgroud)

编辑 1c - 示例表架构

CREATE TABLE dbo.ABC123
(
    [LEAD_CO_MNE] [nvarchar](50) NOT NULL,
    [BRANCH_CO_MNE] [nvarchar](50) NULL,
    [MIS_DATE] [date] NOT NULL,
    [@ID] [nvarchar](150) NOT NULL,
    [ACTIVITY] [nvarchar](150) NULL,
    [ACTION] [nvarchar](max) NULL,
    [CHANGE_DATE_TYPE] [nvarchar](50) NULL,
    [CHANGE_PERIOD] [nvarchar](150) NULL,
    [CHANGE_DATE] [datetime2](7) NULL,
    [CHANGE_ACTIVITY] [nvarchar](150) NULL,
    [PRIOR_DAYS] [int] NULL,
    [CHG_TO_PRODUCT] [nvarchar](150) NULL,
    [ALLOWED_PRODUCT] [nvarchar](max) NULL,
    [RESERVED_6] [nvarchar](150) NULL,
    [RESERVED_5] [nvarchar](150) NULL,
    [INITIATION_TYPE] [nvarchar](50) NULL,
    [DEFAULT_ACTIVITY] [nvarchar](150) NULL,
    [RESERVED_4] [nvarchar](150) NULL,
    [RESERVED_3] [nvarchar](150) NULL,
    [RESERVED_2] [nvarchar](150) NULL,
    [RESERVED_1] [nvarchar](150) NULL,
    [LOCAL_REF] [nvarchar](max) NULL,
    [PR_ATTRIBUTE] [nvarchar](max) NULL,
    [PR_VALUE] [nvarchar](max) NULL,
    [PR_BRK_RES] [nvarchar](max) NULL,
    [PR_BRK_MSG] [nvarchar](max) NULL,
    [PR_BRK_CHARGE] [nvarchar](max) NULL,
    [PR_RESERVED_3] [nvarchar](max) NULL,
    [PR_RESERVED_2] [nvarchar](max) NULL,
    [PR_RESERVED_1] [nvarchar](max) NULL,
    [PR_APP_METHOD] [nvarchar](max) NULL,
    [PR_APP_PERIOD] [nvarchar](max) NULL,
    [SYS_RESERVE7] [nvarchar](150) NULL,
    [SYS_RESERVE6] [nvarchar](150) NULL,
    [SYS_RESERVE5] [nvarchar](150) NULL,
    [SYS_RESERVE4] [nvarchar](150) NULL,
    [SYS_RESERVE3] [nvarchar](150) NULL,
    [SYS_RESERVE2] [nvarchar](150) NULL,
    [SYS_RESERVE1] [nvarchar](150) NULL,
    [DEFAULT_ATTR_OPTION] [nvarchar](150) NULL,
    [DEFAULT_NEGOTIABLE] [nvarchar](50) NULL,
    [NR_ATTRIBUTE] [nvarchar](max) NULL,
    [NR_OPTIONS] [nvarchar](max) NULL,
    [NR_RESERVED2] [nvarchar](max) NULL,
    [NR_RESERVED1] [nvarchar](max) NULL,
    [NR_STD_COMP] [nvarchar](max) NULL,
    [NR_TYPE] [nvarchar](max) NULL,
    [NR_VALUE] [nvarchar](max) NULL,
    [NR_MESSAGE] [nvarchar](max) NULL,
    [CHANGED_FIELDS] [nvarchar](max) NULL,
    [NEGOTIATED_FLDS] [nvarchar](max) NULL,
    [ID_COMP_1] [nvarchar](150) NULL,
    [ID_COMP_2] [nvarchar](150) NULL,
    [ID_COMP_3] [nvarchar](150) NULL,
    [ID_COMP_4] [nvarchar](150) NULL,
    [ID_COMP_5] [nvarchar](150) NULL,
    [ID_COMP_6] [nvarchar](150) NULL,
    [RESERVED2_ID] [nvarchar](150) NULL,
    [TARGET_PRODUCT] [nvarchar](50) NULL,
    [STMT_NOS] [nvarchar](max) NULL,
    [OVERRIDE] [nvarchar](max) NULL,
    [RECORD_STATUS] [nvarchar](50) NULL,
    [CURR_NO] [int] NULL,
    [INPUTTER] [nvarchar](max) NULL,
    [DATE_TIME] [nvarchar](max) NULL,
    [AUTHORISER] [nvarchar](150) NULL,
    [CO_CODE] [nvarchar](50) NULL,
    [DEPT_CODE] [nvarchar](50) NULL,
    [AUDITOR_CODE] [nvarchar](50) NULL,
    [AUDIT_DATE_TIME] [int] NULL,
    [ARRANGEMENT_KEY] [nvarchar](150) NULL,
    [ETL_DQ_RevisionCount] [int] NULL,
    [ETL_DQ_ColumnsRevised] [nvarchar](4000) NULL,
    [ETL_DQ_ErrorMessage] [nvarchar](4000) NULL,
    [ETL_CHANGE_PERIOD] [nvarchar](100) NULL,
    [API_ATTRIBUTE] [nvarchar](max) NULL,
    [NR_ATTRIBUTE_RULE] [nvarchar](max) NULL,
    [NR_VALUE_SOURCE] [nvarchar](max) NULL,
    [OWNING_COMPANY] [nvarchar](max) NULL,
    [ID_COMP_3_DATE]  AS (TRY_CONVERT([date],left([ID_COMP_3],(8)),(112))),
    [ID_COMP_3_ID]  AS (TRY_CAST(right([ID_COMP_3],len([ID_COMP_3])-charindex('.',[ID_COMP_3])) AS [int])),
    [RN_Current_Desc] [smallint] NULL,
    [RN_Future_Asc] [smallint] NULL,
 CONSTRAINT [PK_ABC123] PRIMARY KEY NONCLUSTERED 
(
    [@ID] ASC,
    [LEAD_CO_MNE] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
Run Code Online (Sandbox Code Playgroud)

Sea*_*ser 7

82x 错误很少(如果有的话)是 SQL Server 问题,因为这些错误代码都是无意义的或返回 SQL Server 代码时出错的内容。例如,对于 824,这可能是由 SQL 下面的错误驱动程序或缓存层(包括虚拟机管理程序、SAN、磁盘控制器等硬件和软件)引起的。

有几件事可以尝试缩小问题范围:

  1. 运行SQLIOSIM以查看是否出现任何错误。如果是这样,那它就不是 SQL Server。
  2. 检查虚拟机管理程序的已知问题 - 例如导致 823/824 错误的VMWare 驱动程序,并进行适当的配置更改。
  3. 获取并查看 storport 跟踪,看看是否存在可能导致该时间段内的问题以及返回的数据的其他问题。


小智 4

您是否看过https://www.sqlservercentral.com/blogs/vmware-sql-server-823-824-alerts吗?不确定您是否在 VMWare 上运行以及哪个版本,但 VMware vSphere 7.0 Update 2 存在一个已知问题,听起来与您遇到的情况完全相同。

\n