sqlserver中的锁与事务

时间:2021-08-31 21:37:27

以下内容整理自:

SQL Server中的锁

SQLSERVER中的元数据锁

SQLSERVER中的锁资源类型

浅谈sqlserver中的事务和锁

锁的分类

1.从数据库角度


  • 独占锁(排它锁 X)

    独占锁锁定的资源只允许进行锁定操作的程序使用,其它任何对它的操作均不会被接受。执行INSERT、UPDATE和DELETE命令时,SQLSERVER会自动使用独占锁。但当对象上有其它锁时,无法加独占锁。独占锁一直到事务结束才能被释放。

  • 共享锁(S)

    共享锁锁定的资源可以被其它用户读取,但其它用户不能修改它。在SELECT命了执行时,Sqlserver通常会自动使用共享锁。共享锁的数据页读取完毕后,共享锁会立即释放。

  • 更新锁(U)

    更新锁是为了防止死锁而设立的。当SQL Server 准备更新数据时,它首先对数据对象作更新锁锁定,这样数据将不能被修改,但可以读取。等到SQL Server 确定要进行更新数据操作时,它会自动将更新锁换为独占锁。但当对象上有其它锁存在时,无法对其作更新锁锁定。

    一般更新模式由一个事务组成,此事务读取记录,获取资源(页或行)的共享 (S) 锁,然后修改行,此操作要求锁转换为排它 (X) 锁。如果两个事务获得了资源上的共享模式锁,然后试图同时更新数据,则一个事务尝试将锁转换为排它 (X) 锁。共享模式到排它锁的转换必须等待一段时间,因为一个事务的排它锁与其它事务的共享模式锁不兼容;发生锁等待。第二个事务试图获取排它 (X) 锁以进行更新。由于两个事务都要转换为排它 (X) 锁,并且每个事务都等待另一个事务释放共享模式锁,因此发生死锁。若要避免这种潜在的死锁问题,请使用更新 (U) 锁。一次只有一个事务可以获得资源的更新 (U) 锁。如果事务修改资源,则更新 (U) 锁转换为排它 (X) 锁。否则,锁转换为共享锁。

  • 意向锁

    用于建立锁的层次结构。意向锁的类型为:意向共享 (IS)、意向排它 (IX) 以及与意向排它共享 (SIX).

    意向锁表示 SQL Server 需要在层次结构中的某些底层资源上获取共享 (S) 锁或排它 (X) 锁。例如,放置在表级的共享意向锁表示事务打算在表中的页或行上放置共享 (S) 锁。在表级设置意向锁可防止另一个事务随后在包含那一页的表上获取排它 (X) 锁。意向锁可以提高性能,因为 SQL Server 仅在表级检查意向锁来确定事务是否可以安全地获取该表上的锁。而无须检查表中的每行或每页上的锁以确定事务是否可以锁定整个表。

  • 架构锁

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

  • 大容量更新 (BU)

    向表中大容量复制数据并指定了 TABLOCK 提示时使用

2.从程序员角度


  • 悲观锁(Pessimistic Lock)

    悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。

  • 乐观锁(Optimistic Lock)

    相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。

    而乐观锁机制在一定程度上解决了这个问题。乐观锁,大多是基于数据版本( Version )记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。

锁的粒度

锁的粒度是被*的目标的大小,粒度小则并发性高但开销大;粒度大则并发性低但开销小。

SQL Server支持的锁粒度可以分为为行、页、键、键范围、索引、表或数据库获取锁

下面列出数据库引擎可以锁定的资源

资源

说明

RID

用于锁定堆(heap)中的某一行

KEY

用于锁定索引上的某一行,或者某个索引键

PAGE

锁定数据库中的一个8KB页,例如数据页或索引页

EXTENT

一组连续的8页(区)

HOBT

锁定整个堆或B树的锁

TABLE

锁定包括所有数据和索引的整个表

FILE

数据库文件

APPLICATION

应用程序专用的资源

METADATA

元数据锁

ALLOCATION_UNIT

分配单元

DATABASE

整个数据库

说明:

RID:格式为fileid:pagenumber:rid的标识符,其中fileid标识包含页的文件, pagenumber标识包含行的页,rid标识页上的特定行。

用于锁定索引上的某一行,或者某个索引键

PAG:格式为fileid:pagenumber的数字,其中fileid标识包含页的文件,pagenumbe标识页

EXT:标识区中的第一页的数字。该数字的格式为fileid:pagenumber

TAB:没有提供信息,因为已在sp_lock中的Objid列中标识了表

DB:没有提供信息,因为已经在sp_lock中的dbid列中标识了数据库

FIL:文件的标识符,与sys.database_files目录视图中的file_id列相匹配

SQLSERVER中的锁

  1. NOLOCK(不加锁)

    在这种情况下用户可以读取到未完成的事务或回滚中的数据,即所谓“脏数据”。等于 READ UNCOMMITTED事务隔离级别

  2. HOLDLOCK(保持锁)

    将锁保持至整个事务结束,而不会中途释放。等于SERIALIZABLE事务隔离级别

  3. UPDLOCK(修改锁)

    此选项被选中时,SQL Server 在读取数据时使用修改锁来代替共享锁,并将此锁保持至整个事务或命令结束。使用此选项能够保证多个进程能同时读取数据但只有该进程能修改数据。

  4. TABLOCK(表锁)

    此选项被选中时,SQL Server 将在整个表上置共享锁直至该命令结束。

5.PAGLOCK(页锁)

当被选中时,SQL Server 使用共享页锁。
  1. TABLOCKX(排它表锁)

    SQL Server 将在整个表上置排它锁直至该命令或事务结束。这将防止其他进程读取或修改表中的数据

事务隔离级别

  • DEFAULT

    使用数据库设置的隔离级别(默认),有DBA默认的设置来决定隔离级别

  • READ_UNCOMMITTED

    这是事务最低的隔离级别,它允许另外一个事务可以到这个事务未提交的数据。会出现脏读、不可重复读、幻读(隔离级别最低,并发性最高)

  • READ_COMMITTED

    保证一个事务修改的数据提交后才能被另一个事务读取。另一个事务不能读取该事务未提交的数据。可以避免脏读,但会出现不可重复读、幻读。

  • REPEATABLE_READ

    可以防止脏读、不可重复读,但会出现幻读

  • SERIALIZABLE

    这是花费代价最高但是最可靠的事务级别,事务被处理为顺序执行,保证所有的情况不会发生(锁表)。

脏读:脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。

不可重复读:是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。

幻读: 是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。

锁定时间的长短

锁保持时间的长度为保护所请求级别上的资源所需的时间长度

一般来说,共享锁的锁定时间与事务的隔离级别有关,如果隔离级别为Read Committed的默认级别,只在读取(select)的期间保持锁定,即在查询出数据以后就释放了锁;如果隔离级别为更高的Repeatable read或Serializable,直到事务结束才释放锁。另说明,如果select语句中指定了HoldLock提示,则也要等到事务结束才释放锁。

排他锁直到事务结束才释放。

SQLServer中锁得自定义

死锁就是多个用户申请不同*,由于申请者均拥有一部分*权而又等待其他用户拥有的部分*而引起的无休止的等待。

  1. 处理超时和设置锁超时持续时间

    @@LOCK_TIMEOUT 返回当前会话的当前锁超时设置,单位为毫秒

    SET LOCK_TIMEOUT 设置允许应用程序设置语句等待阻塞资源的最长时间。当语句等待的时间大于 LOCK_TIMEOUT 设置时,系统将自动取消阻塞的语句,并给应用程序返回"已超过了锁请求超时时段"的 1222 号错误信息

  2. 设置事务隔离级别

查看锁得信息

  1. 执行 EXEC SP_LOCK 报告有关锁的信息

  2. 查询分析器中按Ctrl+2可以看到锁的信息

使用注意事项

  1. 使用事务时,尽量缩短事务的逻辑处理过程,及早提交或回滚事务;

  2. 设置死锁超时参数为合理范围,如:3分钟-10分种;超过时间,自动放弃本次操作,避免进程悬挂;

  3. 优化程序,检查并避免死锁现象出现;

  4. 对所有的脚本和SP都要仔细测试,在正是版本之前。

  5. 所有的SP都要有错误处理(通过@error)

  6. 一般不要修改SQL SERVER事务的默认级别。不推荐强行加锁

DBCC USEROPTIONS 返回当前连接的活动(设置)的 SET 选项。

加锁后其它人不可操作,直到加锁用户解锁,用commit或rollback解锁