資料庫事務隔離
目錄
事務:要麼什麼都做,要麼都不做,沒有中間狀態。 All or Nothing。
MySql是一個支援多引擎的系統,但是並不是所有的引擎都支援事務。比如MyISAM引擎就不支援事務,這也是MyISAM被InnoDB取代的重要原因之一。
一說到事務,首先出現在我們腦海裡的是:ACID(Atomicity,Consistency,Isolation.Durability)原子性,一致性,隔離性,永續性。今天主要回憶一下隔離性。
資料庫隔離級別
- 讀未提交(read uncommit):一個事物還未提交,它做的變更就能被別的事務看到,直接返回記憶體記錄的最新值;
- 讀提交(read commit):一個事務提交後,它做的變更才會被其他事務看到;檢視是在執行的時候建立的(oracle預設);
- 可重複讀(repeatable read):一個事務在執行過程中看到的資料,總是跟這個事務在啟動時看到的資料是一致的。未提交變更對其也是不可見的。主要用資料庫的檢視來實現,在事務啟動的時候建立的,該檢視是靜態的,不受其他事務的影響;(Mysql預設的級別)。
- 序列化(serializable):讀和寫都會加鎖,當有衝突的時候,事務必須列隊等待執行,通過加鎖來避免並行訪問;
下面通過一個具體的例子來了解四種隔離級別對取值的影響:
事務a | 事務b |
啟動事務 查詢得到值 1 |
啟動事務 |
查詢得到值 1; | |
將1改為 2; | |
查詢得到值v1 | |
提交事務b | |
查詢得到值v2 | |
提交事務 | |
查詢得到值v3 |
讀未提交隔離:事務a讀取的值v1是: 2。雖然事務b未提交,但是對事務a已經可見了。v2和v3的值也都是2;
讀已提交:v1的值是1。v2和v3的值是2;只有事務b提交後新值才能被事務a看到;
可重複讀:v1和v2的值是1,v3的值是2;事務在啟動到結束期間的資料必須保持一致,通過mvcc和檢視來實現;
序列化:如果是事務b先啟動,那麼在執行期間,會加鎖,事務a需要等到事務b執行完後才可以執行。
隔離級別下的問題
髒讀
對於讀未提交隔離級別出現的髒讀:
事務a | 事務b |
啟動事務 查詢得到值x=1 |
啟動事務
|
查詢得到值x=1 | |
update值x+1=2; | |
查詢得到值x=2 | |
回滾事務 | |
業務處理 | |
提交事務 |
如上圖所示,如果事務b在更新完x的值後又回滾了,此時事務a便發生了髒讀x=2,無效資料。
不可重複讀
讀已提交出現的不可重複讀問題:
事務a | 事務b |
啟動事務 查詢得到值x=1 |
啟動事務
|
查詢得到值x=1 | |
update值x+1=2; | |
事務提交 | |
查詢得到x=2 | |
業務處理 | |
提交事務 |
對於不可重複讀和髒讀的區別也比較明顯:不可重複讀主要是因為事務b執行的比較快,在事務a之前提交了update,但是事務a再次讀取x的值,前後的資料不一致。雖然不一致,但是資料有效。有沒有問題需要根據具體的情況而定。
幻讀
可重複讀由於併發情況下“新插入的行”的影響,則會出現幻讀。幻讀主要的影響是:
(1) 它破壞了加鎖的語義;
(2) 導致了資料一致性的問題,
這個一致性主要是因為binlog和資料庫裡的資料的不一致造成的。
為了解決幻讀:這裡主要採用了next-key lock【行鎖和間隙鎖合稱為next-key lock】 和gap lock[間隙鎖]來解決這個問題。但是間隙鎖也會有新的問題就是“死鎖”問題。
事務的隔離級別問題及小結
級別 | 問題 | 解決辦法 | |
事務隔離 | 讀未提交 | 髒讀 | 加鎖 |
讀已提交 | 不可重複讀-幻讀 | mvcc版本控制 | |
可重複讀 | gap的容易產生的死鎖 +幻讀 | gap lock | |
序列化 | 無衝突,但是效能低 | 加讀/寫鎖 |
我們可以通過如下命令來檢視當前資料庫的隔離級別:
show variables like "tr_isolation" //5.6或更早的版本,8.0直接把tx_isolation刪掉
show variables like "transaction_isolation" //5.7版本以後
事務隔離的實現
在實現上,資料庫裡面會建立一個檢視,訪問的時候以檢視的邏輯結果為準。以“可重複讀”為例:
在mysql中,每條記錄在更新的時候都會同時記錄一條回滾操作undo log。通過回滾操作,我們可以得到最新值的前一個狀態。例如1被順序改為了2-3-4,在回滾日誌中,我們都會記錄這樣一個操作。當前值是1,但是在查詢的時候每個事務會有自己的檢視,read-view。圖2中,變數x的值在A-B-C三個檢視中的值分別為1-2-4。對於read-view C,如果事務A-B都commit了,此時想要讀取x=1的值,就必須通過執行所有的回滾日誌才能得到x=1的值,且A和B的檢視也不會刪除,因為有比A-B更早建立的檢視C依賴它們,引來的問題就是佔用記憶體,尤其是長事務。這就是資料庫中多版本併發控制MVCC(Multi-Version Concurrency Control )。
讀未提交 |
直接返回記憶體記錄 [InnoDB 的buffer pool]的最新值,沒有檢視的概念; |
讀已提交 | 檢視是在執行時建立的; |
可重複讀 | 檢視是在事務開啟時建立的[事務在啟動的時候打一個快照,別人修改的我不care]; |
序列化 | 通過加讀/寫鎖來避免並行化; |
MVCC
可以通過transaction_isolation引數來設定隔離級別;
在mysql中,實際上每條記錄在更新的時候都會記錄一條回滾操作undo log 和嚴格遞增的事務id。記錄上最新值,通過回滾操作,可以得到前一個狀態的值,這也就是多版本併發控制MVCC。
事務的啟動方式
1、顯式的啟動事務:begin或start transaction。
配套的提交語句是commit,回滾語句是rollback。主要通過redo log和undo log來實現。
2、set autocommit
set autocommit=0,這個命令會將這個執行緒的自動提交事務關閉掉。只有主動的去呼叫commit或rollback時,才會提交,可能會導致長事務。
ps:建議使用set autocommit=1來顯示的啟動事務;
思考及解答
問題一:mvcc的回滾日誌什麼時候刪除?
答:當沒有事務需要用到這些回滾日誌的時候回滾日誌就會刪除。當系統裡沒有比這個回滾日誌更早的read-view的時候就可以刪除了。如何理解呢? 參考圖2。
簡單的說:一個查詢事務開啟以後,在這個時刻之後,事務提交/回滾之前,所有的更新產生的undo log都不能被刪除。
問題二:使用長事務的弊病,為什麼長事務會拖垮資料庫?怎麼查詢各個表中的長事務?如何避免長事務?
答:如果我們在訪問資料庫的過程中存在長事務[執行完sql語句,一直未斷開連線],那麼系統裡會存在許多很老的事務檢視,事務提交前,這些回滾記錄都必須保留,這樣就佔用了大量的記憶體。另外長事務還佔用鎖的資源,造成請求擠壓,吞吐量下降,可能拖垮整個資料庫。
為什麼要避免長事務
某些後臺應用經常需要頻繁的操作DB,為了保證資料出錯時能回滾資料,通常都會使用事務。在使用事務的時候,儘量避免使用長事務,比如說:某個業務操作需要批量插入資料,而且資料量還不少,如果這整個操作都包在一個事務裡面,只有等到資料操作完了,DB連線才會被釋放,一旦外部系統發起請求,併發呼叫這個操作,那麼一下子將有大量的DB連線被持有而沒有被釋放掉,這個時候,如果還有其他請求到來,就很大可能獲取不到資料庫連線,請求也就只能等待,整個系統的吞吐量就大大下降。
所以寧願將事務的範圍縮小,快速操作完資料後,立刻把DB連線還給連線池,這樣後續的請求就可以拿到連線。
當然這樣對DB的壓力也會增加,但是總比db連線被耗光來的好。
問題三: 如何避免長事務?
答:從應用和資料庫端兩個方面來分析:
應用端:
1、使用set autocommit=1;顯示語句來啟動事務,並且讓事務自動提交。避免長連線導致的意外長事務;
2、確定是否有不必要的只讀事務。比如好幾個select語句,其實沒有必要使用事務,這樣的可以去掉。
3、使用set MAX_EXECUTION_TIME=3000 來控制每個語句的執行最長時間,避免單個語句執行時間太長,出現長事務;
從資料庫端:
1、監控information_schema.InnoDB_trx表,設定長事務閾值,超過就報警/記錄/kill掉;
2、測試階段可以開啟general_log,分析日誌行為提前發線問題;
監控長事務的命令:
select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>6
學習筆記,內容簡單,用於複習,原內容2月有更新。
##參考資料,《MySql實戰詳解》
相關文章
- 資料庫事務隔離級別資料庫
- 資料庫事務與事務的隔離級別資料庫
- 資料庫事務與隔離級別資料庫
- 資料庫事務及其隔離級別資料庫
- ORACLE資料庫事務隔離級別Oracle資料庫
- 聊聊資料庫的事務隔離級別資料庫
- Oracle資料庫事務隔離級別概述Oracle資料庫
- [資料庫]事務的4種隔離級別資料庫
- MySQL資料庫事務隔離性的實現MySql資料庫
- MySQL資料庫引擎、事務隔離級別、鎖MySql資料庫
- 資料庫事務隔離級別分析----轉載資料庫
- 資料庫事務的四種隔離級別資料庫
- 資料庫系列:事務的4種隔離級別資料庫
- 資料庫事務的四大特性以及事務的隔離級別資料庫
- MySQL資料庫詳解(三)MySQL的事務隔離剖析MySql資料庫
- 資料庫事務併發問題----各種事務隔離下的情況資料庫
- MySQL事務隔離MySql
- MySQL 事務隔離MySql
- KES資料庫實踐指南:探索KES資料庫的事務隔離級別資料庫
- 資料庫事務的隔離級別及四大特性資料庫
- 資料庫事務的四大特性和隔離級別資料庫
- 關係型資料庫的四種事務隔離級別資料庫
- 資料庫隔離資料庫
- 資料庫事務併發產生的問題以及事務的隔離級別資料庫
- 事務隔離(二):基於加鎖方式的事務隔離原理
- 資料庫學習筆記:事務的特性和隔離級別資料庫筆記
- SqlServer事務詳解(事務隔離性和隔離級別詳解)SQLServer
- oracle資料庫事務不同事務隔離級別與v$transaction flag列思考Oracle資料庫
- MySQL資料庫事務各隔離級別加鎖情況--Repeatable ReaMySql資料庫
- 資料庫事務,原子性、一致性、隔離性、永續性資料庫
- 資料庫事務隔離級別– 髒讀、幻讀、不可重複讀資料庫
- 資料庫事務與隔離級別示例(oracle與sql server對比)資料庫OracleSQLServer
- MySQL 事務隔離級別MySql
- 4.MySQL--事務隔離MySql
- PostgreSQL事務隔離級別SQL
- 事務、特性、隔離級別
- PostgreSQL的事務隔離分析SQL
- MySQL事務隔離級別MySql