MySQL必知必會:簡介undo log、truncate、以及undo log如何幫你回滾事物

1、前言

在整理undo log筆記前我感受它應該是在 undo、redo、bin log三者中須要整理的內容最少的。可是實際上並非想象的那麼簡單。html

關於undo log須要整理的兩大塊知識點分別是:mysql

一、簡介undo log、truncate、以及undo log如何幫你回滾事物(本篇分享) sql

二、undolog鏈條、ReadView、以及undo log如何幫你實現MVCC多版本併發控制(明天分享)

數據庫

2、undo log表空間

若是你看了白日夢前面的分享的筆記,你確定知道了什麼表空間。其實所謂的表空間實際上是真實存在於磁盤上的數據文件。而這裏的所說的undolog表空間其實就是磁盤上專門存放undo log的文件。bash

表空間由不少 segment(段) 組成,而這衆多的段中有一種就是 undo segment。併發

默認狀況下undo segment 會存放於系統表空間中,或者說undo log默認會記錄在共享表空間文件中(文件真實存在)。測試

可是MySQL也提供了參數,讓你能夠控制MySQL講undo log寫入到單獨的表空間文件中去。尤爲是當你使用SSD這種存儲時,尤其推薦將undo log從共享表空間中拿出去。spa


3、關於undo log默認的配置

默認狀況下undo log tablespace個數是0,也就是說若是你不干涉MySQL的配置。那麼MySQL就會幫你將undo log記錄到共享表空間中。線程

MySQL默認的配置文件 my.cnf 長下面這樣:設計

若是你如今僅僅是安裝了MySQL,而未曾啓動過mysql,那你去datadir中查看會發現它只是個空目錄。

可是當你啓動過MySQL以後,再去這個datadir中查看會發現裏面多了不少文件,其中就包括共享表空間文件ibdata1(可是沒有undolog表空間文件)。以下:


4、如何將undo log放到單獨的表空間

若是你想將undo log拿到undo log表空間文件中。那你能夠像下面這樣修改MySQL的配置文件my.cnf

修改完後經過以下命令啓動mysql

systemctl start mysqld.service

可是你會發現啓動不了,若是你去排查緣由就會發現:由於曾經初始化過 datadir 目錄中的文件,你添加的新配置innodb_undo_tablespaces和原來的配置是衝突的,須要開闢新的表空間文件,因此致使啓動失敗。

解決的方式:簡單粗暴的將換個datadir文件就好啦,因此若是你從一開始就想將undolog拿到單獨的表空間中,那麼最好從一開始就將這個配置添加進去,不然仍是挺麻煩的。


文章公衆號首發,持續更新中

本文是第14篇,全文近100篇,點擊查看目錄


5、rollback segment

提到了undo log,就不得不說roll back segment這個知識點了。它並不難理解,你能夠閱讀下面的介紹瞭解一下。

InnoDB存儲引擎會先初始化好rollback segment(回滾段),在每一個回滾段中會記錄N個undo log segment,而咱們說的undo log就是在 undo log segment中申請出來的!

在早期的InnoDB版本中只有一個rollback segment,所以在同一時刻它支持的在線事物的上限被限制在1024個。

在MySQL5.7中回滾段已經支持到了128個(上限是128)。其中32個分配給臨時表空間。剩下的96個回滾段能夠分配給修改常規表中數據的事務。

用戶能夠經過參數innodb_rollback_segments調整回滾段的數量。

另外,咱們上面提到的: 每一個回滾段中都記錄了N個undolog segment, 這裏的N和數據頁大小有關

InnoDB頁面大小 回滾段中的撤消插槽數(InnoDB頁面大小/ 16)
4096 (4KB) 256
8192 (8KB) 512
16384 (16KB) 1024
32768 (32KB) 204
65536 (64KB) 4096

6、什麼是undo log truncate

truncate意爲:截斷

其實結合 truncate table sql,就能更好的理解這個概念。當你不須要某個表中的數據時,你能夠執行truncate sql將表中的數據清空掉。一樣的undo log的truncate機制本質上就是爲undo log 表空間文件瘦身,將不須要的undo log清理掉。

在MySQL 5.6(包括5.6)以前Undo tablespace裏面的undo數據文件是沒法收縮的。也就是說在實例的運行過程當中若是遇到有大的事務,會把undo log的文件撐的很是大。浪費大量的空間甚至會把磁盤打爆。同時也增長了數據庫物理備份的時間。

在MySQL5.7中容許用戶在線truncate undo log


7、若是作 undo log truncate

前提:必須使用獨立的undo表空間

而後配合以下的參數輔助:

建立數據表:

create table test (
	id int primary key auto_increment, 
	name varchar(64)
);

而後不斷的往這個測試表中插入數據

insert into test(name) values(repeat('a',64));
insert into test(name) select name from test;

一邊插入一邊觀察undo 表空間文件的變化:你會發現undo003這個表空間文件已經超過了參數:innodb_max_undo_log_size=100M 指定的範圍,意味着這個undolog已經被標記爲可回收了。

當事物提交時,undo log並不會被當即刪除,由於可能存在其它的事物須要使用undo log將數據回滾到以前的版本。最終是否能夠刪除undo log由purge線程決定。

爲了讓pruge線程運行,能夠執行以下的sql

delete from test limit 1;


8、undo log的類型

undo log有兩種類型,分別是 insert undo log 和 update undo log。

前者記錄的是insert 語句對應的undo log。

後者對應的是 update、delete 語句對應的undo log。


9、insert undo log 長啥樣?

對於 insert 類型的sql,會在undo log中記錄下方纔你insert 進來的數據的ID,根據ID完成精準的刪除。

insert 類型的undo log長下面這樣:

可能你打眼一看上圖就能知道各部分都有啥用。

可是,不知道你會不會納悶這樣一個問題:不是說對於insert 類型的undo log MySQL記錄的是方纔插入行ID嗎?怎麼上圖整出來的了這麼多Col一、Col二、Col2。

實際上是MySQL設計的很周到,由於它是針對聯合主鍵設計的。


10、一條update undo log 長啥樣?

一條update sql對應undolog長以下這樣。

其實我感受不必記住這個圖,記住了也會忘。大概看一下它長什麼樣子就好。

重點是下面會分享的,undo log鏈條,而且你得知道這個鏈條能夠幫你實現事務的回滾


11、事物是如何回滾的?(undo log 鏈條)

舉個例子:

對於 insert 類型的sql,會在undo log中記錄下方纔你insert 進來的數據的ID,當你想roll back時,根據ID完成精準的刪除。

對於delete類型的sql,會在undo log中記錄方纔你刪除的數據,當你回滾時會將刪除前的數據insert 進去。

對於update類型的sql,會在undo log中記錄下修改前的數據,回滾時只須要反向update便可。

對於select類型的sql,別費心了,select不須要回滾。

先看一個簡單的insert undo log 鏈條

有一個注意點:由於單純的insert sql不涉及多MVCC的能力。

因此一旦事務commit,這條insert undo log就能夠直接刪除了。

再看一個update類型的undo log

爲了方便畫圖,重點突出鏈條的概念我省略了update undo log的部份內容

一個事物A開啓後插圖了一條記錄:name = tom,MySQL會記錄下這樣一條undo log

隨後前後來了兩個事物:

事物B,事物ID=61,它執行sql將name 改爲jerry。

事物C,事物ID=62,它執行sql將name 改爲tom。

因而MySQL記錄下這樣一條新的undo log

你能夠看到,MySQL會將對一行數據的修改undo log經過DATA_ROLL_ID指針鏈接在一塊兒造成一個undo log鏈表鏈條。這樣事物C若是想回滾,他會將數據回滾到事物B修改後的狀態。而事物B想回滾他會將數據回滾到事物A的狀態。


12、問個問題

在前面的文章中有專門的介紹:表空間、數據表、數據區、數據頁。

表空間、數據頁存在於物理層面。SQL想要修改的數據表、id=xxx的行都是邏輯上的。

而 undo log 幫你作的是邏輯上的數據回滾,而不是物理(數據頁)上是數據回滾。

其實在邏輯層和物理層都能回滾。

那,你有沒有想過爲何undo回滾的層面要設置在邏輯層而不是物理層的數據頁級別?

緣由你能夠這樣想:假如一個數據頁中存了300行數據,而你的update語句其實可能僅僅是更新了這個數據頁中的一行。可是數據庫可不必定是你本身在用!極可能有其餘的用戶也在使用而且修改了該數據頁中的另外200行。那這時若是你基於數據頁層面回滾,豈不是會將別人的不想回滾的數據給改錯?


十3、補充:

在MySQL5.六、MySQL5.7版本中能夠經過innodb_undo_tablespaces參數配置redo log表空間文件的個數,可是官網也有介紹這個參數在將來的MySQL版本中將會被廢棄,在MySQL8.0中初始化MySQL實例時會建立兩個默認的撤消表空間,而且可使用CREATE UNDO TABLESPACE語法建立其餘撤消表空間 。

可是無論怎麼樣,若是你使用的是MySQL5.7仍是推薦使用這些參數以及開啓undo log的自動truncate。


參考:

《MySQL技術內幕》

https://dev.mysql.com/doc/refman/5.7/en/innodb-undo-logs.html

https://dev.mysql.com/doc/refman/5.7/en/innodb-undo-tablespaces.html

https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_undo_tablespace


文章公衆號首發,持續更新中

本文是第14篇,全文近100篇,點擊查看目錄

相關文章
相關標籤/搜索