附录:常见报错与解决方案

附录:常见报错与解决方案

附录:常见报错与解决方案

本附录收录 SQL Server 生产环境中最常见的错误和疑难场景,每条均提供错误含义、根因分析、诊断查询和解决步骤,可作为日常运维的快速参考手册。


错误 1205:死锁(Deadlock Victim)

错误信息

Msg 1205, Level 13, State 51
Transaction (Process ID XX) was deadlocked on lock resources with another process
and has been chosen as the deadlock victim. Rerun the transaction.

根因与诊断

-- 方法1:从系统健康扩展事件中获取死锁图
WITH SystemHealth AS (
    SELECT CAST(target_data AS XML) AS Data
    FROM sys.dm_xe_session_targets t
    JOIN sys.dm_xe_sessions s ON t.event_session_address = s.address
    WHERE s.name = 'system_health' AND t.target_name = 'ring_buffer'
)
SELECT
    node.value('@timestamp','DATETIME2') AS DeadlockTime,
    node.query('.')                       AS DeadlockGraph
FROM SystemHealth
CROSS APPLY Data.nodes('//RingBufferTarget/event[@name="xml_deadlock_report"]') E(node)
ORDER BY DeadlockTime DESC;

-- 方法2:读取系统健康 XEL 文件(磁盘上的历史记录)
SELECT
    CAST(event_data AS XML).value('(event/@timestamp)[1]','DATETIME2') AS EventTime,
    CAST(event_data AS XML).query('(event/data[@name="xml_report"]/value/deadlock)[1]') AS DeadlockGraph
FROM sys.fn_xe_file_target_read_file(
    N'C:\Program Files\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQL\Log\system_health*.xel',
    NULL, NULL, NULL
)
WHERE object_name = 'xml_deadlock_report'
ORDER BY EventTime DESC;

解决方案

-- 短期:应用层捕获1205并重试(3次)
-- C# 伪代码:
-- for (int retry = 0; retry < 3; retry++) {
--     try { ExecuteTransaction(); break; }
--     catch (SqlException ex) when (ex.Number == 1205) {
--         Thread.Sleep(100 * (retry + 1)); // 指数退避
--     }
-- }

-- 长期优化:
-- 1. 统一所有事务对表的访问顺序(最有效!)
--    按 TableA → TableB → TableC 的顺序访问,消除循环等待

-- 2. 缩短事务持续时间
--    避免在事务中做耗时操作(HTTP请求、文件操作、等待用户输入)
--    尽量在事务外完成所有只读查询,事务内只做修改操作

-- 3. 降低锁粒度 / 使用 RCSI
ALTER DATABASE [YourDB] SET READ_COMMITTED_SNAPSHOT ON;

-- 4. 为高频查询添加适当索引(减少扫描范围,缩短持锁时间)

-- 5. 将不必要的 SERIALIZABLE 隔离降为 READ COMMITTED

-- 6. 设置死锁优先级(保护重要批处理)
-- 在不重要的事务中:
SET DEADLOCK_PRIORITY LOW;

错误 2627 / 2601:违反唯一约束

错误信息

Msg 2627, Level 14, State 1
Violation of PRIMARY KEY constraint 'PK_Orders'. Cannot insert duplicate key
in object 'dbo.Orders'. The duplicate key value is (1001).

Msg 2601, Level 14, State 1
Cannot insert duplicate key row in object 'dbo.Orders' with unique index 'IX_Orders_OrderNo'.
The duplicate key value is (ORD-2024-0001).

诊断与解决

-- 1. 定位重复数据
-- 找出违反主键的重复值
SELECT CustomerID, OrderDate, COUNT(*) AS DupCount
FROM Orders
GROUP BY CustomerID, OrderDate
HAVING COUNT(*) > 1;

-- 2. 插入前检查(防御性编程)
-- 方案A:IF NOT EXISTS(存在并发问题,不推荐)
IF NOT EXISTS (SELECT 1 FROM Orders WHERE OrderNo = @OrderNo)
BEGIN
    INSERT INTO Orders (OrderNo, ...) VALUES (@OrderNo, ...);
END;

-- 方案B:MERGE(原子化 Upsert,推荐)
MERGE INTO Orders AS T
USING (VALUES (@OrderNo, @CustomerID, @Amount)) AS S (OrderNo, CustomerID, Amount)
ON T.OrderNo = S.OrderNo
WHEN NOT MATCHED THEN
    INSERT (OrderNo, CustomerID, Amount) VALUES (S.OrderNo, S.CustomerID, S.Amount);

-- 方案C:忽略重复(SQL Server 2022 IGNORE_DUP_KEY 选项)
INSERT INTO Orders (OrderNo, CustomerID, Amount)
SELECT OrderNo, CustomerID, Amount FROM Staging
WHERE OrderNo NOT IN (SELECT OrderNo FROM Orders);

-- 方案D:捕获异常(应用层最终防线)
BEGIN TRY
    INSERT INTO Orders (OrderNo, ...) VALUES (@OrderNo, ...);
END TRY
BEGIN CATCH
    IF ERROR_NUMBER() IN (2601, 2627)
    BEGIN
        -- 重复键,执行更新逻辑
        UPDATE Orders SET Amount = @Amount WHERE OrderNo = @OrderNo;
    END
    ELSE THROW;
END CATCH;

-- 3. 清理已有重复数据(保留最新的一条)
WITH Dups AS (
    SELECT *,
           ROW_NUMBER() OVER (PARTITION BY OrderNo ORDER BY CreatedAt DESC) AS RN
    FROM Orders
)
DELETE FROM Dups WHERE RN > 1;

-- 4. IDENTITY 跳号问题(重启后IDENTITY可能不连续)
-- 检查当前 IDENTITY 值
DBCC CHECKIDENT('dbo.Orders', NORESEED);
-- 重置 IDENTITY 值(谨慎!确保新值大于现有最大值)
DBCC CHECKIDENT('dbo.Orders', RESEED, 10000);
-- 或:
DECLARE @MaxID INT;
SELECT @MaxID = MAX(OrderID) FROM Orders;
DBCC CHECKIDENT('dbo.Orders', RESEED, @MaxID);

错误 823 / 824:数据库物理页损坏

错误信息

Msg 823, Level 24, State 2
The operating system returned error 23(Data error (cyclic redundancy check).)
to SQL Server during a read at offset 0x00000001a40000 in file 'D:\Data\YourDB.mdf'.

Msg 824, Level 24, State 2
SQL Server detected a logical consistency-based I/O error: incorrect checksum (expected: 0x...; actual: 0x...).
It occurred during a read of page (1:245) in database ID 8 at offset ...

诊断与紧急恢复流程

-- ====================================================
-- 紧急响应 SOP(物理页损坏)
-- ====================================================

-- 步骤1:确认损坏范围
DBCC CHECKDB('YourDB') WITH NO_INFOMSGS, ALL_ERRORMSGS, PHYSICAL_ONLY;
-- PHYSICAL_ONLY:只做物理完整性检查,不检查逻辑一致性(更快)

-- 步骤2:查看错误日志确认损坏页
EXEC xp_readerrorlog 0, 1, N'823';
EXEC xp_readerrorlog 0, 1, N'824';

-- 步骤3:尝试页级还原(不停机!需要 Enterprise 版 + 完整恢复模式)
-- 具体步骤参见第7章 7.3.3 节

-- 步骤4:如果没有好的备份,尝试修复(最后手段,会丢失数据!)
-- 先将数据库置为单用户模式
ALTER DATABASE [YourDB] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;

-- REPAIR_REBUILD:无数据丢失的修复(尝试修复,修不了则报错)
DBCC CHECKDB('YourDB', REPAIR_REBUILD) WITH NO_INFOMSGS;

-- 如果 REPAIR_REBUILD 失败,才考虑:
-- DBCC CHECKDB('YourDB', REPAIR_ALLOW_DATA_LOSS) WITH NO_INFOMSGS;
-- ⚠️ 此操作会丢弃损坏的页,导致数据丢失!执行前必须评估业务影响!

ALTER DATABASE [YourDB] SET MULTI_USER;

-- 步骤5:紧急数据抢救(在修复前尝试导出未损坏的数据)
-- 开启跳过损坏页标志(仅在紧急情况下!会话级别)
DBCC TRACEON(2537, -1);  -- 跳过损坏页继续扫描

-- 尝试导出数据到新表
SELECT * INTO RecoveredOrders FROM Orders;  -- 可能跳过损坏页的数据

DBCC TRACEOFF(2537, -1);

-- 预防措施:
-- 1. 定期 DBCC CHECKDB(每周执行)
-- 2. 备份时加 CHECKSUM(BACKUP ... WITH CHECKSUM)
-- 3. 定期 RESTORE VERIFYONLY 验证备份
-- 4. 使用企业级 RAID 或 SAN 阵列
-- 5. 启用即时文件初始化(Instant File Initialization)

TempDB 空间暴涨

症状与诊断

-- 症状:TempDB 数据文件迅速增长,影响其他数据库和系统性能

-- 诊断1:查看 TempDB 空间使用分布
SELECT
    SUM(unallocated_extent_page_count) * 8.0 / 1024 AS FreeSpaceMB,
    SUM(version_store_reserved_page_count) * 8.0 / 1024 AS VersionStoreMB,   -- 快照/RCSI
    SUM(user_object_reserved_page_count) * 8.0 / 1024 AS UserObjectsMB,      -- 临时表/表变量
    SUM(internal_object_reserved_page_count) * 8.0 / 1024 AS InternalObjMB   -- 中间结果/排序
FROM sys.dm_db_file_space_usage;

-- 诊断2:找出占用 TempDB 最多的会话
SELECT TOP 10
    s.session_id,
    s.login_name,
    s.host_name,
    s.program_name,
    (t.user_objects_alloc_page_count + t.internal_objects_alloc_page_count) * 8 / 1024 AS TempDB_MB,
    t.user_objects_alloc_page_count * 8 / 1024     AS UserObj_MB,
    t.internal_objects_alloc_page_count * 8 / 1024 AS InternalObj_MB,
    r.command,
    SUBSTRING(txt.text, (r.statement_start_offset/2)+1, 300) AS CurrentSQL
FROM sys.dm_db_session_space_usage t
JOIN sys.dm_exec_sessions s ON t.session_id = s.session_id
LEFT JOIN sys.dm_exec_requests r ON t.session_id = r.session_id
OUTER APPLY sys.dm_exec_sql_text(r.sql_handle) txt
WHERE (t.user_objects_alloc_page_count + t.internal_objects_alloc_page_count) > 128  -- > 1MB
ORDER BY TempDB_MB DESC;

-- 诊断3:版本存储暴涨(RCSI/Snapshot隔离的长事务未提交)
SELECT
    s.session_id,
    s.login_name,
    t.transaction_begin_time,
    DATEDIFF(MINUTE, t.transaction_begin_time, GETDATE()) AS DurationMin,
    s.host_name,
    s.program_name
FROM sys.dm_tran_active_snapshot_database_transactions t
JOIN sys.dm_exec_sessions s ON t.session_id = s.session_id
ORDER BY t.transaction_begin_time;

-- 诊断4:大型排序/哈希操作溢出到 TempDB
SELECT
    event_data.value('(event/data[@name="is_spilled"]/value)[1]','BIT') AS IsSpilled,
    event_data.value('(event/data[@name="spill_size_kb"]/value)[1]','BIGINT') / 1024 AS SpillMB,
    event_data.value('(event/action[@name="sql_text"]/value)[1]','NVARCHAR(MAX)') AS SQLText
FROM sys.fn_xe_file_target_read_file('C:\...\system_health*.xel', NULL, NULL, NULL)
CROSS APPLY (SELECT CAST(event_data AS XML)) X(event_data)
WHERE object_name = 'sort_warning' OR object_name = 'hash_warning'
ORDER BY SpillMB DESC;

解决方案

-- 短期应急:终止占用最多 TempDB 的会话
-- KILL {session_id};  -- 执行前务必与业务确认!

-- 中期优化:
-- 1. 减少临时表的使用,改用表变量(小数据集)或直接用 CTE
-- 2. 为排序/哈希操作的查询添加索引,避免溢出到 TempDB
-- 3. 检查有无未关闭的快照隔离事务(定期清查长事务)

-- 4. 增加 TempDB 数据文件数量(参见第1章 TempDB 优化)
ALTER DATABASE tempdb
ADD FILE (NAME = 'tempdev2', FILENAME = 'G:\TempDB\tempdev2.ndf', SIZE = 512MB, FILEGROWTH = 256MB);

-- 5. 缩小 TempDB(重启是最干净的方式,但生产不现实)
-- 先杀死所有用户会话,再收缩(效果有限)
DBCC SHRINKFILE ('tempdev', 1024);   -- 尝试收缩到 1024MB

-- 长期预防:
-- 1. 建立 TempDB 使用量监控警报(超过 80% 使用率告警)
-- 2. 代码审查:禁止在循环中创建和删除临时表
-- 3. 对 VersionStore 增长设置告警(查看 log_reuse_wait_desc = 'ACTIVE_TRANSACTION')

其他常见错误快速参考

错误 4060:无法打开数据库

-- Msg 4060: Cannot open database "dbname" requested by the login.
-- 原因:数据库处于 RESTORING/OFFLINE/SUSPECT 状态
SELECT name, state_desc FROM sys.databases WHERE name = 'dbname';

-- 数据库处于 SUSPECT 状态(事务日志损坏)
EXEC sp_resetstatus 'dbname';
ALTER DATABASE dbname SET EMERGENCY;
DBCC CHECKDB('dbname') WITH NO_INFOMSGS;
ALTER DATABASE dbname SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
ALTER DATABASE dbname SET ONLINE;
ALTER DATABASE dbname SET MULTI_USER;

错误 17803 / 701:内存不足

-- Msg 17803: There is insufficient system memory in resource pool 'internal'
-- 解决:限制 SQL Server 最大内存,为 OS 留出足够内存
EXEC sp_configure 'max server memory (MB)', 24576;  -- 留 4GB 给 OS
RECONFIGURE;

-- 监控内存压力
SELECT
    memory_state_desc,
    total_kb / 1024 AS TotalMB,
    available_kb / 1024 AS AvailableMB,
    target_kb / 1024 AS TargetMB
FROM sys.dm_os_memory_clerks
WHERE type = 'MEMORYCLERK_SQLBUFFERPOOL';

错误 233 / 18456:登录失败

-- Msg 18456: Login failed for user 'username'
-- State 值含义:
-- 5  = 无效用户名(用户不存在)
-- 6  = 使用 Windows 账号尝试 SQL 登录(服务器只允许 Windows 验证)
-- 8  = 密码错误
-- 11 = 登录账号在服务器上有效但无法访问数据库(guest账号问题)
-- 16 = 用户不能在当前数据库上登录(db 级别权限缺失)
-- 38 = 用户默认数据库不存在

-- 检查登录和用户映射
SELECT sl.name AS ServerLogin, dp.name AS DBUser, dp.type_desc, dp.default_schema_name
FROM sys.server_principals sl
LEFT JOIN sys.database_principals dp
    ON sl.sid = dp.sid
ORDER BY sl.name;

-- 修复孤立用户(数据库迁移后常见)
-- 查找孤立用户
EXEC sp_change_users_login 'Report';

-- 修复孤立用户(将 DBUser 关联到 ServerLogin)
EXEC sp_change_users_login 'Update_One', 'DBUser', 'ServerLogin';

-- 重置密码
ALTER LOGIN [username] WITH PASSWORD = 'NewStr0ng@Password';
ALTER LOGIN [username] ENABLE;

错误 1222:锁超时

-- Msg 1222: Lock request time out period exceeded.
-- 说明:SET LOCK_TIMEOUT 设置的超时时间已到

-- 找出持有锁的会话
SELECT
    l.request_session_id AS WaitingSPID,
    l2.request_session_id AS BlockingSPID,
    l.resource_type,
    l.request_mode,
    OBJECT_NAME(l.resource_associated_entity_id) AS ObjectName
FROM sys.dm_tran_locks l
JOIN sys.dm_tran_locks l2 ON l.resource_database_id = l2.resource_database_id
                          AND l.resource_associated_entity_id = l2.resource_associated_entity_id
                          AND l.request_status = 'WAIT'
                          AND l2.request_status = 'GRANT'
                          AND l.request_mode <> l2.request_mode;

-- 查看阻塞者的 SQL
SELECT TOP 1 t.text
FROM sys.dm_exec_connections c
CROSS APPLY sys.dm_exec_sql_text(c.most_recent_sql_handle) t
WHERE c.session_id = {BlockingSPID};

报错诊断通用查询工具箱

-- ① 查看 SQL Server 错误日志
EXEC xp_readerrorlog 0, 1;               -- 当前日志,全部
EXEC xp_readerrorlog 0, 1, N'Error';     -- 当前日志,含"Error"的行
EXEC xp_readerrorlog 0, 1, NULL, NULL, '2024-06-15 00:00', '2024-06-15 23:59'; -- 时间范围

-- ② 查看 Windows 事件日志中的 SQL Server 条目
EXEC xp_readerrorlog 0, 3;  -- 参数3=Windows应用程序日志

-- ③ 实时错误监控(通过扩展事件)
-- 捕获所有 Severity >= 16 的错误
CREATE EVENT SESSION [CaptureErrors] ON SERVER
ADD EVENT sqlserver.error_reported (
    WHERE severity >= 16 AND NOT_EQUAL_TO (error_number, 3621)
)
ADD TARGET package0.ring_buffer (SET max_memory = 10240);
ALTER EVENT SESSION [CaptureErrors] ON SERVER STATE = START;

-- ④ 版本和补丁信息(排查版本相关 BUG)
SELECT
    @@VERSION AS FullVersion,
    SERVERPROPERTY('ProductVersion')  AS BuildVersion,    -- 如:16.0.4120.1
    SERVERPROPERTY('ProductLevel')    AS ServicePack,     -- RTM/SP1...
    SERVERPROPERTY('ProductUpdateLevel') AS CU,           -- CU12
    SERVERPROPERTY('Edition')         AS Edition;

-- 根据 BuildVersion 在微软文档查找对应 CU 和已知 Bug 修复列表
-- https://learn.microsoft.com/en-us/troubleshoot/sql/releases/sqlserver-2022/build-versions

快速参考卡

错误号 级别 关键词 首选操作
1205 13 死锁 捕获重试;统一访问顺序;开启 RCSI
1222 16 锁超时 查找并处理阻塞来源
26012627 14 唯一约束 MERGE 或 IF NOT EXISTS;检查 IDENTITY
3960 16 快照冲突 事务重试;检查长事务
4060 11 数据库不可访问 检查数据库状态;sp_resetstatus
5180 22 文件不可访问 检查文件路径和权限
8114 16 数据类型转换失败 检查 IMPLICIT_CONVERT,使用 TRY_CAST
8152 16 字符串截断 检查目标列长度;检查 ANSI_WARNINGS
17803701 1720 内存不足 调整 max server memory;检查内存泄漏
823824 24 物理页损坏 页级还原;DBCC CHECKDB REPAIR

本教程到此结束。建议将各章节代码在测试环境中逐一执行,结合官方文档(docs.microsoft.com/sql)深入探索。

文章版权声明:除非注明,否则均为边学边练网络文章,版权归原作者所有
  • 上一篇:
  • 下一篇: