0、undo物理存儲研究
1>ibdata第五個數據塊(系統事務表)中存儲着128個undo段的段頭塊的地址
2>每一個undo段頭塊有1024行,兩行記錄一個事務,一共可以記錄512個事務
3>一個數據行中存放XID、rollpointr
4>一個數據行被修改
1.新的事務ID
2.新的rollpointr
3.修改后數據
上面三部分數據都會進入到回滾塊中。詳細見:事務工作流程……
1、避免臟讀
1>在操作任何數據之前,首先將數據備份到undo頁中,然后再進行數據的修改;
2>不能看到其他會話未提交的數據;
3>當要讀取被修改數據頁數據行時,會指向備份在undo頁中的數據,而避免臟讀。
2、事務的回滾
undo最基本的作用是rollback,舊數據先放到undo里面存放,等rollback時候再將undo里面的數據回滾回來。
3、DML不阻塞讀
提高並發,如果別的用戶正在修改某數據頁,事務沒有提交,現需要讀該數據頁,發現事務沒有提交,就根據數據行上的rollpointer找到原來的數據(在undo頁上),結合該數據頁將數據返給用戶。
4、MVCC(一致性讀)
多版本控制Multiversion Concurrency Control
5、崩潰恢復(回滾)
自動回滾未提交事務;
redo前滾,undo回滾,未提交事務主動回滾,未提交事務信息在事務槽里寫着。數據庫在運行期間,突然崩了,數據庫啟動之后,需要redo前滾,就會有很多未提交的事務(事務的會話斷了,不可能繼續完成了,就需要對未提交事務回滾了 )也滾回來了:讀取未提交事務事務槽信息,把未提交事務回滾。
1、分配一個事務ID,事務ID依次遞增
2、分配一個事務槽,將事務信息寫入事務槽中
3、開始修改數據行,數據行中存儲事務ID、修改前數據所使用的回滾塊地址
4、回滾塊中存放修改前的數據
5、屬於一個事務的各個回滾塊鏈接起來
6、回滾段段頭塊中的地址指向回滾塊鏈表中的最后一個回滾塊
7、一個回滾塊只能存放一個事務的數據
8、事務提交就是在事務槽中將事務狀態改成已提交
理解不深(轉自:http://www.cnblogs.com/chenpingzhao/p/5065316.html)
1、MVCC的幾個特點:
1>每行數據都存在一個版本,每次數據更新時都更新該版本
2>修改時Copy出當前版本隨意修改,各個事務之間無干擾
3>保存時比較版本號,如果成功(commit),則覆蓋原記錄;失敗則放棄copy(rollback)
也就是每行都有版本號,保存時根據版本號決定是否成功,聽起來含有樂觀鎖的味道;
2、非阻塞讀Innodb的實現方式:
4>事務以排他鎖的形式修改原始數據
5>把修改前的數據存放於undo log,通過回滾指針與主數據關聯
6>修改成功(commit)啥都不做,失敗則恢復undo log中的數據(rollback)
3、區別理解
二者最本質的區別是,當修改數據時是否要排他鎖定;
Innodb的實現真算不上MVCC,因為並沒有實現核心的多版本共存,undo log中的內容只是串行化的結果,記錄了多個事務的過程,不屬於多版本共存。但理想的MVCC是難以實現的,當事務僅修改一行記錄使用理想的MVCC模式是沒有問題的,可以通過比較版本號進行回滾;但當事務影響到多行數據時,理想的MVCC據無能為力了。
比如,如果Transaciton1執行理想的MVCC,修改Row1成功,而修改Row2失敗,此時需要回滾Row1,但因為Row1沒有被鎖定,其數據可能又被Transaction2所修改,如果此時回滾Row1的內容,則會破壞Transaction2的修改結果,導致Transaction2違反ACID。
理想MVCC難以實現的根本原因在於企圖通過樂觀鎖代替二段提交。修改兩行數據,但為了保證其一致性,與修改兩個分布式系統中的數據並無區別,而二提交是目前這種場景保證一致性的唯一手段。二段提交的本質是鎖定,樂觀鎖的本質是消除鎖定,二者矛盾,故理想的MVCC難以真正在實際中被應用,Innodb只是借了MVCC這個名字,提供了讀的非阻塞而已。
redo前滾、undo回滾……
1、兩個保證
1>數據庫保證所有已提交事務的redolog都寫入到了redo logfile中
2>數據庫保證所有臟塊的redolog都早redo logfile中,只有臟塊寫入磁盤以后,redo log才能被覆蓋
結論:redo log有足夠的能力將該有的臟塊都構造出來
2、redo log如何確定使用哪些日志來構造臟塊
1>起點:checkpoint開始
1.innodb buffer pool中存在一條flush list鏈表
2.這個鏈表最舊的那一端對應的redo log就是將來數據庫崩潰恢復redolog前滾的起點
3.clean線程周期性的將需要flush list最舊的那一個臟塊對應的redo log地址寫入到ibdata中
2>終點:redo log current最后一條日志
3、崩潰恢復的過程
第一個階段是前滾:
前滾對應的redo log的啟動和終點已經確定:redolog不害怕多跑,因為redolog有版本,數據塊有版本,如果redolog比數據塊還要舊,就采用空跑的方式
第二個階段是回滾:
崩潰時沒有提交的事務也會被回滾回來,這些事務都屬於死事務,因為這些事務對應的用戶會話已經結束,后續讀到對應的數據塊,發現數據塊上有未提交事務,讀取未提交事務對應的事務信息,發現已經是死事務,主動回滾這個數據塊;
碰到死事務對應的數據塊,誰使用誰回滾。
1、長事務的危害
開始一個事務,長時間不提交,所有的數據都需要undo去保存,可能產生很多undo數據,而且還不能被清空覆蓋,一直保存到該事務提交。很嚴重。
2、大事務的危害
修改批量的數據,占用過多的undo頁(產生undo數據主要是delete產生的,但MySQL對delete做了優化,添加deleted_flag標志位,減少delete對undo的使用),所以危害不是很大,而且正常的事務場景也不會出現大事務。
3、如何判斷大事務和長事務
mysql> desc information_schema.INNODB_TRX;
關鍵參數:
1.trx_started:事務開始的時間,如果時間較當前差很遠說明是長事務
2.trx_rows_modified:事務修改的行數量,如果值很大說明是大事務
3.trx_mysql_thread_id:該事務所對應的線程id(kill線程清理事務)
4、解決棘手的大事務、長事務
處理大事務:kill -9 mysql_process_id:處理大事務,就直接干掉mysql實例,不會主動去回滾所以速度塊,然后重啟。(除非迫不得已,否則不那么干,生產環境重啟服務器是天大的事情)
處理長事務:如果開始的時間不是很長,並且行數不是很多,直接kill掉該事務所在的線程。(在數據庫里kill,可能反應慢)
1、實現undo分離
在MySQL5.5以及之前,除了數據量自然增長之外,一旦出現大事務,其所使用的undo log占用的空間就會一直在ibdata1里面存在,即使這個事務已經關閉。隨着數據庫上線時間越來越長,ibdata1文件會越來越大,物理備份文件越來越大……
MySQL 5.6增加了如下參數,可以把undo log從ibdata1移出來單獨存放。
mysql> show variables like '%undo%'; +--------------------------+------------+
| Variable_name | Value |
+--------------------------+------------+
| innodb_max_undo_log_size | 1073741824 |
| innodb_undo_directory | ./ |
| innodb_undo_log_truncate | ON |
| innodb_undo_logs | 128 |
| innodb_undo_tablespaces | 3 |
+--------------------------+------------+
5 rows in set, 1 warning (0.00 sec)
1> innodb_undo_directory:
指定單獨存放undo表空間的目錄,默認為.(即datadir),可以設置相對路徑或者絕對路徑。該參數實例初始化之后雖然不可直接改動,但是可以通過先停庫,修改配置文件,然后移動undo表空間文件的方式去修改該參數;
2> innodb_undo_tablespaces:
指定單獨存放的undo表空間個數,例如如果設置為3,則undo表空間為undo001、undo002、undo003,每個文件初始大小默認為10M。該參數實例初始化之后不可改動;
3> innodb_undo_logs:
指定回滾段的個數(早期版本該參數名字是innodb_rollback_segments),默認128個。每個回滾段可同時支持1024個在線事務。這些回滾段會平均分布到各個undo表空間中。該變量可以動態調整,但是物理上的回滾段不會減少,只是會控制用到的回滾段的個數。
操作undo分離:實際使用方面,在初始化實例之前,我們只需要設置innodb_undo_tablespaces參數(建議大於等於3)即可將undo log設置到單獨的undo表空間中。
2、在線收縮undo表空間
MySQL 5.7引入了新的參數,innodb_undo_log_truncate,開啟后可在線收縮拆分出來的undo表空間,支持動態設置。
1>實現在線收縮undo的條件
1.innodb_undo_tablespaces>=2:因為truncate undo表空間時,該文件處於inactive狀態,如果只有1個undo表空間,那么整個系統在此過程中將處於不可用狀態;
2.innodb_undo_logs>=35(默認128):因為在MySQL 5.7中,第一個undo log永遠在共享表空間中,另外32個undo log分配給了臨時表空間(即ibtmp1),至少還有2個undo log才能保證2個undo表空間中每個里面至少有1個undo log;
2>滿足以上2個條件后
innodb_undo_log_truncate=ON,即可開啟undo表空間的自動truncate
1.innodb_max_undo_log_size:undo表空間文件超過此值即標記為可收縮,默認1G,truncate之后空間縮小到10M;
2.innodb_purge_rseg_truncate_frequency:指定purge操作被喚起多少次之后才釋放rollback segments。當undo表空間里面的rollback segments被釋放時,undo表空間才會被truncate。(最大是128,最小是1,默認為128)該參數越小,undo表空間被嘗試truncate的頻率越高。