写入#temp的UPDATE语句在生产环境中突发挂起
分析与解决思路
哎,生产环境遇到这种突然挂起的问题确实闹心,明明单独跑查询返回0条记录,但一加上UPDATE就卡住,咱们来一步步拆解可能的原因和解决办法:
一、先排查锁阻塞问题
这是生产环境UPDATE挂起最常见的原因,哪怕没有符合条件的行,也可能因为其他事务持有锁导致进程等待:
- 先找到当前UPDATE进程的会话ID,然后运行下面的SQL查看阻塞情况:
SELECT blocking_session_id, session_id, command, wait_type, wait_time, resource_description FROM sys.dm_exec_requests WHERE session_id = <你的UPDATE进程ID>;
如果blocking_session_id不为0,说明这个进程被其他会话阻塞了,找到那个阻塞会话,看看是不是有长事务(比如未提交的修改、长时间运行的查询)占用了相关表的锁。
二、质疑NOLOCK带来的“假结果”
你单独跑查询时用了NOLOCK提示,这个提示会让查询读取未提交的数据,可能出现幻读或者脏读:
- 试试不带NOLOCK重新跑查询,看看实际有没有符合条件的行:
SELECT STG.TRACKING_ID FROM STAGE_TABLE STG INNER JOIN STAGE_PRE_ACC PRE ON PRE.ID=STG.ID AND PRE.CID=STG.CID WHERE PRE.STATUS = 'D' AND STG.ETLNBR < PRE.ETLNBR;
说不定你带NOLOCK查的时候没数据,但实际有其他事务正在修改PRE.STATUS为'D',导致UPDATE执行时试图锁定这些行,但被其他事务阻塞。
三、检查索引与执行计划
哪怕没有符合条件的行,如果表数据量大且没有合适的索引,UPDATE会做全表扫描,也可能导致长时间运行甚至挂起:
- 查看UPDATE语句的执行计划,看看有没有全表扫描的情况,有没有索引缺失的警告。
- 建议为关联和过滤字段创建索引,比如:
- 在
STAGE_PRE_ACC上建索引:CREATE NONCLUSTERED INDEX IX_PRE_STATUS_ID_CID_ETLNBR ON STAGE_PRE_ACC (STATUS, ID, CID, ETLNBR); - 在
STAGE_TABLE上建索引:CREATE NONCLUSTERED INDEX IX_STG_ID_CID_ETLNBR ON STAGE_TABLE (ID, CID, ETLNBR);
合适的索引能让数据库快速定位到符合条件的行,哪怕结果为空,也能快速结束扫描。
- 在
四、临时表的小概率问题
虽然临时表#TEMP是会话级的,但也可以试试改成表变量@TEMP,看看会不会有差异:
DECLARE @TEMP TABLE (TID INT); -- 根据实际字段类型调整 UPDATE STG SET STATUS = 'D' OUTPUT INSERTED.TRACKING_ID INTO @TEMP (TID) FROM STAGE_TABLE STG INNER JOIN STAGE_PRE_ACC PRE ON PRE.ID=STG.ID AND PRE.CID=STG.CID WHERE PRE.STATUS = 'D' AND STG.ETLNBR < PRE.ETLNBR;
五、其他排查点
- 检查最近生产环境有没有做过 schema 变更(比如表结构修改、索引删除)。
- 查看数据库的性能指标,比如CPU、IO是不是满了,导致进程无法正常执行。
内容的提问来源于stack exchange,提问作者Younus Mohammed
相关产品推荐
相关产品推荐

