Mysql之DELETE操作對應(yīng)的undo日志方式
一、正常記錄鏈表
記錄的頭信息中的next_record屬性組成一個單向鏈表,我們把這個鏈表稱為正常記錄鏈表。
二、垃圾鏈表
被刪除的記錄其實(shí)也會根據(jù)記錄頭信息中的next_record屬性組成一個鏈表,只不過這個鏈表中的記錄所占用的存儲空間可以被重新利用,所以也稱這個鏈表為垃圾鏈表。
三、PAGE_FREE的作用
Page Header部分中有一個名為PAGE_FREE的屬性,它指向由被刪除記錄組成的垃圾鏈表中的頭節(jié)點(diǎn)。每刪除一條記錄,則該記錄都會插入到垃圾鏈表的頭節(jié)點(diǎn)處。
示例:有3條正常記錄和2條被刪除記錄,他們在頁中的記錄分布情況如下圖所示:
注:在垃圾鏈表中,這些記錄占用的存儲空間可以被重新利用。

四、刪除一條記錄的步驟
delete mark階段
僅僅將記錄的deleted_flag標(biāo)識位設(shè)置為1,但是這條記錄并沒有加入到垃圾鏈表中。
也就是說,這條記錄即不是正常記錄,也不是已刪除記錄。
在刪除語句所在的事務(wù)提交之前,被刪除的記錄一直都處于這種中間狀態(tài)(其實(shí)主要是為了實(shí)現(xiàn)MVCC的功能才這樣處理的)。
- 如下圖所示:

purge階段
當(dāng)該刪除語句所在的事務(wù)提交后,會有專門的線程來把該記錄從正常記錄鏈表中移除,并加入到垃圾鏈表中作為頭節(jié)點(diǎn)。
- 如下圖所示:

五、關(guān)于垃圾鏈的重用空間的知識點(diǎn)了解
1、PAGE_GARBAGE是做什么的
Page Header部分有一個名為PAGE_GARBAGE的屬性。該屬性記錄著當(dāng)前頁面中可重用存儲空間占用的總字節(jié)數(shù)。
每當(dāng)有已刪除記錄加入到垃圾鏈表后,都會把這個PAGE_GARBAGE屬性的值加上已刪除記錄占用的存儲空間大小。
2、如何重用垃圾鏈表的存儲空間
- PAGE_FREE指向垃圾鏈表的頭節(jié)點(diǎn),每當(dāng)新插入數(shù)據(jù)的時候:
- 首先:判斷垃圾鏈表頭節(jié)點(diǎn)記錄的存儲空間是否足夠容納這條新插入的記錄。如果可以容納則直接重用這條已刪除記錄的存儲空間。
- 其次:如果不能容納,則直接向頁面申請新的空間來存儲這條記錄。(是的,你沒看錯!并不會嘗試遍歷垃圾鏈表,以找到可以容納新記錄的節(jié)點(diǎn))
3、如果新插入的那條記錄記錄小于重用的記錄空間,那么會有一部分空間用不到,怎么處理?是否可以直接浪費(fèi)掉?
這種情況會頻繁發(fā)生,也就會隨著記錄越插越多而產(chǎn)生越來越多的空間碎片。只有當(dāng)頁面塊滿的時候,如果再插入一條新記錄,無法分配一條完整的記錄空間時,會先查看PAGE_GARBAGE的空間和剩余空間相加是否可以容納這條新的記錄,如果可以,InnoDB則會嘗試重新組織頁內(nèi)的記錄。即:先開辟一個臨時頁面,把原頁面內(nèi)的記錄依次挨著插入一遍到臨時頁,之后,再把臨時頁的內(nèi)容復(fù)制到本頁面,這樣就可以把那些碎片空間都釋放出來了。但是該操作比較耗費(fèi)性能。
由于一旦事務(wù)提交,我們也就不需要再回滾這個事務(wù)了,所以在設(shè)計(jì)undo日志時,只需要考慮delete mark這個階段所做的影響進(jìn)行回滾就可以了。
- TRX_UNDO_DEL_MARK_REC類型的undo日志結(jié)構(gòu)如下圖所示:

上圖解釋:
info bits:記錄頭信息的前4個比特的值。trx_id:舊記錄的trx_id值。roll_pointer:舊記錄的roll_pointer值。len of index_col_info:也就是下邊的【索引列各列信息】部分和本部分占用的存儲空間總和。- 索引列各列信息
<pos, len, value>列表:凡是被索引的列的各列信息。
4、什么TRX_UNDO_DEL_MARK_REC類型的undo日志保存舊記錄的trx_id值和roll_pointer值
保存舊記錄的trx_id值——為了采用事務(wù)id作為版本號,記錄每個undo日志所對應(yīng)的版本是多少。
保存舊記錄的roll_pointer值——可以通過undo日志的roll_pointer屬性找到上一次對該記錄進(jìn)行改動時產(chǎn)生的undo日志,因此可以將日志串成鏈表。這個鏈表就是版本鏈。
- 示例:新增一條記錄,然后再刪除這條記錄的完整操作過程,如下所示:

六、刪除操作生成undo日志的示例
1、 先插入兩條記錄
BEGIN; # 顯示開啟一個事務(wù),假設(shè)該事務(wù)的事務(wù)id為100 # 插入兩條記錄 INSERT INTO sys_user(id, name, city) VALUES(1, 'xz','北京市'), (2, 'tom','天津市'); # 刪除一條記錄 DELETE FROM sys_user WHERE id = 1;

2、上圖字段說明
(1)、索引列各列信息 <pos, len, value>列表<0, 4, 1>:
- 由于id列是主鍵,所以pos=0;
- 由于id列的類型是INT,所以len=4;
- 由于id=1,所以value=1;
(2)、索引列各列信息 <pos, len, value>列表<3, 4, ‘xz’>:
- 由于name列是二級索引,它排在id列、trx_id列、roll_pointer列之后,所以pos=3;
- 由于id列的類型是INT,所以len=4;
- 由于name=‘xz’,所以value=‘xz’;
(3)、len of index_col_info
- pos使用1字節(jié)來存儲。
- len使用1字節(jié)來存儲。
- value根據(jù)具體值,來判斷。比如:id=1,主鍵是INT占4個字節(jié),所以value使用4字節(jié)存儲。name=‘xz’,VARCHAR類型,所以value用4字節(jié)存儲。
- len of index_col_info本身占2個字節(jié)。
(4)、綜上所述,<0, 4, 1><3, 4, ‘xz’>占用空間等于:(1+1+4)+(1+1+4) =12,如下圖所示:

(5)、最后,再加上len of index_col_info屬性本身占2個字節(jié),所以總共14字節(jié)。即:len of index_col_info=14。
總結(jié)
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
記一次mysql5.7測試數(shù)據(jù)庫被刪表的問題
這篇文章主要介紹了記一次mysql5.7測試數(shù)據(jù)庫被刪表的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-11-11
MySQL數(shù)據(jù)庫復(fù)合查詢與內(nèi)外連接圖文詳解
本文詳細(xì)介紹了在SQL中進(jìn)行多表查詢的技術(shù),包括笛卡爾積、自連接、子查詢、內(nèi)連接和外連接等,文章還解釋了union和unionall的區(qū)別,以及如何在from子句中使用子查詢,這些技術(shù)對于處理復(fù)雜的數(shù)據(jù)庫查詢非常重要,可以有效地從不同表中提取和組合數(shù)據(jù),需要的朋友可以參考下2024-10-10
MySQL修改安全策略時報(bào)錯:ERROR?1193?(HY000)的解決辦法
這篇文章主要給大家介紹了關(guān)于MySQL修改安全策略時報(bào)錯:ERROR?1193?(HY000):?Unknown?system?variable?‘validate_password_policy‘的解決方法,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02
MySQL之使用UNION和UNION ALL合并兩個或多個SELECT語句的結(jié)果集
這篇文章主要介紹了MySQL之使用UNION和UNION ALL合并兩個或多個SELECT語句的結(jié)果集,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-04-04

