上接 SQL SERVER 查询性能优化——分析事务与锁(一)
第二步,通过SQL语句分析锁定情况
由于SQL SERVER 2008相比SQL SERVER 2005中的“活动监视器”有了比较大的改变,所以下而我们通过SQL语句进行分析,使用SQL语句进行分析需要通过SP_WHO、SP_WHO2、SP_LOCK等系统存储过程、Master.sys.sysprocesses系统视图,或从SQL 2005(2008)新提供的动态视图管理(DMV)sys.dm_exec_session、sys.dm_tran_locks等获取相关信息。
通过master.sys.sysprocesses 视图找出最初锁住资源及导致后面一连串进程被迫停止的等待源头。
下面我们举一个例子来具体说明,以下代码在SQL SERVER 2005/2008中都可以使用:
--1.创建测试表
CREATE TABLE [dbo].[Book](
[bookid] [int] NOT NULL,
[Name] [varchar](60) NULL,
[category] [varchar](10) NULL,
[numberofcopies] [int] NULL,
[AuthorID] [int] NULL,
CONSTRAINT [PK_Book] PRIMARY KEY CLUSTERED
(
[bookid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
INSERT [dbo].[Book] ([bookid], [Name], [category], [numberofcopies], [AuthorID]) VALUES (1, N'SQL 2008', N'MS', 4, 1)
INSERT [dbo].[Book] ([bookid], [Name], [category], [numberofcopies], [AuthorID]) VALUES (2, N'SharePoint 2007', N'MS', 3, 2)
INSERT [dbo].[Book] ([bookid], [Name], [category], [numberofcopies], [AuthorID]) VALUES (3, N'SharePoint 2010', N'MS', 5, 2)
INSERT [dbo].[Book] ([bookid], [Name], [category], [numberofcopies], [AuthorID]) VALUES (5, N'DB2', N'IBM', 10, 3)
INSERT [dbo].[Book] ([bookid], [Name], [category], [numberofcopies], [AuthorID]) VALUES (7, N'SQL 2012', N'MS', 7, 1)
--2.测试示例
--列出最初锁定资源,导致一连串其他进程被锁住的起始源头。
例一
if exists(select * from master.sys.sysprocesses where spid in (select blocked from master.sys.sysprocesses))
begin
---确定进程被锁住的其他资源
select spid 进程,STATUS 状态, 登录帐号=SUBSTRING(SUSER_SNAME(sid),1,30)
,用户机器名称=SUBSTRING(hostname,1,12)
,是否被锁住=convert(char(3),blocked)
,数据库名称=SUBSTRING(db_name(dbid),1,20),cmd 命令,waittype as 等待类型
,last_batch 最后批处理时间,open_tran 未提交事务的数量
from master.sys.sysprocesses
--列出锁住别人(在别的进程中blocked字段中出现的值)但自己未被锁住(blocked=0)
Where spid in (select blocked from master.sys.sysprocesses) and blocked=0
end
else
begin
select '没有被锁住的进程'
end
---接下来我们通过以下代码来构造一条进程被另外一条进程锁住的情况。
--例二
Use test
Go
Begin tran
update book set Name='MS SQL 2008'
where bookid=1
---切换到另一个查询界面,执行以下代码
--例三
Use test
Go
select * from Book where bookid=1
go
3. 在SQL SERVER 2005/2008中的Microsoft SQL Server Management Studio中打开一个新的查询界面,执行 exec SP_LOCK。结果如下图。
图1
从图1中可以观察到两个进程的相互作用,其中进程“53”要求模式为“独占(X)”,已经被获取允许“GRANT”;进程“56”要求模式为“共享(S)”正等候(WAIT)处理。
如上图1中进程“56”(执行SELECT语句的查询连接)被进程“53”(执行UPDATE语句的查询连接)封锁的现象,并从Book数据表锁定可以看出是因为“独占”锁定某一条索引键值(要求类型为KEY),导致进程“56”放置共享锁定(要求模式为“S”),而在等待状态(要求状态为WAIT)。
4. 在SQL SERVER 2005/2008中的Microsoft SQL Server Management Studio中打开一个新查询界面,通过另外一条连接来执行程序代码(例一),执行结果如下图。
图2
在上图2中可以看出例二查询代码开启事务之后,未关闭事务,因此状态(status)为sleeping,但并未被其他进程锁住(blk),所以“是否被锁住”列的数据为0,没有执行命令,也没有等待某种资源。另外由于该查询的数据库连接是Test,所以数据库名称为Test。
5. 查询sysprocesses系统视图呈现有问题的交易的现象可能有许多种,但最常见的一种就是status字段等于sleeping,waittype字段等于0x0000,last_batch字段表示离最后一次批处理执行的时候已经有一段距离了,以及open_tran字段大于0。例如,直接执行代码例二,这时事务已经开启,但是迟迟没有结束,就可能是程序没有做好事务管理。
可以在在SQL SERVER 2005/2008中的Microsoft SQL Server Management Studio中打开一个新查询界面中执行下面的语句,以查询有问题的连接
select spid 进程,STATUS 状态, 登录帐号=SUBSTRING(SUSER_SNAME(sid),1,30)
,用户机器名称=SUBSTRING(hostname,1,12)
,是否被锁住=convert(char(3),blocked)
,数据库名称=SUBSTRING(db_name(dbid),1,20),cmd 命令,waittype as 等待类型
,last_batch 最后批处理时间,open_tran 未提交事务的数量
from master.sys.sysprocesses
Where status='sleeping' and waittype=0x0000 and open_tran>0
如下图。
图3
status字段等于sleeping表示没有指示符正在执行,waittype字段等于0x0000代表此连接没有等待任何资源,last_batch字段表示最后一次SQL语句执行的时间,如果此时间离现在有一段时间了,以及open_tran字段大于0,就有问题了。一段时间过去了,没有等待任何资源,也没有执行任何SQL语句,那么为什么还要开启事务?
除了上述查询sysprocesses系统视图之外,SQL SERVER 2005/2008 可以通过“sys.dm_tran_locks动态管理视图”呈现目前使用中相关的锁定信息。返回的每一条记录都代表一个已经授权或等待授权的锁定。在结果集的数据行中,主要分成“资源”与“请求”两类,其字段分别以resource与request为前缀。资源群组描述已经锁定或等待的资源,而请求群组则描述已经获取或等待中的锁定请求。
--例四
select t1.resource_type [资源锁定类型],DB_NAME(resource_database_id) as 数据库名
,t1.resource_associated_entity_id 锁定对象,t1.request_mode as 等待者请求的锁定模式
,t1.request_session_id 等待者SID
,t2.wait_duration_ms 等待时间
,(select TEXT from sys.dm_exec_requests r cross apply
sys.dm_exec_sql_text(r.sql_handle) where r.session_id=t1.request_session_id) as 等待者要执行的SQL
,(select SUBSTRING(qt.text,r.statement_start_offset/2+1,
(case when r.statement_end_offset=-1 then DATALENGTH(qt.text) else r.statement_end_offset end -r.statement_start_offset)/2+1
)
from sys.dm_exec_requests r cross apply sys.dm_exec_sql_text(r.sql_handle)qt
where r.session_id=t1.request_session_id) 等待者正要执行的语句
,t2.blocking_session_id [锁定者SID]
,(select TEXT from sys.sysprocesses p cross apply
sys.dm_exec_sql_text(p.sql_handle)
where p.spid=t2.blocking_session_id
) 锁定者执行语句
from sys.dm_tran_locks t1,sys.dm_os_waiting_tasks t2
where t1.lock_owner_address=t2.resource_address
在Sql 2005 的TEST执行上面的代码,结果如下图。(这是针对Wbk_pde_list数据表)
图4
在Sql 2008 的TEST数据库上执行上面的代码,结果如下图。(这是针对BOOK数据表)
图5
备注:
以上第二步是通过在Microsoft SQL Server Management Studio中执行代码进行查询与分析加锁情况,而在SQL SERVER 2005中则可以通过Microsoft SQL Server Management Studio管理获取相当多的信息,方便让你决定当前应该采取什么样步骤。
上而第二步中获取的信息都可以在SQL SERVER 2005中通过Microsoft SQL Server Management Studio中的“活动监视器”获取。
例如通过“活动监视器--》按对象分类的锁”,下拉菜单中选择相应的对象。(如下图)