SQL Server回滚事务后执行Update语句锁表原因咨询
问题原因分析与解决方案
你的问题核心在于事务没有正确结束,导致UPDATE语句持有的锁一直无法释放,进而阻塞后续的SELECT查询。我们来一步步拆解:
关键问题点
保存点回滚后事务仍处于活跃状态
在你的代码里,TRY块中开启了一个事务并创建了保存点T1,当除零异常触发CATCH块时,你执行了ROLLBACK TRANSACTION T1——这只是回滚到保存点,整个事务并没有结束,仍然处于打开状态。UPDATE在未结束的事务中执行
随后的UPDATE语句是在这个未关闭的事务中执行的,SQL Server会为这个UPDATE操作添加排他锁(X锁)。由于事务始终没有提交或完全回滚,这个锁会一直被持有,直到会话结束,这就导致后续对该表的SELECT查询被阻塞(默认隔离级别READ COMMITTED下,查询需要等待排他锁释放)。缺少对事务状态的判断与收尾
你没有在CATCH块中检查事务的状态(通过XACT_STATE()函数),也没有在处理完异常后关闭整个事务,这是锁无法释放的根本原因。
修正后的存储过程代码
ALTER PROCEDURE Test_Tran AS BEGIN BEGIN TRY BEGIN TRANSACTION SAVE TRANSACTION T1; SELECT 1 / 0; -- 触发异常 COMMIT TRANSACTION END TRY BEGIN CATCH -- 检查事务是否处于可回滚状态 IF XACT_STATE() <> 0 BEGIN -- 直接回滚整个事务,因为TRY块中的操作都需要撤销 ROLLBACK TRANSACTION; END -- 在事务外部执行UPDATE,此时会自动提交(自动事务模式) UPDATE [DI].[BOMLC_AMPL_DI_Async_Exec_Results] SET [End_Time] = GETDATE(), [Execution_Status] = 'FAILED', [Error_Number] = ERROR_NUMBER(), -- 用系统函数获取真实错误号 [Error_Message] = 'Exception occurred while processing: ' + ERROR_MESSAGE(), [Last_Updated_On] = GETDATE() WHERE [Token_ID] = 52; PRINT('Something went wrong'); END CATCH END GO EXEC Test_Tran;
关键修正说明
- 完全回滚事务:在
CATCH块中,通过XACT_STATE()判断事务状态,若事务存在则直接回滚整个事务——你的场景中TRY块内的操作全部需要撤销,保存点在这里没有实际意义。 - 在事务外部执行UPDATE:将
UPDATE放在事务关闭之后执行,这样UPDATE会在SQL Server的自动提交模式下运行,执行完成后立即释放锁,不会阻塞后续查询。 - 使用系统函数获取错误信息:替换硬编码的
16为ERROR_NUMBER(),能更准确地捕获实际错误号。
额外验证技巧
你可以通过以下语句查看当前锁状态,验证锁是否正常释放:
SELECT * FROM sys.dm_tran_locks WHERE resource_database_id = DB_ID('你的数据库名');
内容的提问来源于stack exchange,提问作者Prateek Kumar Dalbehera
相关产品推荐
相关产品推荐

