📄 trx0purge.c
字号:
#ifdef UNIV_SYNC_DEBUG ut_ad(mutex_own(&(purge_sys->mutex)));#endif /* UNIV_SYNC_DEBUG */ if (purge_sys->arr->n_used == 0) { trx_purge_truncate_history(); return(TRUE); } return(FALSE);}/***************************************************************************Updates the last not yet purged history log info in rseg when we have purgeda whole undo log. Advances also purge_sys->purge_trx_no past the purged log. */static voidtrx_purge_rseg_get_next_history_log(/*================================*/ trx_rseg_t* rseg) /* in: rollback segment */{ page_t* undo_page; trx_ulogf_t* log_hdr; trx_usegf_t* seg_hdr; fil_addr_t prev_log_addr; dulint trx_no; ibool del_marks; mtr_t mtr;#ifdef UNIV_SYNC_DEBUG ut_ad(mutex_own(&(purge_sys->mutex)));#endif /* UNIV_SYNC_DEBUG */ mutex_enter(&(rseg->mutex)); ut_a(rseg->last_page_no != FIL_NULL); purge_sys->purge_trx_no = ut_dulint_add(rseg->last_trx_no, 1); purge_sys->purge_undo_no = ut_dulint_zero; purge_sys->next_stored = FALSE; mtr_start(&mtr); undo_page = trx_undo_page_get_s_latched(rseg->space, rseg->last_page_no, &mtr); log_hdr = undo_page + rseg->last_offset; seg_hdr = undo_page + TRX_UNDO_SEG_HDR; /* Increase the purge page count by one for every handled log */ purge_sys->n_pages_handled++; prev_log_addr = trx_purge_get_log_from_hist( flst_get_prev_addr(log_hdr + TRX_UNDO_HISTORY_NODE, &mtr)); if (prev_log_addr.page == FIL_NULL) { /* No logs left in the history list */ rseg->last_page_no = FIL_NULL; mutex_exit(&(rseg->mutex)); mtr_commit(&mtr); mutex_enter(&kernel_mutex); /* Add debug code to track history list corruption reported on the MySQL mailing list on Nov 9, 2004. The fut0lst.c file-based list was corrupt. The prev node pointer was FIL_NULL, even though the list length was over 8 million nodes! We assume that purge truncates the history list in moderate size pieces, and if we here reach the head of the list, the list cannot be longer than 20 000 undo logs now. */ if (trx_sys->rseg_history_len > 20000) { ut_print_timestamp(stderr); fprintf(stderr," InnoDB: Warning: purge reached the head of the history list,\n""InnoDB: but its length is still reported as %lu! Make a detailed bug\n""InnoDB: report, and post it to bugs.mysql.com\n", (ulong)trx_sys->rseg_history_len); } mutex_exit(&kernel_mutex); return; } mutex_exit(&(rseg->mutex)); mtr_commit(&mtr); /* Read the trx number and del marks from the previous log header */ mtr_start(&mtr); log_hdr = trx_undo_page_get_s_latched(rseg->space, prev_log_addr.page, &mtr) + prev_log_addr.boffset; trx_no = mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO); del_marks = mach_read_from_2(log_hdr + TRX_UNDO_DEL_MARKS); mtr_commit(&mtr); mutex_enter(&(rseg->mutex)); rseg->last_page_no = prev_log_addr.page; rseg->last_offset = prev_log_addr.boffset; rseg->last_trx_no = trx_no; rseg->last_del_marks = del_marks; mutex_exit(&(rseg->mutex));} /***************************************************************************Chooses the next undo log to purge and updates the info in purge_sys. Thisfunction is used to initialize purge_sys when the next record to purge isnot known, and also to update the purge system info on the next record whenpurge has handled the whole undo log for a transaction. */static voidtrx_purge_choose_next_log(void)/*===========================*/{ trx_undo_rec_t* rec; trx_rseg_t* rseg; trx_rseg_t* min_rseg; dulint min_trx_no; ulint space = 0; /* remove warning (??? bug ???) */ ulint page_no = 0; /* remove warning (??? bug ???) */ ulint offset = 0; /* remove warning (??? bug ???) */ mtr_t mtr; #ifdef UNIV_SYNC_DEBUG ut_ad(mutex_own(&(purge_sys->mutex)));#endif /* UNIV_SYNC_DEBUG */ ut_ad(purge_sys->next_stored == FALSE); rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list); min_trx_no = ut_dulint_max; min_rseg = NULL; while (rseg) { mutex_enter(&(rseg->mutex)); if (rseg->last_page_no != FIL_NULL) { if ((min_rseg == NULL) || (ut_dulint_cmp(min_trx_no, rseg->last_trx_no) > 0)) { min_rseg = rseg; min_trx_no = rseg->last_trx_no; space = rseg->space; ut_a(space == 0); /* We assume in purge of externally stored fields that space id == 0 */ page_no = rseg->last_page_no; offset = rseg->last_offset; } } mutex_exit(&(rseg->mutex)); rseg = UT_LIST_GET_NEXT(rseg_list, rseg); } if (min_rseg == NULL) { return; } mtr_start(&mtr); if (!min_rseg->last_del_marks) { /* No need to purge this log */ rec = &trx_purge_dummy_rec; } else { rec = trx_undo_get_first_rec(space, page_no, offset, RW_S_LATCH, &mtr); if (rec == NULL) { /* Undo log empty */ rec = &trx_purge_dummy_rec; } } purge_sys->next_stored = TRUE; purge_sys->rseg = min_rseg; purge_sys->hdr_page_no = page_no; purge_sys->hdr_offset = offset; purge_sys->purge_trx_no = min_trx_no; if (rec == &trx_purge_dummy_rec) { purge_sys->purge_undo_no = ut_dulint_zero; purge_sys->page_no = page_no; purge_sys->offset = 0; } else { purge_sys->purge_undo_no = trx_undo_rec_get_undo_no(rec); purge_sys->page_no = buf_frame_get_page_no(rec); purge_sys->offset = rec - buf_frame_align(rec); } mtr_commit(&mtr);}/***************************************************************************Gets the next record to purge and updates the info in the purge system. */statictrx_undo_rec_t*trx_purge_get_next_rec(/*===================*/ /* out: copy of an undo log record or pointer to the dummy undo log record */ mem_heap_t* heap) /* in: memory heap where copied */{ trx_undo_rec_t* rec; trx_undo_rec_t* rec_copy; trx_undo_rec_t* rec2; trx_undo_rec_t* next_rec; page_t* undo_page; page_t* page; ulint offset; ulint page_no; ulint space; ulint type; ulint cmpl_info; mtr_t mtr;#ifdef UNIV_SYNC_DEBUG ut_ad(mutex_own(&(purge_sys->mutex)));#endif /* UNIV_SYNC_DEBUG */ ut_ad(purge_sys->next_stored); space = purge_sys->rseg->space; page_no = purge_sys->page_no; offset = purge_sys->offset; if (offset == 0) { /* It is the dummy undo log record, which means that there is no need to purge this undo log */ trx_purge_rseg_get_next_history_log(purge_sys->rseg); /* Look for the next undo log and record to purge */ trx_purge_choose_next_log(); return(&trx_purge_dummy_rec); } mtr_start(&mtr); undo_page = trx_undo_page_get_s_latched(space, page_no, &mtr); rec = undo_page + offset; rec2 = rec; for (;;) { /* Try first to find the next record which requires a purge operation from the same page of the same undo log */ next_rec = trx_undo_page_get_next_rec(rec2, purge_sys->hdr_page_no, purge_sys->hdr_offset); if (next_rec == NULL) { rec2 = trx_undo_get_next_rec(rec2, purge_sys->hdr_page_no, purge_sys->hdr_offset, &mtr); break; } rec2 = next_rec; type = trx_undo_rec_get_type(rec2); if (type == TRX_UNDO_DEL_MARK_REC) { break; } cmpl_info = trx_undo_rec_get_cmpl_info(rec2); if (trx_undo_rec_get_extern_storage(rec2)) { break; } if ((type == TRX_UNDO_UPD_EXIST_REC) && !(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) { break; } } if (rec2 == NULL) { mtr_commit(&mtr); trx_purge_rseg_get_next_history_log(purge_sys->rseg); /* Look for the next undo log and record to purge */ trx_purge_choose_next_log(); mtr_start(&mtr); undo_page = trx_undo_page_get_s_latched(space, page_no, &mtr); rec = undo_page + offset; } else { page = buf_frame_align(rec2); purge_sys->purge_undo_no = trx_undo_rec_get_undo_no(rec2); purge_sys->page_no = buf_frame_get_page_no(page); purge_sys->offset = rec2 - page; if (undo_page != page) { /* We advance to a new page of the undo log: */ purge_sys->n_pages_handled++; } } rec_copy = trx_undo_rec_copy(rec, heap); mtr_commit(&mtr); return(rec_copy);}/************************************************************************Fetches the next undo log record from the history list to purge. It must bereleased with the corresponding release function. */trx_undo_rec_t*trx_purge_fetch_next_rec(/*=====================*/ /* out: copy of an undo log record or pointer to the dummy undo log record &trx_purge_dummy_rec, if the whole undo log can skipped in purge; NULL if none left */ dulint* roll_ptr,/* out: roll pointer to undo record */ trx_undo_inf_t** cell, /* out: storage cell for the record in the purge array */ mem_heap_t* heap) /* in: memory heap where copied */{ trx_undo_rec_t* undo_rec; mutex_enter(&(purge_sys->mutex)); if (purge_sys->state == TRX_STOP_PURGE) { trx_purge_truncate_if_arr_empty(); mutex_exit(&(purge_sys->mutex)); return(NULL); } if (!purge_sys->next_stored) { trx_purge_choose_next_log(); if (!purge_sys->next_stored) { purge_sys->state = TRX_STOP_PURGE; trx_purge_truncate_if_arr_empty(); if (srv_print_thread_releases) { fprintf(stderr, "Purge: No logs left in the history list; pages handled %lu\n", (ulong) purge_sys->n_pages_handled); } mutex_exit(&(purge_sys->mutex)); return(NULL); } } if (purge_sys->n_pages_handled >= purge_sys->handle_limit) { purge_sys->state = TRX_STOP_PURGE; trx_purge_truncate_if_arr_empty(); mutex_exit(&(purge_sys->mutex)); return(NULL); } if (ut_dulint_cmp(purge_sys->purge_trx_no, purge_sys->view->low_limit_no) >= 0) { purge_sys->state = TRX_STOP_PURGE; trx_purge_truncate_if_arr_empty(); mutex_exit(&(purge_sys->mutex)); return(NULL); } /* fprintf(stderr, "Thread %lu purging trx %lu undo record %lu\n", os_thread_get_curr_id(), ut_dulint_get_low(purge_sys->purge_trx_no), ut_dulint_get_low(purge_sys->purge_undo_no)); */ *roll_ptr = trx_undo_build_roll_ptr(FALSE, (purge_sys->rseg)->id, purge_sys->page_no, purge_sys->offset); *cell = trx_purge_arr_store_info(purge_sys->purge_trx_no, purge_sys->purge_undo_no); ut_ad(ut_dulint_cmp(purge_sys->purge_trx_no, (purge_sys->view)->low_limit_no) < 0); /* The following call will advance the stored values of purge_trx_no and purge_undo_no, therefore we had to store them first */ undo_rec = trx_purge_get_next_rec(heap); mutex_exit(&(purge_sys->mutex)); return(undo_rec);}/***********************************************************************Releases a reserved purge undo record. */voidtrx_purge_rec_release(/*==================*/ trx_undo_inf_t* cell) /* in: storage cell */{ trx_undo_arr_t* arr; mutex_enter(&(purge_sys->mutex)); arr = purge_sys->arr; trx_purge_arr_remove_info(cell); mutex_exit(&(purge_sys->mutex));}/***********************************************************************This function runs a purge batch. */ulinttrx_purge(void)/*===========*/ /* out: number of undo log pages handled in the batch */{ que_thr_t* thr;/* que_thr_t* thr2; */ ulint old_pages_handled; mutex_enter(&(purge_sys->mutex)); if (purge_sys->trx->n_active_thrs > 0) { mutex_exit(&(purge_sys->mutex)); /* Should not happen */ ut_error; return(0); } rw_lock_x_lock(&(purge_sys->latch)); mutex_enter(&kernel_mutex); /* Close and free the old purge view */ read_view_close(purge_sys->view); purge_sys->view = NULL; mem_heap_empty(purge_sys->heap); /* Determine how much data manipulation language (DML) statements need to be delayed in order to reduce the lagging of the purge thread. */ srv_dml_needed_delay = 0; /* in microseconds; default: no delay */ /* If we cannot advance the 'purge view' because of an old 'consistent read view', then the DML statements cannot be delayed. Also, srv_max_purge_lag <= 0 means 'infinity'. */ if (srv_max_purge_lag > 0 && !UT_LIST_GET_LAST(trx_sys->view_list)) { float ratio = (float) trx_sys->rseg_history_len / srv_max_purge_lag; if (ratio > ULINT_MAX / 10000) { /* Avoid overflow: maximum delay is 4295 seconds */ srv_dml_needed_delay = ULINT_MAX; } else if (ratio > 1) { /* If the history list length exceeds the innodb_max_purge_lag, the data manipulation statements are delayed by at least 5000 microseconds. */ srv_dml_needed_delay = (ulint) ((ratio - .5) * 10000); } } purge_sys->view = read_view_oldest_copy_or_open_new(NULL, purge_sys->heap); mutex_exit(&kernel_mutex); rw_lock_x_unlock(&(purge_sys->latch)); purge_sys->state = TRX_PURGE_ON; /* Handle at most 20 undo log pages in one purge batch */ purge_sys->handle_limit = purge_sys->n_pages_handled + 20; old_pages_handled = purge_sys->n_pages_handled; mutex_exit(&(purge_sys->mutex)); mutex_enter(&kernel_mutex); thr = que_fork_start_command(purge_sys->query); ut_ad(thr); /* thr2 = que_fork_start_command(purge_sys->query); ut_ad(thr2); */ mutex_exit(&kernel_mutex);/* srv_que_task_enqueue(thr2); */ if (srv_print_thread_releases) { fputs("Starting purge\n", stderr); } que_run_threads(thr); if (srv_print_thread_releases) { fprintf(stderr, "Purge ends; pages handled %lu\n", (ulong) purge_sys->n_pages_handled); } return(purge_sys->n_pages_handled - old_pages_handled);}/**********************************************************************Prints information of the purge system to stderr. */voidtrx_purge_sys_print(void)/*=====================*/{ fprintf(stderr, "InnoDB: Purge system view:\n"); read_view_print(purge_sys->view); fprintf(stderr, "InnoDB: Purge trx n:o %lu %lu, undo n_o %lu %lu\n", (ulong) ut_dulint_get_high(purge_sys->purge_trx_no), (ulong) ut_dulint_get_low(purge_sys->purge_trx_no), (ulong) ut_dulint_get_high(purge_sys->purge_undo_no), (ulong) ut_dulint_get_low(purge_sys->purge_undo_no)); fprintf(stderr, "InnoDB: Purge next stored %lu, page_no %lu, offset %lu,\n" "InnoDB: Purge hdr_page_no %lu, hdr_offset %lu\n", (ulong) purge_sys->next_stored, (ulong) purge_sys->page_no, (ulong) purge_sys->offset, (ulong) purge_sys->hdr_page_no, (ulong) purge_sys->hdr_offset);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -