http://blog.csdn.net/zhaiwx1987/article/details/7211220
由于每次DML操作都会生成Undo页,系统需要定期对这些undo页进行清理,也就是所谓purge操作。在5.5之前这些都是在master线程中完成,但5.5及之后的版本可以通过innodb_purge_threads来控制是否使用独立线程进行purge操作。
下面将就undolog以及purge线程如何运作做介绍
1.undo log
Undolog保存了记录修改前的镜像。其中insertundorecords在事务回滚时会被丢弃,而updateundorecords会被用到rollback,MVCC以及Purge操作中。
对于一次delete操作:DELETEFROM t WHERE c =1,innodb不会立刻移除相应的记录,在innodb的官方博客上是这么解释的:
It marks the record as deleted bysetting a bit in the control bits of therecord.
Stores the before image of themodified columns to the UNDOlog
Updates the system columnsDB_TRX_IDand DB_ROLL_PTRinthe clustered index record.DB_TRX_IDidentifies the transaction that made the lastchange, and DB_ROLL_PTRpoints to the new UNDO log record. This UNDO logrecord contains the old values of DB_TRX_IDand DB_ROLL_PTR,possiblypointing to an older transaction and undo logentry.
在每个记录上有两个重要的系统数据列,用于多版本的控制:
DB_TRX_ID:最近修改这个记录的事务ID
DB_ROLL_PTR:指向由于最近的更新创建的回滚段
同时在每个undorecord上也会指向旧的undo记录,从而形成了一条更新链,通过这个更新链,不同的事务可以找到其对应的版本的undo信息,组合成就版本记录。
当一个事务提交时,由于该事务生成的undolog和标记删除的记录可能被其他事务所使用。当没有事务需要这些数据时,标记删除的记录和相关的undologrecords可以被purge掉。
2.purge
Purge操作会克隆最老旧的read_view,这个read_view中持有控制信息以限制哪些由其他事务引起的变化是可见的。然后以相反的方式读取从最老的到最近的undolog记录;如果当前运行的事务都没有引用这些记录,则分析这些记录并从索引上移除被标记删除的记录。
有两个相关的控制参数:
Innodb_purge_threads:控制是否使用独立purge线程
Innodb_purge_batch_size:表示一次完成多少个undologpage;但这个值有一个有趣的副作用是会影响到undolog的释放,因为总是在128轮purge后释放undologpage,在5.5及之后版本,开始支持128个回滚段。
一个大致的堆栈如下:
#0 row_purge (thr=0xabd4668,node=0xabd46d0)at/home/yinfeng/Percona-Server-5.5.18-rel23.0/storage/innobase/row/row0purge.c:757
#1 row_purge_step(thr=0xabd4668)at/home/yinfeng/Percona-Server-5.5.18-rel23.0/storage/innobase/row/row0purge.c:805
#2 0x0856bb0e in que_thr_step(thr=0xabd4668)at/home/yinfeng/Percona-Server-5.5.18-rel23.0/storage/innobase/que/que0que.c:1259
#3 que_run_threads_low(thr=0xabd4668)at/home/yinfeng/Percona-Server-5.5.18-rel23.0/storage/innobase/que/que0que.c:1319
#4 que_run_threads(thr=0xabd4668)at/home/yinfeng/Percona-Server-5.5.18-rel23.0/storage/innobase/que/que0que.c:1356
#5 0x08492922 in trx_purge(limit=20)at/home/yinfeng/Percona-Server-5.5.18-rel23.0/storage/innobase/trx/trx0purge.c:1194
#6 0x0848766d insrv_purge_thread (arg=0x0)at/home/yinfeng/Percona-Server-5.5.18-rel23.0/storage/innobase/srv/srv0srv.c:3897
#7 0xb76d3d31 in start_thread(arg=0x8d907b70) at pthread_create.c:304
#8 0xb75ed0ce in clone ()at../sysdeps/unix/sysv/linux/i386/clone.S:130
Purge操作的入口函数为srv_purge_thread,主要工作在一个大while循环里。主要处理函数
trx_purge。
Trx_purge的参数也就是Innodb_purge_batch_size
主要流程如下:
1).rw_lock_x_lock(&purge_sys->latch);
2)判断DML操作是否需要被delay,只有当参数innodb_max_purge_lag被设置为一个大于0的值时,才会去判断。
3)获取当前最老旧的事务read_view(read_view_oldest_copy_or_open_new)
4)thr =que_fork_start_command(purge_sys->query);//不是很明白,留待后续研究
5)主函数调用流程:
que_run_threads->que_run_threads_low->que_thr_step->row_purge_step->row_purge
之前以que开头的函数都定义在que0que.cc文件中,看起来是根据查询计划图来调度相应的模块函数。
比如对于purge操作,会在que_thr_step里调用row_purge_step,而对于commit,会在row_purge_step里调用trx_commit_step。这部分内容还不是很了解,后面会跟进分析
那么我们就来看看row_purge到底做了什么吧
1)trx_purge_fetch_next_rec,从历史undolog列表中选择一条记录
——找出拥有最小事务id的回滚段(purge_sys->rseg)
trx_purge_get_rseg_with_min_trx_id
——从回滚段中读取记录
trx_purge_read_undo_rec
这里还会对purge_sys->n_pages_handled做加1,如果已经purge的记录超过了Innodb_purge_batch_size,则设置purge_sys->state=TRX_STOP_PURGE
2)row_purge_parse_undo_rec
按照注释的说法是从undolog里解析出行引用信息和其他信息,返回值为true表明需要执行purge操作.
通过函数trx_undo_rec_get_pars获得undo记录的类型,主要包括以下几个类型:
TRX_UNDO_INSERT_REC:
freshinsert into clusteredindex
TRX_UNDO_UPD_EXIST_REC:
updateof a non-delete-markedrecord
TRX_UNDO_UPD_DEL_REC
updateof a delete marked recordto a not delete marked record; also the
fieldsof the record canchange
TRX_UNDO_DEL_MARK_REC
deletemarking of a record;fields do not change
TRX_UNDO_CMPL_INFO_MULT
compilationinfo is multiplied bythis and ORed to the type above
TRX_UNDO_UPD_EXTERN
Thisbit can be ORed to type_cmplto denote that we updated externalstorage fields: used by purge tofree the external storage *
在进行一系列判断后,当需要对这一行记录执行purge操作时,需要:
row_mysql_freeze_data_dictionary(trx);
这会阻止所有的create/droptable操作,因为会对dict_operation_lock加S锁,这是个全局锁。当create/drop表的时候需要X锁,一些后台操作例如Purge、rollback、外键检查需要S锁,在一个事务里,我们可以通过
trx_struct::dict_operation_lock_mode来查看是否拥有该锁
trx_undo_rec_get_row_ref(从undolog中获得逻辑记录信息??)
trx_undo_update_rec_get_update(基于undolog创建updatevector??)
最后从一个updateundo logrecord中创建一个partialrow;It contains the
columnswhich occur as orderingin any index of the table
3)对一个标记删除的记录做purge操作
row_purge_del_mark
4)row_purge_upd_exist_or_extern()
Purgesan update of an existingrecord. Also purges an update of a deletemarked record if thatrecord contained an externally stored field.
无论哪种purge,会依次查看二级索引,调用row_build_index_entry创建一个老的记录,然后调用row_purge_remove_sec_if_poss从二级索引种将旧记录删除。
最后还会调用row_purge_remove_clust_if_poss来删除主键上的记录
6)row_mysql_unfreeze_data_dictionary,释放在dict_operation_lock上的S锁
7)清理操作
参考资料:
1.http://blogs.innodb.com/wp/2011/07/allow-undo-logs-to-reside-in-their-own-tablespace/
2.http://blogs.innodb.com/wp/2011/04/mysql-5-6-multi-threaded-purge/
3.percona5.5.18