READ COMMITTED 是多数 UPDATE 场景下最稳妥的默认选择,它避免脏读、缩短锁持有时间且开箱即用;相比 REPEATABLE READ 减少阻塞,SNAPSHOT 需显式启用,READ UNCOMMITTED 和 NOLOCK 有数据一致性风险,UPDLOCK+HOLDLOCK 适用于查-更原子操作。

READ COMMITTED 是多数 UPDATE 场景下最稳妥的默认选择,它能避免脏读、减少锁持有时间,且无需额外配置就能生效。
为什么默认 READ COMMITTED 就比 REPEATABLE READ 更适合 UPDATE
SQL Server 默认隔离级别就是 READ COMMITTED,但它常被误认为“不够安全”而手动改成 REPEATABLE READ 或更高——这反而会加剧阻塞。原因在于:
-
REPEATABLE READ会让 UPDATE 持有 S 锁更久(直到事务结束),导致后续 UPDATE 或 SELECT 长时间等待 U/X 锁 -
READ COMMITTED只在语句执行期间持锁,语句一结束就释放(非整个事务),锁窗口明显更短 - 所有 DML 操作(包括 UPDATE)在任何隔离级别下都必须加 X 锁,但读锁(S/U)的持续时间受隔离级别直接影响
SNAPSHOT 隔离能彻底绕过读写阻塞,但要先启用
快照隔离不是开箱即用的,必须显式开启数据库级开关,否则 SET TRANSACTION ISOLATION LEVEL SNAPSHOT 会直接报错 3902(“无法开始快照事务”):
- 先执行:
ALTER DATABASE [YourDB] SET ALLOW_SNAPSHOT_ISOLATION ON - 再执行:
ALTER DATABASE [YourDB] SET READ_COMMITTED_SNAPSHOT ON(推荐,让READ COMMITTED自动走行版本,无需改代码) - 启用后,UPDATE 仍加 X 锁,但 SELECT 不再需要 S 锁,自然不阻塞也不被阻塞
- 注意 tempdb 压力会上升,且 long-running transaction 可能引发 version store cleanup 延迟
慎用 READ UNCOMMITTED 和 NOLOCK 提示
它们确实能跳过锁等待,但代价是可能读到未提交数据或中途回滚的中间态:
-
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED对整个事务生效,风险扩散范围大 -
SELECT ... FROM t WITH (NOLOCK)只影响当前语句,但若该查询结果用于后续 UPDATE 的 WHERE 条件,就可能更新错行 - 报表类只读场景可用,但绝不能用于“先查后更”的业务逻辑(如库存扣减前查余额)
UPDLOCK + HOLDLOCK 组合更适合“查-更”原子操作
当必须先 SELECT 再 UPDATE(比如基于当前值计算新值),又不想被其他事务并发修改干扰时,WITH (UPDLOCK, HOLDLOCK) 比单纯提高隔离级别更精准:
-
UPDLOCK让 SELECT 加 U 锁(而非 S 锁),防止其他事务对该行做 UPDATE/DELETE -
HOLDLOCK等效于SERIALIZABLE,锁住查询范围,防止幻读(比如 WHERE status = 'pending' 被新插入的 pending 行干扰) - 示例:
SELECT id FROM orders WITH (UPDLOCK, HOLDLOCK) WHERE status = 'processing' AND id = 123 - 注意:U 锁与 X 锁兼容性差,过度使用反而增加死锁概率,务必配合索引缩小锁定范围
真正容易被忽略的点是:隔离级别只控制读锁行为,UPDATE 自身的 X 锁无法被绕过;所谓“减少阻塞”,本质是缩短读锁生命周期、避免锁升级、或用版本替代锁——而不是让写操作不加锁。

















