MySQL · 特性分析 ·MySQL 5.7新特性系列三无标题文章

前言:MySQL5.7新特性之一介绍了一些新特性及兼容性问题,MySQL 5.7新特性之二介绍了临时表的优化和实现。
这期我们一起来学习下undo空间管理,重点介绍truncate功能。

1. 背景

InnoDB存储引擎中,undo在完成事务回滚和MVCC之后,就可以purge掉了,但undo在事务执行过程中,进行的空间分配如何回收,就变成了一个问题。 我们亲历用户的小实例,因为一个大事务,导致ibdata file到800G大小。

我们先大致看下InnoDB的undo在不同的版本上的一些演进:

MySQL 5.5的版本上
InnoDB undo是放在系统表空间即ibdata file文件中,这样如果有比较大的事务(即需要生成大量undo的),会撑大ibdata数据文件,
虽然空间可以重用, 但文件大小不能更改。
关于回滚段的,只有这个主要的参数,用来设置多少个rollback segment。

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

MySQL 5.6的版本上
InnoDB undo支持独立表空间, 增加如下参数:

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

这样,在install的时候,就会在data目录下增加undo数据文件,来组成undo独立表空间,但文件变大之后的空间回收还是成为问题。

MySQL 5.7的版本上

InnoDB undo在支持独立表空间的基础上,支持表空间的truncate功能,增加了如下参数:
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线程,会根据innodb_undo_log_truncate开关的设置,和innodb_max_undo_log_size设置的文件大小阈值,以及truncate的频率来进行空间回收和rollback segment的重新初始化。

接下来我们详细看下5.7的InnoDB undo的管理:
  1. undo表空间创建
    设置innodb_undo_tablespaces的个数, 在mysql install的时候,创建指定数量的表空间。
    InnoDB支持128个undo logs,这里特别说明下,从5.7开始,innodb_rollback_segments的名字改成了innodb_undo_logs,但表示的都是回滚段的个数。
    从5.7.2开始,其中32个undo logs为临时表的事务分配的,因为这部分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. */

其中如果是临时表的事务,需要分配两个undo logs,其中一个是non-redo undo logs;这部分用于临时表数据的回滚。
另外一个是redo-enabled undo log,是为临时表的元数据准备的,需要recovery。
而且, 其中32个rollback segment创建在临时表空间中,并且临时表空间中的回滚段在每次server start的时候,需要重建。
每一个rollback segment可以分配1024个slot,也就是可以支持96*1024个并发的事务同时, 但如果是临时表的事务,需要占用两个slot。

InnoDB undo的空间管理简图如下:

undo空间管理
注核心结构说明:

  1. rseg slot
    rseg slot一共128个,保存在ibdata系统表空间中,其位置在:
      /*!< 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,因为undo 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即段内的第一个undo page,其中包括四个比较重要的结构:
    undo segment header 进行段内空间的管理
    undo page header page内空间的管理,page的类型:FIL_PAGE_UNDO_LOG
    undo header 包含undo record的链表,以便安装事务的反顺序,进行回滚
    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
如果有单独设置undo表空间,就不使用system表空间中的undo segment
如果设置的是truncate的就不分配
一旦分配了,就设置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的创建:
从rollback segment里边选择一个free的slot,如果没有,就会报错,通常是并发的事务太多。
错误日志如下:

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

如果有free,就创建一个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主要由下面两个参数控制:innodb_purge_rseg_truncate_frequency,innodb_undo_log_truncate。
  1. innodb_undo_log_truncate是开关参数。
  2. innodb_purge_rseg_truncate_frequency默认128,表示purge undo轮询128次后,进行一次undo的truncate。

当设置innodb_undo_log_truncate=ON的时候, undo表空间的文件大小,如果超过了innodb_max_undo_log_size, 就会被truncate到初始大小,但有一个前提,就是表空间中的undo不再被使用。

其主要步骤如下:

  1. 超过大小了之后,会被mark truncation,一次会选择一个
  2. 选择的undo不能再分配新给新的事务
  3. purge线程清理不再需要的rollback segment
  4. 等所有的回滚段都释放了后,truncate操作,使其成为install db时的初始状态。

默认情况下, 是purge触发128次之后,进行一次rollback segment的free操作,然后如果全部free就进行一个truncate。
但mark的操作需要几个依赖条件需要满足:

  1. 系统至少得有两个undo表空间,防止一个offline后,至少另外一个还能工作
  2. 除了ibdata里的segment,还至少有两个segment可用
  3. undo表空间的大小确实超过了设置的阈值

其核心代码参考:

/** 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)

因为,只要你设置了truncate = on,MySQL就尽可能的帮你去truncate所有的undo表空间,所以它会循环的把undo表空间加入到mark列表中。

最后,循环所有的undo段,如果所属的表空间是marked truncate,就把这个rseg标志位不可分配,加入到trunc队列中,在purge的时候,进行free rollback segment。

注意:
如果是在线库,要注意影响,因为当一个undo tablespace在进行truncate的时候,不再承担undo的分配。只能由剩下的undo 表空间的rollback segment接受事务undo空间请求。

MySQL 5.7 新特性系列,下次进行group replication的分享,敬请期待。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容