我们在 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 到 7.5M 行。我们昨天(在 QA 中)修改了此过程,以限制使用 ROW_NUMBER() 值更新的行数(仅当 rownum=1 时)并将该更新从一次全部更改为 25K 批处理方法。该错误今天在 QA 中再次发生——因此我们删除了计算列上的 PERSISTED 选项。我们实际上正在尝试任何事情来阻止质量检查中的这种情况,因为它似乎受到的影响最大。
DBCC CHECKDB 每天在所有生产数据库上运行,一个除外。一个跳过的数据库非常大,接近 4TB,DBCC CHECKDB 大约需要 12 小时才能完成。DBCC CHECKDB 已在此大型数据库的 QA 中最近的生产备份上运行,并且很干净。
到目前为止,只有 tempdb 在生产中受到影响。DEV/QA 已经看到其他几个数据库受到影响,但 DBCC CHECKDB 没有产生任何结果,即使怀疑页面包含一个条目。当它不是 tempdb 时,我们已将生产数据库恢复到 DEV/QA,只是为了安全。
当 tempdb 受到影响时,我们一直在停止 SQL,并删除有问题的 tempdb 文件并重新启动服务。
值得注意的是,H: 在生产中的使用率为 100%,因为 mdf/ndf 文件占用了整个 150GB 驱动器,但文件中有足够的可用空间。DEV/QA 没有达到 100%,并且有大约 30-40GB 的可用空间,并且都显示 tempdb 受到影响。所以我认为这不是驱动器空间问题。
我已经联系了我们的 DBA,他认为问题是由数据本身引起的。虽然我不同意数据有问题,但我们确实在 2022 年 4 月升级了生成由 ETL 作业读取的 CSV 文件的系统。源系统现在是基于 linux 而不是基于 Windows。ETL 过程无需更改即可读取新文件。文件中的数据在结构上发生了一些变化,但它们在很大程度上是相同的,CLR procs 可以根据每天发送的模式布局文件来适应结构变化。这些文件足够大(每天 55+GB),它们填充了 300 多个表,每个表都包含大量的 nvarchar(max) 字段。
我已经联系了我们的基础架构团队以检查 VM/SAN 运行状况——虽然生产的 VM 在文件摄取阶段报告了高 IO(时间戳完美对齐)——但没有报告错误。我们的基础架构团队在调查期间将 prod 虚拟机迁移到更快的主机,并将文件移动到新的相对未使用的较新的 SAN。
7 月 26 日,DEV/QA SAN 发生驱动器故障,导致两个驱动器被更换和重建。由于驱动器故障,我们在与 Microsoft 合作时没有发生任何事故,但是上周六和上周一 - QA 遇到了更多与 tempdb 相关的错误。
我们已经向 Microsoft 开了一张票,经过几周/电话,我们被告知他们是错误的团队(他们只帮助修复腐败),我们需要一张高级票。因此,我们目前正在向 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)
编辑 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)
编辑 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
82x 错误很少(如果有的话)是 SQL Server 问题,因为这些都是在返回 SQL Server 代码时没有意义或出错的错误代码。例如,对于 824,这可能是由错误的驱动程序或 SQL 下的缓存层(包括硬件和软件,如虚拟机管理程序、SAN、磁盘控制器等)引起的。
有几件事可以尝试缩小问题范围:
你看过https://www.sqlservercentral.com/blogs/vmware-sql-server-823-824-alerts吗?不确定您是否在 VMWare 上运行以及哪个版本,但 VMware vSphere 7.0 Update 2 存在一个已知问题,这听起来与您正在经历的完全一样。