在MySQL5.7.5版本中,增加了一个比较有用的功能,即用户可以自己truncate掉undo log。 对应的changeling entry如下:
我们知道Undo log是MVCC多版本控制的核心模块,一直以来undo log都存储在ibdata系统表空间中,而从5.6开始,用户可以把undo log存储到独立的tablespace中,并拆分成多个Undo log文件。 但5.6及5.6之前的版本都无法缩小文件的大小。而长时间未提交事务导致大量undo 空间的浪费的例子,在我们的生产场景也不是一次两次了。
5.7的undo log的truncate操作是基于独立undo 表空间来实现的
0.相关参数
在能够使用该特性之前,需要先打开独立undo表空间,注意现在只能在install db的时候才能开启,因为他在初始化阶段是写死占用了最小的几个space id的…这种实现方式。。。只能无限吐槽 了- -..
有几个参数控制undo tablespace:
innodb_undo_directory: Undo文件的存储目录
innodb_undo_tablespaces: undo tablespace的个数,至少大于等于2,因为在truncate一个undo log文件时,要保证另外一个是可用的,这样就无需停止业务了.
innodb_undo_logs: undo回滚段的数量, 至少大于等于35,默认128.官方博客对35这个数字的解释:
The value of 35 comes from how the UNDO logs (rollback segments or rsegs) are allocated—0: REDO enabled rseg allocated in the system tablespace, 1-32: non-REDO enabled rsegs allocated in the temporary tablespace, 33-n: REDO enabled rsegs allocated in UNDO table spaces
0 -> rseg resides always in system tablespace
1..32 -> rseg resides in shared temporary tablespace (ibtmp1)
33 -> undo-tablespace-0
34 -> undo-tablespace-1
….
33+n-1 -> undo-tablespace-n-1 (undo tablespace indexes from 0 so n-1 is last).
33+n -> undo-tablespace-0
33+n+1 -> undo-tablespace-1
….
m -> undo-tablespace-x
innodb_purge_rseg_truncate_frequency: 用于控制purge回滚段的频度.
innodb_max_undo_log_size: 控制最大undo tablespace文件的大小,超过这个阀值时才会去尝试truncate. truncate后的大小默认为10M
打开/关闭该特性,使用动态参数innodb_undo_log_truncate控制
root@(none) 06:43:29>set global innodb_undo_log_truncate = ON;
Query OK, 0 rows affected (0.00 sec)
root@(none) 06:43:32>set global innodb_undo_log_truncate = OFF;
Query OK, 0 rows affected (0.00 sec)
下面简单介绍下实现的相关代码
0.新的truncate管理结构体
新的类undo::Truncate被引入,来管理table space truncate的过程
挂在purge_sys->undo_trunc中
1.标记需要truncate的undo tablespace
这个动作实际上是由purge的协调线程发起的,默认情况下每做128次purge后,会调用函数trx_purge_truncate进行清理操作,我们只关心truncate,对应的调用栈为:
trx_purge_truncate
|—>trx_purge_truncate_history
|—>trx_purge_mark_undo_for_truncate
|—>trx_purge_initiate_truncate
trx_purge_mark_undo_for_truncate 是标记truncate undo表空间的入口函数,主要包括如下步骤
step1: 检查是否开启truncate参数,或者已经有table space已经被标记为truncate
step2:检查是否可以进行安全的truncate,也就是上面说的innodb_undo_tablespaces>=2, innodb_undo_logs>=35
step3:从上次扫描的tablespace开始遍历(round-robin),哪些tablespace可以标记为可truncate,如果发现一个需要truncate的 tablespace,则标记其为需要truncate,并从遍历中break出来:
undo_trunc->mark(space_id);
undo::Truncate::add_space_to_trunc_list(space_id);
step4: 遍历被选中的table space中的回滚段,将其设置为不可分配,判断条件为:
917 if (rseg != NULL && !trx_sys_is_noredo_rseg_slot(rseg->id)) {
918 if (rseg->space
919 == undo_trunc->get_marked_space_id()) {
920
921 /* Once set this rseg will not be allocated
922 to new booting transaction but we will wait
923 for existing active transaction to finish. */
924 rseg->skip_allocation = true;
925 undo_trunc->add_rseg_to_trunc(rseg);
926 }
927 }
2. truncate operation
在标记需要truncate的tablespace后,需要先检查需要删除的回滚段是否是可释放的。也就是没有任何活跃的事务会应用到其中的Undo log
入口函数:trx_purge_initiate_truncate
step 1: 检查回滚段是否可释放,如果不可以(有活跃事务可能使用undo做MVCC),直接返回
step 2:做一次redo checkpoint,因为如果随后发生crash,可能针对该undo tablespace的redo 就会无效了,因为文件被truncate了。
log_make_checkpoint_at(LSN_MAX, TRUE); 这会刷新所有脏页和redo log.
step 3.开始之前…
1100 undo_trunc->start_logging(
1101 undo_trunc->get_marked_space_id());
创建一个命名为undo__trunc.log的文件,如果crash重启发现该文件,则表明truncate tablespace可能没有完成,需要重做.
step 4: 清理对应的purge queue,无需继续做Purge 操作
trx_purge_cleanse_purge_queue(undo_trunc);
step5 : 执行真正的truncate
bool success = trx_undo_truncate_tablespace(undo_trunc);
入口函数:trx_undo_truncate_tablespace
..truncate文件
success = fil_truncate_tablespace(
space_id, SRV_UNDO_TABLESPACE_SIZE_IN_PAGES);
文件先被truncate到0,再重新设置到10M,相当于一个新建的undo tablespace.
…重新初始化undo log tablespace的头,这个过程不记录redo log.
fsp_header_init(space_id, SRV_UNDO_TABLESPACE_SIZE_IN_PAGES, &mtr);
…重新初始化该tablespace内的回滚段头
step 6:在完成truncate后,再做一次checkpoint
step 7: 完成后
undo_trunc->done_logging(undo_trunc->get_marked_space_id());
删除undo__trunc.log
step 8: 清理操作
undo_trunc->reset();
undo::Truncate::clear_trunc_list();
worklog:
http://dev.mysql.com/worklog/task/?id=6965
相关代码:
http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8631
http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8629
http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8622
http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8615
官方博客描述:
http://mysqlserverteam.com/online-truncate-of-innodb-undo-tablespaces/