網(wǎng)站運營和維護吉林seo刷關(guān)鍵詞排名優(yōu)化
背景
本文涉及的內(nèi)容較為底層,做了解即可,是以前學(xué)習(xí)《高性能mysql》和《mysql是怎樣運行的》的筆記整理所得。
undolog設(shè)計的初始目的是保證事務(wù)的原子性。mysql的修改操作發(fā)生后,如果所在的事務(wù)未被提交,如mysql服務(wù)或者操作系統(tǒng)發(fā)送了異?;驁?zhí)行了回滾等情況,事務(wù)的已修改操作需被還原。而還原需記錄必要的數(shù)據(jù),這些數(shù)據(jù)就是undolog。針對插入、刪除、修改操作,undolog需要記錄的信息量不同:
[1] 新增: 需要把主鍵記錄下來,回滾時,刪除主鍵對應(yīng)的記錄;
[2] 刪除: 保存整條記錄,回滾時,重新插入記錄;
[3] 修改: 保存修改前后的記錄,回滾時使用舊值替換新值。
通過特定的設(shè)計,可以減少上述信息量的存儲,以提高資源使用效率。如mysql實現(xiàn)刪除操作時,會通過delete_mark標(biāo)記已刪除(中間狀態(tài)),對應(yīng)的undolog日志中就不需要存儲完整的記錄。
1.undo日志內(nèi)容
不同操作類型的undolog存儲的數(shù)據(jù)量不同,為減少undolog存儲空間,mysql分別設(shè)計了對應(yīng)的數(shù)據(jù)存儲格式,本章將以Insert為例介紹。
有幾個概念需要提前了解。
[1] undo ID: 每條Undo日志對應(yīng)一個ID。
[2] table ID: 每個表都對應(yīng)一個ID, 可通過innodb_tables查詢表ID信息,如:
mysql> SELECT NAME, TABLE_ID, SPACE, ROW_FORMAT, SPACE_TYPE FROM information_schema.innodb_tables WHERE name='test/t_student';
+----------------+----------+-------+------------+------------+
| NAME | TABLE_ID | SPACE | ROW_FORMAT | SPACE_TYPE |
+----------------+----------+-------+------------+------------+
| test/t_student | 16148 | 15086 | Redundant | Single |
+----------------+----------+-------+------------+------------+
此時表ID為16148.
[3] db_trx_id和db_roll_pointer
每條記錄包含3個隱藏列,其中兩個是db_trx_id和db_roll_pointer,分別表示事務(wù)ID和回滾指針。
db_trx_id(事務(wù)ID)表示當(dāng)前記錄被新增或者最后一次修改對應(yīng)的事務(wù)ID。
其中,事務(wù)ID是一個不斷遞增的全局變量;事務(wù)只有發(fā)送修改時,才會被分配一個事務(wù)ID,每個事務(wù)的ID全局唯一。
db_roll_pointer(回滾指針)表示指向該條記錄對應(yīng)的undo日志(下文介紹)。
[4] next_record屬性
在[mysql系列2—InnoDB數(shù)據(jù)存儲方式]介紹過: mysql表的每條記錄都有一個指向下一條記錄的指針,記錄之間通過next_record形成鏈表。
1.1 undo日志格式
undo日志格式格式如下圖所示:
包含四個部分:
[1] 邊界信息
end of record存儲本條undo日志的結(jié)束位置;start of end存儲本條undo日志的起始位置;二者用于確定該條undo日志的邊界。
[2] undo type
undo type記錄了當(dāng)前undo日志的類型信息; 不同的類型確定了不同的日志內(nèi)容存儲格式。
[3] undo ID和table ID
undo ID 表示該Undo的全局唯一ID,table ID表示修改操作涉及的表的ID。
[4] 日志信息
存儲undo日志信息的數(shù)據(jù)部分,mysql根據(jù)undo type確定日志格式和對應(yīng)的解析邏輯。Insert、Update、Delete操作在這部分存儲格式有所區(qū)別。
1.2 insert操作
mysql記錄Insert類型的Undo日志,只需要保存主鍵信息即可,如下圖所示:
undo日志部分中存放了主鍵的各列的長度和實際的數(shù)值。
以下結(jié)合案例進行說明。
創(chuàng)建一個t_student表:
CREATE TABLE `t_student` (`id` INT(10) NOT NULL COMMENT '學(xué)號,唯一ID',`name` VARCHAR(50) NOT NULL COMMENT '姓名',`country` VARCHAR(32) NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE,UNIQUE INDEX `unix_name` (`name`) USING BTREE
)
ENGINE=InnoDB
;
包含一個主鍵索引和一個唯一索引;
開啟一個事務(wù)(事務(wù)ID為100),向t_student其中插入兩條數(shù)據(jù):
start transaction;INSERT INTO `t_student` (`id`, `name`, `country`) VALUES (1, 'sy', '中國');
INSERT INTO `t_student` (`id`, `name`, `country`) VALUES (2, 'mf', '澳大利亞');
從information_schema.innodb_tables表中查詢得到t_student的table ID為16148.
此時數(shù)據(jù)記錄和undo日志如下所示:
兩個插入操作對應(yīng)的類型為trx_undo_insert_rec;undo no存放的是undo日志ID, 分別為100和101;table ID存儲的是t_student的table ID為16148; 數(shù)據(jù)部分存儲主鍵大小和實際的值,整形長度為4;主鍵數(shù)值分別為1和2.
為了下文表述方便,對上圖的日志細(xì)節(jié)部分進行忽略,簡化表示為:
圖中插入的兩條記錄通過next_record形成了記錄鏈;每條記錄的db_roll_pointer指向了記錄對應(yīng)的Undo日志。
其中:id=1的undo日志ID為100(后續(xù)用undolog100表示);id=2的undo日志ID為101(后續(xù)用undolog101表示).
1.3 delete操作和update操作
insert和delete對每行記錄的操作只會對應(yīng)產(chǎn)生一條UNDO日志;update語句修改某條記錄的主鍵時產(chǎn)生兩條日志,否則產(chǎn)生一條日志。
delete和update操作對應(yīng)的undo行為是恢復(fù)數(shù)據(jù),因此需要在日志內(nèi)容區(qū)域存儲除主鍵外更多的信息,其中包括原記錄對應(yīng)的事務(wù)ID和Undo指針。以下結(jié)合案例進行說明。
當(dāng)事務(wù)100執(zhí)行插入操作后,繼續(xù)執(zhí)行update操作時:
UPDATE t_student SET country = '上海' WHERE id = 2;
update操作修改了主鍵為2的記錄,對應(yīng)的undo日志ID為102(后續(xù)用undolog102表示). undolog102中存在一個old_roll_pointer指針,指向了undolog101.
當(dāng)事務(wù)100執(zhí)行update操作后,繼續(xù)執(zhí)行delete操作時:
delete from t_student WHERE id = 2;
delete操作刪除了主鍵為2的記錄,減delete_mark標(biāo)記為1表示該記錄已被刪除;對應(yīng)的undo日志ID為103(后續(xù)用undolog103表示). undolog103中存在一個old_roll_pointer指針,指向了undolog102.
說明:也可以提交事務(wù)100后(不提交會因行鎖沖突阻塞更新),再開啟一個事務(wù)執(zhí)行更新或者刪除操作,效果與案例相似(僅保存的舊事務(wù)ID不別)。
1.4 版本鏈
insert語句在事務(wù)提交后UNDO日志會被刪除;而delete和update類型的UNDO日志會參與MVCC,即使所在事務(wù)提交,也不會立即刪除(而是后續(xù)有單獨的線程完成清理)。
上一節(jié)中id為2的記錄對應(yīng)的undolog通過roll_ptr指針相連,將其單獨抽出來得到一個鏈表,叫做版本鏈。
2.undo頁
前面已經(jīng)介紹了undo日志格式,undo日志會按照格式存放到FIL_PAGE_UNDO_LOG (undo頁)中,undo頁的結(jié)構(gòu)如下所示:
File Header和File Trailer是頁的固定格式,在介紹數(shù)據(jù)頁時已經(jīng)介紹過,這里關(guān)注一下Undo Page Header, 包括如下4個部分:
[1] type: Undo日志大類,Insert類型和其他類型(delete和update類型);
[2] page_start: 第一條undo日志的起始位置;
[3] page_free: 下一條undo日志的可寫入位置;
[4] node信息: 存儲鏈表信息,在下一章中介紹。
3.undo鏈表
一個事務(wù)可能有多個操作命令,每個操作命令可能修改多個行;即一個事務(wù)在執(zhí)行過程中可能生成多個Undo日志,因此一個undo頁可能寫不完,所以mysql引入了一個undo鏈的概念。
上一章節(jié)中node存儲的就是串聯(lián)undo頁之間的指針信息,undo頁通過node信息形成雙向鏈表。
mysql整體上將Undo日志分為了兩類: insert類型和其他類型(update和delete)。Insert類型的Undo日志在事務(wù)提交后可以直接刪除;而其他類型的undo日志參與MVCC,不能直接刪除,而是由后續(xù)專門的線程負(fù)責(zé)清理。因此,為了提高效率,mysql將不同類型的日志存放到在不同的undo頁中。
說明:并不是事務(wù)創(chuàng)建時就分配undo鏈,而是按需分配,發(fā)送數(shù)據(jù)修改時才會分配。
每個事務(wù)可能有多條鏈:
除此之外,對于臨時表,還有單獨的兩個鏈表(原理相同,本文不做介紹)。
最后,為了提高undo日志寫入效率,不同的事務(wù)在執(zhí)行過程中生成的undo日志存放到不同的undo頁面鏈表中;即每個事務(wù)有自己單獨的undo鏈表。