30分钟全面解析-SQL事务+隔离级别+阻塞+死锁
閱讀目錄
- 概述:
- 一、事務(wù)
- 二、鎖
- 三、阻塞
- 四、隔離級(jí)別
- 五.死鎖
?以前總是追求新東西,發(fā)現(xiàn)基礎(chǔ)才是最重要的,今年主要的目標(biāo)是精通SQL查詢和SQL性能優(yōu)化。
?本系列主要是針對(duì)T-SQL的總結(jié)。
【T-SQL基礎(chǔ)】01.單表查詢-幾道sql查詢題
【T-SQL基礎(chǔ)】02.聯(lián)接查詢
【T-SQL基礎(chǔ)】03.子查詢
【T-SQL基礎(chǔ)】04.表表達(dá)式-上篇
【T-SQL基礎(chǔ)】04.表表達(dá)式-下篇
【T-SQL基礎(chǔ)】05.集合運(yùn)算
【T-SQL基礎(chǔ)】06.透視、逆透視、分組集
【T-SQL基礎(chǔ)】07.數(shù)據(jù)修改
【T-SQL基礎(chǔ)】08.事務(wù)和并發(fā)
【T-SQL基礎(chǔ)】09.可編程對(duì)象
----------------------------------------------------------
【T-SQL進(jìn)階】01.好用的SQL TVP~~獨(dú)家贈(zèng)送[增-刪-改-查]的例子
?----------------------------------------------------------
【T-SQL性能調(diào)優(yōu)】01.TempDB的使用和性能問題
【T-SQL性能調(diào)優(yōu)】02.Transaction Log的使用和性能問題
【T-SQL性能調(diào)優(yōu)】03.執(zhí)行計(jì)劃
【T-SQL性能調(diào)優(yōu)】04.死鎖分析
持續(xù)更新......歡迎關(guān)注我!
回到頂部概述:
本篇主要是對(duì)SQL中事務(wù)和并發(fā)的詳細(xì)講解。
回到頂部一、事務(wù)
1.什么是事務(wù)
為單個(gè)工作單元而執(zhí)行的一系列操作。如查詢、修改數(shù)據(jù)、修改數(shù)據(jù)定義。
2.語(yǔ)法
(1)顯示定義事務(wù)的開始、提交
| 1 2 3 4 | BEGIN?TRAN INSERT?INTO?b(t1)?VALUES(1) INSERT?INTO?b(t1)?VALUES(2) COMMIT?TRAN |
(2)隱式定義
如果不顯示定義事務(wù)的邊界,則SQL Server會(huì)默認(rèn)把每個(gè)單獨(dú)的語(yǔ)句作為一個(gè)事務(wù),即在執(zhí)行完每個(gè)語(yǔ)句之后就會(huì)自動(dòng)提交事務(wù)。
3.事務(wù)的四個(gè)屬性ACID
(1)原子性Atomicity
1.事務(wù)必須是原子工作單元。事務(wù)中進(jìn)行的修改,要么全部執(zhí)行,要么全都不執(zhí)行;
2.在事務(wù)完成之前(提交指令被記錄到事務(wù)日志之前),系統(tǒng)出現(xiàn)故障或重新啟動(dòng),SQL Server將會(huì)撤銷在事務(wù)中進(jìn)行的所有修改;
3.事務(wù)在處理中遇到錯(cuò)誤,SQL Server通常會(huì)自動(dòng)回滾事務(wù);
4.少數(shù)不太嚴(yán)重的錯(cuò)誤不會(huì)引發(fā)事務(wù)的自動(dòng)回滾,如主鍵沖突、鎖超時(shí)等;
5.可以使用錯(cuò)誤處理來(lái)捕獲第4點(diǎn)提到的錯(cuò)誤,并采取某種操作,如把錯(cuò)誤記錄在日志中,再回滾事務(wù);
6.SELECT @@TRANCOUNT可用在代碼的任何位置來(lái)判斷當(dāng)前使用SELECT @@TRANCOUNT的地方是否位于一個(gè)打開的事務(wù)當(dāng)中,如果不在任何打開的事務(wù)范圍內(nèi),則該函數(shù)返回0;如果在某個(gè)打開的事務(wù)返回范圍內(nèi),則返回一個(gè)大于0的值。打開一個(gè)事務(wù),@@TRANCOUNT=@@TRANCOUNT+1;提交一個(gè)事務(wù),@@TRANCOUNT-1。
?
(2)一致性Consiitency
1.同時(shí)發(fā)生的事務(wù)在修改和查詢數(shù)據(jù)時(shí)不發(fā)生沖突;
2.一致性取決于應(yīng)用程序的需要。后面會(huì)講到一致性級(jí)別,以及如何對(duì)一致性進(jìn)行控制。
?
(3)隔離性Isolation
1.用于控制數(shù)據(jù)訪問,確保事務(wù)只訪問處于期望的一致性級(jí)別下的數(shù)據(jù);
2.使用鎖對(duì)各個(gè)事務(wù)之間正在修改和查詢的數(shù)據(jù)進(jìn)行隔離。
?
(4)持久性Durability
1.在將數(shù)據(jù)修改寫入到磁盤上數(shù)據(jù)庫(kù)的數(shù)據(jù)分區(qū)之前會(huì)把這些修改寫入到磁盤上數(shù)據(jù)庫(kù)的事務(wù)日志中,把提交指令記錄到磁盤的事務(wù)日志中以后,及時(shí)數(shù)據(jù)修改還沒有應(yīng)用到磁盤的數(shù)據(jù)分區(qū),也可以認(rèn)為事務(wù)時(shí)持久化的。
2.系統(tǒng)重新啟動(dòng)(正常啟動(dòng)或在發(fā)生系統(tǒng)故障之后啟動(dòng)),SQL Server會(huì)每個(gè)數(shù)據(jù)庫(kù)的事務(wù)日志,進(jìn)行回復(fù)處理。
3.恢復(fù)處理包含兩個(gè)階段:重做階段和撤銷階段。
4.前滾:在重做階段,對(duì)于提交指令已經(jīng)寫入到日志的事務(wù),但數(shù)據(jù)修改還沒有應(yīng)用到數(shù)據(jù)分區(qū)的事務(wù),數(shù)據(jù)庫(kù)引擎會(huì)重做這些食物所做的所有修改。
5.回滾:在撤銷階段,對(duì)于提交指令沒有寫入到日志中的事務(wù),數(shù)據(jù)庫(kù)引擎會(huì)撤銷這些事務(wù)所做的修改。(這句話需要research,可能是不正確的。因?yàn)樘峤恢噶顩]有寫入到數(shù)據(jù)分區(qū),撤銷修改是指撤銷哪些修改呢???)
?
回到頂部二、鎖
1.事務(wù)中的鎖
(1)SQL Server使用鎖來(lái)實(shí)現(xiàn)事務(wù)的隔離。
(2)事務(wù)獲取鎖這種控制資源,用于保護(hù)數(shù)據(jù)資源,防止其他事務(wù)對(duì)數(shù)據(jù)進(jìn)行沖突的或不兼容的訪問。
2.鎖模式
(1)排他鎖
a.當(dāng)試圖修改數(shù)據(jù)時(shí),事務(wù)只能為所依賴的數(shù)據(jù)資源請(qǐng)求排他鎖。
b.持有排他鎖時(shí)間:一旦某個(gè)事務(wù)得到了排他鎖,則這個(gè)事務(wù)將一直持有排他鎖直到事務(wù)完成。
c.排他鎖和其他任何類型的鎖在多事務(wù)中不能在同一階段作用于同一個(gè)資源。
如:當(dāng)前事務(wù)獲得了某個(gè)資源的排他鎖,則其他事務(wù)不能獲得該資源的任何其他類型的鎖。其他事務(wù)獲得了某個(gè)資源的任何其他類型的鎖,則當(dāng)前事務(wù)不能獲得該資源的排他鎖。
(2)共享鎖
a.當(dāng)試圖讀取數(shù)據(jù)時(shí),事務(wù)默認(rèn)會(huì)為所依賴的數(shù)據(jù)資源請(qǐng)求共享鎖。
b.持有共享鎖時(shí)間:從事務(wù)得到共享鎖到讀操作完成。
c.多個(gè)事務(wù)可以在同一階段用共享鎖作用于同一數(shù)據(jù)資源。
d.在讀取數(shù)據(jù)時(shí),可以對(duì)如何處理鎖定進(jìn)行控制。后面隔離級(jí)別會(huì)講到如何對(duì)鎖定進(jìn)行控制。
3.排他鎖和共享鎖的兼容性
(1)如果數(shù)據(jù)正在由一個(gè)事務(wù)進(jìn)行修改,則其他事務(wù)既不能修改該數(shù)據(jù),也不能讀取(至少默認(rèn)不能)該數(shù)據(jù),直到第一個(gè)事務(wù)完成。
(2)如果數(shù)據(jù)正在由一個(gè)事務(wù)讀取,則其他事務(wù)不能修改該數(shù)據(jù)(至少默認(rèn)不能)。
4.可鎖定的資源的類型
RID、KEY(行)、PAGE(頁(yè))、對(duì)象(例如表)、數(shù)據(jù)庫(kù)、EXTENT(區(qū))、分配單元(ALLOCATION_UNIT)、堆(HEAP)、以及B樹(B-tree)。
RID: 標(biāo)識(shí)頁(yè)上的特定行
格式: fileid: pagenumber: rid (1:109:0 )
其中fileid標(biāo)識(shí)包含頁(yè)的文件, pagenumber標(biāo)識(shí)包含行的頁(yè),rid標(biāo)識(shí)頁(yè)上的特定行。
fileid與sys.databases_files 目錄視圖中的file_id列相匹配
例子:
在查詢視圖sys.dm_tran_locks的時(shí)候有一行的resource_description列顯示RID 是1:109:0 而status列顯示wait,
表示第1個(gè)數(shù)據(jù)文件上的第109頁(yè)上的第0行上的鎖資源。
5.鎖升級(jí)
SQL Server可以先獲得細(xì)粒度的鎖(例如行或頁(yè)),在某些情況下將細(xì)粒度鎖升級(jí)為更粗粒度的鎖(例如,表)。
例如單個(gè)語(yǔ)句獲得至少5000個(gè)鎖,就會(huì)觸發(fā)鎖升級(jí),如果由于鎖沖突而導(dǎo)致無(wú)法升級(jí)鎖,則SQL Server每當(dāng)獲取1250個(gè)新鎖時(shí)出發(fā)鎖升級(jí)。
三、阻塞
1.阻塞
當(dāng)多個(gè)事務(wù)都需要對(duì)某一資源進(jìn)行鎖定時(shí),默認(rèn)情況下會(huì)發(fā)生阻塞。被阻塞的請(qǐng)求會(huì)一直等待,直到原來(lái)的事務(wù)釋放相關(guān)的鎖。鎖定超時(shí)期限可以限制,這樣就可以限制被阻塞的請(qǐng)求在超時(shí)之前要等待的時(shí)間。
階段1:事務(wù)A請(qǐng)求資源S1,事務(wù)不對(duì)資源S1進(jìn)行操作
階段2:事務(wù)A用鎖A鎖定資源S1,事務(wù)B請(qǐng)求對(duì)資源S1進(jìn)行不兼容的鎖定(鎖B),鎖B的請(qǐng)求被阻塞,事務(wù)B將進(jìn)入等待狀態(tài)
階段3:事務(wù)A正在釋放鎖A,事務(wù)B等待鎖A釋放,
階段4:事務(wù)A的鎖A已釋放,事務(wù)B用鎖B鎖定資源S1
?
2.排除阻塞
例子:
(1)準(zhǔn)備工作:
1.準(zhǔn)備測(cè)試數(shù)據(jù)
| 1 2 3 4 5 6 7 8 | --先創(chuàng)建一張表Product作為測(cè)試。id為表的主鍵,price為product的價(jià)格 CREATE?TABLE?[dbo].[myProduct]( ????[id] [int]?NOT?NULL, ????[price] [money]?NOT?NULL )?ON?[PRIMARY] GO --插入一條數(shù)據(jù),id=1,price=10 INSERT?INTO?[TSQLFundamentals2008].[dbo].[myProduct]([id],[price])VALUES(1,10) |
2.模擬阻塞發(fā)生的情況
? 在SQL Server中打開三個(gè)查詢窗口Connection1、Connection2、Connection3,分別按順序執(zhí)行表格中的執(zhí)行語(yǔ)句。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | --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?會(huì)話id , ????????resource_type?AS?請(qǐng)求鎖定的資源類型 , ????????resource_description?AS?描述 , ????????request_mode?AS?模式 , ????????request_status?AS?狀態(tài) FROM????sys.dm_tran_locks |
| 查詢窗口 | 服務(wù)器進(jìn)程標(biāo)識(shí)符SPID | 執(zhí)行語(yǔ)句? | 結(jié)果? | 說(shuō)明? | ||
| ?Connection1 | 52 | ?
| ? | ? 為了更新id=1這一行數(shù)據(jù),會(huì)話必須先獲得一個(gè)排他鎖。事務(wù)處于一直打開狀態(tài),沒有提交,所以事務(wù)一直持有排他鎖,直到事務(wù)提交并完成。 | ||
| ?Connection2 | 56? | ?
| ? | ? 事務(wù)為了讀取數(shù)據(jù),需要請(qǐng)求一個(gè)共享鎖,但是這一行已經(jīng)被其他會(huì)話持有的排他鎖鎖定,而且共享鎖和排他鎖不是兼容的,所以會(huì)話被阻塞,進(jìn)入等待狀態(tài) | ||
| ?Connection3 | 57? | ?
| ? | ? 會(huì)話56: |
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
(2)分析阻塞
1.sys.dm_tran_locks 視圖
(1)該動(dòng)態(tài)視圖可以查詢出哪些資源被哪個(gè)進(jìn)程ID鎖了
(2)查詢出對(duì)資源授予或正在等待的鎖模式
(3)查詢出被鎖定資源的類型
上面的查詢語(yǔ)句3已經(jīng)用到了這個(gè)視圖,可以參考上圖中的分析說(shuō)明。
?
?2.sys.dm_exec_connections 視圖
(1)查詢出該動(dòng)態(tài)視圖可以查詢出進(jìn)程相關(guān)的信息
(2)查詢出最后一次發(fā)生讀操作和寫操作的時(shí)間last_read,last_write
(3)查詢出進(jìn)程執(zhí)行的最后一個(gè)SQL批處理的二進(jìn)制標(biāo)記most_recent_sql_handle
| 查詢窗口 | 服務(wù)器進(jìn)程標(biāo)識(shí)符SPID | 執(zhí)行語(yǔ)句? | 結(jié)果? | 說(shuō)明? | ||
| ?Connection3 | 57? | ?
| ?
| 會(huì)話52: |
?
?
?
?
?
?
?
?
?
?
?3.sys.dm_exec_sql_text 表函數(shù)
(1)該函數(shù)可以將二進(jìn)制標(biāo)記most_recent_sql_handle作為參數(shù),然后返回SQL代碼。
(2)阻塞進(jìn)程在不斷地運(yùn)行,所以在代碼中看到的最后一個(gè)操作不一定是導(dǎo)致問題的語(yǔ)句。在本例中最后一條執(zhí)行語(yǔ)句是導(dǎo)致阻塞的語(yǔ)句。?
| 查詢窗口 | 服務(wù)器進(jìn)程標(biāo)識(shí)符SPID | 執(zhí)行語(yǔ)句? | 結(jié)果? | 說(shuō)明? | ||||||
| ?Connection3 | ?57 |
| ?
| 會(huì)話52:
? |
?
?
?
?
?
?
?
?
?
?
?
?
?
?4.sys.dm_exec_sessions 視圖
(1)會(huì)話建立的時(shí)間login_time
(2)特定于會(huì)話的客戶端工作站名稱host_name
(3)初始化會(huì)話的客戶端程序的名稱program_name
(4)會(huì)話所使用的SQL Server登錄名login_name
(5)最近一次會(huì)話請(qǐng)求的開始時(shí)間last_request_start_time
(6)最近一次會(huì)話請(qǐng)求的完成時(shí)間last_request_end_time?
| 查詢窗口 | 服務(wù)器進(jìn)程標(biāo)識(shí)符SPID | 執(zhí)行語(yǔ)句? | 結(jié)果? | 說(shuō)明? | ||
| ?Connection3 | 57? |
| ?
| ? |
?
?
?
?
?
?
?
?
?
5.sys.dm_exec_requests 視圖
(1)識(shí)別出阻塞鏈涉及到的會(huì)話、爭(zhēng)用的資源、被阻塞會(huì)話等待了多長(zhǎng)時(shí)間
| 查詢窗口 | 服務(wù)器進(jìn)程標(biāo)識(shí)符SPID | 執(zhí)行語(yǔ)句? | 結(jié)果? | 說(shuō)明? | ||
| ?Connection3 | 57? |
| ? ? | ? 會(huì)話56: |
?
?
?
?
?
?
?
?
?
?6.Lock_TIMEOUT 選項(xiàng)
(1)設(shè)置會(huì)話等待鎖釋放的超時(shí)期限
(2)默認(rèn)情況下會(huì)話不會(huì)設(shè)置等待鎖釋放的超時(shí)期限
(3)設(shè)置會(huì)話超時(shí)期限為5秒, SET Lock_TIMEOUT 5000
(4)鎖定如果超時(shí),不會(huì)引發(fā)事務(wù)回滾
(5)取消會(huì)話超時(shí)鎖定的設(shè)置,SET LOCK_TIMEOUT -1
如果超時(shí),將顯示以下錯(cuò)誤:
?
7.KILL <spid> 命令
(1)殺掉會(huì)話52,KILL 52
(2)殺掉會(huì)話,會(huì)引起事務(wù)回滾,同時(shí)釋放排他鎖
回到頂部四、隔離級(jí)別
1.基本概念:
(1)隔離級(jí)別用來(lái)做什么
a.隔離級(jí)別用于決定如何控制并發(fā)用戶讀寫數(shù)據(jù)的操作
(2)寫操作
a.任何對(duì)表做出修改的語(yǔ)句
b.使用排他鎖
c.不能修改讀操作獲得的鎖和鎖的持續(xù)時(shí)間
(3)讀操作:
a.任何檢索數(shù)據(jù)的語(yǔ)句
b.默認(rèn)使用共享鎖
c.使用隔離級(jí)別來(lái)控制讀操作的處理方式
2.隔離級(jí)別的分類
(1)未提交讀 (READ UNCOMMITTED)
(2)已提交讀(READ COMMITTED)(默認(rèn)值)
(3)可重復(fù)讀(REPEATABLE READ)
(4)可序列化(SERIALIZABLE)
(5)快照(SNAPSHOT)
(6)已經(jīng)提交讀快照(READ_COMMITTED_SNAPSHOT)
3.隔離級(jí)別的設(shè)置
(1)設(shè)置整個(gè)會(huì)話的隔離級(jí)別
| 1 | SET?TRANSACTION?ISOLATION?LEVEL?<isolation?name>; |
| 1 | SET?TRANSACTION?ISOLATION?LEVEL?READ?COMMITTED; |
(2)用表提示設(shè)置查詢的隔離級(jí)別
| 1 2 | SELECT?...?FROM?<table>?WITH?(<isolation?name>);<br> SELECT?*?FROM?dbo.myProduct?WITH?(READCOMMITTED); |
注意:
1.設(shè)置會(huì)話選項(xiàng)的隔離級(jí)別時(shí),隔離級(jí)別中的每個(gè)單詞之間需要用空格分隔
2.用表提示的隔離級(jí)別時(shí),隔離級(jí)別中的每個(gè)單詞之間不需要用空格分隔
3.表提示的隔離級(jí)別有同義詞,如:NOLOCK->READUNCOMMITTED,HOLDLOCK->REPEATABLEREAD
4.隔離級(jí)別的嚴(yán)格性:1.未提交讀<2.已提交讀<3.可重復(fù)讀<4.可序列化
5.隔離級(jí)別越高,一致性越高,并發(fā)性越低
6.基于快照的隔離級(jí)別,SQL Server將提交過的行保存到tempdb數(shù)據(jù)庫(kù)中,當(dāng)讀操作發(fā)現(xiàn)行的當(dāng)前版本和它們預(yù)期的不一致時(shí),可以立即得到行的以前版本,從而不用請(qǐng)求共享鎖也能取得預(yù)期的一致性。
4.隔離級(jí)別的行為方式
?1.未提交讀 (READ UNCOMMITTED)
打開兩個(gè)查詢窗口,Connetion1,connection2
Step1: 執(zhí)行Connection1的階段2的SQL 語(yǔ)句,然后執(zhí)行connection2的SQL語(yǔ)句
Step2: 執(zhí)行Connection1的階段3的SQL 語(yǔ)句,執(zhí)行connection2的SQL語(yǔ)句
Step3: 執(zhí)行Connection1的階段4的SQL 語(yǔ)句,執(zhí)行connection2的SQL語(yǔ)句
| 查詢窗口 | 事務(wù) | ?執(zhí)行語(yǔ)句 | ||
| Connetion1 | A |
| ||
| Connection2 | B |
|
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
兩個(gè)事務(wù)的流程圖:
?
?
階段1:Price=10,事務(wù)A對(duì)myProduct表請(qǐng)求排他鎖
階段2:事務(wù)A對(duì)myProduct表使用了排他鎖,更新price = price + 1,然后事務(wù)A查詢price的價(jià)格: price=11。事務(wù)B不請(qǐng)求任何鎖,事務(wù)B在A更新Price之后進(jìn)行查詢,price=11
階段3:事務(wù)A更新price = price + 5,然后事務(wù)A查詢price的價(jià)格,price = 16。事務(wù)B查詢price的價(jià)格: price=16
階段4:事務(wù)A釋放排他鎖
階段5:事務(wù)A中查詢price的價(jià)格:price = 16。事務(wù)B查詢price的價(jià)格: price=16
大家可以看到事務(wù)B有兩種結(jié)果,這就是“未提交讀 (READ UNCOMMITTED)”隔離級(jí)別的含義:
(1)讀操作可以讀取未提交的修改(也稱為臟讀)。
(2)讀操作不會(huì)妨礙寫操作請(qǐng)求排他鎖,其他事務(wù)正在進(jìn)行讀操作時(shí),寫操作可以同時(shí)對(duì)這些數(shù)據(jù)進(jìn)行修改。
(3)事務(wù)A進(jìn)行了多次修改,事務(wù)B在不同階段進(jìn)行查詢時(shí)可能會(huì)有不同的結(jié)果。
?
?2.已提交讀(READ COMMITTED)(默認(rèn)值)
打開兩個(gè)查詢窗口,Connetion1,connection2
Step1: 執(zhí)行Connection1的SQL 語(yǔ)句
Step2: 執(zhí)行Connection2的SQL 語(yǔ)句
| 執(zhí)行語(yǔ)句 | ? | 執(zhí)行語(yǔ)句 | ||
| Connetion1 | A |
| ||
| Connection2 | B |
|
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
兩個(gè)事務(wù)的流程圖:
?
階段1:Price=10,事務(wù)A對(duì)myProduct表請(qǐng)求排他鎖
階段2:事務(wù)A對(duì)myProduct表使用了排他鎖,更新price = price + 1,然后事務(wù)A查詢price的價(jià)格: price=11。然后事務(wù)B請(qǐng)求共享鎖進(jìn)行讀操作,查詢price,
由于在當(dāng)前隔離級(jí)別下,事務(wù)A的排他鎖和事務(wù)B的共享鎖存在沖突,所以事務(wù)B需要等待事務(wù)A釋放排他鎖后才能讀取數(shù)據(jù)。
階段3:事務(wù)A提交事務(wù)(COMMIT TRAN)
階段4:事務(wù)A提交完事務(wù)后,釋放排他鎖
階段5:事務(wù)B獲得了共享鎖,進(jìn)行讀操作,price=11
“已提交讀 (READ UNCOMMITTED)”隔離級(jí)別的含義:
(1)必須獲得共享鎖才能進(jìn)行讀操作,其他事務(wù)如果對(duì)該資源持有排他鎖,則共享鎖必須等待排他鎖釋放。
(2)讀操作不能讀取未提交的修改,讀操作讀取到的數(shù)據(jù)是提交過的修改。
(3)讀操作不會(huì)在事務(wù)持續(xù)期間內(nèi)保留共享鎖,其他事務(wù)可以在兩個(gè)讀操作之間更改數(shù)據(jù)資源,讀操作因而可能每次得到不同的取值。這種現(xiàn)象稱為“不可重復(fù)讀”
?
?3.可重復(fù)讀(REPEATABLE READ)
打開兩個(gè)查詢窗口,Connetion1,connection2
Step1: 執(zhí)行Connection1的SQL 語(yǔ)句
Step2: 執(zhí)行Connection2的SQL 語(yǔ)句
?
| 執(zhí)行語(yǔ)句 | 事務(wù)? | 執(zhí)行語(yǔ)句 | ||
| Connetion1 | A |
| ||
| Connection2 | B |
|
?
?
?
?
?
?
兩個(gè)事務(wù)的流程圖:
?
階段1:Price=10,事務(wù)A對(duì)myProduct表請(qǐng)求共享鎖
階段2:事務(wù)A對(duì)myProduct表使用了共享鎖,事務(wù)A查詢price的價(jià)格: price=10,事務(wù)A一直持有共享鎖直到事務(wù)A完成為止。然后事務(wù)B請(qǐng)求排他鎖進(jìn)行寫操作price=price+1,
由于在當(dāng)前隔離級(jí)別下,事務(wù)A的共享鎖和事務(wù)B請(qǐng)求的排他鎖存在沖突,所以事務(wù)B需要等待事務(wù)A釋放共享鎖后才能修改數(shù)據(jù)。
階段3:事務(wù)A查詢price, price=10, 說(shuō)明事務(wù)B的更新操作被阻塞了,更新操作沒有被執(zhí)行。然后事務(wù)A提交事務(wù)(COMMIT TRAN)
階段4:事務(wù)A提交完事務(wù)后,釋放共享鎖
階段5:事務(wù)B獲得了排他鎖,進(jìn)行寫操作,price=11
“可重復(fù)讀 (REPEATABLE READ)”隔離級(jí)別的含義:
(1)必須獲得共享鎖才能進(jìn)行讀操作,獲得的共享鎖將一直保持直到事務(wù)完成之止。
(2)在獲得共享鎖的事務(wù)完成之前,沒有其他事務(wù)能夠獲得排他鎖修改這一數(shù)據(jù)資源,這樣可以保證實(shí)現(xiàn)可重復(fù)的讀取。
(3)兩個(gè)事務(wù)在第一次讀操作之后都將保留它們獲得的共享鎖,所以任何一個(gè)事務(wù)都不能獲得為了更新數(shù)據(jù)而需要的排他鎖,這種情況將會(huì)導(dǎo)致死鎖(deadlock),不過卻避免了更新沖突。
?4.可序列化(SERIALIZABLE)
打開兩個(gè)查詢窗口,Connetion1,connection2
Step1: 執(zhí)行Connection1的SQL 語(yǔ)句
Step2: 執(zhí)行Connection2的SQL 語(yǔ)句
?
| 執(zhí)行語(yǔ)句 | 事務(wù)? |
| ||
| Connetion1 | A |
| ||
| Connection2 | B |
|
?
?
?
?
?
?
?
兩個(gè)事務(wù)的流程圖:
?
?
階段1:Price=10,事務(wù)A對(duì)myProduct表請(qǐng)求共享鎖
階段2:事務(wù)A對(duì)myProduct表使用了共享鎖,事務(wù)A查詢id=1的price的價(jià)格:1行記錄,price=10,事務(wù)A一直持有共享鎖直到事務(wù)A完成為止。然后事務(wù)B請(qǐng)求排他鎖進(jìn)行插入操作id=1,price=20,
由于在當(dāng)前隔離級(jí)別下,事務(wù)B試圖增加能夠滿足事務(wù)A的讀操作的查詢搜索條件的新行,所以事務(wù)A的共享鎖和事務(wù)B請(qǐng)求的排他鎖存在沖突,事務(wù)B需要等待事務(wù)A釋放共享鎖后才能插入數(shù)據(jù)。
階段3:事務(wù)A查詢出id=1的數(shù)據(jù)只有1行,說(shuō)明事務(wù)B的插入操作被阻塞了,插入操作沒有被執(zhí)行。然后事務(wù)A提交事務(wù)(COMMIT TRAN)
階段4:事務(wù)A提交完事務(wù)后,釋放共享鎖
階段5:事務(wù)B獲得了排他鎖,進(jìn)行插入操作,插入成功,查詢出id=1的數(shù)據(jù)有兩條
?
“可序列化(SERIALIZABLE)”隔離級(jí)別的含義:
(1)必須獲得共享鎖才能進(jìn)行讀操作,獲得的共享鎖將一直保持直到事務(wù)完成之止。
(2)在獲得共享鎖的事務(wù)完成之前,沒有其他事務(wù)能夠獲得排他鎖修改這一數(shù)據(jù)資源,且當(dāng)其他事務(wù)增加能夠滿足當(dāng)前事務(wù)的讀操作的查詢搜索條件的新行時(shí),其他事務(wù)將會(huì)被阻塞,直到當(dāng)前事務(wù)完成然后釋放共享鎖,其他事務(wù)才能獲得排他鎖進(jìn)行插入操作。
(3)事務(wù)中的讀操作在任何情況下讀取到的數(shù)據(jù)是一致的,不會(huì)出現(xiàn)幻影行。
(4)范圍鎖:讀操作鎖定滿足查詢搜索條件范圍的鎖
?
5.隔離級(jí)別總結(jié)
?
臟讀:讀取未提交的更改。
不可重復(fù)讀:讀操作不會(huì)在事務(wù)持續(xù)期間內(nèi)保留共享鎖,其他事務(wù)可以在兩個(gè)讀操作之間更改數(shù)據(jù)資源,讀操作因而可能每次得到不同的取值。
丟失更新:兩個(gè)事務(wù)進(jìn)行讀操作,獲得資源上的共享鎖,讀取完數(shù)據(jù)后,不再持有資源上的任何鎖,兩個(gè)事務(wù)都能更新這個(gè)值,
最后進(jìn)行更新的事務(wù)將會(huì)覆蓋其他事務(wù)做的更改,導(dǎo)致其他事務(wù)更改的數(shù)據(jù)丟失。
幻讀:第一次和第二次讀取到的數(shù)據(jù)行數(shù)不一致。
范圍鎖:讀操作鎖定滿足查詢搜索條件范圍的鎖
?
| 隔離級(jí)別 | 是否讀取未提交的行 | 是否不可重復(fù)讀 | 是否丟失更新 | 是否幻讀 | 共享鎖持續(xù)時(shí)間 | 是否持有范圍鎖 |
| 未提交讀 READ UNCOMMITTED | Y | Y | Y | Y | 當(dāng)前語(yǔ)句 | N |
| 已提交讀 READ COMMITTED | N | Y | Y | Y | 當(dāng)前語(yǔ)句 | N |
| 可重復(fù)讀REPEATABLE READ | N | N | N | Y | 事務(wù)開始到事務(wù)完成 | N |
| 可序列化SERIALZABLE | N | N | N | N | 事務(wù)開始到事務(wù)完成 | Y |
?
?
?
?
?
回到頂部五.死鎖
死鎖是指一種進(jìn)程之間互相永久阻塞的狀態(tài),可能涉及兩個(gè)或更多的進(jìn)程。
打開兩個(gè)查詢窗口,Connetion1,connection2
Step1: 執(zhí)行Connection1的SQL 語(yǔ)句
Step2: 執(zhí)行Connection2的SQL 語(yǔ)句
?
| 執(zhí)行語(yǔ)句 | 事務(wù)? | 執(zhí)行語(yǔ)句 | ||
| Connetion1 | A |
| ||
| Connection2 | B |
|
?
?
?
?
?
?
?
兩個(gè)事務(wù)的流程圖:
階段1:Price=10,事務(wù)A對(duì)myProduct表請(qǐng)求排他鎖。Customer = aaa,事務(wù)B對(duì)myOrder請(qǐng)求排他鎖
階段2:事務(wù)A對(duì)myProduct表使用了排他鎖,更新price = price + 1。然后事務(wù)B對(duì)myOrder表使用了排他鎖,更新customer=ddd。
階段3:事務(wù)A查詢myOrder表,對(duì)myOrder表請(qǐng)求共享鎖,因?yàn)槭聞?wù)A的請(qǐng)求的共享鎖與事務(wù)B的排他鎖沖突,所以事務(wù)A被阻塞。然后事務(wù)B查詢myProduct表,對(duì)myProduct表請(qǐng)求共享鎖,因?yàn)槭聞?wù)B的請(qǐng)求的共享鎖與事務(wù)A的排他鎖沖突,所以事務(wù)B被阻塞。
階段4:事務(wù)A等待事務(wù)B的排他鎖釋放,事務(wù)B等待事務(wù)A的排他鎖釋放,導(dǎo)致死鎖。事務(wù)A和事務(wù)B都被阻塞了。
階段5:SQL Server在幾秒之內(nèi)檢測(cè)到死鎖,會(huì)選擇一個(gè)事務(wù)作為死鎖的犧牲品,終止這個(gè)事務(wù),并回滾這個(gè)事務(wù)所做的操作。在這個(gè)例子中,事務(wù)A被終止,提示信息:事務(wù)(進(jìn)程 ID 53)與另一個(gè)進(jìn)程被死鎖在 鎖 資源上,并且已被選作死鎖犧牲品。請(qǐng)重新運(yùn)行該事務(wù)。
“死鎖 (Dead Lock)”的一些注意事項(xiàng):
(1)如果兩個(gè)事務(wù)沒有設(shè)置死鎖優(yōu)先級(jí),且兩個(gè)事務(wù)進(jìn)行的工作量也差不多一樣時(shí),任何一個(gè)事務(wù)都有可能被終止。
(2)解除死鎖要付出一定的系統(tǒng)開銷,因?yàn)檫@個(gè)過程會(huì)涉及撤銷已經(jīng)執(zhí)行過的處理。
(3)事務(wù)處理的時(shí)間時(shí)間越長(zhǎng),持有鎖的時(shí)間就越長(zhǎng),死鎖的可能性也就越大,應(yīng)該盡可能保持事務(wù)簡(jiǎn)短,把邏輯上可以不屬于同一個(gè)工作單元的操作移到事務(wù)以外。
(4)上面的例子中,事務(wù)A和事務(wù)B以相反順序訪問資源,所以發(fā)生了死鎖。如果兩個(gè)事務(wù)按同樣的順序來(lái)訪問資源,則不會(huì)發(fā)生這種類型的死鎖。在不改變程序的邏輯情況下,可以通過交換順序來(lái)解決死鎖的問題。
關(guān)于分析死鎖的問題,可以參考前面寫的關(guān)于阻塞的內(nèi)容。
?
?
原文鏈接:30分鐘全面解析-SQL事務(wù)+隔離級(jí)別+阻塞+死鎖
參考資料:《T-SQL基礎(chǔ)》
?
作 者:?Jackson0714?
出 處:http://www.cnblogs.com/jackson0714/?
關(guān)于作者:專注于微軟平臺(tái)的項(xiàng)目開發(fā)。如有問題或建議,請(qǐng)多多賜教!?
版權(quán)聲明:本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁(yè)面明顯位置給出原文鏈接。?
特此聲明:所有評(píng)論和私信都會(huì)在第一時(shí)間回復(fù)。也歡迎園子的大大們指正錯(cuò)誤,共同進(jìn)步。或者直接私信我?
聲援博主:如果您覺得文章對(duì)您有幫助,可以點(diǎn)擊文章右下角【推薦】一下。您的鼓勵(lì)是作者堅(jiān)持原創(chuàng)和持續(xù)寫作的最大動(dòng)力!?
?
總結(jié)
以上是生活随笔為你收集整理的30分钟全面解析-SQL事务+隔离级别+阻塞+死锁的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Gif(2)-加载视图-波纹
- 下一篇: 几款开源的图形化Redis客户端管理软件