事務與併發控制
一 事務
1概念
巨集觀上看,事務就是一次完整的操作過程;程式角度看,事務是使用者自定義的資料作業系統,由多條命令組成,內部所有命令語句要被當成一個整體去要麼全部被執行,要麼全部不執行(前面已經執行部分的命令都撤銷—回滾)
以前的後觸發器的機制就是事務
如:a賬號要給B賬號轉N元錢,由兩步完成:
A賬戶號-n
B賬號+n
事務對待:上兩步要麼兩句都成功,要麼一句都不執行。
2 事務的主要特徵
(1)原子性:指事務是資料的邏輯工作單位,每一步都要被執行,不可再分。
(2)一致性:指事務的執行結果必須從資料庫一個狀態確定變到另一個狀態。(對資料的改變是確切的,事務常用於資料修改類的操作)
(3)隔離性:在執行一個事務的過程,不能被其它事務干擾。(即使多個事務要使用到共同的資料,那也要依次進行,不交叉併發處理)
(4)永續性:事務一旦被提交執行,它的操作對資料庫的改變影響是永遠的(事務一旦完成後,不可能回滾)
3 事務分類
(1)隱式事務:不需要人為指定和設定,操作過程當事務對待。SQL本身提供的這些資料操縱語句自動當成事務對待的。如:
Update 學生表 set 性別=’男’,身高=身高+0.5 where 姓名=’楊春’
詳細過程:在表中定位到楊春記錄---複製資料到deleted臨時表—再刪除本記錄---再複製到inserted臨時表---再修改inserted臨時表的資料性別,身高---再把inserted資料存放到物理表
(2)顯示事務:程式設計師寫程式碼時,明確顯示地寫出事務的開始和結束標記
一般事務是定義在過程中的,有可能把整個過程的程式碼當成事務,也可以把過程的部分連續位置的程式碼當成事務。
明尾暗頭—事務的開頭是隱含的,但結束有明確標記(常用於整個過程的開頭號到當前程式碼為事務)
四大事務專用語句
(1) begin transaction: 事務開始
(2) commit transaction: 事務到此已經成功執行,資料已經處理完成,後繼語句可不當事務對待。
(3) rollback transaction: 整數處理過程出錯了,回滾到沒有處理之前的資料狀態,或回滾到事務內部的儲存點。
(4) save transaction: 事務內部的儲存點,如果事務內有此語句,當事務回滾時,可以不全部回滾,只回滾到本位置
例1:模擬上面銀行轉賬
Begin tran
Update 支付表 set 總額=總額-n where 賬號=’a’
Update 支付表 set 總額=總額+n where 賬號=’b’
Commit tran
說明:最簡單的事務。最前面的begin tran可省—明尾暗頭
例:
begin tran
update 學生表 set 年齡=20 where 姓名='楊春'
update 學生表 set 身高=-1 where 名字='李大方'
commit tran
再例:
create proc mypro13 as --事務一般是寫在過程/函式中
begin
begin tran --顯示事務
begin try
insert into 學生表(學號,姓名,年齡) values('2011','aaa',17)
insert into 學生表(學號,姓名,年齡) values('2012','bbb','xx')
insert into 學生表(學號,姓名,年齡) values('2013','ccc',19)
end try
begin catch
select ERROR_NUMBER() as errnum,ERROR_SEVERITY() as err2,ERROR_LINE() as err3,ERROR_PROCEDURE() as eer4, ERROR_MESSAGE() as err4 –-學會錯誤捕獲
-- select ERROR_MESSAGE as err6
if(@@TRANCOUNT>0)
rollback tran
end catch
if(@@TRANCOUNT>0) –如果已經執行到此了,說明上面事務已經完成了
commit tranend
end
execute mypro13
課後:試著改成函式,用函式返回錯誤資訊的字串,被第三方語言來呼叫
例:設定儲存點
create proc mypro16 as
begin
begin tran
begin try
insert into 學生表(學號,姓名,年齡) values('2011','aaa',17)
save tran mysave1 --加一個儲存點,如果後面出錯,則可滾回到此為止
insert into 學生表(學號,姓名,年齡) values('2012','bbb','xx')
insert into 學生表(學號,姓名,年齡) values('2013','ccc',19)
end try
begin catch
select ERROR_NUMBER() as errnum,ERROR_SEVERITY() as err2,ERROR_LINE() as err3,ERROR_PROCEDURE() as eer4, ERROR_MESSAGE() as err4
-- select ERROR_MESSAGE as err6
if(@@TRANCOUNT>0)
rollback tran mysave1
end catch
if(@@TRANCOUNT>0)
commit tran
-- rollback tran mysave1
end
execute mypro16
例:在一臺計算機上模仿兩個程式呼叫同一個資料,觀察資料變化。
如果是成千上萬使用者這樣訪問同一個資料,會不會出錯??---資料在多使用者多工情況下併發控制。
在SQL中分別使用三個查詢視窗,模仿網路上多使用者針對訪問同一個資料
程式碼1: while(5>1)
begin
begin tran
update 學生表 set 年齡=年齡+1 where 姓名='楊春'
waitfor delay '00:00:05'
commit tran
end
程式碼2:
while(5>1)
begin
begin tran
update 學生表 set 年齡=年齡+10 where 姓名='楊春'
waitfor delay '00:00:05'
commit tran
end
程式碼3:
select * from 學生表
二 事務的併發控制-鎖
1 引入
例:在一臺計算機上模仿兩個程式呼叫同一個資料,觀察資料變化。
如果是成千上萬使用者這樣訪問同一個資料,會不會出錯??---資料在多使用者多工情況下併發控制。
在SQL中分別使用三個查詢視窗,模仿網路上多使用者針對訪問同一個資料
程式碼1: while(5>1)
begin
begin tran
update 學生表 set 年齡=年齡+1 where 姓名='楊春'
waitfor delay '00:00:05'
commit tran
end
程式碼2:
while(5>1)
begin
begin tran
update 學生表 set 年齡=年齡+10 where 姓名='楊春'
waitfor delay '00:00:05'
commit tran
end
程式碼3:
select * from 學生表
引例2:開啟兩個查詢視窗,把下面的語句,分別放入2個查詢視窗,在5秒內執行2個事務模組。
begin tran
update 學生表 set 年齡=年齡+10 where 姓名='楊春'
waitfor delay '0:0:5'
update 課程表 set 學時=學時+10 where 課程號='101'
commit tran
select * from 學生表 where 姓名='楊春'
select * from 課程表 where 課程號='101'
begin tran
update 課程表 set 學時=學時+1 where 課程號='101'
waitfor delay '0:0:5'
update 學生表 set 年齡=年齡+1 where 姓名='楊春'
commit tran
select * from 學生表 where 姓名='楊春'
select * from 課程表 where 課程號='101'
為什麼呢,下面我們看看鎖
2 概念
併發事務成敗皆歸於鎖——鎖定
從程式設計師的角度看:分為樂觀鎖和悲觀鎖。
樂觀鎖:完全依靠資料庫來管理鎖的工作。
悲觀鎖:程式設計師自己管理資料或物件上的鎖處理。
在多使用者都用事務同時訪問同一個資料資源的情況下,就會造成以下幾種資料錯誤。
- 更新丟失:多個使用者同時對一個數據資源進行更新,必定會產生被覆蓋的資料,造成資料讀寫異常。
- 不可重複讀:如果一個使用者在一個事務中多次讀取一條資料,而另外一個使用者則同時更新啦這條資料,造成第一個使用者多次讀取資料不一致。
- 髒讀:第一個事務讀取第二個事務正在更新的資料表,如果第二個事務還沒有更新完成,那麼第一個事務讀取的資料將是一半為更新過的,一半還沒更新過的資料,這樣的資料毫無意義。
- 幻讀/幽靈資料:第一個事務讀取一個結果集後,第二個事務,對這個結果集經行增刪操作,然而第一個事務中再次對這個結果集進行查詢時,資料發現丟失或新增。
然而鎖定,就是為解決這些問題所生的,他的存在使得一個事務對他自己的資料塊進行操作的時候,而另外一個事務則不能插足這些資料塊。這就是所謂的鎖定。
鎖定從資料庫系統的角度大致可以分為6種:
- 共享鎖(S):還可以叫他讀鎖。可以併發讀取資料,但不能修改資料。也就是說當資料資源上存在共享鎖的時候,所有的事務都不能對這個資源進行修改,直到資料讀取完成,共享鎖釋放。
- 排它鎖(X):還可以叫他獨佔鎖、寫鎖。就是如果你對資料資源進行增刪改操作時,不允許其它任何事務操作這塊資源,直到排它鎖被釋放,防止同時對同一資源進行多重操作。
- 更新鎖(U):防止出現死鎖的鎖模式,兩個事務對一個數據資源進行先讀取再修改的情況下,使用共享鎖和排它鎖有時會出現死鎖現象,而使用更新鎖則可以避免死鎖的出現。資源的更新鎖一次只能分配給一個事務,如果需要對資源進行修改,更新鎖會變成排他鎖,否則變為共享鎖。
- 意向鎖:SQL Server需要在層次結構中的底層資源上(如行,列)獲取共享鎖,排它鎖,更新鎖。例如表級放置了意向共享鎖,就表示事務要對錶的頁或行上使用共享鎖。在表的某一行上上放置意向鎖,可以防止其它事務獲取其它不相容的的鎖。意向鎖可以提高效能,因為資料引擎不需要檢測資源的每一列每一行,就能判斷是否可以獲取到該資源的相容鎖。意向鎖包括三種類型:意向共享鎖(IS),意向排他鎖(IX),意向排他共享鎖(SIX)。
一個表中,在載入的同時,不允許其它程序訪問該表。
這些鎖之間的相互相容性,也就是,是否可以同時存在。
3 死鎖
什麼是死鎖,為什麼會產生死鎖。我用 “事務把死鎖給整出來啦” 標題下的兩個事務產生的死鎖來解釋應該會更加生動形象點。
例子是這樣的:
第一個事務(稱為A):先更新學生表 --->>停頓5秒---->>更課程表
第二個事務(稱為B):先更新課程表--->>停頓5秒---->>更新學生表
先執行事務A----5秒之內---執行事務B,出現死鎖現象。
過程是這樣子的:
- A更新學生表,請求學生表的排他鎖,成功。
- B更新課程表,請求課程表的排他鎖,成功。
- 5秒過後
- A更新課程表,請求課程表的排它鎖,由於B佔用著課程表的排它鎖,等待。
- B更新學生表,請求學生表的排它鎖,由於A佔用著學生表的排它鎖,等待。
這樣相互等待對方釋放資源,造成資源讀寫擁擠堵塞的情況,就被稱為死鎖現象,也叫做阻塞。而為什麼會產生,上例就列舉出來了。
然而資料庫並沒有出現無限等待的情況,是因為資料庫搜尋引擎會定期檢測這種狀況,一旦發現有情況,立馬選擇一個事務作為犧牲品。犧牲的事務,將會回滾資料。有點像兩個人在過獨木橋,兩個無腦的人都走在啦獨木橋中間,如果不落水,必定要有一個人給退回來。這種相互等待的過程,是一種耗時耗資源的現象,所以能避則避。