1. 程式人生 > >MySQL必知必會:簡介undo log、truncate、以及undo log如何幫你回滾事務

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

[TOC] #### 一、前言 在整理undo log筆記前我感覺它應該是在 undo、redo、bin log三者中需要整理的內容最少的。但是實際上並不是想象的那麼簡單。 關於undo log需要整理的兩大塊知識點分別是:

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

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

#### 二、undo log表空間 如果你看了白日夢前面的分享的筆記,你肯定知道了什麼表空間。其實所謂的表空間其實是真實存在於磁碟上的資料檔案。而這裡的所說的undolog表空間其實就是磁碟上專門存放undo log的檔案。 表空間由很多 segment(段) 組成,而這眾多的段中有一種就是 undo segment。 預設情況下undo segment 會存放於系統表空間中,或者說undo log預設會記錄在共享表空間檔案中(檔案真實存在)。 但是MySQL也提供了引數,讓你可以控制MySQL講undo log寫入到單獨的表空間檔案中去。尤其是當你使用SSD這種儲存時,尤為推薦將undo log從共享表空間中拿出去。
#### 三、關於undo log預設的配置 預設情況下undo log tablespace個數是0,也就是說如果你不干涉MySQL的配置。那麼MySQL就會幫你將undo log記錄到共享表空間中。 ![](https://img2020.cnblogs.com/blog/1496926/202011/1496926-20201130135343845-1956461503.png) MySQL預設的配置檔案 my.cnf 長下面這樣: ![](https://img2020.cnblogs.com/blog/1496926/202011/1496926-20201130135344993-1010751666.png) 如果你現在僅僅是安裝了MySQL,而不曾啟動過mysql,那你去datadir中檢視會發現它只是個空目錄。 但是當你啟動過MySQL之後,再去這個datadir中檢視會發現裡面多了很多檔案,其中就包括共享表空間檔案ibdata1(但是沒有undolog表空間檔案)。如下: ![](https://img2020.cnblogs.com/blog/1496926/202011/1496926-20201130135346220-1176247063.png)
#### 四、如何將undo log放到單獨的表空間 如果你想將undo log拿到undo log表空間檔案中。那你可以像下面這樣修改MySQL的配置檔案my.cnf ![](https://img2020.cnblogs.com/blog/1496926/202011/1496926-20201130135347713-1352405584.png) 修改完後通過如下命令啟動mysql ```bash systemctl start mysqld.service ``` 但是你會發現啟動不了,如果你去排查原因就會發現:因為曾經初始化過 datadir 目錄中的檔案,你新增的新配置innodb_undo_tablespaces和原來的配置是衝突的,需要開闢新的表空間檔案,所以導致啟動失敗。 解決的方式:簡單粗暴的將換個datadir檔案就好啦,所以如果你從一開始就想將undolog拿到單獨的表空間中,那麼最好從一開始就將這個配置新增進去,否則還是挺麻煩的。 ![](https://img2020.cnblogs.com/blog/1496926/202011/1496926-20201130135348792-1051464316.png)
#### 本文是MySQL專題第14篇,全文近100篇 本文是第14篇,全文近100篇,[點選檢視目錄](https://www.cnblogs.com/ZhuChangwu/p/13953209.html)
#### 五、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 |
#### 六、什麼是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
#### 七、如果做 undo log truncate 前提:必須使用獨立的undo表空間 然後配合如下的引數輔助: ![](https://img2020.cnblogs.com/blog/1496926/202011/1496926-20201130135349855-579426094.png) 建立資料表: ```bash create table test ( id int primary key auto_increment, name varchar(64) ); ``` 然後不斷的往這個測試表中插入資料 ```bash 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已經被標記為可回收了。 ![](https://img2020.cnblogs.com/blog/1496926/202011/1496926-20201130135351339-191437303.png) 當事物提交時,undo log並不會被立即刪除,因為可能存在其它的事物需要使用undo log將資料回滾到之前的版本。最終是否可以刪除undo log由purge執行緒決定。 為了讓pruge執行緒執行,可以執行如下的sql ```bash delete from test limit 1; ``` ![](https://img2020.cnblogs.com/blog/1496926/202011/1496926-20201130135352540-917882408.png)
#### 八、undo log的型別 undo log有兩種型別,分別是 insert undo log 和 update undo log。 前者記錄的是insert 語句對應的undo log。 後者對應的是 update、delete 語句對應的undo log。
#### 九、insert undo log 長啥樣? 對於 insert 型別的sql,會在undo log中記錄下方才你insert 進來的資料的ID,根據ID完成精準的刪除。 insert 型別的undo log長下面這樣: ![](https://img2020.cnblogs.com/blog/1496926/202011/1496926-20201130135353227-1301497923.jpg) 可能你打眼一看上圖就能知道各部分都有啥用。 但是,不知道你會不會納悶這樣一個問題:不是說對於insert 型別的undo log MySQL記錄的是方才插入行ID嗎?怎麼上圖整出來的了這麼多Col1、Col2、Col2。 其實是MySQL設計的很周到,因為它是針對聯合主鍵設計的。
#### 十、一條update undo log 長啥樣? 一條update sql對應undolog長如下這樣。 其實我感覺沒必要記住這個圖,記住了也會忘。大概看一下它長什麼樣子就好。 > 重點是下面會分享的,undo log鏈條,並且你得知道這個鏈條可以幫你實現事務的回滾 ![](https://img2020.cnblogs.com/blog/1496926/202011/1496926-20201130135354101-1982078583.jpg)
​ #### 十一、事物是如何回滾的?(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 鏈條 ![](https://img2020.cnblogs.com/blog/1496926/202011/1496926-20201130135355248-220727185.png) 有一個注意點:因為單純的insert sql不涉及多MVCC的能力。 所以一旦事務commit,這條insert undo log就可以直接刪除了。 **再看一個update型別的undo log** 為了方便畫圖,重點突出鏈條的概念我省略了update undo log的部分內容 一個事物A開啟後插圖了一條記錄:name = tom,MySQL會記錄下這樣一條undo log ![](https://img2020.cnblogs.com/blog/1496926/202011/1496926-20201130135355845-138572773.jpg) 隨後先後來了兩個事物: 事物B,事物ID=61,它執行sql將name 改成jerry。 事物C,事物ID=62,它執行sql將name 改成tom。 於是MySQL記錄下這樣一條新的undo log ![](https://img2020.cnblogs.com/blog/1496926/202011/1496926-20201130135357078-895410377.png) 你可以看到,MySQL會將對一行資料的修改undo log通過DATA_ROLL_ID指標連線在一起形成一個undo log連結串列鏈條。這樣事物C如果想回滾,他會將資料回滾到事物B修改後的狀態。而事物B想回滾他會將資料回滾到事物A的狀態。
#### 十二、問個問題 > 在前面的文章中有專門的介紹:表空間、資料表、資料區、資料頁。 > > 表空間、資料頁存在於物理層面。SQL想要修改的資料表、id=xxx的行都是邏輯上的。 而 undo log 幫你做的是邏輯上的資料回滾,而不是物理(資料頁)上是資料回滾。 其實在邏輯層和物理層都能回滾。 那,你有沒有想過為什麼undo回滾的層面要設定在邏輯層而不是物理層的資料頁級別? 原因你可以這樣想:假如一個數據頁中存了300行資料,而你的update語句其實可能僅僅是更新了這個資料頁中的一行。但是資料庫可不一定是你自己在用!很可能有其他的使用者也在使用並且修改了該資料頁中的另外200行。那這時如果你基於資料頁層面回滾,豈不是會將別人的不想回滾的資料給改錯?
#### 十三、補充: 在MySQL5.6、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

#### 本文是MySQL專題第14篇,全文近100篇 本文是第14篇,全文近100篇,[點選檢視目錄](https://www.cnblogs.com/ZhuChangwu/p/13953209.html)