一文读懂Sql Server 中的锁机制

锁 颗粒度(行锁、页锁、表锁)

sql中会有各种 增删改查的操作 为了确保数据安全性,并发情况的处理 所以也会有数据库锁。
SQL Server 的锁机制是数据库并发控制的核心,用于协调多个事务对资源的访问。锁的粒度(行锁、表锁)和锁模式(S、X、U)共同决定了事务之间的兼容性与冲突
在这里插入图片描述
在这里插入图片描述

锁 类型

锁模式定义事务对资源的访问权限,不同模式之间的兼容性决定了事务能否并发执行:

  1. 共享锁(Shared Lock, S)
    作用:用于读取操作(如 SELECT),允许多事务并发读取数据。
    兼容性:
    ✅ 与 其他 S 锁 兼容。
    ❌ 与 X 锁 不兼容。
    释放时机:默认在事务结束(隔离级别为 REPEATABLE READ 或更高时)或读取完成后释放(如 READ COMMITTED)。

  2. 排他锁(Exclusive Lock, X)
    作用:用于写操作(如 INSERT, UPDATE, DELETE),确保事务独占资源。
    兼容性:
    ❌ 与 所有其他锁 不兼容(包括 S、U、X)。
    释放时机:事务结束时释放。

  3. 更新锁(Update Lock, U)
    作用:用于 UPDATE 操作,防止多个事务同时更新同一资源导致的死锁
    行为:
    事务在修改数据前先获取 U 锁,实际修改时升级为 X 锁。
    U 锁与 S 锁兼容,但与其他 U 锁或 X 锁不兼容。
    释放时机:升级为 X 锁后释放
    U 锁的设计主要是为了减少单资源更新时的锁竞争,但它无法完全解决跨资源交叉访问导致的循环等待问题
    在这里插入图片描述

  4. 意向锁(Intent Locks):用于协调不同粒度的锁(如行锁与表锁):
    IS(意向共享锁):表明事务将在某些行上获取 S 锁。
    IX(意向排他锁):表明事务将在某些行上获取 X 锁。
    SIX(共享+意向排他锁):表级锁,表示事务持有 S 锁并将在某些行上获取 X 锁

  5. 行锁与表锁的联系
    意向锁的桥梁作用:
    当某事务对行加 S/X 锁时,会先在表级加 IS/IX 锁。
    其他事务尝试加表级锁时,通过检查意向锁判断是否存在冲突。
    锁升级:
    当行锁数量超过阈值(默认 5000)或内存压力大时,SQL Server 自动将行锁升级为表锁。
    可通过 ALTER TABLE … SET (LOCK_ESCALATION = DISABLE) 禁用锁升级。

死锁

解释一下 就是 因为锁 之间的阻塞 导致互相等待资源 极易在事务这种复杂多资源处理中出现
在这里插入图片描述

更新锁虽然防止了多个事务同时更新同一资源导致的死锁,如果两个事务以不同顺序更新互相依赖的资源,依然可能发生死锁
总结
死锁本质:循环等待资源 + 锁不兼容。
关键预防:统一访问顺序、降低事务粒度、优化索引(减少锁粒度 避免行锁》表锁)
事后处理:重试机制 + 死锁日志分析

不加锁 出现的 脏读 幻读

​一、WITH (NOLOCK) 的作用机制

  1. 避免申请共享锁(S 锁)​
    默认行为:在 SQL Server 的 READ COMMITTED 隔离级别下,普通 SELECT 查询会为读取的数据申请共享锁(S 锁),这会阻塞其他事务对相同数据的修改(X 锁)。
    NOLOCK 的作用
    使用 WITH (NOLOCK) 时,查询会在 READ UNCOMMITTED 隔离级别下运行,直接跳过共享锁(S 锁)的申请,因此不会阻塞其他事务的写操作,也不会被写操作阻塞。
  2. 脏读风险的本质 1-2-1 读出来2
    ​脏读(Dirty Read)​:由于不申请 S 锁,查询可能读取到其他事务未提交的数据。例如:
    事务 A 修改了某行但未提交;
    事务 B 使用 WITH (NOLOCK) 读取该行,会直接获取未提交的数据;
    若事务 A 回滚,事务 B 读取的数据在逻辑上从未存在过。
    ​典型场景:在长时间运行的写事务中,其他会话的 NOLOCK 查询可能读取到中间状态的无效数据

二、性能与风险的权衡

  1. 性能优势
    ​减少锁争用:避免 S 锁的申请可减少事务间的阻塞,提升并发性能,尤其在高写入负载的场景中效果显著。
    ​适用场景:
    非关键业务报表或历史数据分析(允许数据轻微不一致);
    高频查询但对数据实时性要求不高的场景(如监控大盘)。
  2. 潜在风险
    数据不一致性
    除了脏读,还可能导致 ​不可重复读​(同一事务内多次查询结果不同)和 ​幻读​(其他事务插入或删除数据导致结果集变化)
    例如:财务系统中若使用 NOLOCK,可能因读取到未提交的金额导致严重错误。
    ​锁的局限性:
    NOLOCK 仍会申请 ​架构稳定性锁(Sch-S)​,防止其他会话修改表结构(如 ALTER TABLE)

​行版本控制(RCSI) 提升性能不加锁还能 避免 脏读

  1. RCSI 的核心机制
    行版本控制(Read Committed Snapshot Isolation, RCSI)通过在 tempdb 中维护数据行的历史版本,使读操作无需申请共享锁(S锁),从而避免与写操作的锁争用。在 RCSI 模式下:
    ​读操作:基于事务开始时的 ​已提交数据快照 执行,仅读取其他事务已提交的版本。
    ​写操作:仍需要排他锁(X锁),但不会阻塞读操作,因为读操作通过版本快照访问数据。

  2. RCSI 如何避免脏读
    脏读的本质是读取到未提交的数据,而 RCSI 的版本控制机制 ​完全规避了这一问题:
    ​快照的生成规则:
    当数据库启用 RCSI 后,读操作会基于 ​当前语句开始时的最新已提交版本 生成快照。即使其他事务正在修改数据,这些未提交的修改对当前读操作不可见。
    ​示例场景:
    事务 A 更新某行但未提交;
    事务 B 使用 RCSI 读取该行,会获取事务 A 开始前该行的已提交版本,而非未提交的新值;
    事务 A 回滚后,事务 B 的读取结果仍与原始快照一致。

  3. RCSI 与隔离级别的兼容性
    ​默认行为:
    在 SQL Server 中,启用 RCSI 后,READ COMMITTED 隔离级别自动使用行版本控制,而非传统的锁机制。
    ​数据一致性保障:
    RCSI 满足 ANSI SQL 的 READ COMMITTED 标准,即保证 ​不读取未提交数据,因此不会引发脏读。

  4. 潜在问题与注意事项
    ​版本存储开销:
    频繁的更新操作会导致 tempdb 中存储大量行版本,可能增加存储和 I/O 压力。