淺談數據庫事務


概要

    該問講述了我對數據庫事務的一些理解,由於本人才疏學淺,無法保證這些內容的正確性望各位明鑒,希望能夠誤導你,哦是幫到你!
文章后面列出了一些大牛的文章,供大家參考。

事務的定義
    啥是數據庫事務?事務就是….,(此處略去200字)

一個事務的典型例子(中西結合版)
   Mr張三給Mr李四轉帳….(此出略去1000字)

 

到底嘛是事務

  上面分別給出了事務的定義與一個典型場境,相信無論你是菜鳥還是小鳥看到上面這段文時時都會笑而不語 (嘿嘿………哥在這里已經看見你們邪惡的表情了),好了下面我們來點別的。

測試如下問題:
  有一張表MyTable有100萬條記錄,假設使用Delete語句執行刪除全部記錄要20分鍾,你在查詢分析器里輸入如下代碼 “Delete From MyTable” 然后按F5, 執行5分鍾左右你把電源拔了(注意這個動作要猛、准、狠). 問題是:當你重新啟動電腦后,你的MyTable表里還有多少記錄呢?

………
……….
哥在這里休息5分鍾再寫,以便您能完成上面的測試…
………
……………..
您好!很高興您成功啟動了電腦,如果一切正常(關於太陽帶電粒子的問題我們這里先不考慮)您的MyTable表中應該還有100萬條記錄。
這就是傳說中的原子性--A,要么全部刪完,要么一條多沒刪除,順便提下你的任何SQL 即使沒加Begin Tran 都是在事務環境下遠行的,因為上面那個轉帳的例子導致哥許多年來天真的認為不加Begin Tran的SQL語句是沒有事務的,其實
Delete From MyTable

Begin Tran
  Delete from Mytable
  If @@Error >0
    Rollback;
Coomit;
是等價的,前一個語句就是那個天殺的SQL自動事務模式. 好了我們繼續,您在次輸入 Delete From MyTable,注意餓(請確保您的MyTable表中有100萬條記錄),不然一下就刪除沒了。
然后新開個查詢窗口輸入Select * from MyTableA, 接着F5運行,你會發現運行正常,查詢語句顯示MyTableA表中的28條記錄,注意我上面說的是MyTableA, 如果這個時候你執行Select * From MyTable 會顯示什么呢…答案是啥都沒有,只顯示[正在執行查詢....]直接到您的Delete完成,然后顯示的結果是沒有任何記錄的MyTable表。
這就是所謂的隔離性-I,注意上面的Select * From MyTable也是在事務環境下運行的,別拿查詢不當事務餓,而上面的操作我們模擬了兩個並發運行的事務,在Delete完成或取消前,
Select被擋在了外面,以免您的Select語句整出54萬條或別的什么數目的記錄來。
對數據庫來說你執行Delete* From MyTable,后只會有兩種結果,成功或失敗,如果執行成功,那么在執行刪除前語句Select * From MyTable應該顯示100萬,而在刪除執行后所有的查詢只能看到0條記錄,就是不允許你看到它刪除中的54萬或38萬條記錄什么的,這就體現了一致性-C,這個比較抽象請先忽略掉把。
好了說下持久性-D,說到持久我們就會想到硬盤,我們上面的Delete語句執行后被刪除的數據就應該從.mdf文件中清除以實現持久化,不過在執行刪除前后對比.mdf文件大小您可能要失望了,您會發現.mdf沒有變小甚至可能變大,造成功這個現象的原因是,數據庫文件是按8K一頁為單位來組織管理的,數據庫記錄就寫在這些頁上,在您刪除數據后這些頁面沒有被刪除或者說將磁盤空間還給操作系統,只是將這些頁標記為空以便以后使用,不過您確實能觀察到.ldf文件變大了。ACID(事務),可以看作是關系數據庫的一個標准,就像HTML一樣,比方w3c定義了一個A標簽,那么你做瀏覽器開發的就要在程序上實現對a標簽的支持,而通常來說關系數據庫產品要實現ACID標准一般多是通過”日志”與”鎖”技術。

 現在我們來大致來整理下刪除的具體過程

1.查詢語句解析:數據庫接到您的刪除命令后計算出這次刪除要涉及的數據或記錄(頁面)
2.讀取對應頁面到緩存並鎖定:數據庫引擎會讀取這些頁面並加上工享鎖,防止它事務(也就是其他人)進行寫操作。
3.寫日志:將這些將被刪除頁面做個備份寫到log中(這個操作是真真切切要先搞完的),
4.獲取排它鎖禁止其它進程的一切讀寫操作,這步完成時就是你看到上面那個Select 一直處於執行中的原因了.
4.修改緩存中的頁面,並記錄事務點到日志中,釋放排它鎖


至於緩存的頁面啥時候寫到.mdf中就不是事務要關心的事情了,反正讀寫數據都是針對緩存來說,而緩存與數據庫文件之間的一致性,也是通過日志和一個叫檢查點的機制來實現的。

好了針對上面的執行模型再考慮我們那100萬條記錄的刪除情況,當執行到5分鍾時根本沒到事務提交點(.ldf中沒記錄這次刪除完成的點),因此當您重新啟動電腦后,數據庫服務器會根據日志進行一致性檢測,根據日志,將上面那個刪除了一半的操作撤消(因為日志里保存了刪除的頁面,如果有必要系統會據此來還原數據),當然如果您在執行了19分59秒時把電源拔了那么事務可能已經完成並提交了,但是緩存中的頁面沒寫入.mdf,不過這個也不是問題,根據日志文件的記錄,數據庫會再做一次“刪除”,以保證.mdf處於一直狀態,為了避免干擾在完成這些工作前您是無法訪問數據庫的,當然數據庫在進行上面的操作時也會寫日志,防止某些變態的人又突然把電源拔掉。


下面我們看下另外一個場景

有個賣火車票的網站,上面有個可賣火車票的列表,有很多群眾不停的刷新,一旦發現他要的票,就雙擊進入購賣詳細頁(ticketDetail.aspx)然后[提交購買]。
好了那個數據表是 : ticket(Id,status,buyerId,….),字段說明:車票編號、狀態(可買,已買)、購買者編號.

[提交購買]對應的操作是
Update ticket status=’已買’,buyerId=@buyerId where Id=@Id

聰明絕頂的你一看這個可能不行的,這一語句的問題是沒檢測車票是否已被賣出,這樣后面的提交的總是覆蓋掉前面的提交。

改進一

If (Select count(*) from ticket where status=’可買’ and Id=@Id )=1
Update ticket status=’已買’,buyerId=@buyerId where Id=@Id

但是由於群眾太多上面的代碼有些時候會出問題,張三,李四,王五他們同時買Id=1的車票,頁面提示他們全都購買成功了,不過最后事實是李四拿到了票,因為buyerId記錄了李四的編號。

改進二
Update ticket status=’已買’ ,buyerId=@buyerId where Id=@Id and @status=’可買’
我們通過update 返回值是否 >0來判斷是否購買成功,這個方案加入了版本控制字段status(SQL2005中可以使用timestamp來達到同樣的目的),而且語句只有一句了。
現在我們分析下
當張三跟李四同時提交時,出現了A,B兩個並發事務,
緩存中首先加載where條件對應的數據頁,並將更新鎖分配給A或B中的一個,這里假設是A獲得了更新鎖(更新鎖之間互不兼容)
A將U鎖升級成X鎖完成更新操作。而B在A進程完成后再次嘗試獲取對應where條件的數據頁的更新鎖,但是無法找到對應條件的數據頁了,因此更新失敗。

 

一寫參考資料

MSSQL體系結構介紹
http://www.cnblogs.com/yueyue_jwfm/archive/2011/06/22/2087044.html

關於數據庫索引
http://www.codinglabs.org/html/theory-of-mysql-index.html

關於數據庫事務
http://www.cnblogs.com/hustcat/archive/2009/02/14/1390731.html
http://www.cnblogs.com/hustcat/archive/2009/02/26/1398558.html

http://blog.csdn.net/happyflystone/article/details/4604573

http://www.cnblogs.com/lxconan/archive/2011/10/20/2218396.html

 


免責聲明!

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



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