SQL Server 数据库引擎使用意向锁来确保共享锁(S 锁)或排他锁(X 锁)放置在锁层次结构的底层资源上。 意向锁之所以命名为意向锁,是因为在较低级别锁前可获取它们,因此会通知意向将锁放置在较低级别上。
意向锁有两种用途:
防止其他事务以会使较低级别的锁无效的方式修改较高级别资源。
提高SQL Server 数据库引擎在较高的粒度级别检测锁冲突的效率。
意向锁包括意向共享 (IS)、意向排他 (IX) 以及意向排他共享 (SIX)
锁模式 | 说明 |
意向共享 (IS) | 保护针对层次结构中某些(而并非所有)低层资源请求或获取的共享锁。 |
意向排他 (IX) | 保护针对层次结构中某些(而并非所有)低层资源请求或获取的排他锁。IX 是 IS 的超集,它也保护针对低层级别资源请求的共享锁。 |
意向排他共享 (SIX) | 保护针对层次结构中某些(而并非所有)低层资源请求或获取的共享锁以及针对某些(而并非所有)低层资源请求或获取的意向排他锁。 顶级资源允许使用并发 IS 锁。例如,获取表上的 SIX 锁也将获取正在修改的页上的意向排他锁以及修改的行上的排他锁。 虽然每个资源在一段时间内只能有一个 SIX 锁,以防止其他事务对资源进行更新,但是其他事务可以通过获取表级的 IS 锁来读取层次结构中的低层资源。 |
意向更新 (IU) | 保护针对层次结构中所有低层资源请求或获取的更新锁。仅在页资源上使用 IU 锁。 如果进行了更新操作,IU 锁将转换为 IX 锁。 |
共享意向更新 (SIU) | S 锁和 IU 锁的组合,作为分别获取这些锁并且同时持有两种锁的结果。例如,事务执行带有 PAGLOCK 提示的查询,然后执行更新操作。带有 PAGLOCK 提示的查询将获取 S 锁,更新操作将获取 IU 锁。 |
更新意向排他 (UIX) | U 锁和 IX 锁的组合,作为分别获取这些锁并且同时持有两种锁的结果。 |
我们先看看锁的粒度对应的简单锁层次结构(如下图),SQL Server 可以在不同的级别加锁,如行级别、页级别、表级别、数据库级。
现在,假设我们按聚集索引查询某行或几行数据(如下图),此时相应的行数据将加上共享锁(S),而该行数据所在的页、表,将加上意向共享锁(IS)(排它锁或更新锁同理)。这时候,如果其他事务要访问锁定数据页或者表,需要排它锁(X),则该事务将被堵塞,因为意向锁(IS)与排它锁(X)是冲突的。
官网有一张锁模式的兼容性矩阵,可以查看各种锁模式直接的关系。
刚才提到的是其他事务访问同一个资源出现了冲突导致堵塞。那对于同一个事物,行已经加了共享锁,此时需要进一步更改(部分)数据,那么加锁的过程是什么样的呢?
如果需要更改数据,最终是要加上排它锁(X),但是排它锁(X)是与共享锁(S)冲突的。而进程对能对同一个资源同时只能持有一个锁,所有只能释放共享锁(S),再加上排它锁(X),如果直接释放,资源可能被其他进程夺走,这种方法不是好的处理方式。
最好的处理方法,首先在表级资源检测是否能获取意向排他共享(SIX),意向排他共享(SIX)与意向共享锁(IS)并不冲突,可直接转换;在页级资源检测是否能获取意向排它锁(IX),意向排它锁(IX)与意向共享锁(IS)并不冲突,可直接转换;在行级别直接转换为排它锁(X)(以上检查已经确认已可以直接加排他锁)。
类似地,当进程对表具有共享锁(S)并在该表的行上具有更新锁(U)时会发生共享意向更新(SIU),而当进程对表具有更新锁(U)而对行具有排他锁时会发生更新意向排他(UIX)。
意向锁的目的,是告诉其他访问者,在当前锁层次结构中,下面的某个低级别的粒度正常持有锁,这样可以快速检测冲突,快速进行访问,减少了资源消耗。