1. 程式人生 > >【資料庫】事務、事務的四大特性(ACID)、三大併發問題、四種鎖、四大隔離級別以及它們的實現

【資料庫】事務、事務的四大特性(ACID)、三大併發問題、四種鎖、四大隔離級別以及它們的實現

資料庫事務

事務(Transaction)是併發控制的基本單位。所謂的事務,它是一個操作序列,這些操作要麼都執行,要麼都不執行,它是一個不可分割的工作單位。
(資料庫引擎innoDB是支援事務的(預設每一條sql語句為一個事務),MyISAM不支援事務。)
在關係資料庫中,一個事務可以是一條SQL語句、一組SQL語句或整個程式。 
 開始事務:BEGIN TRANSACTION(事務)
 提交事務:COMMIT TRANSACTION(事務)
 回滾事務:ROLLBACK TRANSACTION(事務)


事務的四大屬性:ACID

1.原子性(Atomicity): 事務開始後的所有操作,要麼全部做完,要麼全部不做,不可以停滯在中間環節。事務執行過程中若出錯,會回滾到事務開始前的狀態,所有的操作就像沒有發生一樣。事務是一個不可分割的整體。
2.一致性(C

onsistency): 事務開始前和結束後,資料庫的完整性約束沒有被破壞。
3.隔離性(Isolation): 同一時間,只允許一個事務請求同一資料,不同的事務之間彼此沒有任何干擾。(一個事務內部的操作及正在操作的資料必須封裝起來,不被其它企圖進行修改的事務看到)
4.永續性(Durability): 事務完成後,事務對資料庫的所有更新將被儲存到資料庫,不能回滾。
原子性是事務隔離的基礎,隔離性和永續性是手段,最終目的是為了保持資料的一致性。


事務的併發問題(丟失更新、髒讀、不可重複讀、幻讀)

1、丟失更新:如果兩個事務A和B都要更新資料庫一個欄位,並同時獲得相同資料,然後在各自事務中同時修改了該資料,那麼先提交的事務A更新會被後提交事務B的更新給覆蓋掉,這種情況事務A的更新就被覆蓋掉了、丟失了。
2、髒讀

:事務A讀取了事務B更新(但未提交)的資料,然後B回滾操作,那麼A讀取到的資料是髒資料;
3、不可重複讀:事務 A 中多次讀取同一資料,事務 B 在事務A多次讀取的過程中,對資料作了更新並提交,導致事務A多次讀取同一資料時,結果不一致。
4、幻讀:第一個事務對一個表中的資料進行了修改,比如這種修改涉及到表中的“全部資料行”。同時,第二個事務也修改這個表中的資料,這種修改是向表中插入“一行新資料”。那麼,以後就會發生操作第一個事務的使用者發現表中還存在沒有修改的資料行,就好象發生了幻覺一樣。
小結:不可重複讀的和幻讀很容易混淆,不可重複讀側重於修改,幻讀側重於新增或刪除。解決不可重複讀的問題只需鎖住滿足條件的行,解決幻讀需要鎖表


四種鎖

從資料庫系統的角度來看,鎖分為共享鎖和排他鎖,從程式設計師的角度看,鎖分為悲觀鎖和樂觀鎖。
共享鎖(S鎖,  讀鎖)
共享鎖鎖定的資源可以被其它使用者讀取,但其它使用者不能修改它。在SELECT 命令執行時,SQL Server 通常會對物件進行共享鎖鎖定。通常加共享鎖的資料頁被讀取完畢後,共享鎖就會立即被釋放。
排他/獨佔鎖(X鎖,寫鎖)
獨佔鎖鎖定的資源只允許進行鎖定操作的程式使用,其它任何對它的操作均不會被接受。執行資料更新命令,即INSERT、UPDATE 或DELETE 命令時,SQL Server 會自動使用獨佔鎖。但當物件上有其它鎖存在時,無法對其加獨佔鎖。獨佔鎖一直到事務結束才能被釋放。
共享鎖可以一層套一層得上鎖,但排他鎖只能加一個。
樂觀鎖
總是認為不會產生併發問題,每次去取資料的時候總認為不會有其他執行緒對資料進行修改,因此不會上鎖,但是在更新時會判斷其他執行緒在這之前有沒有對資料進行修改,一般會使用版本號機制或CAS操作實現。

version方式:一般是在資料表中加上一個資料版本號version欄位,表示資料被修改的次數,當資料被修改時,version值會加一。當執行緒A要更新資料值時,在讀取資料的同時也會讀取version值,在提交更新時,若剛才讀取到的version值為當前資料庫中的version值相等時才更新,否則重試更新操作,直到更新成功。
悲觀鎖
總是假設最壞的情況,每次取資料時都認為其他執行緒會修改,所以都會加鎖(讀鎖、寫鎖、行鎖等),當其他執行緒想要訪問資料時,都需要阻塞掛起。可以依靠資料庫實現,如行鎖、讀鎖和寫鎖等,都是在操作之前加鎖,在Java中,synchronized的思想也是悲觀鎖。
根據範圍,鎖還可以劃分成行級鎖和表鎖。

四個隔離級別

READ UNCOMMITTED(讀未提交資料):允許事務讀取未被其他事務提交的變更資料,會出現髒讀、不可重複讀和幻讀問題。
READ COMMITTED(讀已提交資料):只允許事務讀取已經被其他事務提交的變更資料,可避免髒讀,仍會出現不可重複讀和幻讀問題。
REPEATABLE READ(可重複讀):確保事務可以多次從一個欄位中讀取相同的值,在此事務持續期間,禁止其他事務對此欄位的更新,可以避免髒讀和不可重複讀,仍會出現幻讀問題。
SERIALIZABLE(序列化/序列化):它要求事務序列化執行,事務只能一個接著一個地執行,但不能併發執行。確保事務可以從一個表中讀取相同的行,在這個事務持續期間,禁止其他事務對該表執行插入、更新和刪除操作,可避免所有併發問題,但效能非常低。
隔離級別越高,越能保證資料的完整性和一致性,但是對併發效能的影響也越大。對於多數應用程式,可以優先考慮把資料庫系統的隔離級別設為Read Committed,它能夠避免髒讀取,而且具有較好的併發效能。儘管它會導致不可重複讀、虛讀和第二類丟失更新這些併發問題,在可能出現這類問題的個別場合,可以由應用程式採用悲觀鎖或樂觀鎖來控制。
MySQL支援四種事務隔離級別,其中REPEATABLE READ為預設事務隔離級別。