簡單來說,事務就是要保證一組數據庫操作,要么全部成功,要么全部失敗。
在 MySQL 中,事務支持是在引擎層實現的。
MySQL 是一個支持多引擎的系統,但并不是所有的引擎都支持事務。
如 MySQL 原生的 MyISAM 引擎就不支持事務,這也是 MyISAM 被 InnoDB 取代的重要原因之一。
事務的四大特性(ACID)
-
原子性: 事務是最小的執行單位,不允許分割。事務的原子性確保動作要么全部完成,要么完全不起作用;
-
一致性: 執行事務前后,數據保持一致,例如轉賬業務中,無論事務是否成功,轉賬者和收款人的總額應該是不變的;
-
隔離性: 并發訪問數據庫時,一個用戶的事務不被其他事務所干擾,各并發事務之間數據庫是獨立的;
-
持久性: 一個事務被提交之后。它對數據庫中數據的改變是持久的,即使數據庫發生故障也不應該對其有任何影響
事務的并發問題
-
臟讀(Dirty read): 當一個事務正在訪問數據并且對數據進行了修改,而這種修改還沒有提交到數據庫中,這時另外一個事務也訪問了這個數據,然后使用了這個數據。因為這個數據是還沒有提交的數據,那么另外一個事務讀到的這個數據是“臟數據”,依據“臟數據”所做的操作可能是不正確的。
-
丟失修改(Lost to modify): 指在一個事務讀取一個數據時,另外一個事務也訪問了該數據,那么在第一個事務中修改了這個數據后,第二個事務也修改了這個數據。這樣第一個事務內的修改結果就被丟失,因此稱為丟失修改。 例如:事務1讀取某表中的數據A=20,事務2也讀取A=20,事務1修改A=A-1,事務2也修改A=A-1,最終結果A=19,事務1的修改被丟失。
-
不可重復讀(Unrepeatableread): 指在一個事務內多次讀同一數據。在這個事務還沒有結束時,另一個事務也訪問該數據。那么,在第一個事務中的兩次讀數據之間,由于第二個事務的修改導致第一個事務兩次讀取的數據可能不太一樣。這就發生了在一個事務內兩次讀到的數據是不一樣的情況,因此稱為不可重復讀。
-
幻讀(Phantom read): 幻讀與不可重復讀類似。它發生在一個事務(T1)讀取了幾行數據,接著另一個并發事務(T2)插入了一些數據時。在隨后的查詢中,第一個事務(T1)就會發現多了一些原本不存在的記錄,就好像發生了幻覺一樣,所以稱為幻讀。
不可重復度和幻讀區別:
不可重復讀的重點是修改,針對的數據是多行。
幻讀的重點在于新增或者刪除,針對數據是多行。
事務的隔離級別
SQL 標準定義了四個隔離級別:
- READ-UNCOMMITTED(讀取未提交): 最低的隔離級別,允許讀取尚未提交的數據變更,可能會導致臟讀、幻讀或不可重復讀。
- READ-COMMITTED(讀取已提交): 允許讀取并發事務已經提交的數據,可以阻止臟讀,但是幻讀或不可重復讀仍有可能發生。
- REPEATABLE-READ(可重復讀): 對同一字段的多次讀取結果都是一致的,除非數據是被本身事務自己所修改,可以阻止臟讀和不可重復讀,但幻讀仍有可能發生。
- SERIALIZABLE(可串行化): 最高的隔離級別,完全服從ACID的隔離級別。所有的事務依次逐個執行,這樣事務之間就完全不可能產生干擾,也就是說,該級別可以防止臟讀、不可重復讀以及幻讀。
MySQL InnoDB 存儲引擎的默認支持的隔離級別是 REPEATABLE-READ(可重讀,RR),
Oracle和sql server的默認隔離級別是READ-COMMITTED(讀取已提交,RC)。
我們可以通過
SELECT @@tx_isolation;
命令來查看,MySQL 8.0 該命令改為
SELECT @@transaction_isolation;
MySQL InnoDB 的 REPEATABLE-READ(可重讀)并不保證避免幻讀,需要應用使用加鎖讀來保證。
而這個加鎖度使用到的機制就是 Next-Key Locks。
因為隔離級別越低,事務請求的鎖越少,所以大部分數據庫系統的隔離級別都是 READ-COMMITTED(讀取提交內容) ,但是你要知道的是 InnoDB 存儲引擎默認使用 REPEATABLE-READ(可重讀,RR) 并不會有任何性能損失
事務隔離的實現
在實現上,數據庫里面會創建一個視圖,訪問的時候以視圖的邏輯結果為準。
- 在“可重復讀”隔離級別下,這個視圖是在事務啟動時創建的,整個事務存在期間都用這個視圖。
- 在“讀提交”隔離級別下,這個視圖是在每個 SQL 語句開始執行的時候創建的。
- 這里需要注意的是,“讀未提交”隔離級別下直接返回記錄上的最新值,沒有視圖概念;
- 而“串行化”隔離級別下直接用加鎖的方式來避免并行訪問。
每條記錄在更新的時候都會同時記錄一條回滾操作。
同一條記錄在系統中可以存在多個版本,這就是數據庫的多版本并發控制(MVCC)
回滾日志什么時候刪除?
系統會判斷當沒有事務需要用到這些回滾日志的時候,回滾日志會被刪除。
什么時候不需要了?
當系統里么有比這個回滾日志更早的read-view的時候。
為什么盡量不要使用長事務?
長事務意味著系統里面會存在很老的事務視圖,在這個事務提交之前,回滾記錄都要保留,這會導致大量占用存儲空間。
除此之外,長事務還占用鎖資源,可能會拖垮庫。