一文講清,MySQL事務隔離級別
業務系統在運行的時候,往往有很多線程同時在操作數據庫,MySQL也需要多線程的處理多個請求,那么每個事務里的多個SQL語句是如何執行的呢?
基本都是從磁盤加載數據頁到Buffer Pool的緩存頁里去,然后更新Buffer Pool里的緩存頁,同時記錄redo log和undo log。
多個線程并發執行的時候,會有一些問題:
多個事務并發執行,可能會對緩存頁里的同一行數據進行更新,這種沖突怎么解決?
有的事務在對一行數據進行更新,另一個事務在對這行數據進行查詢,這個沖突怎么解決?
接下來要講的內容,主要包括多事務并發運行存在的問題、MySQL的事務隔離級別、MVCC多版本控制、鎖。
臟讀
事務1讀取了事務2更新的數據,然后事務2回滾操作,那么事務1讀取到的數據就是臟數據。
例如:
張三的工資是8000,現在領導要給他漲工資到10000。
事務1把他的工資改為10000,但事務還沒提交。
事務2正在讀取張三的工資,獨到的是10000。
事務1此時回滾了,張三的工資又變成8000了。
事務2讀取到張三的工資10000為臟數據,事務2做了一次臟讀。
不可重復讀
事務1多次讀取同一數據,事務2在事務1多次讀取的過程中,對數據作了更新并提交,導致事務1多次讀取同一數據時,結果不一致。
例如:
事務1,讀取到張三的工資8000。
事務2,把張三的工資改為10000,提交事務。
事務1,再次讀取張三的工資,此時工資為10000。
在一個事務中前后兩次讀取同一個數據的結果不一樣,就是不可重復讀。
幻讀
一個事務用一樣的SQL多次查詢,每次查詢發現查到一些之前沒看到過的數據。
比如,目前公司工資為8000的人有10人。
事務1,讀取所有工資為8000的人數為10人。
事務2,插入一條工資為10000的記錄。
事務1再次讀取工資為8000的人,為11人。
此時,事務1出現幻覺似的,同樣的SQL查詢語句,第一次查出來10人,第二次查出來11人。
不可重復讀的和幻讀很容易混淆,不可重復讀側重于修改,幻讀側重于新增或刪除。解決不可重復讀的問題只需鎖住滿足條件的行,解決幻讀需要鎖表。
臟讀、不可重復讀、幻讀,數據庫并發執行,每個線程可能會開啟一個事務,每個事務都會執行crud操作。
數據庫并發的執行多個事務,多個事務并發的對緩存頁里的同一批數據進行crud,就可能導致臟讀、不可重復讀、幻讀這些問題。
所以,這些問題的本質是,數據庫多事務并發問題,為了解決這些問題,數據庫設計了事務隔離級別、MVCC多版本控制、鎖機制。
事務隔離級別
SQL標準中定義了4種事務隔離級別,就是說事務并發運行的時候,互相是如何隔離的,MySQL默認是可重復讀(RR)。
不同的隔離級別是可以避免不同的事務并發問題的。
read-uncommitted(RU),可能會發生臟讀、不可重復讀、幻讀。
read-committed(RC),不會發生臟讀,但是會發生不可重復讀、幻讀
也就是說,事務沒提交的情況下修改的值,你是讀不到的。但是,一旦事務提交了,你事務就能讀到,所以可能你多次讀到的值不一樣。
repeatable-read(RR),MySQL默認的事務隔離級別,做了大量復雜的工作,解決了臟讀、不可重復讀、幻讀問題,一般生成環境使用這個隔離級別就可以了。
serializable,事務串行執行,根本不會并發執行,所以不會有臟讀、不可重復讀、幻讀這些問題。但缺點也非常明顯,就是并發太差了。一般生產環境也不會使用