30分鍾全面解析-SQL事務+隔離級別+阻塞+死鎖


 以前總是追求新東西,發現基礎才是最重要的,今年主要的目標是精通SQL查詢和SQL性能優化。

 本系列主要是針對T-SQL的總結。

【T-SQL基礎】01.單表查詢-幾道sql查詢題

【T-SQL基礎】02.聯接查詢

【T-SQL基礎】03.子查詢

【T-SQL基礎】04.表表達式-上篇

【T-SQL基礎】04.表表達式-下篇

【T-SQL基礎】05.集合運算

【T-SQL基礎】06.透視、逆透視、分組集

【T-SQL基礎】07.數據修改

【T-SQL基礎】08.事務和並發

【T-SQL基礎】09.可編程對象

----------------------------------------------------------

【T-SQL進階】01.好用的SQL TVP~~獨家贈送[增-刪-改-查]的例子

 ----------------------------------------------------------

【T-SQL性能調優】01.TempDB的使用和性能問題

【T-SQL性能調優】02.Transaction Log的使用和性能問題

【T-SQL性能調優】03.執行計划

【T-SQL性能調優】04.死鎖分析

持續更新......歡迎關注我!

概述:

本篇主要是對SQL中事務和並發的詳細講解。

一、事務

1.什么是事務

為單個工作單元而執行的一系列操作。如查詢、修改數據、修改數據定義。

2.語法

(1)顯示定義事務的開始、提交

BEGIN TRAN
INSERT INTO b(t1) VALUES(1)
INSERT INTO b(t1) VALUES(2)
COMMIT TRAN

(2)隱式定義

如果不顯示定義事務的邊界,則SQL Server會默認把每個單獨的語句作為一個事務,即在執行完每個語句之后就會自動提交事務。

3.事務的四個屬性ACID

(1)原子性Atomicity

1.事務必須是原子工作單元。事務中進行的修改,要么全部執行,要么全都不執行;

2.在事務完成之前(提交指令被記錄到事務日志之前),系統出現故障或重新啟動,SQL Server將會撤銷在事務中進行的所有修改;

3.事務在處理中遇到錯誤,SQL Server通常會自動回滾事務;

4.少數不太嚴重的錯誤不會引發事務的自動回滾,如主鍵沖突、鎖超時等;

5.可以使用錯誤處理來捕獲第4點提到的錯誤,並采取某種操作,如把錯誤記錄在日志中,再回滾事務;

6.SELECT @@TRANCOUNT可用在代碼的任何位置來判斷當前使用SELECT @@TRANCOUNT的地方是否位於一個打開的事務當中,如果不在任何打開的事務范圍內,則該函數返回0;如果在某個打開的事務返回范圍內,則返回一個大於0的值。打開一個事務,@@TRANCOUNT=@@TRANCOUNT+1;提交一個事務,@@TRANCOUNT-1。

 

(2)一致性Consiitency

1.同時發生的事務在修改和查詢數據時不發生沖突;

2.一致性取決於應用程序的需要。后面會講到一致性級別,以及如何對一致性進行控制。

 

(3)隔離性Isolation

1.用於控制數據訪問,確保事務只訪問處於期望的一致性級別下的數據;

2.使用鎖對各個事務之間正在修改和查詢的數據進行隔離。

 

(4)持久性Durability

1.在將數據修改寫入到磁盤上數據庫的數據分區之前會把這些修改寫入到磁盤上數據庫的事務日志中,把提交指令記錄到磁盤的事務日志中以后,及時數據修改還沒有應用到磁盤的數據分區,也可以認為事務時持久化的。

2.系統重新啟動(正常啟動或在發生系統故障之后啟動),SQL Server會每個數據庫的事務日志,進行回復處理。

3.恢復處理包含兩個階段:重做階段和撤銷階段。

4.前滾:在重做階段,對於提交指令已經寫入到日志的事務,但數據修改還沒有應用到數據分區的事務,數據庫引擎會重做這些食物所做的所有修改。

5.回滾:在撤銷階段,對於提交指令沒有寫入到日志中的事務,數據庫引擎會撤銷這些事務所做的修改。(這句話需要research,可能是不正確的。因為提交指令沒有寫入到數據分區,撤銷修改是指撤銷哪些修改呢???)

 

二、鎖

1.事務中的鎖

(1)SQL Server使用鎖來實現事務的隔離。

(2)事務獲取鎖這種控制資源,用於保護數據資源,防止其他事務對數據進行沖突的或不兼容的訪問。

2.鎖模式

(1)排他鎖

  a.當試圖修改數據時,事務只能為所依賴的數據資源請求排他鎖。

  b.持有排他鎖時間:一旦某個事務得到了排他鎖,則這個事務將一直持有排他鎖直到事務完成。

  c.排他鎖和其他任何類型的鎖在多事務中不能在同一階段作用於同一個資源。

    如:當前事務獲得了某個資源的排他鎖,則其他事務不能獲得該資源的任何其他類型的鎖。其他事務獲得了某個資源的任何其他類型的鎖,則當前事務不能獲得該資源的排他鎖。

(2)共享鎖

  a.當試圖讀取數據時,事務默認會為所依賴的數據資源請求共享鎖。

  b.持有共享鎖時間:從事務得到共享鎖到讀操作完成。

  c.多個事務可以在同一階段用共享鎖作用於同一數據資源。

  d.在讀取數據時,可以對如何處理鎖定進行控制。后面隔離級別會講到如何對鎖定進行控制。

3.排他鎖和共享鎖的兼容性

(1)如果數據正在由一個事務進行修改,則其他事務既不能修改該數據,也不能讀取(至少默認不能)該數據,直到第一個事務完成。

(2)如果數據正在由一個事務讀取,則其他事務不能修改該數據(至少默認不能)。

4.可鎖定的資源的類型

RID、KEY(行)、PAGE(頁)、對象(例如表)、數據庫、EXTENT(區)、分配單元(ALLOCATION_UNIT)、堆(HEAP)、以及B樹(B-tree)。

RID: 標識頁上的特定行
  格式: fileid: pagenumber: rid (1:109:0 )
    其中fileid標識包含頁的文件, pagenumber標識包含行的頁,rid標識頁上的特定行。
    fileid與sys.databases_files 目錄視圖中的file_id列相匹配
  例子:
    在查詢視圖sys.dm_tran_locks的時候有一行的resource_description列顯示RID 是1:109:0 而status列顯示wait,
    表示第1個數據文件上的第109頁上的第0行上的鎖資源。

5.鎖升級

SQL Server可以先獲得細粒度的鎖(例如行或頁),在某些情況下將細粒度鎖升級為更粗粒度的鎖(例如,表)。
例如單個語句獲得至少5000個鎖,就會觸發鎖升級,如果由於鎖沖突而導致無法升級鎖,則SQL Server每當獲取1250個新鎖時出發鎖升級。

三、阻塞

1.阻塞

當多個事務都需要對某一資源進行鎖定時,默認情況下會發生阻塞。被阻塞的請求會一直等待,直到原來的事務釋放相關的鎖。鎖定超時期限可以限制,這樣就可以限制被阻塞的請求在超時之前要等待的時間。

階段1:事務A請求資源S1,事務不對資源S1進行操作

階段2:事務A用鎖A鎖定資源S1,事務B請求對資源S1進行不兼容的鎖定(鎖B),鎖B的請求被阻塞,事務B將進入等待狀態

階段3:事務A正在釋放鎖A,事務B等待鎖A釋放,

階段4:事務A的鎖A已釋放,事務B用鎖B鎖定資源S1

 

2.排除阻塞

例子:

(1)准備工作:

  1.准備測試數據

--先創建一張表Product作為測試。id為表的主鍵,price為product的價格
CREATE TABLE [dbo].[myProduct](
	[id] [int] NOT NULL,
	[price] [money] NOT NULL
) ON [PRIMARY]
GO
--插入一條數據,id=1,price=10
INSERT INTO [TSQLFundamentals2008].[dbo].[myProduct]([id],[price])VALUES(1,10)

  2.模擬阻塞發生的情況

   在SQL Server中打開三個查詢窗口Connection1、Connection2、Connection3,分別按順序執行表格中的執行語句。

--Connection1
BEGIN TRAN
UPDATE dbo.myProduct SET price = price + 1 WHERE id=1
 
--Connection2
SELECT * FROM dbo.myProduct WHERE id=1
 
--Connection3
SELECT  request_session_id AS 會話id ,
		resource_type AS 請求鎖定的資源類型 ,
		resource_description AS 描述 ,
		request_mode AS 模式 ,
		request_status AS 狀態
FROM    sys.dm_tran_locks
查詢窗口

服務器進程標識符SPID


執行語句 

結果  說明 
 Connection1 52  
--語句1:
BEGIN TRAN
UPDATE dbo.myProduct SET price = price + 1 WHERE id=1

更新產品價格10.00->11.00

  

   

為了更新id=1這一行數據,會話必須先獲得一個排他鎖。事務處於一直打開狀態,沒有提交,所以事務一直持有排他鎖,直到事務提交並完成。

 Connection2 56   
--語句2:
SELECT * FROM dbo.myProduct WHERE id=1

  

   

事務為了讀取數據,需要請求一個共享鎖,但是這一行已經被其他會話持有的排他鎖鎖定,而且共享鎖和排他鎖不是兼容的,所以會話被阻塞,進入等待狀態

 Connection3 57   
--語句3:
SELECT request_session_id AS 會話id ,
resource_type AS 請求鎖定的資源類型 ,
resource_description AS 描述 ,
request_mode AS 模式 ,
request_status AS 狀態
FROM sys.dm_tran_locks

  

   

會話56:
(1)狀態WAIT-等待鎖
(2)正在等待第1個數據文件上的第109頁上的第0行資源的共享鎖
(3)持有第1個數據文件上的第109頁資源的意向共享鎖
(3)持有OBJECT資源,意向共享鎖
(4)持有DATABASE資源,意向共享鎖
會話52:
(1)狀態WAIT-授予鎖
(2)正在等待第1個數據文件上的第109頁上的第0行資源的排他鎖(3)持有第1個數據文件上的第109頁資源的排他鎖
(3)持有OBJECT資源,排他鎖
(4)持有DATABASE資源,排他鎖

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

(2)分析阻塞

★ 1.sys.dm_tran_locks 視圖

(1)該動態視圖可以查詢出哪些資源被哪個進程ID鎖了

(2)查詢出對資源授予或正在等待的鎖模式

(3)查詢出被鎖定資源的類型

上面的查詢語句3已經用到了這個視圖,可以參考上圖中的分析說明。

 

★ 2.sys.dm_exec_connections 視圖

(1)查詢出該動態視圖可以查詢出進程相關的信息

(2)查詢出最后一次發生讀操作和寫操作的時間last_read,last_write

(3)查詢出進程執行的最后一個SQL批處理的二進制標記most_recent_sql_handle

查詢窗口

服務器進程標識符SPID


執行語句 

結果  說明 
 Connection3 57   
SELECT  session_id ,
        connect_time ,
        last_read ,
        last_write ,
        most_recent_sql_handle
FROM    sys.dm_exec_connections

WHERE   session_id IN ( 52, 56 )

  

 

  

會話52:
(1)connect_time連接時間:2016-06-07 07:09:41.103
(2)last_read最后一次讀操作時間:2016-06-07 07:10:56.233
(3)last_write最后一次寫操作時間:2016-06-07 07:10:57.873
(4)most_recent_sql_handle這是一個二進制標記,最后一個SQL批處理

會話56:
(1)狀態WAIT-授予鎖
(2)正在等待第1個數據文件上的第109頁上的第0行資源的排他鎖(3)持有第1個數據文件上的第109頁資源的排他鎖
(3)持有OBJECT資源,排他鎖
(4)持有DATABASE資源,排他鎖

 

 

 

 

 

 

 

 

 

 

★ 3.sys.dm_exec_sql_text 表函數

(1)該函數可以將二進制標記most_recent_sql_handle作為參數,然后返回SQL代碼。

(2)阻塞進程在不斷地運行,所以在代碼中看到的最后一個操作不一定是導致問題的語句。在本例中最后一條執行語句是導致阻塞的語句。 

查詢窗口

服務器進程標識符SPID

執行語句  結果  說明 
 Connection3  57
SELECT  session_id ,
        text
FROM    sys.dm_exec_connections
        CROSS APPLY sys.dm_exec_sql_text
        (most_recent_sql_handle) AS ST
WHERE   session_id IN ( 52, 56 )

 

  

會話52:
執行的SQL語句:

BEGIN TRAN
UPDATE dbo.myProduct
SET price = price + 1
WHERE id = 1


會話56:
執行的SQL語句:

(@1 tinyint)
SELECT * FROM [dbo].[myProduct] 
WHERE [id]=@1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

★ 4.sys.dm_exec_sessions 視圖

(1)會話建立的時間login_time

(2)特定於會話的客戶端工作站名稱host_name

(3)初始化會話的客戶端程序的名稱program_name

(4)會話所使用的SQL Server登錄名login_name

(5)最近一次會話請求的開始時間last_request_start_time

(6)最近一次會話請求的完成時間last_request_end_time 

查詢窗口

服務器進程標識符SPID


執行語句 

結果  說明 
 Connection3 57 
SELECT * FROM sys.dm_exec_sessions

 

  

 

 

 

 

 

 

 

 

 

 

★ 5.sys.dm_exec_requests 視圖

(1)識別出阻塞鏈涉及到的會話、爭用的資源、被阻塞會話等待了多長時間

查詢窗口

服務器進程標識符SPID


執行語句 

結果  說明 
 Connection3 57 
SELECT * FROM sys.dm_exec_sessions

 

 

 

會話56:
(1)被會話52阻塞,blocking_session_id = 52
(2)會話52的開始時間start_time
(3)狀態掛起status = suspended
(4)掛起的命令command=select

 

 

 

 

 

 

 

 

 

★ 6.Lock_TIMEOUT 選項

(1)設置會話等待鎖釋放的超時期限

(2)默認情況下會話不會設置等待鎖釋放的超時期限

(3)設置會話超時期限為5秒, SET Lock_TIMEOUT 5000

(4)鎖定如果超時,不會引發事務回滾

(5)取消會話超時鎖定的設置,SET LOCK_TIMEOUT -1

如果超時,將顯示以下錯誤:

 

7.KILL <spid> 命令

(1)殺掉會話52,KILL 52

(2)殺掉會話,會引起事務回滾,同時釋放排他鎖

四、隔離級別

1.基本概念:

(1)隔離級別用來做什么

  a.隔離級別用於決定如何控制並發用戶讀寫數據的操作

(2)寫操作
  a.任何對表做出修改的語句

  b.使用排他鎖

  c.不能修改讀操作獲得的鎖和鎖的持續時間

(3)讀操作:

  a.任何檢索數據的語句

  b.默認使用共享鎖

  c.使用隔離級別來控制讀操作的處理方式

2.隔離級別的分類

(1)未提交讀 (READ UNCOMMITTED

(2)已提交讀(READ COMMITTED)(默認值)

(3)可重復讀(REPEATABLE READ

(4)可序列化(SERIALIZABLE

(5)快照(SNAPSHOT

(6)已經提交讀快照(READ_COMMITTED_SNAPSHOT

3.隔離級別的設置

(1)設置整個會話的隔離級別

SET TRANSACTION ISOLATION LEVEL <isolation name>;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

(2)用表提示設置查詢的隔離級別

SELECT ... FROM <table> WITH (<isolation name>);
SELECT * FROM dbo.myProduct WITH (READCOMMITTED);


注意:

1.設置會話選項的隔離級別時,隔離級別中的每個單詞之間需要用空格分隔

2.用表提示的隔離級別時,隔離級別中的每個單詞之間不需要用空格分隔

3.表提示的隔離級別有同義詞,如:NOLOCK->READUNCOMMITTED,HOLDLOCK->REPEATABLEREAD

4.隔離級別的嚴格性:1.未提交讀<2.已提交讀<3.可重復讀<4.可序列化

5.隔離級別越高,一致性越高,並發性越低

6.基於快照的隔離級別,SQL Server將提交過的行保存到tempdb數據庫中,當讀操作發現行的當前版本和它們預期的不一致時,可以立即得到行的以前版本,從而不用請求共享鎖也能取得預期的一致性。

4.隔離級別的行為方式

★ 1.未提交讀 (READ UNCOMMITTED)

打開兩個查詢窗口,Connetion1,connection2

Step1: 執行Connection1的階段2的SQL 語句,然后執行connection2的SQL語句

Step2: 執行Connection1的階段3的SQL 語句,執行connection2的SQL語句

Step3: 執行Connection1的階段4的SQL 語句,執行connection2的SQL語句

查詢窗口 事務  執行語句
Connetion1 A
--階段2
UPDATE  myProduct
SET     price = price + 1
WHERE   id = 1;
 
SELECT  id ,
        price
FROM    dbo.myProduct
WHERE   id = 1;
 
--階段3
UPDATE  myProduct
SET     price = price + 5
WHERE   id = 1;
 
SELECT  id ,
        price
FROM    dbo.myProduct
WHERE   id = 1;
 
--階段4
COMMIT TRAN

  

Connection2 B
--在階段2執行之后
SET TRAN ISOLATION LEVEL READ UNCOMMITTED
BEGIN TRAN;
SELECT  id ,
        price
FROM    dbo.myProduct
WHERE   id = 1

COMMIT TRAN;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

兩個事務的流程圖:

 

 

階段1:Price=10,事務A對myProduct表請求排他鎖

階段2:事務A對myProduct表使用了排他鎖,更新price = price + 1,然后事務A查詢price的價格: price=11。事務B不請求任何鎖,事務B在A更新Price之后進行查詢,price=11

階段3:事務A更新price = price + 5,然后事務A查詢price的價格,price = 16。事務B查詢price的價格: price=16

階段4:事務A釋放排他鎖

階段5:事務A中查詢price的價格:price = 16。事務B查詢price的價格: price=16


大家可以看到事務B有兩種結果,這就是“未提交讀 (READ UNCOMMITTED)”隔離級別的含義:

(1)讀操作可以讀取未提交的修改(也稱為臟讀)。

(2)讀操作不會妨礙寫操作請求排他鎖,其他事務正在進行讀操作時,寫操作可以同時對這些數據進行修改。

(3)事務A進行了多次修改,事務B在不同階段進行查詢時可能會有不同的結果。

 

★ 2.已提交讀(READ COMMITTED)(默認值)


打開兩個查詢窗口,Connetion1,connection2

Step1: 執行Connection1的SQL 語句

Step2: 執行Connection2的SQL 語句

執行語句   執行語句
Connetion1 A
UPDATE dbo.myProduct SET price = price + 1 WHERE id=1
SELECT * FROM dbo.myProduct WHERE id =1

  

Connection2 B
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
SELECT * FROM dbo.myProduct WHERE id = 1

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

兩個事務的流程圖:

 

階段1:Price=10,事務A對myProduct表請求排他鎖

階段2:事務A對myProduct表使用了排他鎖,更新price = price + 1,然后事務A查詢price的價格: price=11。然后事務B請求共享鎖進行讀操作,查詢price,

  由於在當前隔離級別下,事務A的排他鎖和事務B的共享鎖存在沖突,所以事務B需要等待事務A釋放排他鎖后才能讀取數據。

階段3:事務A提交事務(COMMIT TRAN)

階段4:事務A提交完事務后,釋放排他鎖

階段5:事務B獲得了共享鎖,進行讀操作,price=11


“已提交讀 (READ UNCOMMITTED)”隔離級別的含義:

(1)必須獲得共享鎖才能進行讀操作,其他事務如果對該資源持有排他鎖,則共享鎖必須等待排他鎖釋放。

(2)讀操作不能讀取未提交的修改,讀操作讀取到的數據是提交過的修改。

(3)讀操作不會在事務持續期間內保留共享鎖,其他事務可以在兩個讀操作之間更改數據資源,讀操作因而可能每次得到不同的取值。這種現象稱為“不可重復讀”

 

3.可重復讀(REPEATABLE READ)

打開兩個查詢窗口,Connetion1,connection2

Step1: 執行Connection1的SQL 語句

Step2: 執行Connection2的SQL 語句

 

執行語句 事務  執行語句
Connetion1 A
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
SELECT * FROM dbo.myProduct WHERE id = 1
Connection2 B
UPDATE dbo.myProduct SET price = price + 1 WHERE id=1

 

 

 

 

 

 

兩個事務的流程圖:

 

階段1:Price=10,事務A對myProduct表請求共享鎖

階段2:事務A對myProduct表使用了共享鎖,事務A查詢price的價格: price=10,事務A一直持有共享鎖直到事務A完成為止。然后事務B請求排他鎖進行寫操作price=price+1,

由於在當前隔離級別下,事務A的共享鎖和事務B請求的排他鎖存在沖突,所以事務B需要等待事務A釋放共享鎖后才能修改數據。

階段3:事務A查詢price, price=10, 說明事務B的更新操作被阻塞了,更新操作沒有被執行。然后事務A提交事務(COMMIT TRAN)

階段4:事務A提交完事務后,釋放共享鎖

階段5:事務B獲得了排他鎖,進行寫操作,price=11


“可重復讀 (REPEATABLE READ)”隔離級別的含義:

(1)必須獲得共享鎖才能進行讀操作,獲得的共享鎖將一直保持直到事務完成之止。

(2)在獲得共享鎖的事務完成之前,沒有其他事務能夠獲得排他鎖修改這一數據資源,這樣可以保證實現可重復的讀取。

(3)兩個事務在第一次讀操作之后都將保留它們獲得的共享鎖,所以任何一個事務都不能獲得為了更新數據而需要的排他鎖,這種情況將會導致死鎖(deadlock),不過卻避免了更新沖突。


★ 4.可序列化(SERIALIZABLE)

打開兩個查詢窗口,Connetion1,connection2
Step1: 執行Connection1的SQL 語句
Step2: 執行Connection2的SQL 語句

 

執行語句 事務 
執行語句
Connetion1 A
BEGIN TRANSACTION
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
SELECT * FROM dbo.myProduct WHERE id = 1
Connection2 B
INSERT INTO dbo.myProduct(id, price) VALUES (1, 20)

 

 

 

 

 

 

 

兩個事務的流程圖:

 

 

階段1:Price=10,事務A對myProduct表請求共享鎖

階段2:事務A對myProduct表使用了共享鎖,事務A查詢id=1的price的價格:1行記錄,price=10,事務A一直持有共享鎖直到事務A完成為止。然后事務B請求排他鎖進行插入操作id=1,price=20,

  由於在當前隔離級別下,事務B試圖增加能夠滿足事務A的讀操作的查詢搜索條件的新行,所以事務A的共享鎖和事務B請求的排他鎖存在沖突,事務B需要等待事務A釋放共享鎖后才能插入數據。

階段3:事務A查詢出id=1的數據只有1行,說明事務B的插入操作被阻塞了,插入操作沒有被執行。然后事務A提交事務(COMMIT TRAN)

階段4:事務A提交完事務后,釋放共享鎖

階段5:事務B獲得了排他鎖,進行插入操作,插入成功,查詢出id=1的數據有兩條


“可序列化(SERIALIZABLE)”隔離級別的含義:

(1)必須獲得共享鎖才能進行讀操作,獲得的共享鎖將一直保持直到事務完成之止。

(2)在獲得共享鎖的事務完成之前,沒有其他事務能夠獲得排他鎖修改這一數據資源,且當其他事務增加能夠滿足當前事務的讀操作的查詢搜索條件的新行時,其他事務將會被阻塞,直到當前事務完成然后釋放共享鎖,其他事務才能獲得排他鎖進行插入操作。

(3)事務中的讀操作在任何情況下讀取到的數據是一致的,不會出現幻影行(幻讀)

(4)范圍鎖:讀操作鎖定滿足查詢搜索條件范圍的鎖

 

5.隔離級別總結

 

臟讀:讀取未提交的更改。

不可重復讀:讀操作不會在事務持續期間內保留共享鎖,其他事務可以在兩個讀操作之間更改數據資源,讀操作因而可能每次得到不同的取值。

丟失更新:兩個事務進行讀操作,獲得資源上的共享鎖,讀取完數據后,不再持有資源上的任何鎖,兩個事務都能更新這個值,

    最后進行更新的事務將會覆蓋其他事務做的更改,導致其他事務更改的數據丟失。

幻讀:第一次和第二次讀取到的數據行數不一致。

范圍鎖:讀操作鎖定滿足查詢搜索條件范圍的鎖

 

隔離級別 是否讀取未提交的行 是否不可重復讀 是否丟失更新 是否幻讀 共享鎖持續時間 是否持有范圍鎖
未提交讀 READ UNCOMMITTED Y Y Y Y 當前語句 N
已提交讀 READ COMMITTED N Y Y Y 當前語句 N
可重復讀REPEATABLE READ N N N Y 事務開始到事務完成 N
可序列化SERIALZABLE N N N N 事務開始到事務完成 Y

 

 

 

 

 

五、死鎖

死鎖是指一種進程之間互相永久阻塞的狀態,可能涉及兩個或更多的進程。

打開兩個查詢窗口,Connetion1,connection2

Step1: 執行Connection1的SQL 語句

Step2: 執行Connection2的SQL 語句

 

執行語句 事務  執行語句
Connetion1 A
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
BEGIN TRAN
UPDATE dbo.myProduct SET price = price + 1 WHERE id=1
SELECT * FROM dbo.myOrder WHERE id =1
Connection2 B
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
BEGIN TRAN
UPDATE dbo.myOrder SET customer = 'ddd' WHERE id = 1
SELECT * FROM dbo.myProduct WHERE id = 1

 

 

 

 

 

 

 

兩個事務的流程圖:

階段1:Price=10,事務A對myProduct表請求排他鎖。Customer = aaa,事務B對myOrder請求排他鎖

階段2:事務A對myProduct表使用了排他鎖,更新price = price + 1。然后事務B對myOrder表使用了排他鎖,更新customer=ddd。

階段3:事務A查詢myOrder表,對myOrder表請求共享鎖,因為事務A的請求的共享鎖與事務B的排他鎖沖突,所以事務A被阻塞。然后事務B查詢myProduct表,對myProduct表請求共享鎖,因為事務B的請求的共享鎖與事務A的排他鎖沖突,所以事務B被阻塞。

階段4:事務A等待事務B的排他鎖釋放,事務B等待事務A的排他鎖釋放,導致死鎖。事務A和事務B都被阻塞了。

階段5:SQL Server在幾秒之內檢測到死鎖,會選擇一個事務作為死鎖的犧牲品,終止這個事務,並回滾這個事務所做的操作。在這個例子中,事務A被終止,提示信息:事務(進程 ID 53)與另一個進程被死鎖在 鎖 資源上,並且已被選作死鎖犧牲品。請重新運行該事務。


“死鎖 (Dead Lock)”的一些注意事項:

(1)如果兩個事務沒有設置死鎖優先級,且兩個事務進行的工作量也差不多一樣時,任何一個事務都有可能被終止。

(2)解除死鎖要付出一定的系統開銷,因為這個過程會涉及撤銷已經執行過的處理。

(3)事務處理的時間時間越長,持有鎖的時間就越長,死鎖的可能性也就越大,應該盡可能保持事務簡短,把邏輯上可以不屬於同一個工作單元的操作移到事務以外。

(4)上面的例子中,事務A和事務B以相反順序訪問資源,所以發生了死鎖。如果兩個事務按同樣的順序來訪問資源,則不會發生這種類型的死鎖。在不改變程序的邏輯情況下,可以通過交換順序來解決死鎖的問題。

關於分析死鎖的問題,可以參考前面寫的關於阻塞的內容。

 

 

原文鏈接:30分鍾全面解析-SQL事務+隔離級別+阻塞+死鎖

參考資料:《T-SQL基礎》

 


作  者: Jackson0714
出  處:http://www.cnblogs.com/jackson0714/
關於作者:專注於微軟平台的項目開發。如有問題或建議,請多多賜教!
版權聲明:本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。
特此聲明:所有評論和私信都會在第一時間回復。也歡迎園子的大大們指正錯誤,共同進步。或者直接私信
聲援博主:如果您覺得文章對您有幫助,可以點擊文章右下角推薦一下。您的鼓勵是作者堅持原創和持續寫作的最大動力!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM