溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Innodb undo結(jié)構(gòu)是什么

發(fā)布時間:2021-11-08 11:35:53 來源:億速云 閱讀:154 作者:iii 欄目:MySQL數(shù)據(jù)庫

本篇內(nèi)容介紹了“Innodb undo結(jié)構(gòu)是什么”的有關(guān)知識,在實(shí)際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

一、大體結(jié)構(gòu)

rollback segments(128)

undo segments(1024)

undo log (header insert/modify 分開的) <-> undo page

undo record
undo record

作為undo segments的第一個undo page可以存放多個事物的undo log,因?yàn)槿绻@個塊的undo 記錄沒有填滿3/4則會進(jìn)入 rollback segment的cache list,那么下次可以繼續(xù)使用,但是如果第一個塊不足以裝下事物的undo 記錄,那么很顯然需要分配新的undo page,這種情況下一個undo page就只能包含一個事物的undo記錄了。
事物每次需要分配rollback segments然后分配undo segments然后初始化好undo log header,insert和update/delete需要分配不同的undo segments,一個undo segments往往對應(yīng)了一個undo log,undo log可以包含多個undo record(因?yàn)閺膁ebug來看undo log header的初始化只做了一次),對于操作的每行都會留下一個undo record作為mvcc構(gòu)建歷史版本的基礎(chǔ)。
undo生成的基本單位是undo record,每行記錄都會包含一個undo record,而rollback ptr指向的是undo record的偏移量,對于每行的記錄都會去判斷其可見性,如果需要構(gòu)建前版本則通過本指針進(jìn)行構(gòu)建包含:

  • 第1位是否是insert 第2到8位是undo segment id 第9到40位為page no 第41位到56位為 offset

每一個undo log包含一個trx_undo_t結(jié)構(gòu)體
每一個rollback segments包含一個trx_rseg_t結(jié)構(gòu)體

二、物理結(jié)構(gòu)

  • undo page header 每一個undo page都包含

/** Transaction undo log page header offsets *//* @{ */#define TRX_UNDO_PAGE_TYPE  0   /*!< TRX_UNDO_INSERT or
                    TRX_UNDO_UPDATE */#define TRX_UNDO_PAGE_START 2   /*!< Byte offset where the undo log
                    records for the LATEST transaction
                    start on this page (remember that
                    in an update undo log, the first page
                    can contain several undo logs) */#define TRX_UNDO_PAGE_FREE  4   /*!< On each page of the undo log this
                    field contains the byte offset of the
                    first free byte on the page */#define TRX_UNDO_PAGE_NODE  6   /*!< The file list node in the chain
                    of undo log pages */
  • undo semgent header 第一個page 才會用 undo segment header信息

#define TRX_UNDO_STATE      0   /*!< TRX_UNDO_ACTIVE, ... */#ifndef UNIV_INNOCHECKSUM#define TRX_UNDO_LAST_LOG   2   /*!< Offset of the last undo log header
                    on the segment header page, 0 if
                    none */#define TRX_UNDO_FSEG_HEADER    4   /*!< Header for the file segment which
                    the undo log segment occupies */#define TRX_UNDO_PAGE_LIST  (4 + FSEG_HEADER_SIZE)
                    /*!< Base node for the list of pages in
                    the undo log segment; defined only on
                    the undo log segment's first page */
  • 每一個undo log

    • undo log header

    • undo log record 相應(yīng)的undo實(shí)際內(nèi)容

    • undo log record 相應(yīng)的undo實(shí)際內(nèi)容

undo log header 包含

#define TRX_UNDO_TRX_ID     0   /*!< Transaction id */#define TRX_UNDO_TRX_NO     8   /*!< Transaction number of the
                    transaction; defined only if the log
                    is in a history list */#define TRX_UNDO_DEL_MARKS  16  /*!< Defined only in an update undo
                    log: TRUE if the transaction may have
                    done delete markings of records, and
                    thus purge is necessary */#define TRX_UNDO_LOG_START  18  /*!< Offset of the first undo log record
                    of this log on the header page; purge
                    may remove undo log record from the
                    log start, and therefore this is not
                    necessarily the same as this log
                    header end offset */#define TRX_UNDO_XID_EXISTS 20  /*!< TRUE if undo log header includes
                    X/Open XA transaction identification
                    XID */#define TRX_UNDO_DICT_TRANS 21  /*!< TRUE if the transaction is a table
                    create, index create, or drop
                    transaction: in recovery
                    the transaction cannot be rolled back
                    in the usual way: a 'rollback' rather
                    means dropping the created or dropped
                    table, if it still exists */#define TRX_UNDO_TABLE_ID   22  /*!< Id of the table if the preceding
                    field is TRUE */#define TRX_UNDO_NEXT_LOG   30  /*!< Offset of the next undo log header
                    on this page, 0 if none */#define TRX_UNDO_PREV_LOG   32  /*!< Offset of the previous undo log
                    header on this page, 0 if none */#define TRX_UNDO_HISTORY_NODE   34  /*!< If the log is put to the history
                    list, the file list node is here */

三、分配步驟和寫入

  • 第一步為 分配rollback segments

#0  get_next_redo_rseg (max_undo_logs=128, n_tablespaces=4) at /root/mysqlc/percona-server-locks-detail-5.7.22/storage/innobase/trx/trx0trx.cc:1138#1  0x0000000001c0bce8 in trx_assign_rseg_low (max_undo_logs=128, n_tablespaces=4, rseg_type=TRX_RSEG_TYPE_REDO)
    at /root/mysqlc/percona-server-locks-detail-5.7.22/storage/innobase/trx/trx0trx.cc:1314#2  0x0000000001c1097d in trx_set_rw_mode (trx=0x7fffd7804080) at /root/mysqlc/percona-server-locks-detail-5.7.22/storage/innobase/trx/trx0trx.cc:3352#3  0x0000000001a64013 in lock_table (flags=0, table=0x7ffeac012ae0, mode=LOCK_IX, thr=0x7ffe7c92ef48)
    at /root/mysqlc/percona-server-locks-detail-5.7.22/storage/innobase/lock/lock0lock.cc:4139#4  0x0000000001b7950e in row_search_mvcc (buf=0x7ffe7c92e350 "\377", mode=PAGE_CUR_GE, prebuilt=0x7ffe7c92e7d0, match_mode=1, direction=0)
    at /root/mysqlc/percona-server-locks-detail-5.7.22/storage/innobase/row/row0sel.cc:5100#5  0x00000000019d5443 in ha_innobase::index_read (this=0x7ffe7c92de10, buf=0x7ffe7c92e350 "\377", key_ptr=0x7ffe7cd57590 "\004", key_len=4, 
    find_flag=HA_READ_KEY_EXACT) at /root/mysqlc/percona-server-locks-detail-5.7.22/storage/innobase/handler/ha_innodb.cc:9536#6  0x0000000000f9345a in handler::index_read_map (this=0x7ffe7c92de10, buf=0x7ffe7c92e350 "\377", key=0x7ffe7cd57590 "\004", keypart_map=1, 
    find_flag=HA_READ_KEY_EXACT) at /root/mysqlc/percona-server-locks-detail-5.7.22/sql/handler.h:2942#7  0x0000000000f83e44 in handler::ha_index_read_map (this=0x7ffe7c92de10, buf=0x7ffe7c92e350 "\377", key=0x7ffe7cd57590 "\004", keypart_map=1, 
    find_flag=HA_READ_KEY_EXACT) at /root/mysqlc/percona-server-locks-detail-5.7.22/sql/handler.cc:3248

第二步 對于主鍵每行更改操作都會調(diào)用trx_undo_report_row_operation 他會分配undo segments 并且會負(fù)責(zé)寫入undo record

#0  trx_undo_report_row_operation (flags=0, op_type=2, thr=0x7ffe7c932828, index=0x7ffea4016590, clust_entry=0x7ffe7c932cc0, update=0x0, cmpl_info=0, 
    rec=0x7fffb580d369 "", offsets=0x7fffec0f3e00, roll_ptr=0x7fffec0f3688) at /root/mysqlc/percona-server-locks-detail-5.7.22/storage/innobase/trx/trx0rec.cc:1866#1  0x0000000001c5795b in btr_cur_del_mark_set_clust_rec (flags=0, block=0x7fffb4ccaae0, rec=0x7fffb580d369 "", index=0x7ffea4016590, offsets=0x7fffec0f3e00, 
    thr=0x7ffe7c932828, entry=0x7ffe7c932cc0, mtr=0x7fffec0f38f0) at /root/mysqlc/percona-server-locks-detail-5.7.22/storage/innobase/btr/btr0cur.cc:4894#2  0x0000000001b9f218 in row_upd_del_mark_clust_rec (flags=0, node=0x7ffe7c932550, index=0x7ffea4016590, offsets=0x7fffec0f3e00, thr=0x7ffe7c932828, referenced=0, 
    mtr=0x7fffec0f38f0) at /root/mysqlc/percona-server-locks-detail-5.7.22/storage/innobase/row/row0upd.cc:2778#3  0x0000000001b9f765 in row_upd_clust_step (node=0x7ffe7c932550, thr=0x7ffe7c932828)
    at /root/mysqlc/percona-server-locks-detail-5.7.22/storage/innobase/row/row0upd.cc:2923#4  0x0000000001b9fc74 in row_upd (node=0x7ffe7c932550, thr=0x7ffe7c932828) at /root/mysqlc/percona-server-locks-detail-5.7.22/storage/innobase/row/row0upd.cc:3042#5  0x0000000001ba0155 in row_upd_step (thr=0x7ffe7c932828) at /root/mysqlc/percona-server-locks-detail-5.7.22/storage/innobase/row/row0upd.cc:3188#6  0x0000000001b3d3a0 in row_update_for_mysql_using_upd_graph (mysql_rec=0x7ffe7c9318d0 "\375\001", prebuilt=0x7ffe7c931d50)
    at /root/mysqlc/percona-server-locks-detail-5.7.22/storage/innobase/row/row0mysql.cc:3040#7  0x0000000001b3d6a1 in row_update_for_mysql (mysql_rec=0x7ffe7c9318d0 "\375\001", prebuilt=0x7ffe7c931d50)
    at /root/mysqlc/percona-server-locks-detail-5.7.22/storage/innobase/row/row0mysql.cc:3131#8  0x00000000019d47c3 in ha_innobase::delete_row (this=0x7ffe7c931390, record=0x7ffe7c9318d0 "\375\001")
    at /root/mysqlc/percona-server-locks-detail-5.7.22/storage/innobase/handler/ha_innodb.cc:9141

大概流程

switch (op_type)
{    case TRX_UNDO_INSERT_OP:
        undo = undo_ptr->insert_undo; //如果是 insert 則使用insert_undo 類型為trx_undo_t 指針
        if (undo == NULL) { //如果已經(jīng)分配了就不用分配了
            err = trx_undo_assign_undo( //分配undo segment 同時初始化 undo log header
                trx, undo_ptr, TRX_UNDO_INSERT);
            undo = undo_ptr->insert_undo;
  ...
        }        break;    default:
        ut_ad(op_type == TRX_UNDO_MODIFY_OP); //斷言
        undo = undo_ptr->update_undo;        if (undo == NULL) {
            err = trx_undo_assign_undo(
                trx, undo_ptr, TRX_UNDO_UPDATE); //分配undo segment 同時初始化 undo log header
            undo = undo_ptr->update_undo;
    ...
    }
  ...  case TRX_UNDO_INSERT_OP://注意是每行都會操作
            offset = trx_undo_page_report_insert( //寫入insert undo log record
                undo_page, trx, index, clust_entry, &mtr);            break;        default:
            ut_ad(op_type == TRX_UNDO_MODIFY_OP); //寫入delete update undo log record
            offset = trx_undo_page_report_modify(
                undo_page, trx, index, rec, offsets, update,
                cmpl_info, clust_entry, &mtr);
        }
    ...
    
    *roll_ptr = trx_undo_build_roll_ptr( //構(gòu)建rollback ptr 主鍵中每行都有這個 用于MVCC構(gòu)建回滾版本
                op_type == TRX_UNDO_INSERT_OP,
                undo_ptr->rseg->id, page_no, offset);

四、分解undo log record

我將undo log record的寫入到了錯誤日志,下面進(jìn)行簡單的分解。
表結(jié)構(gòu)如下:

mysql> show create table t1;
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------------+
| t1    | CREATE TABLE `t1` (  `id1` int(11) NOT NULL,  `id2` int(11) DEFAULT NULL,
  PRIMARY KEY (`id1`),
  KEY `id2` (`id2`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------------+1 row in set (0.00 sec)
  • insert 的undo記錄,具體構(gòu)造在trx_undo_page_report_insert中
    語句

mysql> insert into t1 values(28,28);
Query OK, 1 row affected (0.00 sec)

輸出如下:

trx_undo_assign_undo:assign undo space:
RSEG SLOT:34,RSEG SPACE ID:2 PAGE NO:3UNDO SLOT:0,UNDO SPACE ID:2 UNDO LOG HEADER PAGE NO:27,UNDO LOG HEADER OFFSET:86,UNDO LOG LAST PAGE:27trx_undo_page_report_insert:undo log record
TABLE_NAME:test/t1 TRX_ID:12591,UODO RECORD LEN:10
 len 10; hex 011e0b0032048000001c;

011e0b0032048000001c就是undo record的實(shí)際記錄解析如下:

011c page內(nèi)部本undo record結(jié)束的位置0b 類型為 #define  TRX_UNDO_INSERT_REC 11(0X0b)00 undo no,提交才會有32 table_id 可以查詢 INNODB_SYS_TABLES 對照04 字段長度4個字節(jié)8000001c 我插入的記錄主鍵 28(0X1c)
  • update 的undo記錄,具體構(gòu)造在trx_undo_page_report_modify中
    語句:

mysql> update t1 set id2=1000 where id1=14;
Query OK, 1 row affected (5 min 40.91 sec)
Rows matched: 1  Changed: 1  Warnings: 0

輸出如下:

trx_undo_assign_undo:assign undo space:
RSEG SLOT:41,RSEG SPACE ID:1 PAGE NO:5UNDO SLOT:1,UNDO SPACE ID:1 UNDO LOG HEADER PAGE NO:37,UNDO LOG HEADER OFFSET:1389,UNDO LOG LAST PAGE:37trx_undo_page_report_modify:undo log record
TABLE_NAME:test/t1 TRX_ID:12604,UODO RECORD LEN:47
 len 47; hex 06560c0032000000003136e0260000002c052e048000000e010304800003e7000e00048000000e0304800003e70627;

06560c0032000000003136e0260000002c052e048000000e010304800003e7000e00048000000e030480
就是undo record的記錄
大體解析如下:

0656 :page內(nèi)部本undo record結(jié)束的位置
0c:類型為 #define  TRX_UNDO_UPD_EXIST_REC  12(0X0c)
00: undo no,提交才會有
32: table_id 可以查詢 INNODB_SYS_TABLES 對照
00:
0000003136e0:事物ID260000002c052e:undo回滾指針
04:主鍵長度
8000000e:主鍵值
01
03:位置
04:被修改值的長度
800003e7:值為999(0x3e7)
000e:接下來字符的長度,記錄原始值?
00:位置
04:長度
8000000e:主鍵值
03:位置
04:長度
800003e7:值為999(0x3e7)
0627:page內(nèi)部本undo record開始的位置,0X0656-0X0627就是長度

“Innodb undo結(jié)構(gòu)是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI