Mysql是怎麼實現事務的

2021-10-05 01:51:50 字數 2886 閱讀 6608

mysql的innodb儲存引擎是通過事務來保證資料的一致性的

資料庫事務通常包含了乙個序列的對資料庫的讀/寫操作包含有以下兩個目的

為資料庫操作序列提供了乙個從失敗中恢復到正常狀態的方法,同時提供了資料庫即使再異常狀態下仍能保持一致性的方法

當多個應用程式再併發訪問資料庫時,可以再這些應用程式之間提供乙個隔離方法,以防止彼此的操作互相干擾

特性:事務的特性有原子性,隔離性,永續性,一致性,其實,原子性,隔離性,永續性都是為了保證一致性

原子性:原子性是指多個操作都是不可分割的,智慧型全部執行成功,或者全部執行失敗。mysql的原子性通過undo log來實現,undolog時innodb儲存引擎特有的,具體的實現方式是:將所有對資料的修改(增刪改)都寫入日誌(undo log),如果乙個事務中的一部分操作已經成功,但是另一部分操作由於斷電,系統崩潰等原因無法成功操作,則通過回溯日誌將已經執行成功的操作撤銷,從而達到全部失敗的目的。 undo log是邏輯日誌,可以理解為記錄和事務相反的sql語句,事務執行insert語句,undo就記錄delete語句,它可以追加寫的方式記錄日誌,不會覆蓋之前的日誌,除此之外,undo log還用來實現資料庫多版本併發控制(mvcc)

永續性:永續性是指,乙個事務對資料的所有修改都會永久的儲存再資料庫中,mysql的永續性是通過redo log來實現的,redolog也是innodb特有的,具體實現方式是當發生資料修改時,innodb會將記錄首先寫道redo log,並更新記憶體,此時更新就算完成了,同時innodb會在合適的時機將記錄重新整理到磁碟。redo log時物理日誌,記錄的是再某個資料頁做了什麼修改,而不是以sql的形式記錄,它有固定大小,是迴圈寫的方式記錄日誌,空間用完後會覆蓋之前的日誌。undolog和redolog並不是直接寫道磁碟的,而是先寫入logbuffer,再等待合適的時機同步到osbuffer,再由作業系統決定合適刷到磁碟。    既然undolog和redolog都是從logbuffer到osbuffer,再到磁碟,所以中途還是有可能因為斷電/硬體故障等原因導致日誌丟失,為此mysql提供了三種持久化方式,通過innodb_flush_log_at_trx_commit來控制,這個引數由三個值0 , 1 ,2 ,這三個值的意思分別是:

0:提交後寫入logbuffer ,每秒寫入osbufer並呼叫fsync()重新整理到磁碟

1(預設):每次提交後直接寫入osbuffer,並且呼叫系統函式fsync()把日誌寫道磁碟,就保證資料的一致性來說,這種方式是最安全的,但是安全就意味著效率低下,每次都直接寫到osbuffer並寫道磁碟,無疑會導致單位時間內io的次數過多而導致效率低下

2:每次提交寫入osbuffer,每秒呼叫fsync重新整理到磁碟。

那麼出現異常後,資料庫重啟會首先讀取redo日誌,把成功提交但是還沒來得及寫入磁碟的資料重新寫入磁碟,保證了永續性,再讀取undo log將還沒有成功提交的事務進行回滾,保證了原子性

隔離性:資料庫事務的隔離性是指:多個事務併發執行時乙個事務的執行不影響其他事務的執行;資料庫的隔離級別有:未提交讀,已提交讀,可重複讀,序列化;

可重複讀時mysql的預設隔離級別,比如事務1執行select 查詢出一條資料,然後事務2 執行了插入操作,這時事務1再次查詢就可能會出現查詢出兩條資料的,就出現了幻讀。但是我們實際操作下發現和預期的結果並不一致,沒有出現幻讀,實際上,mysql再可重複讀的隔離級別下用mvcc解決了select普通查詢的幻讀現象,具體實現是:事務開始時,第一條select語句查詢的結果集會生成乙個快照,並且這個事務結束前,同樣的select語句返回的都是這個快照的結果,而不是最新的查詢結果,這就是mysql可重複讀隔離級別對普通select查詢使用的快照讀

快照讀和mvcc有什麼關係;mvcc是多版本併發控制,快照就是其中的乙個版本,具體實現方式是mysql會給每個表自動建立三個隱藏列:db_trx_id 事務id, db_rll_ptr 回滾指標, db_row_id 隱藏id ,由於undolog中記錄了各個版本的資料,並且通過db_roll_ptr可以找到各個歷史版本,並且通過db_trx_id決定使用哪個版本(快照)所以相當於是undolog實現了mvcc,mvcc實現了快照i讀。

那麼如此看來,mysql的可重複讀利用快照讀已經解決了幻讀問題?但是事實上並非如此,如果第二次查詢前事務2插入一條資料insert into student(name) values('wangwu'),然後事務1進行一次更新:update student set name = 'zhaoliu' where name = 'wangwu' ,之後的查詢會發現還是只有兩條一跳資料,但是如果我們看的話會發現事務2插入的資料的名字程式設計了wangwu;

這其實是mysql對insert,update和delete語句使用的是當前讀,因為涉及到資料的修改,所以mysql必須拿到最新資料才能修改。

那麼再可重複讀下是怎麼解決幻讀的呢,

間隙鎖:我們都知道innodb支援行鎖,並且行鎖是鎖住索引,而間隙鎖是鎖定索引記錄間隙,確保索引記錄的間隙不變,間隙鎖是為可重複讀或以上級別設計的,間隙鎖和行鎖一起組成了nextkey lock當,innodb掃瞄索引記錄的時候,會首先對索引記錄加上行鎖,再對索引記錄兩邊的間隙加上間隙鎖,加上間隙鎖之後,其他事務就不能再這個間隙插入記錄,這樣就有效防止了幻讀的發生。

預設情況下,innodb工作在可重複讀級別下,並且以next-key lock的方式對索引進行加鎖,當查詢的索引具有唯一性的時候,innodb會對next-key lock進行優化,將其降為行鎖,僅僅鎖住索引本身,而不是範圍,若是普通索引,則會使用next-key lock將記錄和間隙一起鎖定

使用快照讀的語句

select * from ...

1使用當前讀的語句

select * from ... lock in share mode

select * from ... for update

insert into table...

update table set ...

delete table where ...

事務可重複讀是怎麼實現的?

一 事務 t 啟動的時候會建立乙個檢視 read view,之後事務 t 執行期間,即使有其他事務修改了資料,事務 t 看到的仍然跟在啟動時看到的一樣。二 begin start transaction 命令並不是乙個事務的起點,在執行到它們之後的第乙個操作 innodb 表的語句,事務才真正啟動。...

mysql事務怎麼玩 mysql事務

定義 transaction 事務 乙個最小的不可再分的工作單元 通常乙個事務對應乙個完整的業務 例如銀行賬戶轉賬業務,該業務就是乙個最小的工作單元 乙個完整的業務需要批量的dml insert update delete 語句共同聯合完成 事務只和dml語句有關,或者說dml語句才有事務。這個和業...

mysql索引實現 mysql 索引是怎麼實現的?

看了很多關於索引的部落格,講的大同小異。但是始終沒有讓我明白關於索引的一些概念,如b tree索引,hash索引,唯一索引.或許有很多人和我一樣,沒搞清楚概念就開始研究b tree,b tree等結構,導致在面試的時候答非所問!本文中有關儲存引擎請檢視mysql儲存引擎 innodb和myisam ...