前言:整理歸納,個人溫習之用,請支持正版極客時間
1、何謂事務?
2、隔離性與隔離級別
-
髒讀:當數據庫中一個事務A正在修改一個數據但是還未提交或者回滾,另一個事務B 來讀取了修改後的內容並且使用了,之後事務A提交了。此情況僅會發生在: 讀未提交的隔離級別
-
不可重複讀(虛讀):在一個事務A中多次操作數據,在事務A操作過程中(未最終提交),事務B也做了處理,並且該值發生了改變,這時候就會導致A在事務操作的時候,發現數據與第一次不一樣了。此情況僅會發生在:讀未提交、讀提交的隔離級別
-
幻讀:第一個事務對一個表中的數據進行了修改,這種修改涉及到表中的全部數據行。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入一行新數據。那麼,以後就會發生操作第一個事務的用戶發現表中還有沒有修改的數據行,就好象發生了幻覺一樣。此情況會回發生在:讀未提交、讀提交、可重複讀的隔離級別。解決幻讀的方法是增加範圍鎖RangeS,鎖定檢索範圍爲只讀。
-
讀未提交:一個事務還沒提交時,它做的變更就能被別的事務看到
-
讀提交:一個事務提交之後,它做的變更纔會被其他事務看到
-
可重複讀:一個事務執行過程中看到的數據,總是跟這個事務在啓動時看到的數據是一致的。當然在可重複讀隔離級別下,未提交變更對其他事務也是不可見的
-
串行化:顧名思義是對於同一行記錄,“寫”會加“寫鎖”,“讀”會加“讀鎖”。當出現讀寫鎖衝突的時候,後訪問的事務必須等前一個事務執行完成,才能繼續執行。
mysql> create table T(c int) engine=InnoDB;
insert into T(c) values(1);
-
“讀未提交”: V1 的值就是 2。這時候事務 B 雖然還沒有提交,但是結果已經被 A 看到了,V2、V3 也都是 2
-
“讀提交”: V1 是 1,V2 的值是 2。事務 B 的更新在提交後才能被 A 看到, V3 的值也是 2
-
“可重複讀”: V1、V2 是 1,V3 是 2。之所以 V2 還是 1,遵循的就是這個要求:事務在執行期間看到的數據前後必須是一致的
-
“串行化”:在事務 B 執行“將 1 改成 2”的時候,會被鎖住。直到事務 A 提交後,事務 B 纔可以繼續執行。所以從 A 的角度看, V1、V2 值是 1,V3 的值是 2。
-
“讀提交”隔離級別下,這個視圖是在每個 SQL 語句開始執行的時候創建的。
-
“讀未提交”隔離級別下直接返回記錄上的最新值,沒有視圖概念
-
“可重複讀”隔離級別下,這個視圖是在事務啓動時創建的,整個事務存在期間都用這個視圖(靜態,不受其它事物更新的影響)
-
“串行化”隔離級別下直接用加鎖的方式來避免並行訪問
3、事務隔離的實現
4、事務的啓動方式
-
顯式啓動事務語句, begin 或 start transaction。配套的提交語句是 commit,回滾語句是 rollback
-
set autocommit=0,這個命令會將這個線程的自動提交關掉。意味着如果你只執行一個 select 語句,這個事務就啓動了,而且並不會自動提交。這個事務持續存在直到你主動執行 commit 或 rollback 語句,或者斷開連接。
5、總結
-
首先,從應用開發端來看:確認是否使用了 set autocommit=0。這個確認工作可以在測試環境中開展,把 MySQL 的 general_log 開起來,然後隨便跑一個業務邏輯,通過 general_log 的日誌來確認。一般框架如果會設置這個值,也就會提供參數來控制行爲,你的目標就是把它改成 1。確認是否有不必要的只讀事務。有些框架會習慣不管什麼語句先用 begin/commit 框起來。我見過有些業務並沒有這個需要,但是也把好幾個 select 語句放到了事務中。這種只讀事務可以去掉。業務連接數據庫的時候,根據業務本身的預估,通過 SET MAX_EXECUTION_TIME 命令,來控制每個語句執行的最長時間,避免單個語句意外執行太長時間。(爲什麼會意外?在後續的文章中會提到這類案例)
-
其次,從數據庫端來看:監控 information_schema.Innodb_trx 表,設置長事務閾值,超過就報警 / 或者 kill;Percona 的 pt-kill 這個工具不錯,推薦使用;在業務功能測試階段要求輸出所有的 general_log,分析日誌行爲提前發現問題;如果使用的是 MySQL 5.6 或者更新版本,把 innodb_undo_tablespaces 設置成 2(或更大的值)。如果真的出現大事務導致回滾段過大,這樣設置後清理起來更方便。