數據庫事務( transaction)是訪問並可能操作各種數據項的一個數據庫操作序列,這些操作要么全部執行,要么全部不執行,是一個不可分割的工作單位。事務由事務開始與事務結束之間執行的全部數據庫操作組成。 --百度百科
眾所周知,事務有ACID特性(原子性、一致性、隔離性、持久性),其中隔離性又包括四個級別:
- READ_UNCOMMITTED:讀未提交, 這種隔離級別指的是A事務可以讀取到B事務還沒有提交的數據,顯然這種隔離級別是不安全的,它容易引發不可重復讀、臟讀和幻讀。
- READ_COMMITTED:讀已提交,這種隔離級別指的是A事務只能讀取到B事務已經提交的數據,這種隔離級別比上面一種安全一些,但是只能防止臟讀。
- REPEATABLE_READ:可重復讀,雖然名字叫可重復讀,但是它的意思是:重復讀同一條數據的情況下,能保證讀的結果是一致的。不少人被這個名字誤解,它的原理大致是A事務在開始后會將數據庫中的數據假凍結,也就是說無論B事務對數據作什么樣的改變,A事務讀到的數據都是事務開始時的樣子,這樣就做到了無論讀多少次數據都是一致的。但是這種情況下還是會有幻讀的情況出現。
- SERIALIZABLE:串行化,這種隔離級別也是最高的,所有的事務都必須像單線程一樣,一個個執行。它可以防止幻讀。
四種隔離級別中,並發編程下:1的性能最高,但是最不安全,4的性能最低,但是最安全,mysql中的默認隔離級別是REPEATABLE_READ。
查看mysql8.0的事務隔離級別
/*查看當前會話的事務隔離級別*/ select @@transaction_isolation; /*查看mysql的事務隔離級別*/ select @@global.transacton_isolation;
修改mysql8.0中的事務隔離級別
/*設置當前會話的隔離級別 */ /*設置read uncommitted級別*/ set session transaction isolation level read uncommitted; /*設置read committed級別*/ set session transaction isolation level read committed; /*設置repeatable read級別*/ set session transaction isolation level repeatable read; /*設置serializable級別*/ set session transaction isolation level serializable; /*設置mysql全局的隔離級別 */ /*設置read uncommitted級別*/ set global transaction isolation level read uncommitted; /*設置read committed級別*/ set global transaction isolation level read committed; /*設置repeatable read級別*/ set global transaction isolation level repeatable read; /*設置serializable級別*/ set global transaction isolation level serializable;
---------------------------------------------------------------------------------下面是實例演示:-----------------------------------------------------------------------------------------------------------
下面將用實例來演示四種隔離級別以及可能出現的問題:
首先先新增一張Student表, 表中有id,name,score字段,並填入測試字段
1.READ_UNCOMMITTED
然后我們打開兩個會話,並將兩個會話的事務隔離級別都設置為read_uncommitted
我們在left會話中先開啟一個事務,然后查詢s1的分數,此時可以看到分數是10
然后在right會話中再開啟一個事務,然后把s1的分數改為99,但是先不要提交,也不要回滾
然后我們再去left會話中單獨執行查詢s1的分數語句,可以看到明明事務還沒有提交, 卻可以看到被修改成了99。這就是臟讀
2.READ_COMMITTED
還是打開兩個窗口模擬兩個事務。 然后將兩個窗口的事務隔離級別都修改為read_committed,因為修改方式和上面一樣就不放圖了。
然后還是在left中查詢s1的成績
然后再次在right中修改為99
再次執行在left中執行的話,可以看到還是10,因為我們改變了事務的隔離級別,right沒有提交的修改我們在left中看不到,這種隔離級別就防止了臟讀的出現
然后我們執行right的commit
再次在left中查詢s1,可以看到成績變為了99。因為right已經提交了事務,所以可以在left中看到,但是這又出現了left事務中同一條語句查詢兩次而得到的查詢結果不一致的問題,這就是數據的不可重復讀問題。
其實我覺得這個名字很容易誤導很多人, 要是翻譯成 "讀取結果不一致" 的話可以更貼近事實
3.REPEATABLE_READ
可以從這個隔離級別的字面意思上理解“可重復讀”,這個可重復讀可以解決就可以解決不可重復讀的問題。
我們還是先將left和right的隔離級別都設置為repeatable_read
和之前一樣的,還是先在left中開啟事務,查詢s1
然后在right中開啟事務,修改為100,最后直接提交
然后我們返回到left中查看,可以看到雖然right的事務提交了,但是left中的結果依然是99。這就是repeatable_read這個隔離級別解決了不可重復讀的問題(其實換一種更直接的說法就是:repeatable_read這個隔離級別解決了多次讀取數據不一致的問題,在一個事務當中,多次讀取同一條數據,無論如何都是相同的)
但是這種隔離級別還是會有幻讀的情況出現,比如說:先在left中開啟事務, 查看所有的學習成績
此時可以看到一共有s1和s2兩條數據,然后我們去right中將s2刪掉
然后再回到left中查看,還是兩條,沒有任何問題,因為repeatable_read會將當前事務中的數據進行假凍結
然后這時我們將s2的成績修改成100,這時可以看到這個update語句的影響行數為0,這時我們再去重新執行select語句可以發現明明是有s2的,但是這條更新語句就是沒有效果,就像我們看到的s2是幻覺一樣,這就是幻讀。
4.SERIALIZABLE
從字面意思上理解的話是序列化、串行化。 但是這里指的其實是所有的事務必須一個個的按順序執行。這種隔離級別可以有效的防止以上任意一種錯誤出現
還是先將left和right的隔離級別都改為serializable
我們再重現剛剛的場景:先在left開啟一個事務,然后查詢student
然后在right中刪除s2,可以看到right就一直是正在處理
如果一直等待的話這就會因為超時而無法執行,因為這個隔離級別的所有事務都必須像單線程一樣一個個順序執行,沒有辦法同時處理,只要left不提交,right就永遠沒有辦法執行。所以right沒有辦法將s2刪除, 自然就不會有幻讀的情況出現了。不過正如我們看到的那樣,因為隔離級別太高了,導致事務只能按順序執行,這種的性能是非常差的。所以mysql退而求其次選擇了可重復讀(也就是多次讀取時結果一致)這種隔離策略
最后總結一下:
① Serializable (串行化):可避免臟讀、不可重復讀、幻讀的發生。
② Repeatable read (可重復讀):可避免臟讀、不可重復讀的發生。
③ Read committed (讀已提交):可避免臟讀的發生。
④ Read uncommitted (讀未提交):最低級別,任何情況都無法保證。