1. 程式人生 > >資料庫(三) 事務、引擎與鎖機制

資料庫(三) 事務、引擎與鎖機制

一、事務

事務:事務是併發控制的基本單位。即一組操作序列,這些序列要麼都執行,要麼都不執行,它是一組不可分割的工作單位;事務的提出主要是為了解決併發情況下保持資料一致性的問題,事務具有以下四個基本特徵:

(1)原子性(Atomic):事務中包含的每個操作都被看成一個邏輯單元,這個邏輯單元中的操作要麼全部成功,要麼全部失敗;

(2)一致性(Consistency):事務執行的結果必須是使資料庫從一個一致性狀態變到另一個一致性狀態。也就是事務執行前和事務執行後資料內在的邏輯始終是成立的;比如轉賬前後兩個人的存款總和始終不變;

(3)隔離性(Isolation):一個事務的執行不能被其他事務干擾;

(4)永續性(Durability):一個事務一旦提交,它對資料庫中資料的改變就是永久性的,接下來的操作或故障不應該對其執行結果有影響;

原子性一定能保證一致性嗎?

不,原子性不能完全保證一致性。因為在多個事務並行進行的情況下,即使保證了每個事務的原子性,仍然可能導致資料不一致的結果。比如一個事務的執行結果被另一個事務覆蓋掉了;

事務ACID特性可能遭到破壞的原因有:

(1)多個事務並行執行時,不同的事務操作交叉執行;此時需要保證多個事務的交叉執行不影響這些事務的原子性;

(2)事務在執行過程中被強行停止;此時必須保證被強行終止的事務對資料庫和其他事務沒有任何影響;

二、隔離級別

(1)讀未提交(Read uncommitted):如果一個事務已經開始寫資料,則另外一個事務不允許同時進行寫操作,但允許其他事務讀取此行資料。該隔離級別可以通過排他鎖實現。避免了丟失修改,但是卻可能出現髒讀;

(2)讀已提交(Read committed):讀取資料的事務允許其他事務繼續訪問該行資料,但是未提交的寫事務將會禁止其他事務訪問該行。讀已提交是在讀未提交的基礎上避免了髒讀;但是問題又來了,假如某行記錄有一列值為0,事務T1正在對其進行更新操作,把值update為1,同時事務T2正在對該行記錄進行查詢操作。由於T2不允許讀取T1未提交的資料,此時T2讀到的值為0;假設T1已經提交,但是T2並未結束,並且又對該記錄進行了已查詢,發現此列資料的值變成了1,那麼問題出現了:T2在對同一資料進行相同的兩次查詢操作時,得到的資料的值並不一致,此時發生了不可重複讀的問題;

(3)可重複讀(Repeatable read):讀取資料的事務將會禁止寫事務,但是允許讀事務;而寫資料的事務則禁止其他事務。可以通過共享鎖和排他鎖避免不可重複讀和髒讀,但有時可能出現幻讀;

(4)序列化(Serializable):提供嚴格的事務隔離,它要求事務序列化執行,即事務只能一個接著一個執行,不能併發執行。如果僅僅通過行級鎖是無法實現事務序列化的,必須通過其他機制保證新插入的資料不會被剛執行查詢操作的事務訪問到。序列化是最高的事務隔離級別,同時代價也花費最高,但是效能最低,一般很少用。在該級別下,事務順序執行,不僅可以避免髒讀、不可重複讀,還可以避免幻讀;

隔離級別越高,越能保證資料的完整性和一致性,但是對併發效能的影響也越大。對於多數應用程式,比如Sql Server和Oracle,預設的是讀已提交。對於MySQL,預設隔離級別是可重複讀;

如果不設定隔離級別,則會出現以下問題:

(1)髒讀:一個事務處理過程裡讀取到了另一個未提交的事務中的資料。事務T1修改某一資料並將其寫回磁碟,事務T2讀取同一資料後,T1由於某種原因被撤銷,此時被T1修改過的資料恢復原值,T2讀到的資料就與資料庫中的資料不一致,即T2讀到的資料就為髒資料;

(2)不可重複讀:在同一個事務內,針對同一資料進行多次相同的查詢但是返回的結果不同;當事務T1讀取某一資料之後,事務T2對其進行了修改,當事務T1再次讀取該資料時,得到與前一次不同的值;

(3)丟失修改:事務T1撤銷時把已經提交的事務T2更新的資料覆蓋了是第一類丟失修改;兩個事務T1和T2讀入同一資料並修改,T2提交的結果破壞了T1提交的結果,導致T1的修改被丟失;

(4)幻讀:事務T1按一定條件從資料庫中讀取某些資料記錄之後,事務T2刪除或者插入了一些記錄,當T1再次按相同條件讀取資料時,發現某些記錄消失或者多了出來;

幻讀和不可重複讀的區別:

幻讀和不可重複讀都是在同一次事務中按照相同的條件查詢發現結果不一致,區別在於,不可重複讀的重點在於修改:同樣的條件,讀取出來發現值不一樣;而幻讀的重點在於新增或者刪除:同樣的條件,第一次和第二次讀取出來的記錄數不一樣;

從控制的角度來看,區別更大。對於不可重複讀,只需要鎖住滿足條件的記錄;而對於幻讀,它是由於併發事務增加或者刪除記錄導致的,不能像不可重複讀通過記錄加鎖解決,因為對於新增的記錄根本無法加鎖,需要將事務序列化,才能避免幻讀;

三、資料庫正規化

第一正規化1NF:屬性不可分;

第二正規化2NF:所有屬性完全依賴於主鍵;

第三正規化3NF:消除傳遞依賴;

正規化具有包含關係,比如第三正規化包含第二正規化,第二正規化包含第一正規化。

第一正規化—>第二正規化---->第三正規化

(從左到右)資料冗餘越來越少,查詢越來越複雜; (從右到左)有資料冗餘,但查詢簡單;

四、引擎

Innodb引擎:提供了對資料庫ACID事務的支援,並且實現了SQL標準的四種隔離級別,該引擎提供了行級鎖和外來鍵約束。該引擎沒有儲存表的行數,有的時候需要掃描全表。當需要使用資料庫,且併發性較高時,使用Innodb會提升效率。但是使用行級鎖也不是絕對的,如果在執行一個SQL語句時MySQL不能確定要掃描的範圍,Innodb同樣會鎖全表;

MyIASM引擎:不提供對資料庫事務的支援,也不支援行級鎖和外來鍵。儲存了表的行數,如果表的讀操作遠遠多於寫操作且不需要資料庫事務的支援,那麼MyIASM是首選;

區別: (1)Innodb支援事務,而MyIASM不支援;

(2)Innodb支援外來鍵,而MyIASM不支援;

(3)Innodb支援行級鎖,MyIASM支援表級鎖;

(4)Innodb不支援全文索引,MyIASM支援;

五、鎖模式

共享鎖(S鎖):若事務T對資料A加上S鎖,則事務T可以讀A但是不能修改A,其他事務只能對A加S鎖而不能加X鎖,直到T釋放A上的S鎖為止。這就保證了其他事務可以讀A,但在T釋放A上的S鎖之前不能對A進行任何修改;

排他鎖(X鎖):若事務T對資料A加上X鎖,則只允許T讀取和修改A,其他任何事務都不能對A加任何型別的鎖,直到T釋放A上的鎖為止。這就保證了其他事務在T釋放A上的鎖之前不能再讀取和修改A;

死鎖出現:如果兩個事務獲得了資源上的S鎖,然後試圖同時更新資料,則兩個事務都要轉換S鎖為X鎖,並且每個事務都等待另一個事務釋放S鎖的情況下,就產生了死鎖;

更新鎖(U鎖):可以避免這種潛在的死鎖問題。一次只有一個事務可以獲得資源的U鎖,如果事務修改資源,則U鎖轉換為X鎖,否則,轉換為S鎖;

悲觀鎖:每次去拿資料的時候都認為別人會修改,所以每次在拿資料的時候都會上鎖,這樣別人想拿這個資料就會block直到它拿到鎖;

樂觀鎖:假設認為資料一般情況下不會造成衝突,所以在資料進行提交更新的時候,才會正式對資料的衝突與否進行檢測,如果發現衝突了,則讓返回使用者錯誤的資訊,讓使用者決定如何去做。一般的實現樂觀鎖的方式就是記錄資料版本;