天天看点

MySQL 5.7 新特性:在线truncate undo log文件

在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         -&gt; rseg resides always in system tablespace 1..32  -&gt; rseg resides in shared temporary tablespace (ibtmp1) 33     -&gt; undo-tablespace-0 34     -&gt; undo-tablespace-1 …. 33+n-1 -&gt; undo-tablespace-n-1 (undo tablespace indexes from 0 so n-1 is last). 33+n   -&gt; undo-tablespace-0 33+n+1 -&gt; undo-tablespace-1 m      -&gt; 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&gt;set global innodb_undo_log_truncate = on; query ok, 0 rows affected (0.00 sec) root@(none) 06:43:32&gt;set global innodb_undo_log_truncate = off; 下面简单介绍下实现的相关代码 0.新的truncate管理结构体 新的类undo::truncate被引入,来管理table space truncate的过程 挂在purge_sys-&gt;undo_trunc中 1.标记需要truncate的undo tablespace 这个动作实际上是由purge的协调线程发起的,默认情况下每做128次purge后,会调用函数trx_purge_truncate进行清理操作,我们只关心truncate,对应的调用栈为: trx_purge_truncate |—&gt;trx_purge_truncate_history |—&gt;trx_purge_mark_undo_for_truncate |—&gt;trx_purge_initiate_truncate trx_purge_mark_undo_for_truncate 是标记truncate undo表空间的入口函数,主要包括如下步骤 step1: 检查是否开启truncate参数,或者已经有table space已经被标记为truncate step2:检查是否可以进行安全的truncate,也就是上面说的innodb_undo_tablespaces&gt;=2,  innodb_undo_logs&gt;=35 step3:从上次扫描的tablespace开始遍历(round-robin),哪些tablespace可以标记为可truncate,如果发现一个需要truncate的 tablespace,则标记其为需要truncate,并从遍历中break出来: undo_trunc-&gt;mark(space_id); undo::truncate::add_space_to_trunc_list(space_id); step4: 遍历被选中的table space中的回滚段,将其设置为不可分配,判断条件为: 917                 if (rseg != null &amp;&amp; !trx_sys_is_noredo_rseg_slot(rseg-&gt;id)) { 918                         if (rseg-&gt;space 919                                 == undo_trunc-&gt;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-&gt;skip_allocation = true; 925                                 undo_trunc-&gt;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-&gt;start_logging( 1101                         undo_trunc-&gt;get_marked_space_id()); 创建一个命名为undo_&lt;space_id&gt;_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, &amp;mtr); …重新初始化该tablespace内的回滚段头 step 6:在完成truncate后,再做一次checkpoint step 7: 完成后 undo_trunc-&gt;done_logging(undo_trunc-&gt;get_marked_space_id()); 删除undo_&lt;space_id&gt;_trunc.log step 8: 清理操作 undo_trunc-&gt;reset(); undo::truncate::clear_trunc_list(); worklog: <a href="http://dev.mysql.com/worklog/task/?id=6965">http://dev.mysql.com/worklog/task/?id=6965</a> 相关代码: <a href="http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8631">http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8631</a> <a href="http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8629">http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8629</a> <a href="http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8622">http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8622</a> <a href="http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8615">http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8615</a> 官方博客描述: <a href="http://mysqlserverteam.com/online-truncate-of-innodb-undo-tablespaces/">http://mysqlserverteam.com/online-truncate-of-innodb-undo-tablespaces/</a>