【云圖說】第132期 小云妹帶您快速玩轉RDS實例操作(2)——刪除與退訂
809
2025-03-31
上一期我們聊到了事務的臟讀、不可重復讀、幻讀及隔離級別。實現這些隔離級別的底層機制是什么呢?這一期我們來扒一扒。
并發控制的主要技術有封鎖(locking)(編者注:是指悲觀鎖)、時間戳(timestamp)、樂觀控制法(optimistic sheduler)(編者注:是指樂觀鎖)和多版本并發控制(multi-version concurrency control,MVCC)等。
封鎖(悲觀鎖)
鎖的共存關系
封鎖是實現并發控制的一個非常重要的技術。基本的封鎖類型有兩種:排他鎖(X鎖)和共享鎖(S鎖)。
排它鎖又稱寫鎖,若事務T1對數據對象A加上X鎖,則只允許T1讀取和修改A,其他任何事務都不能再對A加任何類型的鎖直到T1釋放A上的鎖為止。
共享鎖又稱讀鎖,若事務T1對數據對象A加上S鎖,則事務T1可以讀A但不能修改A,其他事務只能再對A加S鎖,而不能加X鎖。
鎖間的共存關系如下表所示:(x表示是排它鎖(Exclusive),s表示共享鎖(Share),-表示不加鎖。Y表示可以共存,N表示不能共存)
上圖表示可以共存的鎖,如,第二行表示,一個事務T1給某數據加了X鎖,則事務T2就不能再給那數據加X鎖了,同時也不能再加S鎖了,只有到T1事務提交完成之后,才可以。默認來說,當sql腳本修改更新某條記錄的時候,會給該條記錄加X鎖,讀的話加的是S鎖。
封鎖協議
在運用 排他鎖 和 共享鎖 對數據對象加鎖時,還需要約定一些規則,例如何時申請 排他鎖 或 共享鎖、持鎖時間、何時釋放等。稱這些規則為封鎖協議(Locking Protocol)。對封鎖方式規定不同的規則,就形成了各種不同的封鎖協議。不同的封鎖協議對應不同的隔離級別。
在標準SQL規范中,定義了4個事務隔離級別,不同的隔離級別對事務的處理不同:
A.?Read uncommited:允許臟讀取,但不允許丟失修改。
對應一級封鎖協議:事務T在修改數據R之前必須先對其加X鎖,直到事務結束才釋放。事務結束包括正常結束(COMMIT)和非正常結束(ROLLBACK)。
B.?Read Committed:允許不可重復讀取,但不允許臟讀取和丟失修改。這可以通過“瞬間共享讀鎖”和“排他寫鎖”實現。
對應二級封鎖協議:一級封鎖協議加上事務T在讀取數據R之前必須先對其加S鎖,讀完后即可釋放S鎖(瞬間S鎖)。
C.?可重復讀取(Repeatable Read):禁止不可重復讀取和臟讀取和丟失修改,但是有時可能出現幻影數據。這可以通過“共享讀鎖”和“排他寫鎖”實現。
對應三級封鎖協議:一級封鎖協議加上事務T在讀取數據R之前必須先對其加S鎖,直到事務結束才釋放。
D.?序列化(Serializable):提供嚴格的事務隔離。它要求事務序列化執行,事務只能一個接著一個地執行,但不能并發執行。
四級封鎖協議是對三級封鎖協議的增強,其實現機制也最為簡單,直接對事務中所讀取或者更改的數據所在的表加表鎖,也就是說,其他事務不能讀寫該表中的任何數據。這樣所有的臟讀,不可重復讀,幻讀,都得以避免!
活鎖&死鎖
并發控制會造成活鎖和死鎖。
活鎖指的是T1封鎖了數據R,T2同時也請求封鎖數據R,T3也請求封鎖數據R,當T1釋放了鎖之后,T3會鎖住R,T4也請求封鎖R,則T2就會一直等待下去。解決這種活鎖等待的處理辦法是采用“先來先服務”策略。
死鎖就是我等你,你又等我,雙方就會一直等待下去,比如:T1封鎖了數據R1,正請求對R2封鎖,而T2封住了R2,正請求封鎖R1,這樣就會導致死鎖。
死鎖這種沒有完全解決的方法,只能盡量預防。預防的方法有:①一次封鎖法,指的是一次性把所需要的數據全部封鎖住,但是這樣會擴大了封鎖的范圍,降低系統的并發度;②順序封鎖法,指的是事先對數據對象指定一個封鎖順序,要對數據進行封鎖,只能按照規定的順序來封鎖,但是這個一般不大可能的。
另外,系統如何判斷出現死鎖呢,畢竟出現死鎖不能一直干等下去,要及時發現死鎖同時盡快解決出現的死鎖,診斷和判斷死鎖有兩種方法,一是超時法,二是等待圖法。超時法就是如果某個事物的等待時間超過指定時限,則判定為出現死鎖;等待圖法指的是如果事務等待圖中出現了回路,則判斷出現了死鎖。對于解決死鎖的方法,只能是撤銷一個處理死鎖代價最小的事務,釋放此事務持有的所有鎖,同時對撤銷的事務所執行的數據修改操作必須加以恢復。
行級鎖&表級鎖
最后,說下行級鎖和表級鎖。
行級鎖是一種排他鎖,防止其他事務修改此行;在使用以下語句時,Oracle會自動應用行級鎖:INSERT、UPDATE、DELETE、SELECT … FOR UPDATE [OF columns] [WAIT n | NOWAIT]; SELECT … FOR UPDATE語句允許用戶一次鎖定多條記錄進行更新 使用COMMIT或ROLLBACK語句釋放鎖
表級鎖又分為5類:
行共享 (ROW SHARE) – 禁止其他事務使用排他鎖鎖定表。
行排他(ROW EXCLUSIVE) – 禁止其他事務使用排他鎖和共享鎖
共享鎖(SHARE) - 鎖定表,對記錄只讀不寫,多個用戶可以同時在同一個表上應用此鎖。
共享行排他(SHARE ROW EXCLUSIVE) – 比共享鎖更多的限制,禁止其他事務使用共享鎖及更高的鎖。
排他(EXCLUSIVE) – 限制最強的表鎖,僅允許其他用戶查詢該表的行。禁止修改和鎖定表。
以上內容參考自:
http://blog.sina.com.cn/s/blog_548bd2090100ir7k.html
https://www.cnblogs.com/ismallboy/p/5574006.html
樂觀控制法(樂觀鎖)
https://www.jianshu.com/p/d2ac26ca6525
https://blog.csdn.net/xlgen157387/article/details/47906553
上面兩篇帖子對悲觀鎖和樂觀鎖的概念介紹、使用場景及對比介紹的比較全面了,大家可以查閱。
mysql樂觀鎖和悲觀鎖詳解這篇貼中也通過MySQL的舉例對悲觀鎖和樂觀鎖進行了生動說明,其中有我們比較喜歡的總結信息:
樂觀鎖,簡單地說,就是從應用系統層面上做并發控制,去加鎖。
實現樂觀鎖常見的方式:版本號version。
應用程序在數據表中增加版本號字段,每次對一條數據做更新之前,先查出該條數據的版本號,每次更新數據都會對版本號進行更新。在更新時,把之前查出的版本號跟庫中數據的版本號進行比對,如果相同,則說明該條數據沒有被修改過,執行更新。如果比對的結果是不一致的,則說明該條數據已經被其他人修改過了,則不更新,客戶端進行相應的操作提醒。
悲觀鎖,簡單地說,就是從數據庫層面上做并發控制,去加鎖。
悲觀鎖的實現方式有兩種:共享鎖(讀鎖)和排它鎖(寫鎖)
時間戳
時間戳就是在數據庫表中單獨加一列時間戳,比如“TimeStamp”,每次讀出來的時候,把該字段也讀出來,當寫回去的時候,把該字段加1,提交之前 ,跟數據庫的該字段比較一次,如果比數據庫的值大的話,就允許保存,否則不允許保存,這種處理方法雖然不使用數據庫系統提供的鎖機制,但是這種方法可以大大提高數據庫處理的并發量,因為這種方法可以避免了長事務中的數據庫加鎖開銷(操作員A 和操作員B操作過程中,都沒有對數據庫數據加鎖),大大提升了大并發量下的系統整體性能表現。
這樣看來時間戳也是一種樂觀控制法。
查詢了一些其他博主的博客,也佐證了時間戳是實現樂觀鎖的一種辦法這個觀點。部分博客貼在下面。
https://blog.csdn.net/crazy_scott/article/details/90452113
https://blog.csdn.net/sanshi_lxl/article/details/83740635
https://blog.csdn.net/webdesman/article/details/6360633?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-9.add_param_isCf&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-9.add_param_isCf
MVCC
實現數據庫的并發訪問控制,最簡單的方式就是加鎖訪問。由于加鎖會將讀寫操作串行化,所以不會出現不一致的狀態。但是,讀操作會被寫操作阻塞,大幅降低讀性能。MVCC(Multiversion Concurrency Control多版本并發控制)能解決采用鎖帶來的寫操作堵塞讀操作的并發問題。MVCC是通過使用數據的多個版本保證并發讀寫不沖突的一種機制。不同的數據庫會有不同的實現。(參見https://www.jdon.com/repository/database-mvcc.html?)
MVCC的兩種不同實現方式
第一種實現方式是將數據記錄的多個版本保存在數據庫中,當這些不同版本數據不再需要時,垃圾收集器回收這些記錄。這個方式被postgresql和Firebird/Interbase采用,SQL Server使用的類似機制,所不同的是舊版本數據不是保存在數據庫中,而保存在不同于主數據庫的另外一個數據庫tempdb中/
第二種實現方式只在數據庫保存最新版本的數據,但是會在使用undo時動態重構舊版本數據,這種方式被Oracle和MySQL/InnoDB使用。
有關MySQL和PG的MVCC機制介紹可參考下面的兩篇博客。
Mysql中MVCC的使用及原理詳解
postgresql系列_MVCC機制以及鎖機制理解
總結
經過上面的多方查閱和梳理,我們可以做如下總結。
控制數據庫事務并發控制的機制有悲觀控制法和樂觀控制法。
悲觀控制法對應的是數據庫的鎖機制。悲觀鎖只需要用戶或應用程序設置了DBMS所采用的隔離級別,DBMS后臺就會通過鎖來實現隔離級別,避免臟讀、不可重復讀或幻讀。
樂觀鎖可以由應用程序通過給數據庫表加version字段或時間戳的方式實現,也可以由數據庫的MVCC機制實現。
留個疑問在本期,待后續考證:數據庫既支持鎖機制,也支持MVCC機制,那如何設置來選擇這兩種不同的機制呢?
MySQL 數據庫
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。