锁升级阈值
如果没有使用 ALTER TABLE SET LOCK_ESCALATION 选项来禁用表的锁升级并且满足以下任一条件时,则将触发锁升级:
- 单个 Transact-SQL 语句在单个无分区表或索引上获得至少 5,000 个锁。
- 单个 Transact-SQL 语句在已分区表的单个分区上获得至少 5,000 个锁,并且 ALTER TABLE SET LOCK_ESCALATION 选项设为 AUTO。
- 数据库引擎实例中的锁的数量超出了内存或配置阈值。
如果由于锁冲突导致无法升级锁,则数据库引擎每当获取 1,250 个新锁时便会触发锁升级。
Transact-SQL 语句的升级阈值
当 Transact-SQL 语句在单个表或索引的引用上获取至少 5,000 个锁时,或在表已分区的情况下,在单个表分区或索引分区的引用上获取至少 5,000 个锁时,会触发锁升级。例如,如果该语句在一个索引上获取 3,000 个锁,在同一表中的另一个索引上获取 3,000 个锁,这种情况下不会触发锁升级。同样,如果语句中含有表的自联接,并且表的每一个引用仅在表中获取 3,000 个锁,则不会触发锁升级。
只有触发升级时已经访问的表才会发生锁升级。假定某个 SELECT 语句是一个按 TableA、TableB 和 TableC 的顺序访问三个表的联接。该语句在 TableA 的聚集索引中获取 3,000 个行锁,在 TableB 的聚集索引中获取至少 5,000 个行锁,但是仍无法访问 TableC。当数据库引擎检测到该语句在 TableB 中获取至少 5,000 个行锁时,会尝试升级当前事务在 TableB 中持有的所有锁。它还会尝试升级当前事务在 TableA 中持有的所有锁,但是由于 TableA 中锁的数量 < 5000,因此,升级无法成功。但它不会尝试在 TableC 中进行锁升级,因为发生升级时尚未访问该表。
数据库引擎实例的升级阈值
每当锁的数量大于锁升级的内存阈值时,数据库引擎都会触发锁升级。内存阈值取决于 locks 配置选项的设置:
- 如果 locks 选项设置为默认设置 0,当锁对象使用的内存是数据库引擎使用的内存的 40%(不包括 AWE 内存)时,将达到锁升级阈值。用于表示锁的数据结构大约有 100 个字节长。该阈值是动态的,因为 数据库引擎动态地获得和释放内存来针对变化的工作负荷进行调整。
- 如果 locks 选项设置为非 0 值,则锁升级阈值是 locks 选项的值的 40%(或者更低,如果存在内存不足的压力)。
数据库引擎可以为升级选择任何会话中的活动语句,而且,只要实例中使用的锁内存保持在阈值之上,每获取 1,250 个新锁,它就会为升级选择语句。
升级混合锁类型
发生锁升级时,为堆或索引选择的锁必须足够强,才能满足限制性最强的较低级别的锁的要求。
例如,假定会话执行下列操作:
- 开始一个事务。
- 更新包含聚集索引的表。
- 发出引用同一个表的 SELECT 语句。
UPDATE 语句将获取下列锁:
- 已更新数据行上的排他锁(X 锁)。
- 包含那些行的聚集索引页上的意向排他锁(IX 锁)。
- 聚集索引上的 IX 锁和表上的 IX 锁。
SELECT 语句将获取下列锁:
- 所读取的所有数据行上的共享锁(S 锁),除非行已被来自 UPDATE 语句的 X 锁保护。
- 包含那些行的所有聚集索引页上的意向共享锁,除非页已被 IX 锁保护。
- 在聚集索引或表上不会获取锁,因为它们已被 IX 锁保护。
如果 SELECT 获取了触发锁升级的足够锁并且升级成功,表上的 IX 锁将被转换为 X 锁,而所有行、页和索引锁都将被释放。更新和读取操作都受表上的 X 锁保护。
减少锁定和升级
在大多数情况下,数据库引擎使用默认的锁定和锁升级设置进行操作时提供的性能最佳。如果数据库引擎实例生成大量锁并且频繁进行锁升级,请考虑通过下列方法减少锁定:
- 对于读取操作,使用不会生成共享锁的隔离级别。
- 当 READ_COMMITTED_SNAPSHOT 数据库选项为 ON 时,使用 READ COMMITTED 隔离级别。
- 使用 SNAPSHOT 隔离级别。
- 使用 READ UNCOMMITTED 隔离级别。此隔离级别只能用于能对脏读进行操作的系统。
注意:
更改隔离级别会影响数据库引擎实例上的所有表。
- 使用 PAGLOCK 或 TABLOCK 表提示,使数据库引擎使用页、堆或索引锁而不是行锁。但是,使用此选项增加了用户阻止其他用户尝试访问相同数据的问题,对于并发用户较多的系统,不应使用此选项。
- 对于已分区表,使用 ALTER TABLE 的 LOCK_ESCALATION 选项将锁升级到 HoBT 级而不是表级,或者禁用锁升级。
还可以使用跟踪标志 1211 和 1224 来禁用所有或某些锁升级。有关详细信息,请参阅跟踪标志 (Transact-SQL)。此外,还可以使用 SQL Server Profiler Lock:Escalation 事件监视锁升级