UNDO及MVCC、崩潰恢復


0、undo物理存儲研究

  1>ibdata第五個數據塊(系統事務表)中存儲着128個undo段的段頭塊的地址

  2>每一個undo段頭塊有1024行,兩行記錄一個事務,一共可以記錄512個事務

  3>一個數據行中存放XID、rollpointr

  4>一個數據行被修改

    1.新的事務ID

    2.新的rollpointr

    3.修改后數據

  上面三部分數據都會進入到回滾塊中。詳細見:事務工作流程……

 

一、UNDO特性

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、事務提交就是在事務槽中將事務狀態改成已提交

 

三、MVCC原理機制

理解不深(轉自: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,可能反應慢)

 

六、UNDO的優化處理

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的頻率越高。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM