用戶反饋:「SQL Server 2000 經常出現 SELECT 死鎖,一條 SELECT 導致所有客戶端卡死。」
**關鍵澄清**:純粹的 SELECT 語句(無 HOLDLOCK/TABLOCKX 等提示)**極難產生傳統意義的死鎖**(環狀等待)。所謂「死鎖」多為:**長時間阻塞**被誤判為死鎖,或因 SELECT 掃描海量數據導致 鎖升級、TempDB 擠壓、日誌寫入瓶頸,引發連鎖超時。
無索引支持的 SELECT 觸發 Table Scan,持有大量行鎖/頁鎖,超過閾值(默認 5000)升級為 **表鎖 (TAB)**,阻塞所有寫入甚至並發讀取(取決於隔離級別)。
對策:對 WHERE、JOIN、ORDER BY 涉及列建立**覆蓋索引**(INCLUDE 非鍵列),將掃描轉為 Index Seek。
SELECT * FROM BigTable 返回百萬行,客戶端緩存區滿導致服務端發送緩衝區堵塞,事務長時間不提交,鎖持續持有。
對策:
SELECT TOP 100 ... 或 ROW_NUMBER() OVER (...) WHERE rn BETWEEN @start AND @end。SELECT *。SET ROWCOUNT 或應用層限流。應用代碼隱式開啟事務(如 ADO.NET 默認 ReadCommitted + 隱式事務),或使用 Serializable 級別,導致共享鎖持續到事務結束。
對策:
READ COMMITTED SNAPSHOT (RCSI) 隔離級別(SQL 2005+),讀不阻塞寫。SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED (NOLOCK) —— **僅限報表類容忍臟讀場景**。SQL 2000 不支持自動更新統計(需 sp_updatestats 或 UPDATE STATISTICS),導致優化器誤選掃描計劃。
對策:建立定時任務每日更新統計信息,重建碎片索引(DBCC DBREINDEX)。
-- 1. 查看當前阻塞鏈
SELECT * FROM master..sysprocesses WHERE blocked > 0;
-- 2. 找出消耗資源最重的查詢
SELECT TOP 10
qs.total_elapsed_time / qs.execution_count AS avg_elapsed_ms,
SUBSTRING(qt.text, qs.statement_start_offset/2 + 1,
(CASE WHEN qs.statement_end_offset = -1
THEN LEN(CONVERT(nvarchar(max), qt.text)) * 2
ELSE qs.statement_end_offset END - qs.statement_start_offset)/2) AS query_text,
qs.execution_count
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) qt
ORDER BY avg_elapsed_ms DESC;
-- 注:SQL 2000 無 DMV,需用 DBCC INPUTBUFFER(spid) 或 Profiler 追蹤
-- 3. 為高頻查詢添加覆蓋索引(示例)
CREATE NONCLUSTERED INDEX IX_Orders_CustDate_Cover
ON Orders (CustomerID, OrderDate)
INCLUDE (TotalAmount, Status); -- SQL 2000 不支持 INCLUDE,改為復合索引鍵包含
SQL Server 2000 「SELECT 死鎖」本質多為 **全表掃描+鎖升級+大結果集** 引發的**嚴重阻塞**。解決路徑:**補索引 → 限結果集 → 降隔離級別 → 讀寫分離 → 升級數據庫版本**(SQL 2000 已停止支持 2013 年,強烈建議遷移至 SQL 2019/2022)。