您好,登錄后才能下訂單哦!
本篇內容介紹了“MySQL的事務隔離級別介紹”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
數據庫事務(簡稱:事務)是數據庫管理系統執行過程中的一個邏輯單位,由一個有限的數據庫操作序列構成。—— 維基百科
事務的概念看上去不難,但是需要注意以下幾個點:
1、首先,事務就是要保證一組數據庫操作,要么全部成功,要么全部失敗;
2、在 MySQL 中,事務支持是在引擎層實現的;
3、并不是所有引擎都支持事務,如 MyISAM 就不支持,InnoDB 就支持;
今天,我們的主角是隔離性,隔離性是指當多個用戶并發操作數據庫時,數據庫為每一個用戶開啟不同的事務,這些事務之間相互不干擾,相互隔離。
如果事務之間不是互相隔離的,可能將會出現以下問題。
臟讀(dirty read),簡單來說,就是一個事務在處理過程中讀取了另外一個事務未提交的數據。
這種未提交的數據我們稱之為臟數據。依據臟數據所做的操作肯能是不正確的。
還記得上節中我們提到的 dirty page 嗎?這種臨時處理的未提交的,都是「臟」的。
舉例
但是,若該事務未提交成功,最終所有操作都會回滾,小編看到的一分錢也只是鏡花水月。比如,你給小編贊賞 1 分錢,整個事務需要兩個步驟:
①給小編賬號加一分錢,這時小編看到了,覺得很欣慰;
②你的賬號減一分錢;
不可重復讀(non-repeatable read),是指一個事務范圍內,多次查詢某個數據,卻得到不同的結果。
在第一個事務中的兩次讀取數據之間,由于第二個事務的修改,第一個事務兩次讀到的數據可能就是不一樣的。
舉例
接著上一個例子,假設你真給小編打賞了一分錢,小編樂得屁顛屁顛地去準備提現,一查,發現真多了一分錢。
在這同時,在我還沒有提現成功之前,小編的老婆已經提前將這一分錢支走了,小編此時再次查賬,發現一分錢也沒了。
臟讀和不可重復讀有點懵逼?
二者的區別是,臟讀是某一事務讀取了另外一個事務未提交的數據,不可重復讀是讀取了其他事務提交的數據。
其實,有些情況下,不可重復讀不是問題,比如,小編提現期間,一分錢被老婆支走了,這不是問題!
而臟讀,是可以通過設置隔離級別避免的。
幻讀(phantom read),是事務非獨立執行時發生的一種現象。
例如事務 T1 對一個表中所有的行的某個數據項做了從“1”修改為“2”的操作,這時事務 T2 又對這個表中插入了一行數據項為“1”的數據,并且提交給數據庫。
而操作事務 T1 的用戶如果再查看剛剛修改的數據,會發現數據怎么還是 1?其實這行是從事務 T2 中添加的,就好像產生幻覺一樣,這就是發生了幻讀。
舉例
其實上面的解釋已經是一個例子了,但是還是要舉個例子。
比如,小編準備提取你打賞的一分錢,提取完了,這時又有其他熱心網友打賞了一分錢,小編一看,明明已經取出了,怎么又有一分錢!?
小編此時以為像做夢一樣,我覺得也可以叫「夢讀」,哈哈。
幻讀和不可重復讀都是讀取了另一條已經提交的事務(這點就臟讀不同),所不同的是不可重復讀查詢的都是同一個數據項,而幻讀針對的是一批數據整體(比如數據的個數)。
為了解決上面可能出現的問題,我們就需要設置隔離級別,也就是事務之間按照什么規則進行隔離,將事務隔離到什么程度。
首先,需要明白一點,隔離程度越強,事務的執行效率越低。
ANSI/ISO SQL 定義了 4 種標準隔離級別:
① Serializable(串行化):花費最高代價但最可靠的事務隔離級別。
“寫”會加“寫鎖”,“讀”會加“讀鎖”。當出現讀寫鎖沖突的時候,后訪問的事務必須等前一個事務執行完成,才能繼續執行。
事務 100% 隔離,可避免臟讀、不可重復讀、幻讀的發生。
② Repeatable read(可重復讀,默認級別):多次讀取同一范圍的數據會返回第一次查詢的快照,即使其他事務對該數據做了更新修改。事務在執行期間看到的數據前后必須是一致的。
但如果這個事務在讀取某個范圍內的記錄時,其他事務又在該范圍內插入了新的記錄,當之前的事務再次讀取該范圍的記錄時,會產生幻行,這就是幻讀。
可避免臟讀、不可重復讀的發生。但是可能會出現幻讀。
③ Read committed (讀已提交):保證一個事物提交后才能被另外一個事務讀取。另外一個事務不能讀取該事物未提交的數據。
可避免臟讀的發生,但是可能會造成不可重復讀。
大多數數據庫的默認級別就是 Read committed,比如 Sql Server , Oracle。
④ Read uncommitted (讀未提交):最低的事務隔離級別,一個事務還沒提交時,它做的變更就能被別的事務看到。
任何情況都無法保證。
下圖中是一個很好的例子,分別解釋了四種事務隔離級別下,事務 B 能夠讀取到的結果。
看著還是有點懵逼?那我們再舉個例子。
A,B 兩個事務,分別做了一些操作,操作過程中,在不同隔離級別下查看變量的值:
隔離級別是串行化,則在事務 B 執行「將 1 改成 2」的時候,會被鎖住。直到事務 A 提交后,事務 B 才可以繼續執行。
再次總結
讀未提交:別人改數據的事務尚未提交,我在我的事務中也能讀到。
讀已提交:別人改數據的事務已經提交,我在我的事務中才能讀到。
可重復讀:別人改數據的事務已經提交,我在我的事務中也不去讀。
串行:我的事務尚未提交,別人就別想改數據。
這 4 種隔離級別,并行性能依次降低,安全性依次提高。
總的來說,事務隔離級別越高,越能保證數據的完整性和一致性,但是付出的代價卻是并發執行效率的低下。
事務的機制是通過視圖(read-view)來實現的并發版本控制(MVCC),不同的事務隔離級別創建讀視圖的時間點不同。
可重復讀是每個事務重建讀視圖,整個事務存在期間都用這個視圖。
讀已提交是每條 SQL 創建讀視圖,在每個 SQL 語句開始執行的時候創建的。隔離作用域僅限該條 SQL 語句。
讀未提交是不創建,直接返回記錄上的最新值
串行化隔離級別下直接用加鎖的方式來避免并行訪問。
這里的視圖可以理解為數據副本,每次創建視圖時,將當前已持久化的數據創建副本,后續直接從副本讀取,從而達到數據隔離效果。
我們每一次的修改操作,并不是直接對行數據進行操作。
比如我們設置 id 為 3 的行的 A 屬性為 10,并不是直接修改表中的數據,而是新加一行。
同時數據表其實還有一些隱藏的屬性,比如每一行的事務 id,所以每一行數據可能會有多個版本,每一個修改過它的事務都會有一行,并且還會有關聯的 undo 日志,表示這個操作原來的數據是什么,可以用它做回滾。
那么為什么要這么做?
因為如果我們直接把數據修改了,那么其他事務就用不了原先的值了,違反了事務的一致性。
那么一個事務讀取某一行的數據到底返回什么結果呢?
取決于隔離級別,如果是 Read Committed,那么返回的是最新的事務的提交值,所以未提交的事務修改的值是不會讀到的,這就是 Read Committed 實現的原理。
如果是 Read Repeatable 級別,那么只能返回發起時間比當前事務早的事務的提交值,和比當前事務晚的刪除事務刪除的值。這其實就是 MVCC 方式。
undo log 中存儲的是老版本數據。假設修改表中 id=2 的行數據,把 Name='B' 修改為 Name = 'B2' ,那么 undo 日志就會用來存放 Name='B' 的記錄,如果這個修改出現異常,可以使用 undo 日志來實現回滾操作,保證事務的一致性。
當一個舊的事務需要讀取數據時,為了能讀取到老版本的數據,需要順著 undo 鏈找到滿足其可見性的記錄。當版本鏈很長時,通常可以認為這是個比較耗時的操作。
假設一個值從 1 被按順序改成了 2、3、4,在回滾日志里面就會有類似下面的記錄。
當前值是 4,但是在查詢這條記錄的時候,不同時刻啟動的事務會有不同的 read-view。
如圖中看到的,在視圖 A、B、C 里面,這一個記錄的值分別是 1、2、4,同一條記錄在系統中可以存在多個版本,就是數據庫的多版本并發控制(MVCC)。對于 read-view A,要得到 1,就必須將當前值依次執行圖中所有的回滾操作得到。
同時你會發現,即使現在有另外一個事務正在將 4 改成 5,這個事務跟 read-view A、B、C 對應的事務是不會沖突的。
另外,在回滾段中的 undo log 分為: insert undo log 和 update undo log:
insert undo log : 事務對 insert 新記錄時產生的 undolog,只在事務回滾時需要,并且在事務提交后就可以立即丟棄。(誰會對剛插入的數據有可見性需求呢!!)
update undo log : 事務對記錄進行 delete 和 update 操作時產生的 undo log。不僅在事務回滾時需要,一致性讀也需要,所以不能隨便刪除,只有當數據庫所使用的快照中不涉及該日志記錄,對應的回滾日志才會被 purge 線程刪除。
何時刪除?
在不需要的時候才刪除。也就是說,系統會判斷,當沒有事務再需要用到這些回滾日志時,回滾日志會被刪除。
就是當系統里沒有比這個回滾日志更早的 read-view 的時候。
直觀感覺,一個事務花費很長時間不能夠結束,就是一個長的事務,簡稱長事務(Long Transaction)。
長事務是數據庫用戶經常會碰到且是非常令人頭疼的問題。長事務處理需要恰當進行,如處理不當可能引起數據庫的崩潰,為用戶帶來不必要的損失。
根據上面的論述,長事務意味著系統里面會存在很老的事務視圖。
由于這些事務隨時可能訪問數據庫里面的任何數據,所以這個事務提交之前,數據庫里面它可能用到的 undo log 都必須保留,這就會導致大量占用存儲空間。
在 MySQL 5.5 及以前的版本,回滾日志是跟數據字典一起放在 ibdata 文件里的,即使長事務最終提交,回滾段被清理,文件也不會變小。
除了對回滾段的影響,長事務還占用鎖資源,也可能拖垮整個庫,這個我們會在后面講鎖的時候展開。
因此,我們要盡量避免長事務。
“MySQL的事務隔離級別介紹”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。