在實現上,數據庫裏面會建立一個視圖,訪問的時候以視圖的邏輯結果爲準。在「可重複 讀」隔離級別下,這個視圖是在事務啓動時建立的,整個事務存在期間都用這個視圖。 在「讀提交」隔離級別下,這個視圖是在每一個 SQL 語句開始執行的時候建立的。這裏須要 注意的是,「讀未提交」隔離級別下直接返回記錄上的最新值,沒有視圖概念;而「串行 化」隔離級別下直接用加鎖的方式來避免並行訪問。
每條記錄在更新的時候都會同時記錄一條回滾操做(也就是說redo log會記錄,undo log也會同時記錄).mysql
記錄上的最新值,經過回滾操做,均可以獲得前一個狀態的值。sql
回滾日誌刪除問題.數據庫
在不須要的時候才刪除。也就是說,系統會判斷,當沒有事務再須要用到這些回滾日誌時,回滾日誌會被刪除。 何時纔不須要了呢?就是當系統裏沒有比這個回滾日誌更早的 read-view 的時候。
回滾日誌存儲位置數組
在 MySQL 5.5 及之前的版本,回滾日誌是跟數據字典一塊兒放在 ibdata 文件裏的(系統表空間),即便長 事務最終提交,回滾段被清理,文件也不會變小。我見過數據只有 20GB,而回滾段有 200GB 的庫。最終只好爲了清理回滾段,重建整個庫。
回滾流程session
長事務意味着系統裏面會存在很老的事務視圖。因爲這些事務隨時可能訪問數據庫裏面的任何數據,因此這個事務提交以前,數據庫裏面它可能用到的回滾記錄都必須保留,這就會致使大量佔用存儲空間。spa
還佔用鎖資源,也可能拖垮整個庫線程
詳解日誌
好比,在某個時刻(今天上午9:00)開啓了一個事務A(對於可重複讀隔離級別,此時一個視圖read-view A也建立了),這是一個很長的事務…… 事務A在今天上午9:20的時候,查詢了一個記錄R1的一個字段f1的值爲1…… 今天上午9:25的時候,一個事務B(隨之而來的read-view B)也被開啓了,它更新了R1.f1的值爲2(同時也建立了一個由2到1的回滾日誌),這是一個短事務,事務隨後就被commit了。 今天上午9:30的時候,一個事務C(隨之而來的read-view C)也被開啓了,它更新了R1.f1的值爲3(同時也建立了一個由3到2的回滾日誌),這是一個短事務,事務隨後就被commit了。 …… 到了下午3:00了,長事務A尚未commit,爲了保證事務在執行期間看到的數據在先後必須是一致的,那些老的事務視圖、回滾日誌就必須存在了(read-view B,read-view C),這就佔用了大量的存儲空間。 源於此,咱們應該儘可能不要使用長事務。
select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60code
顯式啓動事務語句orm
begin 或 start transaction。 配套的提交語句是 commit, 回滾語句是 rollback。
set autocommit=0,會將線程的自動提交關掉.
意味着若是你只執行一個 select 語句,這個事務就啓動了,並且並不會自動提交。這個事務持續存在直到你主 動執行 commit 或 rollback 語句,或者斷開鏈接。
select也是事物
mysql> CREATE TABLE `t` ( `id` int(11) NOT NULL, `k` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB; insert into t(id, k) values(1,1),(2,2);
在可重複讀RR隔離級別模式下,begin/start transaction 命令並非一個事務的起點,在執行到它們以後的第一個操做 InnoDB 表的語句,事務才真正啓動。若是你想要立刻啓動一個事務,可使用 start transaction with consistent snapshot 這個命令。
上面圖1執行的結果是:
在可重複讀隔離級別下,事務在啓動的時候就「拍了個快照」。注意,這個快照是基於整庫的。
InnoDB 裏面每一個事務有一個惟一的事務 ID,叫做 transaction id。它是在事務開始的時候向 InnoDB 的事務系統申請的,是按申請順序嚴格遞增的。
每行數據也都是有多個版本的,涉及到transaction id
每次事務更新數據的時候,都會生成一個新的數據版本,而且把 transaction id 賦值給這個數據版本的事務 ID,記爲 row trx_id
同時,舊的數據版本要保留,而且在新的數據版本中,可以有信息能夠直接拿到它。
也就是說,數據表中的一行記錄,其實可能有多個版本 (row),每一個版本有本身的 row trx_id。
圖中虛線框裏是同一行數據的 4 個版本,當前最新版本是 V4,k 的值是 22,它是被transaction id 爲 25 的事務更新的,所以它的 row trx_id 也是 25。
前面的文章不是說,語句更新會生成 undo log(回滾日誌)嗎?那麼,<font color=red>undo log 在哪呢?</font>
實際上,<font color=red>圖 2 中的三個虛線箭頭,就是 undo log</font>;而 V一、V二、V3 並非物理上真實存 在的,而是每次須要的時候根據當前版本和 undo log 計算出來的。好比,須要 V2 的時 候,就是經過 V4 依次執行 U三、U2 算出來。
按照可重複讀的定義,一個事務啓動的時候,可以看到全部已經提交的事務結果。可是以後,這個事務執行期間,其餘事務的更新對它不可見。
一個事務只須要在啓動的時候聲明說,「以我啓動的時刻爲準,若是一個數據版本是在我啓動以前生成的,就認;若是是我啓動之後才生成的,我就不認,我必需要找到它的上一個版本」。 固然,若是「上一個版本」也不可見,那就得繼續往前找。還有,若是是這個事務本身更 新的數據,它本身仍是要認的。
在實現上, InnoDB 爲每一個事務構造了一個數組,用來保存這個事務啓動瞬間,當前正 在「活躍」的全部事務 ID。「活躍」指的就是,啓動了但還沒提交。數組裏面事務 ID 的最小值記爲低水位,當前系統裏面已經建立過的事務 ID 的最大值加 1 記爲高水位。這個視圖數組和高水位,就組成了當前事務的一致性視圖(read-view)。
當開啓事務時,須要保存活躍事務的數組(A),而後獲取高水位(B)二者中間會不會產生新的事務?
<font color=red>數據版本的可見性規則,就是基於數據的 row trx_id 和這個一致性視圖的對比結果獲得 的。這個視圖數組把全部的 row trx_id 分紅了幾種不一樣的狀況。</font>
對於當前事務的啓動瞬間來講,假設當前trx id爲98 , 在當前事務開始後,計算活躍事務以前又產生了個新事務trx id爲99沒有commit,假設活躍事務的id組成的數據爲下面的數組[80,88,99],此時事務80/88/99爲活躍事務,99爲當前系統中事務最大ID, 高水位100是當前系統最大事務id99加1計算出來的,則會有如下幾種可能:
若是落在綠色部分,表示這個版本是已提交的事務或者是當前事務本身生成的,這個數據是可見的; 即80之前的事務均可見
若是落在紅色部分,表示這個版本是由未來啓動的事務生成的,是確定不可見的; 100及100之後的事務都不可見
若是落在黃色部分,那就包括兩種狀況
a. 若 row trx_id 在數組中,表示這個版本是由還沒提交的事務生成的,不可見; 80/88/99爲活躍事務,不可見
b. 若 row trx_id 不在數組中,表示這個版本是已經提交了的事務生成的,可見。80~99中間,去除80/88/99,好比81等其他的是可見的.
InnoDB 利用了「全部數據都有多個版本」的這個特性,利用數據可見性規則實現了「秒級建立快照」的能力。
爲何會出現sessionB查詢到的k值爲3,sessionA查詢到的k值爲1呢,根據上面的數據可見性分析以下:
這裏,咱們不妨作以下假設:
事務 A 的視圖數組就是 [99,100], 事務 B 的視圖數組是 [99,100,101], 事務 C 的視 圖數組是 [99,100,101,102]。
從圖中能夠看到,第一個有效更新是事務 C,把數據從 (1,1) 改爲了 (1,2)。這時候,這個數據的最新版本的 row trx_id 是 102,而 90 這個版本已經成爲了歷史版本。 第二個有效更新是事務 B,把數據從 (1,2) 改爲了 (1,3)。這時候,這個數據的最新版本 (即 row trx_id)是 101,而 102 又成爲了歷史版本。{備註:按理說事務B是[99,100,101],此時找到(1,2)的時候判斷出row trx_id=102,比它本身的高水位大,處於紅色區域,不可見,應該往前找,找(1,1)版本,可是此時它倒是找的(1,2)row trx_id=102的版本,這是什麼緣由的,是由於更新都是「當前讀」(current read),當前讀這個概念下面解釋} 你可能注意到了,在事務 A 查詢的時候,其實事務 B 尚未提交,可是它生成的 (1,3) 這 個版本已經變成當前版本了。但這個版本對事務 A 必須是不可見的,不然就變成髒讀了。 好,如今事務 A 要來讀數據了,它的視圖數組是 [99,100]。固然了,讀數據都是從當前版本讀起的。因此,事務 A 查詢語句的讀數據流程是這樣的: 找到 (1,3) 的時候,判斷出 row trx_id=101,比高水位大,處於紅色區域,不可見; 接着,找到上一個歷史版本,一看 row trx_id=102,比高水位大,處於紅色區域,不可見; 再往前找,終於找到了(1,1),它的 row trx_id=90,比低水位小,處於綠色區域,可見。 這樣執行下來,雖然期間這一行數據被修改過,可是事務 A 不論在何時查詢,看到這 行數據的結果都是一致的,因此咱們稱之爲一致性讀。
上面的分析判斷規則是從代碼邏輯直接轉譯過來的,一個數據版本,對於一個事務視圖來講,除了本身的更新老是可見之外,有三種狀況:
1. 版本未提交,不可見; 2. 版本已提交,可是是在視圖建立後提交的,不可見; 3. 版本已提交,並且是在視圖建立前提交的,可見。
<font color=red>事務 B 的 update 語句,若是按照一致性讀,好像結果不對 哦?事務 B 的視圖數組是先生成的,以後事務 C 才提交,不是應該看不見 (1,2) 嗎,怎麼能算出 (1,3) 來?</font>
事務 C’的不一樣是,更新後並無立刻提交,在它提交前,事務 B 的更新語句先發起了。前面說過了,雖然事務 C’還沒提交,可是 (1,2) 這個版本也已經生成了,而且是當前的 最新版本。那麼,事務 B 的更新語句會怎麼處理呢?
<font color=red>start transaction with consistent snapshot; 在都提交下與start transaction等效.</font>