广告
返回顶部
首页 > 资讯 > 数据库 >一文分析SQL Server中事务使用的锁
  • 600
分享到

一文分析SQL Server中事务使用的锁

2024-04-02 19:04:59 600人浏览 泡泡鱼
摘要

目录序SQL Server使用的锁及锁对象sql Server执行Select时使用的锁SQL Server执行insert时使用的锁SQL Server执行update时使用的锁总结序 本文属于基础知识的回顾

本文属于基础知识的回顾,在日常技术交流和日常工作中经常发现有些同事了解关于数据库事务的基本知识,会看SQL语句的执行计划,也知道数据库有X锁、U锁和S锁等各种锁,但是对于这些锁在数据库事务执行期间是如何工作?为何这样配合才能完成数据库事务?数据库是如何对于各种资源加锁的?等等这类的问题不太了解,那么对于事务的执行肯定不会有深刻的认识。

这类知识虽然从网上搜索可以找到很多,但是大多内容重复,并且只注重理论知识而没有实践路径。就好比池塘中的青莲只可远观而无法靠近仔细观察,犹如雾里看花水中望月,对于其真实原理总是似懂非懂。

纸上得来终觉浅,绝知此事要躬行,只有亲自动手进行分析才能对这些问题有深入的认识,因此本文计划从数据库的基础知识入手,以详细的实践分析步骤引导认识数据库事务的执行过程,以期读者可以对于事务有更加深刻的理解。

SQL Server使用的锁及锁对象

数据库引擎使用不同的锁模式锁定资源,通过不同锁的组合使用达到不同的数据库事务隔离级别。

锁模式编号效果说明
共享锁S共享锁,通常用于不修改数据也不希望数据被修改的场景
更新锁U用于可更新的资源,防止这类资源在读取、锁定以及随后可能进行的资源更新时出现死锁
排他锁X用于修改数据的操作,例如insert、update和delete,防止对同一个资源进行多重修改
意向锁 包括意向共享、意向更新和意向排他三种,用于保护较低级别的锁并提升性能
架构 用于执行依赖表结构的操作时使用,包括架构修改 (Sch-M) 和架构稳定性 (Sch-S)
大容量更新BU在将数据大容量复制到表中且指定了 TAblock 提示时使用
键范围 当使用可序列化事务隔离级别时保护查询读取的行的范围。

意向锁又细分为多种类型:

锁模式编号效果说明
意向共享IS保护针对底层资源的共享锁
意向排他IX保护针对底层资源的排他锁是,IS的超集
共享意向排他SIX保护针对低层资源请求或获取的意向排他锁以意向共享锁
意向更新IU保护针对底层资源的更新锁
共享意向更新SIUS锁和IU锁的组合,作为分别获取并同时具备两种锁的组合效果
更新意向排他UIXU锁和IX锁的组合,作为分别获取并同时具备两种锁的组合效果

架构锁细分为两种类型:

锁模式编号效果说明
架构修改锁Sch-MDDL执行期间使用架构修改锁,该锁会阻止对于表的所有访问
架构稳定锁Sch-S该锁不会影响S、U以及X锁的执行,但是会阻止DDL的执行

通常开发人员谈到数据库的锁的时候习惯说数据库锁、表锁或者行锁。这种描述通常是从被锁定资源的角度来谈论,通过检索SQL Server2016的文档发现数据库上锁定更多的资源不只是这三种维度,还有11种类型。

锁对象关于锁对象的说明
AllocUnit分配单元
Application应用程序专用的资源
Database整个数据库
Extent一组连续的8个页
File数据库文件
Heap/B-tree堆或者B树
Key索引上的某一行
Metadata元数据
Object表、存储过程、视图等包括所有的数据和索引
OIB用于联机索引构建时的锁
Page数据库上8KB页
RID堆上的某一行
RowGroup列存储索引行组的时候使用的锁
Xact事务的锁定资源

了解了数据库的锁及其锁定对象,那么日常使用的select、insert和update语句到底是如何应用这些概念呢?

SQL Server执行Select时使用的锁

首先通过建表脚本创建一个数据库表:

USE [Test]
Go

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[UserTable](
	[id] [varchar](36) NOT NULL,
	[name] [varchar](256) NULL,
	[code] [varchar](256) NULL,
	[createtime] [datetime] NULL,
	[lastmodifytime] [datetime] NULL,
PRIMARY KEY CLUSTERED 
(
	[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
USE [Test]
GO
INSERT [dbo].[UserTable] ([id], [name], [code], [createtime], [lastmodifytime]) VALUES (N'5E4B68B0-71B8-43FB-B6B4-8E9D43A30589', N'test1', N'123456', CAST(N'2022-06-29T18:02:21.517' AS DateTime), CAST(N'2022-06-29T18:02:21.517' AS DateTime))
GO

由于Select语句在SQL Server的默认事务隔离级别(read commited)中执行完成后就会释放相关的锁,而非等到事务结束,在这种情况下无法通过sp_lock或者sys.dm_tran_locks视图观察select语句执行过程中锁的执行情况,因此比较方便的办法是在查询语句执行之前调整当前会话的事务隔离级别为repeatable read,在这个隔离级别中select语句默认会在事故执行完成后提交,比较方便分析。

在SQL Server Manager Studio的查询窗口中执行语句:

set transaction isolation level repeatable read
set statistics profile on 
begin tran
select * from usertable where  id='5E4B68B0-71B8-43FB-B6B4-8E9D43A30589'

在前面的事务目前是已经执行未提交的状态,此时可以通过dm_tran_locks查询到该语句目前持有的锁:

select request_session_id,resource_type,request_status,request_mode,resource_description,
case resource_type
when 'Page' then OBJECT_NAME(p.object_id)
when 'object' then OBJECT_NAME(lock.resource_associated_entity_id)
when 'database' then (select name from master..SysDatabases where dbid=resource_database_id)
when 'key' then object_name(p.object_id)
end 
as objectName from sys.dm_tran_locks lock
left join sys.partitions p 
on p.hobt_id=lock.resource_associated_entity_id
order by lock.request_session_id
request_session_idresource_typerequest_statusrequest_moderesource_descriptionobjectName
62DATABASEGRANTS Test
62PAGEGRANTIS0.236111111UserTable
62OBJECTGRANTIS UserTable
62KEYGRANTS(0ee48b5e6942)UserTable

查询结果字段说明:

  • request_session_id:会话编号
  • resource_type:被锁定的资源类型
  • request_status:请求的状态
  • request_mode:锁类型
  • resource_description 资源描述情况
  • objectName:对象名称

目前select查询持有的锁:

  • 通过目前的查询结果可以看到在DATABASE上加了S锁(数据库名为Test);
  • 在数据所属的页上增加了意向共享锁;
  • 表上增加了意向共享锁;
  • 数据行上增加了共享锁;

目前的事务执行过程中只对于匹配到的数据行进行了锁定,如果插入删除语句并未涉及到该数据行就不会受到影响,但是如果涉及到这行数据那肯定需要等S锁释放后才能进行。

SQL Server执行insert时使用的锁

首先在事务中执行insert语句并且不提交(注意将上个章节中的事务提交):

begin tran

insert into UserTable (id,code,name,createtime,lastmodifytime)
values(newid(),'test2','测试用户2',getdate(),getdate())

insert的时候默认会有事务,因此主动声明一个事务并只执行不提交就可以很容易的查到当前会话持有的锁。

通过dm_tran_locks查询到该语句目前持有的锁:

request_session_idresource_typerequest_statusrequest_moderesource_descriptionobjectName
70DATABASEGRANTS Test
70PAGEGRANTIX1:280UserTable
70OBJECTGRANTIX UserTable
70KEYGRANTX(c75ad92ba798)UserTable

该事务持有的锁:

  • 数据库层面的共享锁;
  • 数据页上的意向排他锁;
  • 数据表的意向排他锁;
  • 数据行的排他锁;

结合上文中对于锁类型的讲解可以很容易理解数据库增加这些锁的用意。数据库层面增加S锁可以保护当前正在进行的事务的安全,同时针对发生数据变化的数据页和数据表增加意向排他锁可以防止其他事务对于数据库和数据页进行更高层的修改(比如架构级别或者DDL之类的事务),IX锁对于IX和IS是可以并存的,因此可以最大限度上支持同一个区域内的其他修改和查询事务。

SQL Server执行update时使用的锁

首先在数据库中执行update语句而不提交(注意将上个章节中的事务提交或者回滚):

begin tran 
update UserTable set lastmodifytime=GETDATE()  where id ='06757850-68D6-416C-B3D1-FD3B29BAD4BB'

通过dm_tran_locks查询到该语句目前持有的锁:

request_session_idresource_typerequest_statusrequest_moderesource_descriptionobjectName
52DATABASEGRANTS Test
52PAGEGRANTIX1:280UserTable
52OBJECTGRANTIX UserTable
52KEYGRANTX(ead909dc80bf)UserTable

该事务持有的锁:

  • 数据库层面的共享锁;
  • 数据页上的意向排他锁;
  • 数据表上面的意向排他锁;
  • 数据行的排他锁;

有了insert的经验后,理解update语句使用的锁难度就不大了。其与insert使用的锁的类型基本一样,由于本次是使用主键进行修改,数据库可以直接定位到需要进行变更的数据行,因此只需要在对应的行上增加X锁就可以满足事务的需要。

日常使用的时候很少直接通过id更新数据,往往基于一些非聚集索引更新数据,在这种情况下数据库对于锁的使用会有什么不一样呢?首先针对测试的数据表增加两个索引:

create nonclustered index idx_UserTable_Name on UserTable(name)
create nonclustered index idx_UserTable_LastModifyTime on UserTable(lastmodifytime)

然后将update语句修改为根据name更新数据:

begin tran 
set statistics profile on 
update UserTable set lastmodifytime=GETDATE()  where name like '%test%'

该语句对应的锁的情况统计:

request_session_idresource_typerequest_statusrequest_moderesource_descriptionobjectName
52DATABASEGRANTS Test
52PAGEGRANTIX1:280UserTable
52PAGEGRANTIX1:368UserTable
52KEYGRANTX(ba4eae1b81ad)UserTable
52KEYGRANTX(500c265deab6)UserTable
52KEYGRANTX(a1a185fdb4ae)UserTable
52OBJECTGRANTIX UserTable
52KEYGRANTX(ff4928fe375a)UserTable
52KEYGRANTX(0ee48b5e6942)UserTable

可以发现通过非聚集索引更新数据的时候,数据库需要检查的内容明显增加,并且增加IX锁的数据也多了不少。只看这个表格可能不太好理解,这些key对应的X锁为什么要增加,以及是使用的哪个索引呢?

为了了解更多的信息,上文中查询事务锁的语句需要进行一些改动,增加对于索引的关联查询:

with indexs 
as (
SELECT  索引名称 = a.name ,
        表名 = c.name ,
        索引字段名 = d.name ,
        a.indid
FROM    sysindexes a
        JOIN sysindexkeys b ON a.id = b.id
                               AND a.indid = b.indid
        JOIN sysobjects c ON b.id = c.id
        JOIN syscolumns d ON b.id = d.id
                             AND b.colid = d.colid
WHERE   a.indid NOT IN ( 0, 255 )  
AND   c.name='UserTable' --查指定表 
)
select request_session_id,resource_type,request_status,request_mode,resource_description,
case resource_type
when 'Page' then OBJECT_NAME(p.object_id)
when 'object' then OBJECT_NAME(lock.resource_associated_entity_id)
when 'database' then (select name from master..SysDatabases where dbid=resource_database_id)
when 'key' then object_name(p.object_id)
end 
as objectName,index_id,i.索引名称 from sys.dm_tran_locks lock
left join sys.partitions p on p.hobt_id=lock.resource_associated_entity_id
left join indexs  i on i.indid=index_id
order by lock.request_session_id

通过关联查询索引信息,得到了更丰富的内容:

request_session_idresource_typerequest_statusrequest_moderesource_descriptionobjectNameindex_id索引名称
52DATABASEGRANTS TestNULLNULL
52PAGEGRANTIX1:280UserTable1PK__Test1__3213E83F133024F3
52PAGEGRANTIX1:368UserTable4idx_UserTable_LastModifyTime
52KEYGRANTX(ba4eae1b81ad)UserTable4idx_UserTable_LastModifyTime
52KEYGRANTX(500c265deab6)UserTable4idx_UserTable_LastModifyTime
52KEYGRANTX(a1a185fdb4ae)UserTable1PK__Test1__3213E83F133024F3
52OBJECTGRANTIX UserTableNULLNULL
52KEYGRANTX(ff4928fe375a)UserTable4idx_UserTable_LastModifyTime
52KEYGRANTX(0ee48b5e6942)UserTable1PK__Test1__3213E83F133024F3
52KEYGRANTX(150ba0b85c41)UserTable4idx_UserTable_LastModifyTime

从上表中可以看出在更新数据的时候,由于涉及到多行的非聚集索引上面的数据,因此对于该索引涉及到的数据行都增加了X锁,涉及到的数据页也比之前更多了。类型为X锁,同时索引名称为PK__Test1__3213E83F133024F3的有两行,因为本次事务匹配到了两行数据;类型为X锁,同时索引名称为idx_UserTable_LastModifyTime的一共有四行。为什么是四行呢?因为有两个旧的数据需要删除,同时新增了两个新的数据,所以是四行。其他的非聚集索引的数据并没有修改,所以本次不需要申请X锁。

总结

数据库中的各种事务隔离级别都是通过对于不同锁的综合运用实现的。对于锁的认识可以从两个角度进行:锁模式和锁对象。哪怕是一个简单的select语句都会有默认的某种锁以保护数据的正确性。需要注意不同的数据组合情况、不同的事务隔离级别下SQL语句的执行过程可能是不一样的,因此其使用的锁也会千变万化,本文所列举的只是一些很简单的情况,但是规则类似,分析路径也是基本一致的,有兴趣的可以自己尝试下日常工作中语句的执行过程中使用的锁,这对于理解数据库工作原理,有针对性的对于SQL语句调优都有一定帮助(注意不要在生产环境执行这类分析)。

到此这篇关于一文分析SQL Server中事务使用的锁的文章就介绍到这了,更多相关SQL Server事务锁内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

您可能感兴趣的文档:

--结束END--

本文标题: 一文分析SQL Server中事务使用的锁

本文链接: https://www.lsjlt.com/news/33956.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 一文分析SQL Server中事务使用的锁
    目录序SQL Server使用的锁及锁对象SQL Server执行Select时使用的锁SQL Server执行insert时使用的锁SQL Server执行update时使用的锁总结序 本文属于基础知识的回顾...
    99+
    2022-09-02
  • 一文分析SQL Server中事务使用的锁
    目录序SQL Server使用的锁及锁对象SQL Server执行Select时使用的锁SQL Server执行insert时使用的锁SQL Server执行update时...
    99+
    2022-11-13
  • SQL Server中事务与锁的示例分析
    这篇文章将为大家详细讲解有关SQL Server中事务与锁的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。一  概述在数据库方面,对于非DBA的程序员来...
    99+
    2022-10-18
  • sql server中死锁排查的示例分析
    这篇文章主要介绍sql server中死锁排查的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!死锁的四个必要条件:互斥条件(Mutual exclusion):资源不能被共享...
    99+
    2022-10-18
  • Sql Server事务语法及使用方法的实例分析
    这篇文章将为大家详细讲解有关Sql Server事务语法及使用方法的实例分析,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。实例讲述了Sql Server事务...
    99+
    2022-10-19
  • Sql Server事务语法及使用方法实例分析
    本文实例讲述了Sql Server事务语法及使用方法。分享给大家供大家参考,具体如下: 事务是关于原子性的。原子性的概念是指可以把一些事情当做一个不可分割的单元来看待。从数据库的角度看,它是指应全部执行或全...
    99+
    2022-10-18
  • SQL Server中四类事务并发问题的示例分析
    这篇文章将为大家详细讲解有关SQL Server中四类事务并发问题的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。SQL Server中四类事务并发问题的实例再现...
    99+
    2022-10-19
  • SQL Server中使用判断语句的实例分析
    本篇内容主要讲解“SQL Server中使用判断语句的实例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“SQL Server中使用判断语句的实例分析”吧!SQL Server判断语句(IF ...
    99+
    2023-06-20
  • MySQL中事务和锁的示例分析
    小编给大家分享一下MySQL中事务和锁的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧! My...
    99+
    2022-10-18
  • MySQL中InnoDB事务锁阅读锁信息状态的示例分析
    这篇文章主要介绍了MySQL中InnoDB事务锁阅读锁信息状态的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。 ...
    99+
    2022-10-18
  • Spring事务源码分析专题(一)JdbcTemplate使用及源码分析
    Spring中的数据访问,JdbcTemplate使用及源码分析 前言 本系列文章为事务专栏分析文章,整个事务分析专题将按下面这张图完成 对源码分析前,我希望先介绍一下Spring中数据访问的相关内容,然后层层递进到事物的源码分析,主要分...
    99+
    2019-10-22
    Spring事务源码分析专题(一)JdbcTemplate使用及源码分析 数据库入门 数据库基础教程 数据库 mysql
  • MySQL中常用查看锁和事务的SQL语句
    MySQL中常用查看锁和事务的SQL语句 当我们在使用MySQL数据库时,了解如何查看锁和事务的状态是非常重要的。这些信息可以帮助我们调试和优化数据库性能,以及解决并发访问的问题。在本博客中,我将介...
    99+
    2023-09-08
    mysql sql 数据库
  • 一文解析spring中事务的传播机制
    今天小编给大家分享的是一文解析spring中事务的传播机制,相信很多人都不太了解,为了让大家更加了解,所以给大家总结了以下内容,一起往下看吧。一定会有所收获的哦。Spring中的事务Spring的事务其实就是数据库的事务操作,符合ACID标...
    99+
    2023-07-06
  • SQL Server中怎么使用分页的存储过程
    本篇文章为大家展示了SQL Server中怎么使用分页的存储过程,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。一、 以学生表为例,在数据库中有一个Student表,...
    99+
    2022-10-18
  • 分析MySQL数据库Innodb中的事务隔离级别和锁的关系
    本篇内容主要讲解“分析MySQL数据库Innodb中的事务隔离级别和锁的关系”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“分析MySQL数据库Innodb中的事...
    99+
    2022-10-18
  • MySQL中的插入意向锁使用案例分析
    这篇文章主要讲解了“MySQL中的插入意向锁使用案例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“MySQL中的插入意向锁使用案例分析”吧! ...
    99+
    2023-02-07
    mysql
  • Mysql事务和数据中一致性处理的示例分析
    这篇文章主要介绍了Mysql事务和数据中一致性处理的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。在工作中,我们经常会遇到这样的问题...
    99+
    2022-10-18
  • 如何使用SQL语句在MySQL中进行数据事务和锁定管理?
    如何使用SQL语句在MySQL中进行数据事务和锁定管理?数据事务和锁定管理是数据库中非常重要的概念,通过合适的事务管理和锁定机制,可以确保数据的一致性和安全性。MySQL作为一个最流行的关系型数据库管理系统,提供了丰富的SQL语句来支持数据...
    99+
    2023-12-17
    数据管理 SQL事务 MySQL锁定
  • 一文搞懂Mysql中的共享锁、排他锁、悲观锁、乐观锁及使用场景
    目录一、常见锁类型二、mysql引擎介绍三、常用引擎间的区别 四、共享锁与排他锁五、排他锁的实际应用六、共享锁的实际应用七、死锁的发生八、另一种发生死锁的情景九、死锁的解决方式十、意向锁和计划锁十一、乐观锁和悲...
    99+
    2022-07-04
    mysql排他锁和共享锁 mysql排它锁 共享锁 意向锁 mysql乐观锁怎么实现
  • 一文掌握python中的__init__的意思及使用场景分析
    目录__init__解释用__init__与不用__init__对比__init__直接输出(不建议这么用)__init__普通用法不用__init__总结__init__解释 __...
    99+
    2023-02-10
    python中的__init__使用 python中的__init__
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作