首先,咱們都知道數據庫事務的四大特性:mysql
用於處理數據,page是Innodb存儲的最基本結構,也是Innodb磁盤管理的最小單位,數據變動的時候,緩存裏的數據頁和磁盤的數據頁不一致,該數據頁被稱爲髒頁。sql
redo log保證數據的可靠性,存儲數據以前首先要存儲變動數據的日誌,一旦系統出現故障能夠從日誌中找。Innodb採用了Write Ahead Log(預寫日誌)策略,就是當事務提交時,先寫重作日誌,而後再擇時將髒頁落盤。Force Log at Commit機制保證事務的持久性,事務提交的時候,必須先將該事務的全部日誌記錄落盤,在每次將重作日誌緩衝寫入到重作日誌後,必須調用一次fsync操做,將緩衝日誌從文件系統緩存真正寫入磁盤數據庫
redo log落盤機制有三種,能夠經過參數innodb_flush_log_at_trx_commit進行設置,默認值爲1緩存
上圖是數據落盤和redo log日誌落盤的機制,咱們說下這個數據落盤時候的雙寫機制和檢查點bash
Innodb經過雙寫機制保證數據的可靠性。Double Write由兩個部分構成,一個是內存中的double write buffer,大小爲2MB。第二部分是物理磁盤,共享表空間中的128個連續的頁,大小也是2MB。數據結構
在對緩衝池中的髒頁進行刷新的時候,並非直接將數據寫到磁盤。首先,經過mencpy函數將髒頁複製到內存中的double write buffer區,而後將double write buffer中的數據分兩次,每次1MB,將數據順序地寫入到共享表空間的磁盤上,而後立刻調用fsnyc真正地落盤。完成以後,再講double write buffer中的數據寫入到各個表空間中。若是操做系統在寫入磁盤的過程當中崩潰了,能夠從共享表空間中找到數據副本恢復數據。架構
表示將髒頁寫入到磁盤的時機:併發
當數據庫崩潰以後會利用redo log將尚未及時落盤的數據恢復,從新寫入磁盤。在恢復的過程當中還須要去回滾尚未提交的事務,回滾事務就須要利用到undo log,而undo log的完整性和可靠性須要redo log保證,所以恢復數據庫的時候,首先將redo log裏面數據恢復,而後作undo log的回滾。異步
數據和回滾日誌的每條記錄都會有三個額外的字段:函數
undo log並無使用額外的文件存儲,而是存放在共享表空間的回滾段中。undo log的產生也能夠當作是數據庫的數據,所以,undo log 也會寫入到redo log中,也就是undo log 的產生會伴隨着redo log的產生,undo log的完整性和可靠性也是由redo log來保證
原子性,一致性和持久性主要是經過redo log,undo log和Force Log at commit機制來完成的。redo log用在數據庫崩潰的時候,從redo log恢復數據,undo log用於對事務的影響進行撤銷,也就是回滾,還用於多咱們後面會講到的多版本控制。Force log at commit保證事務提交以後可以持久化到redo log。
針對上面的四個問題,事務之間有不一樣的隔離級去解決上面的問題
在Innodb中,使用的是MVCC來保證事務之間的隔離,MVCC使得廣泛的select語句不會加鎖,提升數據庫的併發處理能力
MVCC:多版本併發控制,是一種併發控制的方法,用來實現數據庫的事務性。
在MVCC中,讀操做能夠分紅兩種
在Innodb中簡單的select語句屬於快照讀,不加鎖,讀的是歷史版本。而其餘的增刪改語句屬於當前讀,須要加鎖,讀的是當前版本。
一致性非鎖定讀:Innodb引擎經過MVCC讀取當前數據庫中行數據的方式。若是讀取的是正在執行刪除或者更新操做的記錄,那麼本次讀操做不會所以阻塞去等待鎖的釋放。而會去讀取該行的一個最新的可見快照。
MVCC的實現主要依賴的是undo log和read view(事務鏈表)
咱們知道undo log的行記錄中有三個隱藏字段:分別對應該行的rowid、事務號db_trx_id和回滾指針db_roll_ptr,其中db_trx_id表示最近修改的事務的id,db_roll_ptr指向回滾段中的undo log。 根據行爲的不一樣,undo log分爲兩種
事務鏈表,mysql中的事務在開始到提交的階段都會被保持在一個叫trx_sys的事務鏈表中啊,事務鏈表中保持的都是還未提交的事務,事務一旦被提交,就會從鏈表中刪除該事務。
說道這裏,你們就應該清楚了爲何RR能夠避免不能夠重複讀而RC不行,由於二者獲取事務鏈表的方式不一樣,RC每一個語句都會從新獲取,所以能夠讀取到其餘事務最新提交的數據。 咱們能夠經過show engine innodb status語句查看事務鏈表
咱們知道事務開啓的時候會獲取事務鏈表,這個類中存儲了當前鏈表中最大的事務ID和最小的事務id。 假設當前活躍的事務鏈表以下:
ct-trx-->trx11 --> trx10 --> trx9 --> trx5 --> trx2;
複製代碼
ct-trx表示當前事務id,對應的read_view數據結構以下
read_view->creator_trx_id = ct-trx;
read_view->up_limit_id = trx2; 低水位
read_view->low_limit_id = trx11; 高水位
read_view->trx_ids = [trx11, trx10, trx9, trx5, trx2];
複製代碼