server 層會創(chuàng)建一個 SAVEPOINT 對象,用于存放 savepoint 信息。binlog 會把 binlog offset 寫入 server 層為它分配的一塊 8 字節(jié)的內(nèi)存里。 InnoDB 會維護自己的 savepoint 鏈表,里面保存著 trx_named_savept_t 對象。
1.undo 日志序號
InnoDB 的事務(wù)對象有一個名為 undo_no 的屬性。事務(wù)每次改變(插入、更新、刪除)某個表的一條記錄,都會產(chǎn)生一條 undo 日志。這條 undo 日志中會存儲它自己的序號。這個序號就來源于事務(wù)對象的 undo_no 屬性。
也就是說,事務(wù)對象的 undo_no 屬性中保存著事務(wù)改變(插入、更新、刪除)某個表中下一條記錄產(chǎn)生的 undo 日志的序號。
每個事務(wù)都維護著各自獨立的 undo 日志序號,和其它事務(wù)無關(guān)。
每個事務(wù)的 undo 日志序號都從 0 開始。事務(wù)產(chǎn)生的第 1 條 undo 日志的序號為 0,第 2 條 undo 日志的序號為 1,依此類推。
InnoDB 的 savepoint 結(jié)構(gòu)中會保存創(chuàng)建 savepoint 時事務(wù)對象的 undo_no 屬性值。2.savepoint 結(jié)構(gòu)
我們通過 SQL 語句創(chuàng)建一個 savepoint 時,server 層、binlog、InnoDB 會各自創(chuàng)建用于保存 savepoint 信息的結(jié)構(gòu)。
server 層的 savepoint 結(jié)構(gòu)是一個 SAVEPOINT 類型的對象,主要屬性如下:
- prev:指向 server 層的 savepoint 鏈表中,上一次創(chuàng)建的 SAVEPOINT 對象。
- name:savepoint 的名字。
- mdl_savepoint:創(chuàng)建這個 savepoint 之前,事務(wù)加了哪些 MDL 鎖。 binlog 的 savepoint 結(jié)構(gòu)很簡單,是一個 8 字節(jié)的整數(shù)。這個整數(shù)的值,是創(chuàng)建 savepoint 時事務(wù)已經(jīng)產(chǎn)生的 binlog 日志的字節(jié)數(shù),也是接下來新產(chǎn)生的 binlog 日志寫入 trx_cache 的 offset。
為了方便介紹,我們把這個整數(shù)值稱為 binlog offset。
InnoDB 的 savepoint 結(jié)構(gòu)是一個 trx_named_savept_t 類型的對象,主要屬性如下:
- name:InnoDB 的 savepoint 名字。這個名字是 InnoDB 自己生成的,和 server 層的 SAVEPOINT 對象中保存的 savepoint 名字不一樣。
- savept:也是一個對象,類型為 trx_savept_t,里面保存著創(chuàng)建 savepoint 時,事務(wù)對象的 undo_no 屬性值。
- trx_savepoints:InnoDB 中多個 trx_named_savept_t 對象形成的鏈表。 創(chuàng)建 savepoint 時,server 層會分配一塊 96 字節(jié)的內(nèi)存,除了存放它自己的 SAVEPOINT 對象,還會存放 binlog offset 和 InnoDB 的 trx_named_savept_t 對象。
server 層的 SAVEPOINT 對象占用這塊內(nèi)存的前 48 字節(jié),InnoDB 的 trx_named_savept_t 對象占用中間的 40 字節(jié),binlog offset 占用最后的 8 字節(jié)。
圖片
3.查找同名 savepoint
客戶端連接到 MySQL 之后,MySQL 會分配一個專門用于該連接的用戶線程。
用戶線程中有一個 m_savepoints 鏈表,用戶創(chuàng)建的多個 savepoint 通過 prev 屬性形成鏈表,m_savepoints 就指向最新創(chuàng)建的 savepoint。
圖片
server 層創(chuàng)建 savepoint 之前,會按照創(chuàng)建時間從新到老,逐個查看鏈表中是否存在和本次創(chuàng)建的 savepoint 同名的 savepoint。
4.刪除同名 savepoint
如果在用戶線程的 m_savepoints 鏈表中找到了和本次創(chuàng)建的 savepoint 同名的 savepoint,需要先刪除 m_savepoints 鏈表中的同名 savepoint。
找到的同名 savepoint,是 server 層的 SAVEPOINT 對象,它后面的內(nèi)存區(qū)域分別保存著 InnoDB 的 trx_named_savept_t 對象、binlog offset。
binlog 是個老實孩子,乖乖的把 binlog offset 寫入了 server 層為它分配的內(nèi)存里。刪除同名 savepoint 時,不需要單獨處理 binlog offset。
InnoDB 就不老實了,雖然 server 層也為 InnoDB 的 trx_named_savept_t 對象分配了內(nèi)存,但是 InnoDB 并沒有往里面寫入內(nèi)容。
事務(wù)執(zhí)行過程中,用戶每次創(chuàng)建一個 savepoint,InnoDB 都會創(chuàng)建一個對應(yīng)的 trx_named_savept_t 對象,并加入 InnoDB 事務(wù)對象的 trx_savepoints 鏈表的末尾。
因為 InnoDB 自己維護了一個存放 savepoint 結(jié)構(gòu)的鏈表,server 層刪除同名 savepoint 時,InnoDB 需要找到這個鏈表中對應(yīng)的 savepoint 結(jié)構(gòu)并刪除,流程如下:
- server 層把同名 savepoint 的 SAVEPOINT 對象后面分配給 trx_named_savept_t 對象的內(nèi)存地址傳給 InnoDB。
- InnoDB 根據(jù)自己的算法把內(nèi)存地址轉(zhuǎn)換為字符串,作為 InnoDB 的 savepoint 名字,到事務(wù)對象的 trx_savepoints 鏈表中找到對應(yīng)的 trx_named_savept_t 對象,并從鏈表中刪除該對象。
InnoDB 從事務(wù)對象的 trx_savepoints 鏈表中刪除 trx_named_savept_t 對象之后,server 層接著從用戶線程的 m_savepoints 鏈表中刪除 server 層的 SAVEPOINT 對象,也就連帶著清理了 binlog offset。
5.保存 savepoint
處理完查找、刪除同名 savepoint 之后,server 層就正式開始創(chuàng)建 savepoint 了,這個過程分為 3 步。
第 1 步,binlog 會生成一個 Query_log_event。
以創(chuàng)建名為 test_savept 的 savepoint 為例,這個 event 的內(nèi)容如下:
SAVEPOINT test_savept binlog event 寫入 trx_cache 之后,binlog offset 會寫入 server 層為它分配的 8 字節(jié)的內(nèi)存中。
第 2 步,InnoDB 創(chuàng)建 trx_named_savept_t 對象,并放入事務(wù)對象的 trx_savepoints 鏈表的末尾。
圖片
trx_named_savept_t 對象的 name 屬性值是 InnoDB 的 savepoint 名字。這個名字是根據(jù) server 層為 InnoDB 的 trx_named_savept_t 對象分配的內(nèi)存的地址計算得到的。
trx_named_savept_t 對象的 savept 屬性,是一個 trx_savept_t 類型的對象。這個對象里保存著創(chuàng)建 savepoint 時,事務(wù)對象中 undo_no 屬性的值,也就是下一條 undo 日志的序號。
第 3 步,把 server 層的 SAVEPOINT 對象加入用戶線程的 m_savepoints 鏈表的尾部。
圖片
6.總結(jié)
server 層會創(chuàng)建一個 SAVEPOINT 對象,用于存放 savepoint 信息。
binlog 會把 binlog offset 寫入 server 層為它分配的一塊 8 字節(jié)的內(nèi)存里。
InnoDB 會維護自己的 savepoint 鏈表,里面保存著 trx_named_savept_t 對象。
如果 m_savepoints 鏈表中存在和本次創(chuàng)建的 savepoint 同名的 savepoint, 創(chuàng)建新的 savepoint 之前,server 層會從鏈表中刪除這個同名的 savepoint。
server 層創(chuàng)建的 SAVEPOINT 對象會放入 m_savepoints 鏈表的末尾。
InnoDB 創(chuàng)建的 trx_named_savept_t 對象會放入事務(wù)對象的 trx_savepoints 鏈表的末尾。