1. 程式人生 > 其它 >事務與併發控制

事務與併發控制

一  事務 

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,出現死鎖現象。

過程是這樣子的:

  1. A更新學生表,請求學生表的排他鎖,成功。
  2. B更新課程表,請求課程表的排他鎖,成功。
  3. 5秒過後
  4. A更新課程表,請求課程表的排它鎖,由於B佔用著課程表的排它鎖,等待。
  5. B更新學生表,請求學生表的排它鎖,由於A佔用著學生表的排它鎖,等待。

這樣相互等待對方釋放資源,造成資源讀寫擁擠堵塞的情況,就被稱為死鎖現象,也叫做阻塞。而為什麼會產生,上例就列舉出來了。

然而資料庫並沒有出現無限等待的情況,是因為資料庫搜尋引擎會定期檢測這種狀況,一旦發現有情況,立馬選擇一個事務作為犧牲品。犧牲的事務,將會回滾資料。有點像兩個人在過獨木橋,兩個無腦的人都走在啦獨木橋中間,如果不落水,必定要有一個人給退回來。這種相互等待的過程,是一種耗時耗資源的現象,所以能避則避。