简要记录跟踪代码,很多代码流程没有细细的跟进去,只是了解了个大概,杂七杂八,还有太多不了解的地方。
不过,一知半解总比一无所知要好点…sign…
////////////////////////////////////////////
初始化各种全局信号量,kernel_mutex、srv_sys以及srv_sys->threads数组中每个slot的event(调用os_event_create初始化),同样的还需要初始化srv_mysql_table数组(类型为srv_slot_t)dict_ind_init();//初始化dict_ind_redundant和dict_ind_compact,为infimum 和supremum 记录 创建一个名为SYS_DUMMY1/SYS_DUMMY2的表结构,暂时不了解用途。初始化srv_conc_slots
err = recv_recovery_from_checkpoint_start(LOG_CHECKPOINT,IB_ULONGLONG_MAX,min_flushed_lsn,max_flushed_lsn);a.创建recovery子系统 recv_sys //recv_sys_createb.recv_sys_init(buf_pool_get_curr_size())buf_flush_init_flush_rbt();//初始化红黑树,主要用来加速插入到flush_list上。对应rbt在每个buf_pool->flush_rbt上
确定recv_n_pool_free_frames的大小,该值表示当我们扫描日志并存储扫描到的记录到bp中时,必须至少保留这么多空闲的frame.这样我们就可以把数据page读入内存并执行日志记录
当bp>=10MB时, recv_n_pool_free_frames = 512;
当bp>=32MB时,recv_n_pool_free_frames = 1024;
为recv_sys->buf分配2MB内存,及其他相关结构成员内存分配
c.设置recv_recovery_on = TRUE;表明recovery已经开始了,在很多代码逻辑里,都需要根据这个变量来判断是否处于崩溃恢复状态。
d.从日志组里找到最大的checkpointerr = recv_find_max_checkpoint(&max_cp_group, &max_cp_field);
LOG_GROUP_ID | 0 | 日志组ID |
LOG_FILE_START_LSN | 4 | 在该日志文件中数据开始的LSN |
LOG_FILE_NO | 12 | 4 byte的归档日志文件号,暂不清楚归档日志用途 |
LOG_FILE_WAS_CREATED_BY_HOT_BACKUP | 16 |
当日志文件是通过ibbackup –restore产生时,保留32-byte记录了
字符串‘ibbackup’以及日志的创建时间
|
LOG_FILE_OS_FILE_LOG_BLOCK_SIZE | 64 | 用于记录xtradb的log_block_size,在xtradb中这是可调整的,默认为512 |
LOG_FILE_ARCH_COMPLETED | OS_FILE_LOG_BLOCK_SIZE | 4-byte,为true表示归档日志完成 |
LOG_FILE_END_LSN | OS_FILE_LOG_BLOCK_SIZE + 4 | lsn where the archived log file at least extends: actually the archived log file may extend to a later lsn, as long as it is within the same log block as this lsn; this field is defined only when an archived log file has been completely written |
LOG_CHECKPOINT_1 | OS_FILE_LOG_BLOCK_SIZE | 日志文件中的第一个checkpoint字段,当完成一次新的checkpoint 时,可选的选择是否记录,注意这只记录在第一个日志文件头部 |
LOG_CHECKPOINT_2 | (3 * OS_FILE_LOG_BLOCK_SIZE) | 日志头部的第二个checkpoint字段 |
LOG_FILE_HDR_SIZE | 4 * OS_FILE_LOG_BLOCK_SIZE | 日志头部长度,为4个log block size |
在一个while循环中>>读取每个log头部的checkpoint 字段(log_group_read_checkpoint_info(group, field))>>验证checksum(函数recv_check_cp_is_consistent,读取头部的LOG_CHECKPOINT_CHECKSUM_1及LOG_CHECKPOINT_CHECKSUM_2)>>读取checkpoint字段记录的LOG_CHECKPOINT_LSN、LOG_CHECKPOINT_NO、LOG_CHECKPOINT_OFFSET>>比较checkpoint no,找出最大的那个日志文件
e.从找到的最大checkpoint_lsn开始扫描,需要做一个预处理:
contiguous_lsn = ut_uint64_align_down(recv_sys->scanned_lsn,OS_FILE_LOG_BLOCK_SIZE);对contiguous_lsn 以log block size做对齐处理
g.开始遍历日志文件,从contiguous_lsn 开始读取日志记录,函数recv_group_scan_log_recs>>调用函数log_group_read_log_seg读取一个日志段到内存中,每个段默认4个page(RECV_SCAN_SIZE)>>扫描日志数据记录 //recv_scan_log_recs>>>遍历刚刚读取到内存中的日志数据的每一个block
>>>检查block头部的block no以及checksum信息
>>>如果这个block是一次flush操作的开始(log_block_get_flush_bit(log_block)),则意味着该block之前的刷新操作都已经完成了,因此更新contiguous_lsn为scanned_lsn(不太理解)
>>>找到block中第一条mtr日志的起点位置
>>>如果当前scanned_lsn > recv_sys->scanned_lsn,表明有新的entries,随后需要解析这些记录,做崩溃恢复
recv_init_crash_recovery–>fil_load_single_table_tablespaces()
—遍历data目录,读取其中所有的ibd文件
—从每个ibd的第一个page获取space id//fsp_header_get_space_id
—打开文件,创建文件节点加入到fil_system中//fil_load_single_table_tablespace
–>srv_force_recovery小于6(SRV_FORCE_NO_LOG_REDO)时,需要从double write buffer中检查page,如果数据文件中的page是损坏的,则从double write buffer中恢复(调用函数trx_sys_doublewrite_init_or_restore_pages)
>>>将日志记录拷贝到recv_sys->buf中//recv_sys_add_to_parsing_buf>>>recv_parse_log_recs–解析日志记录(recv_parse_log_rec),获取其mtr 类型,space id,page no以及指向日志记录的指针,并将其存储到hash中(recv_add_to_hash_table),用于后续的merge.这里会针对一个mtr有一条还是多条日志记录分别作处理。>>>如果存储在hash table中的记录超出限制,则调用recv_apply_hashed_log_recs来应用这些日志 //稍后细述如何应用
h.recv_synchronize_groups(up_to_date_group);//将最新日志组中的信息()
i.其他…
15.dict_boot();//向dict_sys中加载系统表
16.trx_sys_init_at_db_start() //获取undo
a.读取ibdata中的第FSP_TRX_SYS_PAGE_NO个Page,从该page的第TRX_SYS偏移量开始表示sys_header = trx_sysf_get(&mtr);
b.trx_rseg_list_and_array_init->trx_rseg_create_instance遍历128(TRX_SYS_N_RSEGS)个回滚段,读取sys_header存储的每个回滚段slot的page no
如果该回滚段slot没有被使用,即page_no = FIL_NULL,则设置sys->rseg_array[n] = NULL
否则从sys_header头读取该回滚段slot所对应的space id(trx_sysf_rseg_get_space),并在内存中创建回滚段对象(trx_rseg_mem_create),读取所有undo page的信息
>>初始化当前回滚段的trx_rseg_t信息,分配内存,并将其加入到trx_sys->rseg_list中
>>读取回滚段头部数据到内存rseg_header = trx_rsegf_get_new(space, zip_size, page_no, mtr);
>>sum_of_undo_sizes = trx_undo_lists_init(rseg);
根据rseg_header初始化undo log list,并将读取的undo log加入到 rseg->insert_undo_list和rseg->insert_undo_cached中
具体没有深入进去看。
>>获取回滚段trx_rseg_t的last_trx_no、last_del_marks、last_page_no、last_offset,并封装成rseg_queue_t存储到binary heap中,这样便于根据事务id进行排序
c.基于undo log list,构建需要回滚的事务,并加入到trx_sys->trx_list中(trx_list_insert_ordered)对于处于prepare状态的事务,需要用户在重启后手动commit或rollback掉
d.最后创建purge_sys子系统 //trx_purge_sys_create