SQL Server学习笔记之事务、锁定、阻塞、死锁用法详解

本文实例讲述了SQL Server学习笔记之事务、锁定、阻塞、死锁用法。分享给大家供大家参考,具体如下:

1、事务

隐式事务

/*==================================================================
当以create,drop,
 fetch,open,
 revoke,grand,
 alter table,select,insert,delete,update,truncate table
语句首先执行的时候,SQL Server会话自动打开一个新的事务,
如果在会话中激活了隐式事务模式,那么这个事务会一直保持打开状态,
直到rollback或commit语句这个事务才结束,如果忘记提交事务,
那么在相应的隔离级别下,事务占用的锁可能不会释放,因此尽量不要用隐式事务。
====================================================================*/
--会话1
set implicit_transactions on
update t
set v = 'ext12'
set implicit_transactions off
select @@TRANCOUNT --输出:1,说明事务没有释放
     --占用的X独占锁不会释放,会阻塞其他会话
--会话2,被会话1阻塞住了,不会返回任何记录
select *
from t

在会话1中执行commit来提交事务,那么会话2马上就会返回记录了。

现在把两个会话的执行顺序调换一下:

--会话1
set implicit_transactions on --打开了隐式事务
select *
from t
set implicit_transactions off
select @@TRANCOUNT --输入:1,说明这个会话中的事务也没有提交
--会话2,会话2没有被会话1阻塞,
--之所以这样是因为会话的默认隔离级别是read committed,
--会话1中的事务虽然没有提交,但是select语句在这种隔离级别下,
--运行完就会释放占用的S共享锁,所以不会阻塞写操作
update t
set v = 'ext'

显示数据库最早的活动事务

/*==============================================================
如果事务在数据库中始终打开,有可能会阻塞其他进程的操作,
为什么是有可能而不是一定呢,
原因就是:在默认隔离级别下的select语句查询到数据后就会立即释放共享锁。
另外,日志备份也只会截断不活动事务的那部分日志,所以活动的事务
会导致日志数据越来越多。
为了找到没有提交的事务,可以用下面的命令显示某个数据库最早的活动事务.
不过有个例外,就是下面的命令不会返回:不占用锁资源的未提交事务
================================================================*/
begin tran --开始显示事务
select *
from t  --运行后立即释放共享锁
select @@TRANCOUNT --输入:1,说明没有提交事务
dbcc opentran('wc') --显示数据库最早的活动事务,
       --但是这儿显示"没有处于打开状态的活动事务"

通过会话来查询事务信息

--由于上面未提交事务中的select语句在默认的隔离级别下执行后自动释放了共享锁,
--所以dbcc opentran命令并没有返回这个活动事务,
--不过下面的视图解决了这个问题,可以找到所有活动事务。
--找到活动事务
select session_id,      --session_id与transaction_id的对应关系
  transaction_id,
  is_user_transaction,
  is_local
from sys.dm_tran_session_transactions --会话中的事务,识别所有打开的事务
where is_user_transaction =1
--找到活动事务对应的执行语句
select c.session_id,     --session_id与connection_id的对应关系
  c.connection_id,
  c.most_recent_sql_handle,
  s.text
from sys.dm_exec_connections c  --执行连接,最近执行的查询信息
cross apply sys.dm_exec_sql_text(c.most_recent_sql_handle) s
where c.session_id = 361
--活动事务的具体信息
select t.transaction_id,
  t.name,      --这里显示user_transaction
  t.transaction_begin_time,
  case t.transaction_type   --事务类型
   when 1 then '读/写事务'
   when 2 then '只读事务'
   when 3 then '系统事务'
   when 4 then '分布式事务'
  end 'transaction type',
  case t.transaction_state
   when 0 then '事务尚未完全初始化'
   when 1 then '事务已初始化但尚未启动'
   when 2 then '事务处于活动状态'
   when 3 then '事务已结束。该状态用于只读事务'
   when 4 then '已对分布式事务启动提交进程'
   when 5 then '事务处于准备就绪状态且等待解析'
   when 6 then '事务已提交'
   when 7 then '事务正在被回滚'
   when 8 then '事务已回滚'
  end 'transaction state'
from sys.dm_tran_active_transactions t --活动的事务
where transaction_id = 150764485

2、锁定

当一个用户要读取另一个用户正在修改的数据,或者一个用户正在修改另一个用户正在读取的数据,或者一个用户要修改另一个用户正在修改的数据,就会出现并发问题。锁定能防止并发问题。

资源的锁定方式称为锁定模式,SQL Server中的锁定模式:共享锁,意向锁,更新锁,排他锁,架构稳定锁,架构修改锁,大批量更新锁,键范围锁。不是所有锁模式都是兼容的,如:一个加了排他锁的资源不能再加其他锁,其他事务必须等待,直到释放排他锁。

可以锁定SQL Server中的各类对象,可以锁定的资源在粒度上差异很大,从细粒度(行、键)到粗粒度(数据库)。细粒度的锁允许用户能查询那些未被锁定的行,并发性更高,但是需要更多的锁资源(每个被锁定的行都需要一个锁资源);粗粒度的锁降低了并发性,但需要的锁资源很少。

在SQL Server中可锁定的资源:

DB(数据库)
 Metadata(系统元数据)
 Object(数据库对象:视图,函数,存储过程,触发器)
 Table(表)
  Hobt(堆或B树)
   Allocation Unit(按照数据的类型(数据,行溢出、大对象)分组的相关页面)
    Extent(8个8KB的页面)
     Page(8KB数据页面)
      Rid(行标示符对应一个堆表的行)
      Key(键范围上的锁、B树中的键)
File
Application

查看锁的活动

select resource_type,     --资源类型
  resource_database_id,   --资源所在的数据库id
  resource_associated_entity_id, --数据库中与资源相关联的实体的 ID。
          --该值可以是对象ID、Hobt ID 或分配单元 ID,
          --具体视资源类型而定
  object_name(resource_associated_entity_id,resource_database_id),
  resource_lock_partition, --已分区锁资源的锁分区ID。对于未分区锁资源值为 0
  resource_description, --资源的说明,其中只包含从其他资源列中无法获取的信息
  request_session_id, --请求资源的会话
  request_type,   --请求类型,该值为 LOCK
  request_mode,  --请求的模式,对于已授予的请求,为已授予模式,
       --对于等待请求,为正在请求的模式(锁定模式)
  request_status   --请求的当前状态,
        --可能值为 GRANTED、CONVERT 或 WAIT
from sys.dm_tran_locks
WHERE request_session_id = 361

控制表的锁升级

每个锁都会消耗内存资源,当锁的数量增加时,那么所需要的内存就会增加,而系统内可用的内存就会减少。如果锁占用的内存比率超过一个阀值,SQL Server会将细粒度锁(行锁)升级为粗粒度锁(表锁),这个过程就是锁升级。

锁升级的优点是可以减少锁的数量,相应的减少内存的使用量,而缺点是由于锁住了更大的资源,所以会导致阻塞,降低并发性。

--默认值,不管是不是分区表,会在表级别启用锁升级
ALTER TABLE t
SET (lock_escalation = TABLE)
--当表升级时,如果表已经分区,会在分区级别启用锁升级
ALTER TABLE t
SET (lock_escalation = auto)
--在表级别禁用锁升级,如果用了TabLock提示或在Serializable隔离级别下查询,还是会有表锁
ALTER TABLE t
SET (lock_escalation = disable)

影响锁定的除了上面提到的锁定模式、锁的粒度,还有就是事务的隔离级别。

所谓隔离级别其实就是事务与事务之间相互影响的程度,比如,一个事务修改了数据,那么其他事务是否能看到这些修改的数据,无论事务是否提交。对于最高的隔离级别,这个事务所做的修改,其他任何事务都看不到;而最低的隔离级别,这个事务所做的修改,可以被其他任何事务看到。

SQL Server隔离级别:

1.read uncommitted能解决丢失更新的问题,但是会导致脏读。

2.read committed读取的是已提交的数据,所以解决了脏读的问题,但是会有不可重复读取的问题,也就是在一个事务中有两次读取,第一次读取的和第二次读取的同一条数据,可能值是不同的,因为在事务中的select语句在读取完之后就立即释放的共享锁,而此时有另一个事务把刚才第一个事务读取的那条数据修改了,这样第一次读和第二次读到的值就会不同。

3.repeatable read解决了不可重复读取的问题,也就是在一个事务中的前后两次读取,读取到的数据值是一样的,但是会有幻读的可能,也就是第一次读出的数据确实和第二次读取的数据一样,但是第二次读取的记录条数可能多于第一次读取的记录条数,因为在读取的时候确实是锁住了被读取的记录,但是这个表可能添加了新的记录。

4.serializable通过锁住查询范围内的键、键与键之间的范围来解决幻读的问题,比如where id >=5 and id <=10,加入表表中只有id为7,9的两条记录,那么5-6、7-8、9-10这3个范围都会被锁住。

5.在ALLOW_SNAPSHOT_ISOLATION下的snapshot这种隔离级别允许读取事务一致性版本的数据,但可能不是最新的版本,也就是说在一个事务中只能读到某个版本,比如,在一个事务中有两次读取,第一次读完后,数据被另一个事务修改且事务提交了,此时进行第2次读取,那么读出来的还是和第一次读取一样的数据,这就是在一个事务中如果数据被其他事务修改了,读出来的数据也一样。优点是数据读取不会阻塞写,写也不会阻塞读取。另外,如果两个事务同时修改同一行数据,会导致更新冲突错误。

6.在READ_COMMITTED_SNAPSHOT下的read committed隔离级别允许在同一事务中总是能读取运行的已提交的数据,而且数据读取不会阻塞写,写也不会阻塞读取,也不会导致更新冲突。

上面是关于锁定的概念,那么接下来就是如何找到阻塞的进程,并解决阻塞问题。

--会话1,修改数据,但没有提交事务
BEGIN TRAN
select @@SPID --输出:287
UPDATE t
SET v = '88888'
WHERE idd = 1
--会话2,由于会话一事务没有提交,导致阻塞
BEGIN TRAN
select @@SPID --输出:105
UPDATE t
SET v = '888'
WHERE idd = 1
--查询会话1的等待信息
select session_id,   --查询的会话,也就是被阻塞的会话
  wait_duration_ms,  --等待毫秒数
  wait_type,   --等待类型,如:LCK_M_X表示正在等待获取排他锁
  blocking_session_id --阻塞session_id会话的会话
from sys.dm_os_waiting_tasks
where session_id = 105
--查询这个被阻塞的会话请求的资源情况
select resource_type,
  request_status,
  request_mode,
  request_session_id
from sys.dm_tran_locks
where request_session_id = 105
--说明会话2在update时一共获取了4个锁,共享数据库锁、2个意向独占锁(锁定表、数据页),
--一个键锁锁住那条要更新的记录,只有这个键锁的请求状态时wait,
--其他3个锁状态为grant表示已经会话2已经获得了锁。
--另一种查看阻塞会话的方法:--查看当前会话的执行请求
select session_id,
  status,
  blocking_session_id,
  wait_type,
  wait_time
from sys.dm_exec_requests
where session_id = 105
--配置语句等待锁释放的时间
--设置语句的锁请求超时时段
--超时时段是以毫秒为单位,超时后会返回锁定错误返回错误:(1 行受影响)消息 1222,级别 16,状态 51,第 7 行已超过了锁请求超时时段。语句已终止。

3、死锁

当两个事务分别锁定了资源,而又继续请求对方已获取的资源,那么就会产生死锁。

发生死锁的原因:

A、会话以不同的顺序访问表。

B、会话长时间运行事务,在一个事务中更新了很多表或行,这样增加了冲突的可能。

C、会话1申请了一些行锁,会话2申请了一些行锁,之后决定将其升级为表锁。

如果这些行在相同的数据页面中,并且两个会话同时在相同的页面上升级锁粒度,就会产生死锁。

set lock_timeout 1000
--跟踪死锁--会话1
set transaction isolation level serializable
begin tran
update t
set v ='563'
where idd =2
waitfor delay '00:00:10'
update t
set v = '963'
where idd =1commit--会话2
set transaction isolation level serializable
begin tran
update t
set v ='234'
where idd =1
waitfor delay '00:00:10'
update t
set v = '987'
where idd=2
commit

再开启一个会话,开启跟踪:

/*===================================================================
开启跟踪标志位:
    DBCC TRACEON(trace#[,...n],-1) [With No_InfoMsgs]
检查某种或某些标志位是开启,还是关闭:
    DBCC TRACESTATUS(trace#[,...n],-1) [With No_InfoMsgs]
1.trace#:指定一个或多个需要开启或需要检查状态的跟踪标志位数字
2. -1:如果指定了-1,则以全局方式打开某种或某些跟踪标志位
3.with No_InfoMsgs:当命令中包含此参数时,则禁止DBCC输出信息性消息
=====================================================================*/
--跟踪1222能把详细的死锁信息返回到SQL Server的日志中
--标志位-1表示跟踪标志位1222应该对所有SQL Server连接全局启用
DBCC TraceOn(1222,-1)
go
--验证标志位是否启动
DBCC TraceStatus
go
--关闭标志位
DBCC TraceOff(1222,-1)
go
设置死锁优先级--设置死锁的优先级,调整一个查询会话由于死锁而被终止运行的可能性
SET DeadLock_Priority Low | Normal | High | numeric-priority
--是当前连接很有可能被终止运行
set deadlock_priority Low
--SQL Server终止回滚代价较小的连接
set deadlock_priority Normal
--减少连接被终止的可能性,除非另一个连接也是High或数值优先级大于5
set deadlock_priority High
--数值优先级:-10到10的值,-10最有可能被终止运行,10最不可能被终止运行,
--两个数字谁大,谁就越不可能在死锁中被终止
set deadlock_priority 10

希望本文所述对大家SQL Server数据库程序设计有所帮助。

(0)

相关推荐

  • Sqlserver 存储过程中结合事务的代码

    复制代码 代码如下: --方式一 if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[USP_ProcedureWithTransaction_Demo]') and OBJECTPROPERTY(id, N'IsProcedure') = 1) drop procedure [dbo].[USP_ProcedureWithTransaction_Demo] GO -- ===================

  • SQL Server触发器及触发器中的事务学习

    如果你有对触发器和事务的概念,有些了解,这篇文章,对你来说会是很简单,或能让你更进一步的了解触发器里面的一些故事,和触发器中事务个故事.在这边文章里面,我不会从触发器和事务的概念去讲述,而是从常见的两种触发器类型(DML触发器 & DDL触发器)和After触发器 &  Instead Of 触发器的应用不同,开始说起它们,然后是说与事务有关的故事.如果,你有什么建议和意见,都可以通过文章后面的回复与我沟通,或者通过E-Mail方式,与 我交流:我的Email地址是:glal@163.co

  • sqlserver2008锁表语句详解(锁定数据库一个表)

    锁定数据库的一个表 复制代码 代码如下: SELECT * FROM table WITH (HOLDLOCK) 注意: 锁定数据库的一个表的区别 复制代码 代码如下: SELECT * FROM table WITH (HOLDLOCK) 其他事务可以读取表,但不能更新删除 复制代码 代码如下: SELECT * FROM table WITH (TABLOCKX) 其他事务不能读取表,更新和删除 SELECT 语句中"加锁选项"的功能说明 SQL Server提供了强大而完备的锁机

  • sql server 2000阻塞和死锁问题的查看与解决方法

    数据库发生阻塞和死锁的现象: 一.数据库阻塞的现象:第一个连接占有资源没有释放,而第二个连接需要获取这个资源.如果第一个连接没有提交或者回滚,第二个连接会一直等待下去,直到第一个连接释放该资源为止.对于阻塞,数据库无法处理,所以对数据库操作要及时地提交或者回滚.二.数据库死锁的现象:第一个连接占有资源没有释放,准备获取第二个连接所占用的资源,而第二个连接占有资源没有释放,准备获取第一个连接所占用的资源.这种互相占有对方需要获取的资源的现象叫做死锁.对于死锁,数据库处理方法:牺牲一个连接,保证另外

  • SqlServer查询和Kill进程死锁的语句

    查询死锁进程语句 select request_session_id spid, OBJECT_NAME(resource_associated_entity_id) tableName from sys.dm_tran_locks where resource_type='OBJECT' 杀死死锁进程语句 kill spid 下面再给大家分享一段关于sqlserver检测死锁;杀死锁和进程;查看锁信息 --检测死锁 --如果发生死锁了,我们怎么去检测具体发生死锁的是哪条SQL语句或存储过程?

  • 利用sys.sysprocesses检查SqlServer的阻塞和死锁

    MSDN:包含正在 SQL Server 实例上运行的进程的相关信息.这些进程可以是客户端进程或系统进程. 视图中主要的字段: 1. Spid:Sql Servr 会话ID 2. Kpid:Windows 线程ID 3. Blocked:正在阻塞求情的会话 ID.如果此列为 Null,则标识请求未被阻塞 4. Waittype:当前连接的等待资源编号,标示是否等待资源,0 或 Null表示不需要等待任何资源 5. Waittime:当前等待时间,单位为毫秒,0 表示没有等待 6. DBID:当前

  • SqlServer表死锁的解决方法分享

    其实不光是上面描述的情况会锁住表,还有很多种场景会使表放生死锁,解锁其实很简单,下面用一个示例来讲解: 1 首先创建一个测试用的表: 复制代码 代码如下: CREATE TABLE Test ( TID INT IDENTITY(1,1) ) 2 执行下面的SQL语句将此表锁住: 复制代码 代码如下: SELECT * FROM Test WITH (TABLOCKX) 3 通过下面的语句可以查看当前库中有哪些表是发生死锁的: 复制代码 代码如下: SELECT request_session_

  • SQLSERVER分布式事务使用实例

    复制代码 代码如下: --BEGIN DISTRIBUTED TRANSACTION [transactionname]--标志一个由分布式事务处理协调器MSDTC管理的TSQL分布式事务开始--SERVER A服务器为主控服务器.当连接发出后续COMMIT TRANSACTION或--ROLLBACK TRANSACTION语句时,主控服务器请求MSDTC在所涉及的服务器间管理--分布式事务的完成 --SQLSERVER使用链接服务器或者远程服务器作为分布式事务处理的平台,提供--远程存储过程

  • sqlserver进程死锁关闭的方法

    1.首先我们需要判断是哪个用户锁住了哪张表. --查询被锁表 select request_session_id spid,OBJECT_NAME(resource_associated_entity_id) tableName from sys.dm_tran_locks where resource_type='OBJECT' 查询后会返回一个包含spid和tableName列的表. 其中spid是进程名,tableName是表名. 2.了解到了究竟是哪个进程锁了哪张表后,需要通过进程找到锁

  • 查找sqlserver查询死锁源头的方法 sqlserver死锁监控

    查找出SQLServer的死锁和阻塞的源头 --查找出SQLServer死锁和阻塞的源头 复制代码 代码如下: use mastergodeclare @spid int,@bl intDECLARE s_cur CURSOR FORselect  0 ,blockedfrom (select * from sysprocesses where  blocked>0 ) awhere not exists(select * from (select * from sysprocesses whe

随机推荐