你们好,欢迎回到性能调优培训。上2个星期咱们已经讨论了SQLServer里的悲观和乐观锁。今天我想谈下SQL Server里对于锁的一个特殊现象:所谓的锁升级(Lock Escalations)。在咱们进入那个问题的细节前,我想先谈下SQL Server内部使用的锁层级(Lock Hierarchy)。html
2个星期前,当咱们开始讨论悲观并发模式(pessimistic concurrency)时,我告诉你SQLServer在记录层会获取共享锁(Shared Locks)和排它锁(Exclusive Locks)。遗憾的是,这不是所有事实。完整事实是SQL Server会在不一样粒度(granularities)得到锁,例如数据库,不一样页,最后在记录层。SQL Server实现整个所层级(lock hierarchy),以下图所示:数据库
一旦你使用一个数据库,你的会话会在数据库上得到一个共享锁(Shared Lock)。那个共享锁是须要的,所以没有其余人能够删除数据库,或还原数据库备份。这些操做会被阻塞,由于你打开的会话。SQL Server不但在行层上有共享锁和排它锁,SQL Server也在表和页层使用所谓的意向锁(Intent Locks)。安全
意向锁(Intent Locks)用来做为1个信号,表示在锁层级(lock hierarchy)里(极可能)有1个不兼容的锁在低一层已得到。意向锁是关系数据库主要性能调优。没有它们的话,锁管理器须要在低1层彻底进入列表,来决定高1层的锁是否能够获取。若是你在表层有一个意向排它锁(IX),你就不能在表层得到排它锁(X),由于有些记录在表自己里已是排它锁(X):在表层得到排它锁(X)会阻塞,由于在表上已经有意向排它锁(IX)。并发
遗憾的是这个多粒度锁并非免费的:在SQL Server里每一个锁须要96 bytes,所以会消耗一些内存,SQL Server必须保证没有查询使用太多的内存空间,否则的话内存会被耗尽。这就是为何会有锁升级(lock escalations)的存在。性能
假设下列情景:你更新散布在20万个数据页上的1百万条记录。在那个状况下,你须要在记录自己得到1百万个排它锁(X),在不一样页上得到20万个意向排它锁(IX),在表自己上得到1个意向排它锁(IX),你的查询合计须要得到1200001,在锁管理器须要近110M的锁空间——就只对这个简单查询。依据内存占用这个方法很是危险。所以你在一层一旦得到超过5000个锁,SQL Server就会触发锁升级(Lock Escalations)——例如在记录层。在那个状况下,SQL Server升级你个体细密度行锁为1个粗颗粒的表锁:spa
下图演示了锁升级发生先后的锁保持状况:scala
经过锁升级内存占用确定会降低——但这也会影响你数据库的并发!在表上的排它锁(X)意味着没有其余人能够从你的表读写,在表层上的共享锁(S)意味着你的表是只读的,没有人能够写它!你数据库的吞吐量只会降低!code
当你在1个层得到超过5000个锁,SQL Server就会触发锁升级。这是系统硬码限定,不一样经过任何配置选项修改。自SQL Server 2008开始,你能够经过以下代码,控制经过ALTER TABLE DDL语句的锁升级:htm
1 ALTER TABLE MyTableName 2 SET 3 ( 4 LOCK_ESCALATION = TABLE -- or AUTO or DISABLE 5 ) 6 GO
默认状况,SQL Server老是升级到表级别(Table选项)。若是你设置升级选项为AUTO,当你的表是分区的话,SQL Server能够升级到分区级别。但对这个选项,你要很是仔细,由于若是你用错误的顺序访问分区,它会致使死锁。使用DISABLE选项,对表你停用了锁升级——这会带来刚才提到的全部各个反作用(内存消耗)。如今的问题是,你如何高效修改或删除5000条记录而不触发锁升级?blog
锁升级(Lock Escalations)是SQL Server提供的安全保障。它们为何存在有个好理由,但当升级发生时,这个会引入更少并发的反作用。所以当你在写一次处理超过5000条记录的代码时要很是仔细。或许你能够逐步处理这些记录,而不是用1个大的UPDATE/DELETE语句。若是你想了解更多锁升级信息,能够看下我之前写一篇文章《锁升级》。
下周咱们继续SQL Server里的锁和阻塞,讲下死锁,还有SQL Server如何处理它们。请继续关注!