SQL SERVER的锁机制(一)——概述(锁的种类与范围)

锁定:通俗的讲就是加锁。锁定是 Microsoft SQL Server 数据库引擎用来同步多个用户同时对同一个数据块的访问的一种机制。

定义:当有事务操作时,数据库引擎会要求不同类型的锁定,如相关数据行、数据页或是整个数据表,当锁定运行时,会阻止其他事务对已经锁定的数据行、数据页或数据表进行操作。只有在当前事务对于自己锁定的资源不在需要时,才会释放其锁定的资源,供其他事务使用。

一、锁的种类与范围(如下表)


锁类型


说明


共享 (S)


用于不更改或不更新数据的读取操作,如 SELECT 语句。


更新 (U)


用于可更新的资源中。 防止当多个会话在读取、锁定以及随后可能进行的资源更新时发生常见形式的死锁。


独占(也可称排他)(X)


用于数据修改操作,例如 INSERT、UPDATE 或 DELETE。 确保不会同时对同一资源进行多重更新。


意向


用于建立锁的层次结构。 意向锁包含三种类型:意向共享 (IS)、意向排他 (IX) 和意向排他共享 (SIX)。


架构


在执行依赖于表架构的操作时使用。 架构锁包含两种类型:架构修改 (Sch-M) 和架构稳定性 (Sch-S)。


大容量更新 (BU)


在向表进行大容量数据复制且指定了 TABLOCK 提示时使用。


键范围


当使用可序列化事务隔离级别时保护查询读取的行的范围。 确保再次运行查询时其他事务无法插入符合可序列化事务的查询的行。

(一)共享锁

共享锁(S 锁)允许并发事务在封闭式并发控制下读取 (SELECT) 资源。

当查询(SELECT)某条记录时,SQL SERVER 会尝试取得该条记录的上存在共享锁(S 锁),或无法获取,就必须等待别人释放对该记录中某几种与共享锁互斥的锁,才能在设置共享锁之后,获取该条记录。

举例来说:当某人查询某张表的一条记录时,就会在该记录上放置共享锁,在而其他人也要查询这张表的此记录时,因为共享锁彼此不互斥,所以也可以再次放置共享锁,也就是说SQL SERVER允许不同连接同时读取相同的数据。如果此时有人要更新此记录,因为独占锁与共享锁互斥,所以无法放置独占锁,要等到所有读取此记录的人都读取完毕,释放了共享锁,更新数据的人才能对该记录设置独占锁,并进一步更新数据。一般情况下,在默认的事务隔离级别下,当数据读取完毕,SQL SERVER就会释放共享锁,除非有特别的设置。

(二)更新锁

更新锁是一种中继锁。当同一项资源从原来的查询操作转换为更新操作时,锁定机制会从共享锁变为更新锁,再进一步变成独占锁。

在可重复读或可序列化事务中,此事务读取数据 [获取资源(页或行)的共享锁(S 锁)],然后修改数据 [此操作要求锁转换为独占锁(X 锁)]。 如果两个事务获得了资源上的共享模式锁,然后试图同时更新数据,则一个事务尝试将锁转换为独占锁(X 锁)。 共享模式到独占锁的转换必须等待一段时间,因为一个事务的独占锁与其他事务的共享模式锁不兼容;发生锁等待。 第二个事务试图获取独占锁(X 锁)以进行更新。 由于两个事务都要转换为独占锁(X 锁),并且每个事务都等待另一个事务释放共享模式锁,因此发生死锁。

若要避免这种潜在的死锁问题,请使用更新锁(U 锁)。 一次只有一个事务可以获得资源的更新锁(U 锁)。 如果事务修改资源,则更新锁(U 锁)转换为独占锁(X 锁)。

例如,当查询一条记录后,要将此内容更新(Update语句加上Where条件),一定会先查找记录,在查找的过程中就会对相关的记录放置共享锁,等找到相应的记录之后,SQL SERVER 会先对记录放置更新锁,以避免发生死锁。因为共享锁与更新锁并不互斥,如果两个人同时对同一条记录放置共享锁,先进行更新的人,可以在别人也对同一条记录放置了共享锁时,继续放置更新锁,但因为更新锁互斥,所以当另一个人想再放置更新锁时,将无法设置,而进入停止等待状态。

(三)独占锁(也可称为排他锁)

独占锁(排他锁)(X 锁)可以防止并发事务对资源进行访问。 使用独占锁(X 锁)时,任何其他事务都无法修改数据;仅在使用 NOLOCK 提示或未提交读隔离级别时才会进行读取操作。

对数据进行添加、修改、删除操作时(如 INSERT、UPDATE 和 DELETE), 语句在执行所需的操作之前首先执行读取操作以获取数据。 因此,需先对所在的资源放置独占锁,以确保以上操作未完成时,不受到干扰,独占锁在开启事务之后,一直保留到事务结束。例如,UPDATE 语句可能根据与一个表的联接修改另一个表中的行。 在此情况下,除了请求更新行上的独占锁之外,UPDATE 语句还将请求在联接表中读取的行上的共享锁。

(四)意向锁

在记录上放置共享锁之前,需要对存放该记录的更大范围(如数据页或数据表)上设置意向锁,以避免其他连接对该页放置独占锁。

数据库引擎使用意向锁来保护共享锁(S 锁)或独占锁(X 锁)放置在锁层次结构的底层资源上。 意向锁之所以命名为意向锁,是因为在较低级别锁前可获取它们,因此会通知意向将锁放置在较低级别上。

意向锁有两种用途:

· 防止其他事务以会使较低级别的锁无效的方式修改较高级别资源。

· 提高数据库引擎在较高的粒度级别检测锁冲突的效率。

例如,在该表的数据页或数据行上请求共享锁(S 锁)之前,在表级(或页级)请求共享意向锁,以防止另一个事务随后在包含那一页的表上尝试放置独占锁(X 锁)。 意向锁可以提高性能,因为数据库引擎仅在表级检查意向锁来确定事务是否可以安全地获取该表上的锁。 而不需要检查表中的每行或每页上的锁以确定事务是否可以锁定整个表。如下图。

意向锁包括意向共享 (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 锁的组合,作为分别获取这些锁并且同时持有两种锁的结果。

下面来实际举例来说明

--示例代码一:

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ

BEGIN TRAN

SELECT * FROM [Book] WHERE [bookid]=1

WAITFOR DELAY ‘00:00:10‘

COMMIT TRAN

可以通过另一条连接执行SP_LOCK 来查看上面代码的执行结果,如下图。在数据表上与数据页上都放置了意向共享锁,而在锁定的记录上放置了共享锁。

--示例代码二:

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ

BEGIN TRAN

SELECT * FROM [WBK_PDE_LIST] WHERE [WBOOK_NO]=‘BE404942450020‘ and cop_g_no=‘60217445‘

WAITFOR DELAY ‘00:00:10‘

COMMIT TRAN

可以通过另一条连接执行SP_LOCK 来查看上面代码的执行结果,如下图。由于上述代码中的[WBK_PDE_LIST]是一张堆表,所以直接就对数据表加了共享锁。

对上述示例中表WBK_PDE_LIST添加索引,

CREATE NONCLUSTERED INDEX [IX_WBK_PDE_LIST_WBOOKNO] ON [dbo].[WBK_PDE_LIST] 

(

[WBOOK_NO] ASC,

[COP_G_NO] ASC

)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF

, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

GO

然后再次执行代码示例二,再打开一个新的查询分析器,在查询分析器中执行SP_LOCK 来查看上面代码的执行结果,如下图。在数据表与数据页上分别放置了意向共享锁,在索引与数据行上放置了共享锁。

(五)架构锁

数据库引擎在表数据定义语言 (DDL) 操作(例如添加列或删除表)的过程中使用架构修改 (Sch-M) 锁。 保持该锁期间,Sch-M 锁将阻止对表进行并发访问。 这意味着 Sch-M 锁在释放前将阻止所有外围操作。

某些数据操作语言 (DML) 操作(例如表截断)使用 Sch-M 锁阻止并发操作访问受影响的表。

数据库引擎在编译和执行查询时使用架构稳定性 (Sch-S) 锁。 Sch-S 锁不会阻止某些事务锁,其中包括排他 (X) 锁。 因此,在编译查询的过程中,其他事务(包括那些针对表使用 X 锁的事务)将继续运行。 但是,无法针对表执行获取 Sch-M 锁的并发 DDL 操作和并发 DML 操作。

(六)大容量更新锁

数据库引擎在将数据大容量复制到表中时,指定 TABLOCK 提示或使用 sp_tableoption 选项(将数据表设置为 table lock on bulk load),则是使用大容量更新锁(BU)。 大容量更新锁(BU 锁)允许多个线程将数据并发地大容量加载到同一表,以降低数据表的锁定竞争,同时防止其他不进行大容量加载数据的进程访问该表。

(七)键范围锁

在使用可序列化事务隔离级别时,保护用户对于查询时所读取的数据行范围,以确保其他事务无法插入受“键范围锁”保护的数据行。键范围锁放置在索引上,指定开始与结束的索引键值。这些操作会先在索引上获取锁定,此种锁定可以封锁任何尝试进行插入、修改、删除索引键值在“键范围锁”中的数据行。例如:在索引键值“AAA”至“CZZ”范围中放置键范围锁,避免其他事务将含有索引键值的数据行插入到该范围内的任何地方,例如:“ABC”、“BCD”、“CEF”。另外当UPDATE语句搭配WHERE子句时,当SQL SERVER还在查找数据时,也有可能会设置键范围锁。

原文地址:https://www.cnblogs.com/littlewrong/p/9025743.html

时间: 2024-10-31 06:46:58

SQL SERVER的锁机制(一)——概述(锁的种类与范围)的相关文章

在SQL Server里为什么我们需要更新锁

今天我想讲解一个特别的问题,在我每次讲解SQL Server里的锁和阻塞(Locking & Blocking)都会碰到的问题:在SQL Server里,为什么我们需要更新锁?在我们讲解具体需要的原因前,首先我想给你介绍下当更新锁(Update(U)Lock)获得时,根据它的兼容性锁本身是如何应对的. 一般来说,当执行UPDATE语句时,SQL Server会用到更新锁(Update Lock).如果你查看对应的执行计划,你会看到它包含3个部分 读取数据 计算新值 写入数据 在查询计划的第1部分

sql server对并发的处理-乐观锁和悲观锁

假如两个线程同时修改数据库同一条记录,就会导致后一条记录覆盖前一条,从而引发一些问题. 例如: 一个售票系统有一个余票数,客户端每调用一次出票方法,余票数就减一. 情景: 总共300张票,假设两个售票点,恰好在同一时间出票,它们做的操作都是先查询余票数,然后减一. 一般的sql语句: 1 2 3 4 5 6 7 8 9 declare @count as int begin tran     select @count=count from ttt     WAITFOR DELAY '00:0

SQL SERVER错误:已超过了锁请求超时时段。 (Microsoft SQL Server,错误: 1222)

在SSMS(Microsoft SQL Server Management Studio)里面,查看数据库对应的表的时候,会遇到"Lock Request time out period exceeded.(Microsoft SQL Server, 错误1222)",对应的中文错误提示为"已超过了锁请求超时时段. (Microsoft SQL Server,错误: 1222)",如下截图所示,不管是用一般权限的账号还是具有sysadmin角色的登录名都是如此. 这

sql server对并发的处理-乐观锁和悲观锁【粘】

假如两个线程同时修改数据库同一条记录,就会导致后一条记录覆盖前一条,从而引发一些问题. 例如: 一个售票系统有一个余票数,客户端每调用一次出票方法,余票数就减一. 情景: 总共300张票,假设两个售票点,恰好在同一时间出票,它们做的操作都是先查询余票数,然后减一. 一般的sql语句: 1 2 3 4 5 6 7 8 9 declare @count as int begin tran     select @count=count from ttt     WAITFOR DELAY '00:0

数据库为什么需要锁机制?有哪些锁机制?

[为什么要锁] 数据库是一个多用户使用的共享资源,比如一个用户表t_user,两个浏览器前面的人登录了同个一个账号,把电话号码改了.当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况.若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性(脏读,不可重复读,幻读等),可能产生死锁.为了解决这个问题,加锁是一个非常重要的技术,对实现数据库并发控制是一个好的方案.简单说,当一个执行sql语句的事务想要操作表记录之前,先向数据库发出请求,对你访问的记录集加锁,

深入浅出 Java Concurrency (15): 锁机制 part 10 锁的一些其它问题[转]

主要谈谈锁的性能以及其它一些理论知识,内容主要的出处是<Java Concurrency in Practice>,结合自己的理解和实际应用对锁机制进行一个小小的总结. 首先需要强调的一点是:所有锁(包括内置锁和高级锁)都是有性能消耗的,也就是说在高并发的情况下,由于锁机制带来的上下文切换.资源同步等消耗是非常可观的.在某些极端情况下,线程在锁上的消耗可能比线程本身的消耗还要多.所以如果可能的话,在任何情况下都尽量少用锁,如果不可避免那么采用非阻塞算法是一个不错的解决方案,但是却也不是绝对的.

java 多线程8 : synchronized锁机制 之 方法锁

脏读 一个常见的概念.在多线程中,难免会出现在多个线程中对同一个对象的实例变量或者全局静态变量进行并发访问的情况,如果不做正确的同步处理,那么产生的后果就是"脏读",也就是取到的数据其实是被更改过的.注意这里 局部变量是不存在脏读的情况 多线程线程实例变量非线程安全 看一段代码: public class ThreadDomain13 { private int num = 0; public void addNum(String userName) { try { if ("

mysql中的锁机制之悲观锁和乐观锁

1.悲观锁? 悲观锁顾名思义就是很悲观,悲观锁认为数据随时就有可能会被外界进行修改,所以悲观锁一上来就会把数据给加上锁.悲观锁一般都是依靠关系型数据库提供的锁机制,然而事实上关系型数据库中的行锁,表锁不论是读写锁都是悲观锁. 2.乐观锁? 乐观锁顾名思义,就是很乐观,每次自己操作数据的时候认为没有人会来修改它,所以不会去对数据进行加锁.但是在更新的时候会去判断在此期间数据有没有被修改,需要用户自己去实现乐观锁.乐观锁不会发生并发抢占资源,只有在提交操作的时候检查是否违反数据完整性. 2.1.为什

MySql性能调优(五)采用合适的锁机制之表锁的演示

MySql的锁有以下几种形式: 1. 表级锁:开销小,加锁快:不会出现死锁:锁定粒度大,发生锁冲突的概率最高 ,并发度最低.MyISAM引擎属于这种类型. 2. 行级锁:开销大,加锁慢:会出现死锁:锁定粒度最小,发生锁冲突概率最低,并发度也最高.InnoDB引擎属于这种类型. 3. 页面锁:开销和加锁时间介于表锁和行锁之间:会出现死锁:锁定粒度也介于两者之间,并发度一般.NDB属于这种类型. 一. 表锁的演示 MyISAM存储引擎只支持表锁,所以对其进行操作会存在以下情况: 1.对MyISAM表

MySQL innoDB引擎锁机制(一) —— 行锁和表锁

我们都知道,MyISAM引擎使用的是表锁,而innoDB最小粒度为行锁.但在实际使用中我们有时发现就算我们操作的是不同行的数据,还是会发生锁表.我们先来看一个例子. session1开启事务并更新id=1的数据: session2开启事务,并更新id=2的数据,但session2被阻塞了: 不是说innoDB支持行锁吗,我们这里明明更新的不是同一条数据,为什么还会被阻塞.其实这是因为MySQL innoDB给数据加锁的方式和oracle不一样.oracle是给这条数据行加锁,而innoDB是给索