SQL SERVER之事務

 在實際對數據庫的使用中,會出現多個用戶同時對某一張表進行操作,當多個用戶在同一時間對同一張數據表進行讀取或者修改操作時,若處理不當就有可能發生衝突問題。爲了解決這樣的問題,就需要使用事務的控制和管理機制。

 

事務

   單個邏輯工作單元執行操作的集合,也可以看作是多條語句封裝的結果。通過事務可以保證數據表中數據的一致性。

事務的特性

 

 

原子性

   是指事務中所有的執行操作,要麼全部成功,要麼不執行。如在商場購物中,管理員同時對用戶進行充值操作。

  1. 修改賬戶A中的現金數。

  2. 修改賬戶A中的現金數

    如果在執行第一個SQL語句之後,第二個語句之前,突然斷電了該如何辦?

     

一致性

所謂的事務的一致性,是指一個事務操作執行完周,數據庫中的數據必須處於合法一致的狀態。例如如果賬戶A給賬戶B轉賬1000元后,那麼賬戶A應該減少1000,賬戶B應該增加1000,但是兩人的錢數總和還是一致的,應該處於合法的狀態中。

 隔離性

    就是事務看到的數據庫中的數據要麼是這個事務被修改之前的狀態,要麼是被修改之後的狀態。

 

 持久性

   如果一個事務被成功地修改,其結果在數據庫中不會因爲軟件,硬件等故障而改變,數據會永久的保留下來

 

 

控制事務的流程

START TRANSACTION(開始事務)COMMIT(提交)ROLLBACK(事務回滾)

 

顯示開始一個事務

 

使用START TRANSACTION或者BEGIN語句可以顯示開始一個新的事務

 

語法規則:

START TRANSACTION{事務名}

     

隱式開始一個事務

 

SQL語句中的第一條語句開始就表示隱式開始了一個新的事務

 

提交事務

 

顯示提交:COMMIT[事務名]

[sql] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片
  1. start transaction   --開始事務  
  2. insert into accounttable values('A',5000)  
  3. update accounttable set cashvalue=cashvalue+1000  
  4. where accountuser ='A'  
  5. commit   --提交事務  

(這裏把兩條語句封裝到了一起,一個是插入語句,一個是更新語句)

 

隱式提交

 

是指通過使用SQL語句就可以完成事務的提交,如果執行了CREATE TABLE,DROP TABLE

 

操作就會自動提交事務。

 

 

自動提交

 自動提交指通過設置AUTOCOMMIT命令完成事務的提交。

 

自動提交語句

SET AUTOCOMMIT=1

SET AUTOCOMMIT ON

 

關閉自動提交方式

SET AUTOCOMMIT=0

SET AUTOCOMMIT OFF

 

 

回滾事務

  是表示當事務執行失敗時,數據庫恢復到該事務操作之前的那一個合法狀態中,並撤銷對該表的一些操作。同時在數據庫中還可以設置保存點,可以當發生意外時,回滾到保存點狀態。

[sql] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片
  1. begin try  
  2. begin transaction  
  3. --使用try語句進行捕捉錯誤  
  4.   
  5. insert into accounttable values(5000,'A')  
  6. save tran a1  
  7. insert into accounttable values(5000,'C')  
  8. commit  
  9. end try  
  10.   
  11. begin catch  
  12. --當發生錯誤時,進行回滾  
  13. rollback tran a1  
  14. end catch  

 

(這段SQL語句,分別設置了2個保存點,分別是A1,A2,並使用rollback回滾機制)

 

併發事務的工作流程

  1. 用戶user1使用SELECT語句查詢到accounttable賬戶中有5000元,但是由於某些原因,用戶user1並沒有提取現金

  2. 用戶2也通過SELECT語句查詢到accounttable賬戶中有5000元,此時他開始事務,從賬戶中提取出了1000元,但是並沒有提交事務。此時用戶user1查到的還是5000

  3. 在用戶user1查詢到賬戶餘額爲5000後,希望全部提取出來,但是由於user2的事務並沒有提交,所以用戶1並不能提取

  4. 用戶2提取1000後,提交了事務,此時剩餘4000

  5. 用戶1終於可以執行提取5000元操作了,可是此時只有4000元,操作將被撤銷,回滾到他之前的操作狀態

  6. 如果用戶1還想全部取出的話,就必須開始一個新的事務

     

通過以上的敘述我們發現事物併發處理中存在的問題

 

  1. 讀髒數據

    是指那些已經更改但還是沒有被提交的數據。如用戶2取走1000後,用戶1提取5000時,會發現賬戶的餘額不足。

  2. 不能重複讀

    同一個事物中在多次執行時,由於其他事務對其做的修改或者刪除等更新操作時,使得每次查詢時返回的數據結果都不相同。如上例中,用戶2已經取出1000元,而用戶1查詢到的仍然是5000

     

     

  3. 幻想讀

    是指讀取了其他事務中執行完插入或者更新操作後的錯誤數據。

     

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

 

 

事務的隔離級別

1.READUNCOMMITTED:未提交讀。正如上面敘述的那樣,由於用戶2沒有提交,所以用戶1還是讀出了5000元。在READ UNCOMMITED隔離級別下,會隔離UPDATE語句,但不隔離SELECT語句。它的隔離級別最低。

 

 

 2.READ COMMITTED:提交讀。給隔離級別在讀取數據時對其加共享鎖,可以避免讀髒數據,但是在READ COMMITTED隔離級別下,事務在結束前更改可以更改數據,因此不能避免不能重複讀或者幻想讀。

 

3.REPEATABLE READ:可重複讀。在該隔離級別中會將查詢中使用的所有數據鎖定,防止其他用戶對改數據進行操作,可以避免產生不能重複讀。

4.SERIALLZABLE :可串行化。該隔離級別在事務提交之前,會鎖定整個數據表,防止其他用戶對數據進行增加、刪除和修改等更新操作。

 

下面是有關四種隔離級別允許不同的類型的行爲

 

隔離級別

髒讀

不可重複讀取

幻象

未提交讀

提交讀

可重複讀

可串行讀

 

 

牛刀小試

下面的例子均以下表爲例

                                     Table

Money

Int

Name

Char(4)

 

 

 

髒讀操作

 由上面的表格可知,髒讀操作發生在未提交數據時,正如我們的例子中,用戶1查到了5000的情況。如下操作

第一個連接語句

[sql] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片
  1. <span style="font-family:SimSun;font-size:18px;">begin tran  
  2. update table set money=103 where name='A'  
  3. waitfor delay '00:00:10' --等待10秒  
  4.   
  5. update table set money=104 where name='A'  
  6. commit tran</span>  

接着馬上執行第二個連接語句

[sql] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片
  1. <span style="font-family:SimSun;font-size:18px;">set transaction isolation level read uncommitted  
  2. begin tran  
  3. select money from table where name='A'  
  4. commit tran</span>  

最終結果是103,而不是104,這就是髒數據,可知如果我們把第二個連接中的事務隔離級別設置爲 READ MOMMITEDREPEATABLE READ或者SERIALLZABLE就可以避免這種情況發生。

 

非重複讀操作

第一個連接語句

[sql] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片
  1. <span style="font-family:SimSun;font-size:18px;">set transaction isolation level read committed  
  2. --或者是set transaction isolation level read uncommitted  
  3.   
  4. begin tran  
  5. select money from table where name='A'  
  6. waitfor delay '00:00:10'  --等待10秒  
  7. select money from table where name='A'  
  8. commit tran  
  9. </span>  

接着馬上執行第二個連接語句

 

[sql] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片
  1. <span style="font-family:SimSun;font-size:18px;">begin tran  
  2. update table set money=10 where name='A'  
  3. commit tran</span>  

我們發現第一個連接中兩次返回賬號的餘額不一樣,第一次是100,第二次是10,這就是典型的“非重複讀”的問題

根據上表所示,如果把事務的隔離級別設置爲REPEATABLE READ或者SERIALLZABLE可以防止此類問題

 

幻象讀

根據上表所示,當事務的隔離級別爲READ COMMITTEDREAD UNCOMMITTEDREPEATABLE READ時就會發生幻象

 

先看下面的例子(賬戶餘額爲100

第一個連接語句

[sql] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片
  1. <span style="font-family:SimSun;font-size:18px;">begin tran  
  2. select * from table  
  3. waitfor delay '00:00:10'--等待10秒  
  4. select * from table  
  5. commit tran</span>  

接着馬上執行第二個連接語句

 

[sql] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片
  1. <span style="font-family:SimSun;font-size:18px;">begin tran  
  2. insert into table values(300,'a')  
  3. commit tran</span>  

我們發現兩次查詢的結果不一樣,這就是典型的幻象讀問題,可知解決方法爲把隔離級別設置爲SERIALLZABLE即可。

 

 

小結:在實際應用的時候,採用何種隔離級別應視具體情況而定。

發佈了7 篇原創文章 · 獲贊 0 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章