1. 程式人生 > >MySQL之UNDO及MVCC、崩潰恢復

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

@author:http://www.cnblogs.com/geaozhang/

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