MySQL · 特性分析 ·MySQL 5.7新特性系列三無標(biāo)題文章

前言:MySQL5.7新特性之一介紹了一些新特性及兼容性問題牵舱,MySQL 5.7新特性之二介紹了臨時表的優(yōu)化和實(shí)現(xiàn)历造。
這期我們一起來學(xué)習(xí)下undo空間管理蒲讯,重點(diǎn)介紹truncate功能垫蛆。

1. 背景

InnoDB存儲引擎中违孝,undo在完成事務(wù)回滾和MVCC之后,就可以purge掉了集索,但undo在事務(wù)執(zhí)行過程中屿愚,進(jìn)行的空間分配如何回收,就變成了一個問題抄谐。 我們親歷用戶的小實(shí)例渺鹦,因?yàn)橐粋€大事務(wù),導(dǎo)致ibdata file到800G大小蛹含。

我們先大致看下InnoDB的undo在不同的版本上的一些演進(jìn):

MySQL 5.5的版本上
InnoDB undo是放在系統(tǒng)表空間即ibdata file文件中毅厚,這樣如果有比較大的事務(wù)(即需要生成大量undo的),會撐大ibdata數(shù)據(jù)文件浦箱,
雖然空間可以重用吸耿, 但文件大小不能更改。
關(guān)于回滾段的酷窥,只有這個主要的參數(shù)咽安,用來設(shè)置多少個rollback segment。

mysql> show global variables like '%rollback_segment%';
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| innodb_rollback_segments   | 128   |
+----------------------------+-------+

MySQL 5.6的版本上
InnoDB undo支持獨(dú)立表空間蓬推, 增加如下參數(shù):

+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| innodb_undo_directory   | .     |
| innodb_undo_logs        | 128   |
| innodb_undo_tablespaces | 1     |
+-------------------------+-------+

這樣妆棒,在install的時候,就會在data目錄下增加undo數(shù)據(jù)文件沸伏,來組成undo獨(dú)立表空間糕珊,但文件變大之后的空間回收還是成為問題。

MySQL 5.7的版本上

InnoDB undo在支持獨(dú)立表空間的基礎(chǔ)上毅糟,支持表空間的truncate功能红选,增加了如下參數(shù):
mysql> show global variables like '%undo%'; +--------------------------+------------+
| Variable_name | Value |
+--------------------------+------------+
| innodb_max_undo_log_size | 1073741824 |
| innodb_undo_directory | ./ |
| innodb_undo_log_truncate | OFF |
| innodb_undo_logs | 128 |
| innodb_undo_tablespaces | 3 |
+--------------------------+------------+
mysql> show global variables like '%truncate%';
+--------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------+-------+
| innodb_purge_rseg_truncate_frequency | 128 |
| innodb_undo_log_truncate | OFF |
+--------------------------------------+-------+
InnoDB的purge線程,會根據(jù)innodb_undo_log_truncate開關(guān)的設(shè)置姆另,和innodb_max_undo_log_size設(shè)置的文件大小閾值喇肋,以及truncate的頻率來進(jìn)行空間回收和rollback segment的重新初始化。

接下來我們詳細(xì)看下5.7的InnoDB undo的管理:
  1. undo表空間創(chuàng)建
    設(shè)置innodb_undo_tablespaces的個數(shù)迹辐, 在mysql install的時候蝶防,創(chuàng)建指定數(shù)量的表空間。
    InnoDB支持128個undo logs明吩,這里特別說明下间学,從5.7開始,innodb_rollback_segments的名字改成了innodb_undo_logs贺喝,但表示的都是回滾段的個數(shù)菱鸥。
    從5.7.2開始,其中32個undo logs為臨時表的事務(wù)分配的躏鱼,因?yàn)檫@部分undo不記錄redo氮采,不需要recovery,另外從33-128一共96個是redo-enabled undo染苛。

2.rollback segment的分配如下:
Slot-0: reserved for system-tablespace.
Slot-1....Slot-N: reserved for temp-tablespace.
Slot-N+1....Slot-127: reserved for system/undo-tablespace. */

其中如果是臨時表的事務(wù)鹊漠,需要分配兩個undo logs,其中一個是non-redo undo logs茶行;這部分用于臨時表數(shù)據(jù)的回滾躯概。
另外一個是redo-enabled undo log,是為臨時表的元數(shù)據(jù)準(zhǔn)備的畔师,需要recovery娶靡。
而且, 其中32個rollback segment創(chuàng)建在臨時表空間中看锉,并且臨時表空間中的回滾段在每次server start的時候姿锭,需要重建。
每一個rollback segment可以分配1024個slot伯铣,也就是可以支持96*1024個并發(fā)的事務(wù)同時呻此, 但如果是臨時表的事務(wù),需要占用兩個slot腔寡。

InnoDB undo的空間管理簡圖如下:

undo空間管理
注核心結(jié)構(gòu)說明:

  1. rseg slot
    rseg slot一共128個焚鲜,保存在ibdata系統(tǒng)表空間中,其位置在:
      /*!< the start of the array of rollback segment specification slots */
    ######  #define TRX_SYS_RSEGS       (8 + FSEG_HEADER_SIZE) 

每一個slot保存著rollback segment header的位置放前。包括space_id + page_no忿磅,占用8個bytes。其宏定義:

/* Rollback segment specification slot offsets */
/*-------------------------------------------------------------*/
#######define   TRX_SYS_RSEG_SPACE  0   /* space where the segment
                    header is placed; starting with
                    MySQL/InnoDB 5.1.7, this is
                    UNIV_UNDEFINED if the slot is unused */
#######define   TRX_SYS_RSEG_PAGE_NO    4   /*  page number where the segment
                    header is placed; this is FIL_NULL
                    if the slot is unused */
/* Size of a rollback segment specification slot */
#######define TRX_SYS_RSEG_SLOT_SIZE    8
  1. rseg header
    rseg header在undo表空間中犀斋,每一個rseg包括1024個undo segment slot贝乎,每一個slot保存著undo segment header的位置,包括page_no叽粹,暫用4個bytes览效,因?yàn)閡ndo segment不會跨表空間,所以space_id就沒有必要了虫几。
    其宏定義如下:
/* Undo log segment slot in a rollback segment header */
/*-------------------------------------------------------------*/
#define TRX_RSEG_SLOT_PAGE_NO   0   /* Page number of the header page of
                    an undo log segment */
/*-------------------------------------------------------------*/
/* Slot size */
#define TRX_RSEG_SLOT_SIZE  4
  1. undo segment header
    undo segment header page即段內(nèi)的第一個undo page锤灿,其中包括四個比較重要的結(jié)構(gòu):
    undo segment header 進(jìn)行段內(nèi)空間的管理
    undo page header page內(nèi)空間的管理,page的類型:FIL_PAGE_UNDO_LOG
    undo header 包含undo record的鏈表辆脸,以便安裝事務(wù)的反順序但校,進(jìn)行回滾
    undo record 剩下的就是undo記錄了。
  1. undo段的分配
    undo段的分配比較簡單啡氢,其過程如下:
    首先是rollback segment的分配:
trx->rsegs.m_redo.rseg = trx_assign_rseg_low(
  srv_undo_logs, srv_undo_tablespaces,
  TRX_RSEG_TYPE_REDO);
使用round-robin的方式來分配rollback segment
如果有單獨(dú)設(shè)置undo表空間状囱,就不使用system表空間中的undo segment
如果設(shè)置的是truncate的就不分配
一旦分配了术裸,就設(shè)置trx_ref_count,不允許truncate亭枷。

具體代碼參考:

/******************************************************************//**
Get next redo rollback segment. (Segment are assigned in round-robin fashion).
@return assigned rollback segment instance */
static
trx_rseg_t*
get_next_redo_rseg(
/*===============*/
    ulong   max_undo_logs,  /*!< in: maximum number of UNDO logs to use */
    ulint   n_tablespaces)  /*!< in: number of rollback tablespaces */

其次是undo segment的創(chuàng)建:
從rollback segment里邊選擇一個free的slot袭艺,如果沒有,就會報錯叨粘,通常是并發(fā)的事務(wù)太多猾编。
錯誤日志如下:

ib::warn() << "Cannot find a free slot for an undo log. Do"
    " you have too many active transactions running"
    " concurrently?";

如果有free,就創(chuàng)建一個undo的segment升敲。

核心的代碼如下:

/***************************************************************//**
Creates a new undo log segment in file.
@return DB_SUCCESS if page creation OK possible error codes are:
DB_TOO_MANY_CONCURRENT_TRXS DB_OUT_OF_FILE_SPACE /
static
dberr_t
trx_undo_seg_create(
/
================/
trx_rseg_t
rseg attribute((unused)),/!< in: rollback segment /
trx_rsegf_t
rseg_hdr,/
!< in: rollback segment header, page
x-latched /
ulint type, /
!< in: type of the segment: TRX_UNDO_INSERT or
TRX_UNDO_UPDATE /
ulint
id, /!< out: slot index within rseg header /
page_t
undo_page,
/!< out: segment header page x-latched, NULL
if there was an error /
mtr_t
mtr) /
!< in: mtr */

/*  fputs(type == TRX_UNDO_INSERT
? "Creating insert undo log segment\n"
: "Creating update undo log segment\n", stderr); */
slot_no = trx_rsegf_undo_find_free(rseg_hdr, mtr);

if (slot_no == ULINT_UNDEFINED) {
    ib::warn() << "Cannot find a free slot for an undo log. Do"
        " you have too many active transactions running"
        " concurrently?";

    return(DB_TOO_MANY_CONCURRENT_TRXS);
}
  1. undo的truncate
    undo的truncate主要由下面兩個參數(shù)控制:innodb_purge_rseg_truncate_frequency答倡,innodb_undo_log_truncate。
  1. innodb_undo_log_truncate是開關(guān)參數(shù)驴党。
  2. innodb_purge_rseg_truncate_frequency默認(rèn)128瘪撇,表示purge undo輪詢128次后,進(jìn)行一次undo的truncate港庄。

當(dāng)設(shè)置innodb_undo_log_truncate=ON的時候设江, undo表空間的文件大小,如果超過了innodb_max_undo_log_size攘轩, 就會被truncate到初始大小叉存,但有一個前提,就是表空間中的undo不再被使用度帮。

其主要步驟如下:

  1. 超過大小了之后歼捏,會被mark truncation,一次會選擇一個
  2. 選擇的undo不能再分配新給新的事務(wù)
  3. purge線程清理不再需要的rollback segment
  4. 等所有的回滾段都釋放了后笨篷,truncate操作瞳秽,使其成為install db時的初始狀態(tài)。

默認(rèn)情況下率翅, 是purge觸發(fā)128次之后练俐,進(jìn)行一次rollback segment的free操作,然后如果全部free就進(jìn)行一個truncate冕臭。
但mark的操作需要幾個依賴條件需要滿足:

  1. 系統(tǒng)至少得有兩個undo表空間腺晾,防止一個offline后,至少另外一個還能工作
  2. 除了ibdata里的segment辜贵,還至少有兩個segment可用
  3. undo表空間的大小確實(shí)超過了設(shè)置的閾值

其核心代碼參考:

/** Iterate over all the UNDO tablespaces and check if any of the UNDO
tablespace qualifies for TRUNCATE (size > threshold).
@param[in,out]  undo_trunc  undo truncate tracker */
static
void
trx_purge_mark_undo_for_truncate(
    undo::Truncate* undo_trunc)

因?yàn)槊醪酰灰阍O(shè)置了truncate = on,MySQL就盡可能的幫你去truncate所有的undo表空間托慨,所以它會循環(huán)的把undo表空間加入到mark列表中鼻由。

最后,循環(huán)所有的undo段,如果所屬的表空間是marked truncate蕉世,就把這個rseg標(biāo)志位不可分配蔼紧,加入到trunc隊(duì)列中,在purge的時候狠轻,進(jìn)行free rollback segment歉井。

注意:
如果是在線庫,要注意影響哈误,因?yàn)楫?dāng)一個undo tablespace在進(jìn)行truncate的時候,不再承擔(dān)undo的分配躏嚎。只能由剩下的undo 表空間的rollback segment接受事務(wù)undo空間請求蜜自。

MySQL 5.7 新特性系列,下次進(jìn)行g(shù)roup replication的分享卢佣,敬請期待重荠。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市虚茶,隨后出現(xiàn)的幾起案子戈鲁,更是在濱河造成了極大的恐慌,老刑警劉巖嘹叫,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件婆殿,死亡現(xiàn)場離奇詭異,居然都是意外死亡罩扇,警方通過查閱死者的電腦和手機(jī)婆芦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來喂饥,“玉大人消约,你說我怎么就攤上這事≡卑铮” “怎么了或粮?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長捞高。 經(jīng)常有香客問我氯材,道長,這世上最難降的妖魔是什么硝岗? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任浓体,我火速辦了婚禮,結(jié)果婚禮上辈讶,老公的妹妹穿的比我還像新娘命浴。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布生闲。 她就那樣靜靜地躺著媳溺,像睡著了一般。 火紅的嫁衣襯著肌膚如雪碍讯。 梳的紋絲不亂的頭發(fā)上悬蔽,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機(jī)與錄音捉兴,去河邊找鬼蝎困。 笑死,一個胖子當(dāng)著我的面吹牛倍啥,可吹牛的內(nèi)容都是我干的禾乘。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼虽缕,長吁一口氣:“原來是場噩夢啊……” “哼始藕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起氮趋,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤伍派,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后剩胁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體诉植,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年昵观,在試婚紗的時候發(fā)現(xiàn)自己被綠了倍踪。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡索昂,死狀恐怖建车,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情椒惨,我是刑警寧澤缤至,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站康谆,受9級特大地震影響领斥,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜沃暗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一月洛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧孽锥,春花似錦嚼黔、人聲如沸细层。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽疫赎。三九已至,卻和暖如春碎节,著一層夾襖步出監(jiān)牢的瞬間捧搞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工狮荔, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留胎撇,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓殖氏,卻偏偏與公主長得像晚树,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子受葛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容