SQL常見死鎖例子及分析

use XTS
--步驟一=============創建測試表===================================================
--測試表1
if(exists(select 1 from sysobjects where id=OBJECT_ID('testa')))
   drop table testa
CREATE TABLE testa
(
	id int primary key,--Id,並標記爲主建
	name varchar(10),--姓名
	age decimal(26,4),--年齡
	city varchar(10),--所在城市
	address varchar(10),--家庭地址
	remark text --備註
)
--創建非聚集索引
create unique nonclustered index testa_idx1 on testa(name,age) 

--測試表2
if(exists(select 1 from sysobjects where id=OBJECT_ID('testb')))
   drop table testb
CREATE TABLE testb
(
	id int primary key,--Id,並標記爲主建
	name varchar(10),--姓名
	age decimal(26,4),--年齡
	city varchar(10),--所在城市
	address varchar(10),--家庭地址
	remark text --備註
)

--創建非聚集索引
create unique nonclustered index testb_idx1 on testb(name,age) 


--步驟二===================添加測試數據================================================
insert into testa(id,name,age,city,address)
select 1,'joe',20,'hz','zjwz'

insert into testa(id,name,age,city,address)
select 2,'jill',25,'hz','zjhz'

insert into testa(id,name,age,city,address)
select 3,'Bob',27,'hz','zjhz'

insert into testb(id,name,age,city,address)
select 1,'joe',20,'hz','zjwz'

insert into testb(id,name,age,city,address)
select 2,'jill',25,'hz','zjhz'

insert into testb(id,name,age,city,address)
select 3,'Bob',27,'hz','zjhz'

--添加多一點,是爲了查詢效率降低,以便快速得到驗證結果
declare @i int=4
while (@i<1000)
begin
 insert into testa(id,name,age,city,address)
select @i,@i,27,'xxxx','xxxx'
set @i=@i+1
end

--步驟三===================創建測試存儲過程及備用表================================================
IF OBJECT_ID ('p1') IS NOT NULL DROP PROC p1
IF OBJECT_ID ('p2') IS NOT NULL DROP PROC p2
--創建備份表,用於查詢存儲過程的結果存儲
if(exists(select 1 from sysobjects where id=OBJECT_ID('testback')))
   drop table testback
CREATE TABLE testback
(
	id int primary key,--Id,並標記爲主建
	name varchar(10),--姓名
	age decimal(26,4),--年齡
	city varchar(10),--所在城市
	address varchar(10)--家庭地址
)

if(exists(select 1 from sysobjects where id=OBJECT_ID('testback1')))
   drop table testback1
CREATE TABLE testback1
(
	id int primary key,--Id,並標記爲主建
	name varchar(10),--姓名
	age decimal(26,4),--年齡
	city varchar(10),--所在城市
	address varchar(10)--家庭地址
)

if(exists(select 1 from sysobjects where id=OBJECT_ID('testback2')))
   drop table testback2
CREATE TABLE testback2
(
	id int primary key,--Id,並標記爲主建
	name varchar(10),--姓名
	age decimal(26,4),--年齡
	city varchar(10),--所在城市
	address varchar(10)--家庭地址
)

--創建存儲過程p1,更新存儲過程
CREATE PROC p1 AS
   UPDATE testa SET age = age+1 WHERE id = 1
   UPDATE testa SET age = age-1 WHERE id = 1
GO

--創建存儲過程p2,查詢存儲過程
CREATE PROC p2 AS
truncate table testback
insert into testback(id,name,age,address)
select id,name,age,address from testa where name='joe'
GO

---死鎖案例一============兩個事物各佔有自己的資源,同時又需要對方的資源==================
--查詢一
begin transaction
update a set address='TT' from testa a where id=1
waitfor delay '00:00:10'
select * from testb where id=1
commit transaction
--查詢二
begin transaction
update a set address='TT' from testb a where id=1
waitfor delay '00:00:10'
select * from testa where id=1
commit transaction


--死鎖案例二============書籤查詢引起的死鎖=============================================
--高頻率update
while (1=1) exec p1

--高頻率select
while (1=1) exec p2

Set statistics profile off
UPDATE testa SET age = age+1 WHERE id = 1
select id,name,age,address from testa where name='joe'

--死鎖三================在較高隔離級別的事務中,兩個事務同時執行查詢及更新語句============
--查詢事務一
--需要設置事務隔離級別可重複瀆或加事務保持鎖
SET TRANSACTION ISOLATION LEVEL Repeatable read
while(1=1)
begin
begin transaction
truncate table testback1
insert into testback1(id,name,age,address)
select id,name,age,address from testa where id=1
waitfor delay '00:00:10'
update testa set age=age+1 where id=1
update testa set age=age-1 where id=1
commit transaction
end


--查詢事務二
SET TRANSACTION ISOLATION LEVEL Repeatable read
while(1=1)
begin
begin transaction
truncate table testback2
insert into testback2(id,name,age,address)
select id,name,age,address from testa where id=1
waitfor delay '00:00:10'
update testa set age=age+1 where id=1
update testa set age=age-1 where id=1
commit transaction
end

--死鎖四===========同一個表當兩個事務都在更新不同的記錄時,即使沒有更新索上的字段,
--也會引起死鎖,因爲查詢的字段沒有全部在所建的普通索引上面,所以同樣需要通過聚集索引做全表查詢
--但是聚集索引上的行在同一時間點上被不同事務在擁有,這樣就造成了兩個事務查詢的時候都無法獲取
--全部的資源,即造成了死鎖
--測試數據如下 
drop table testx
create table testx(id int primary key,name varchar(10),age int)
create index testx_ind1 on testx(name)
insert into testx(id,name,age)
select '1','袁*','22'
insert into testx(id,name,age)
select '2','程*','20'


create table testx1(id int primary key,name varchar(10),age int)
create table testx2(id int primary key,name varchar(10),age int)

--查詢一
while(1=1)
begin
begin transaction
update testx set age=age+1 where name='程*'
truncate table testx1
insert into testx1(id,name,age)
select id,name,age from testx where name='程*'
commit transaction
end

--查詢二
while(1=1)
begin
begin transaction
update testx set age=age+1 where name='袁*'
truncate table testx2
insert into testx2(id,name,age)
select id,name,age from testx where name='袁*'
commit transaction
end

--那你們可能就會有疑問,那我是不是單純更新,不做查詢的時候也會死鎖?因爲更新如果也是通過非主鍵字段更新時,也是全表掃描。
--答案:不會,至於爲什麼不會,我們只能理解數據庫本身在更新和查詢時上鎖的原理不一樣,這個我們也可以做個測試例子。
--查詢一=====事務A更新20秒後結束
begin transaction
update testx set age=40 where name='程*'
waitfor delay '00:00:20'
commit transaction

                     
--查詢二======事務B在5秒後結束
begin transaction
update testx set age=40 where name='袁*'
waitfor delay '00:00:05'
commit transaction

--查詢三====再分開執行下面兩個查詢
select * from testx where name='袁*'
select * from testx where name='程*'

--測試結果:
--當我們依次執行查詢一,再執行查詢二,最後執行查詢三:
--結果就是查詢二5秒後(不需要等待查詢一完成)就執行完成了,而查詢三無論執行那個查詢語句都需要等待20秒後(即必須要等待查詢一完成)才能出來查詢查詢結果
--結論就是更新和查詢時上鎖原理不一樣。

--=================隔離級別的演示=============================
DBCC USEROPTIONS 
--未提交讀(Read uncommitted)演示
--查詢一
SET TRANSACTION ISOLATION LEVEL Read uncommitted
select * from testa where id=100
begin transaction
update testa set city='newxx' where id=100
waitfor delay '00:00:10'
rollback transaction 

--查詢二
SET TRANSACTION ISOLATION LEVEL Read uncommitted
select * from testa where id=100

--已提交讀(Read committed)也叫不可重複瀆
--查詢一
SET TRANSACTION ISOLATION LEVEL Read committed
select * from testa where id=100
begin transaction
update testa set city='newx' where id=100
waitfor delay '00:00:10'
rollback transaction 

--查詢二
SET TRANSACTION ISOLATION LEVEL Read committed
select * from testa where id=100

--可重複讀(Repeatable read)演示(保證在同一個事務中,瀆取數據不會被其他事務更改)
--查詢一
SET TRANSACTION ISOLATION LEVEL Repeatable read
begin transaction
select * from testa where id=100
waitfor delay '00:00:10'
commit transaction

--查詢二
SET TRANSACTION ISOLATION LEVEL Repeatable read
update testa set city='new' where id=100


--可序列化演示(事務的最高級別,保證事務的串行執行)
--查詢一
SET TRANSACTION ISOLATION LEVEL Serializable
begin transaction
select * from testa
update testa set city='new' where id=100
waitfor delay '00:00:10'
select * from testa 
commit transaction 

--查詢二
SET TRANSACTION ISOLATION LEVEL Serializable
insert into testa(id,name,age,city,address)
select 4000,'Bobx',27,'hz','zjhz'


--創建覆蓋索引
drop index testa_idx1 on testa
create index testa_idx1 on testa(name,age) include(id,address)


--設置隔離級別爲可重複瀆
SET TRANSACTION ISOLATION LEVEL Repeatable read
DBCC USEROPTIONS 


--開啓事務隔離的方法
declare @sql varchar(8000) 
select @sql = ' 
ALTER DATABASE ' + DB_NAME() + ' SET SINGLE_USER WITH ROLLBACK IMMEDIATE ; 
ALTER DATABASE ' + DB_NAME() + ' SET TRANSACTION ISOLATION LEVEL read committed; 
ALTER DATABASE ' + DB_NAME() + ' SET MULTI_USER;' 
Exec(@sql) 

--查詢事務隔離
DBCC Useroptions 


--清除緩存
DBCC DROPCLEANBUFFERS
DBCC FREEPROCCACHE
DBCC FREESYSTEMCACHE('ALL') 

--開啓SQL性能分析
Set statistics profile on

--開啓SQL執行時間統計
set statistics time ON 

--開啓磁盤的讀寫統計
set statistics io on

--鎖查詢相關
SELECT request_session_id, resource_type, resource_associated_entity_id,
request_status, request_mode, resource_description
FROM sys.dm_tran_locks

select   request_session_id   spid,OBJECT_NAME(resource_associated_entity_id) tableName   
from   sys.dm_tran_locks where resource_type='OBJECT'

sp_lock
sp_who
select *  from sys.sysprocesses
dbcc inputbuffer(spid)













發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章