求助:我的Azure SQL数据库表为何被永久锁定?
兄弟,你这情况我太熟了——当你在跑针对1M条记录的大规模UPDATE时,后续对同表的INSERT/UPDATE/DELETE操作被卡住,本质就是锁阻塞在搞鬼!SQL Server会给被更新的行(甚至可能因为锁升级变成表级锁)持有排他锁(X锁),其他操作请求锁的时候只能排队等大事务释放,自然就动不了了。
结合你的测试环境(单用户、隔离的数据库),给你几个落地的解决办法:
1. 把大UPDATE拆成批量小事务
别一次性更完所有记录,分批次处理,每批更个1000-5000条,每批提交一次事务。这样锁的持有时间会大幅缩短,其他操作就能插空执行了。
给你个现成的示例代码:
DECLARE @UpdatedRows INT = 1; -- 循环直到没有可更新的记录 WHILE @UpdatedRows > 0 BEGIN BEGIN TRANSACTION; -- 每次更新1000条未处理的记录 UPDATE TOP(1000) MyTable SET YourColumn = NewValue WHERE /* 这里放你的过滤条件,确保只更新没处理过的行 */; SET @UpdatedRows = @@ROWCOUNT; -- 获取本次更新的行数 COMMIT TRANSACTION; -- 可选:加1秒延迟,给其他操作留足时间 WAITFOR DELAY '00:00:01'; END
2. 开启快照隔离(测试环境放心用)
如果你只是做测试,不需要严格的强一致性,可以给数据库开READ_COMMITTED_SNAPSHOT ISOLATION,这样其他操作会读取行的版本,而不是傻等锁释放。
先检查当前数据库的快照隔离状态:
SELECT name, is_read_committed_snapshot_on FROM sys.databases WHERE name = '你的数据库名';
如果没开,直接执行开启命令(测试环境用ROLLBACK IMMEDIATE会强制终止现有事务,放心用):
ALTER DATABASE 你的数据库名 SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE;
开启后,后续的INSERT/UPDATE/DELETE就不会被阻塞了,唯一要注意的是tempdb的开销会增加一点,但测试环境完全没问题。
3. 先排查阻塞情况(确认问题根源)
如果你想百分百确认是锁阻塞,在SSMS里跑这个查询,就能看到当前的阻塞链:
SELECT blocking_session_id AS 阻塞会话ID, session_id AS 当前会话ID, resource_type AS 锁定资源类型, request_mode AS 请求锁类型, text AS 执行的SQL语句 FROM sys.dm_exec_requests r CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) WHERE blocking_session_id <> 0;
跑出来的结果会明确告诉你,是不是你的大规模UPDATE会话在阻塞其他操作,以及被卡住的查询是什么。
4. 优化UPDATE的执行计划,加快执行速度
如果你的UPDATE跑得慢,锁持有时间自然就长。可以看看执行计划有没有全表扫描——如果WHERE条件里的列没加索引,SQL Server会扫整个表,锁的范围大还慢。给过滤条件的列加个非聚集索引,能大幅加快UPDATE速度,减少锁的持有时间。
内容的提问来源于stack exchange,提问作者kspearrin

