diff options
Diffstat (limited to 'storage/innobase/trx/trx0purge.c')
-rw-r--r-- | storage/innobase/trx/trx0purge.c | 399 |
1 files changed, 205 insertions, 194 deletions
diff --git a/storage/innobase/trx/trx0purge.c b/storage/innobase/trx/trx0purge.c index 4c787579a03..02ec9f1c072 100644 --- a/storage/innobase/trx/trx0purge.c +++ b/storage/innobase/trx/trx0purge.c @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. +Copyright (c) 1996, 2011, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -57,8 +57,8 @@ UNIV_INTERN mysql_pfs_key_t trx_purge_latch_key; #endif /* UNIV_PFS_RWLOCK */ #ifdef UNIV_PFS_MUTEX -/* Key to register purge_sys_mutex with performance schema */ -UNIV_INTERN mysql_pfs_key_t purge_sys_mutex_key; +/* Key to register purge_sys_bh_mutex with performance schema */ +UNIV_INTERN mysql_pfs_key_t purge_sys_bh_mutex_key; #endif /* UNIV_PFS_MUTEX */ /*****************************************************************//** @@ -219,13 +219,16 @@ Creates the global purge system control structure and inits the history mutex. */ UNIV_INTERN void -trx_purge_sys_create(void) -/*======================*/ +trx_purge_sys_create( +/*=================*/ + ib_bh_t* ib_bh) /*!< in, own: UNDO log min binary heap */ { ut_ad(mutex_own(&kernel_mutex)); - purge_sys = mem_alloc(sizeof(trx_purge_t)); + purge_sys = mem_zalloc(sizeof(trx_purge_t)); + /* Take ownership of ib_bh, we are responsible for freeing it. */ + purge_sys->ib_bh = ib_bh; purge_sys->state = TRX_STOP_PURGE; purge_sys->n_pages_handled = 0; @@ -237,8 +240,9 @@ trx_purge_sys_create(void) rw_lock_create(trx_purge_latch_key, &purge_sys->latch, SYNC_PURGE_LATCH); - mutex_create(purge_sys_mutex_key, - &purge_sys->mutex, SYNC_PURGE_SYS); + mutex_create( + purge_sys_bh_mutex_key, &purge_sys->bh_mutex, + SYNC_PURGE_QUEUE); purge_sys->heap = mem_heap_create(256); @@ -288,9 +292,12 @@ trx_purge_sys_close(void) trx_undo_arr_free(purge_sys->arr); rw_lock_free(&purge_sys->latch); - mutex_free(&purge_sys->mutex); + mutex_free(&purge_sys->bh_mutex); mem_heap_free(purge_sys->heap); + + ib_bh_free(purge_sys->ib_bh); + mem_free(purge_sys); purge_sys = NULL; @@ -311,34 +318,31 @@ trx_purge_add_update_undo_to_history( mtr_t* mtr) /*!< in: mtr */ { trx_undo_t* undo; - trx_rseg_t* rseg; trx_rsegf_t* rseg_header; -#ifdef UNIV_DEBUG - trx_usegf_t* seg_header; -#endif /* UNIV_DEBUG */ trx_ulogf_t* undo_header; - ulint hist_size; undo = trx->update_undo; ut_ad(undo); - rseg = undo->rseg; + ut_ad(mutex_own(&undo->rseg->mutex)); - ut_ad(mutex_own(&(rseg->mutex))); - - rseg_header = trx_rsegf_get(rseg->space, rseg->zip_size, - rseg->page_no, mtr); + rseg_header = trx_rsegf_get( + undo->rseg->space, undo->rseg->zip_size, undo->rseg->page_no, + mtr); undo_header = undo_page + undo->hdr_offset; + /* Add the log as the first in the history list */ + + if (undo->state != TRX_UNDO_CACHED) { + ulint hist_size; #ifdef UNIV_DEBUG - seg_header = undo_page + TRX_UNDO_SEG_HDR; + trx_usegf_t* seg_header = undo_page + TRX_UNDO_SEG_HDR; #endif /* UNIV_DEBUG */ - if (undo->state != TRX_UNDO_CACHED) { /* The undo log segment will not be reused */ - if (undo->id >= TRX_RSEG_N_SLOTS) { + if (UNIV_UNLIKELY(undo->id >= TRX_RSEG_N_SLOTS)) { fprintf(stderr, "InnoDB: Error: undo->id is %lu\n", (ulong) undo->id); @@ -347,42 +351,50 @@ trx_purge_add_update_undo_to_history( trx_rsegf_set_nth_undo(rseg_header, undo->id, FIL_NULL, mtr); - hist_size = mtr_read_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE, - MLOG_4BYTES, mtr); + hist_size = mtr_read_ulint( + rseg_header + TRX_RSEG_HISTORY_SIZE, MLOG_4BYTES, mtr); + ut_ad(undo->size == flst_get_len( seg_header + TRX_UNDO_PAGE_LIST, mtr)); - mlog_write_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE, - hist_size + undo->size, MLOG_4BYTES, mtr); + mlog_write_ulint( + rseg_header + TRX_RSEG_HISTORY_SIZE, + hist_size + undo->size, MLOG_4BYTES, mtr); } - /* Add the log as the first in the history list */ - flst_add_first(rseg_header + TRX_RSEG_HISTORY, - undo_header + TRX_UNDO_HISTORY_NODE, mtr); - mutex_enter(&kernel_mutex); - trx_sys->rseg_history_len++; - mutex_exit(&kernel_mutex); - - if (!(trx_sys->rseg_history_len % srv_purge_batch_size)) { - /* Inform the purge thread that there is work to do. */ - srv_wake_purge_thread_if_not_active(); - } + flst_add_first( + rseg_header + TRX_RSEG_HISTORY, + undo_header + TRX_UNDO_HISTORY_NODE, mtr); /* Write the trx number to the undo log header */ + mlog_write_ull(undo_header + TRX_UNDO_TRX_NO, trx->no, mtr); + /* Write information about delete markings to the undo log header */ if (!undo->del_marks) { - mlog_write_ulint(undo_header + TRX_UNDO_DEL_MARKS, FALSE, - MLOG_2BYTES, mtr); + mlog_write_ulint( + undo_header + TRX_UNDO_DEL_MARKS, FALSE, + MLOG_2BYTES, mtr); } - if (rseg->last_page_no == FIL_NULL) { + if (undo->rseg->last_page_no == FIL_NULL) { + undo->rseg->last_trx_no = trx->no; + undo->rseg->last_offset = undo->hdr_offset; + undo->rseg->last_page_no = undo->hdr_page_no; + undo->rseg->last_del_marks = undo->del_marks; - rseg->last_page_no = undo->hdr_page_no; - rseg->last_offset = undo->hdr_offset; - rseg->last_trx_no = trx->no; - rseg->last_del_marks = undo->del_marks; + /* FIXME: Add a bin heap validate function to check that + the rseg exists. */ + } + + mutex_enter(&kernel_mutex); + trx_sys->rseg_history_len++; + mutex_exit(&kernel_mutex); + + if (!(trx_sys->rseg_history_len % srv_purge_batch_size)) { + /* Inform the purge thread that there is work to do. */ + srv_wake_purge_thread_if_not_active(); } } @@ -411,7 +423,6 @@ trx_purge_free_segment( /* fputs("Freeing an update undo log segment\n", stderr); */ - ut_ad(mutex_own(&(purge_sys->mutex))); loop: mtr_start(&mtr); mutex_enter(&(rseg->mutex)); @@ -515,8 +526,6 @@ trx_purge_truncate_rseg_history( mtr_t mtr; trx_id_t undo_trx_no; - ut_ad(mutex_own(&(purge_sys->mutex))); - mtr_start(&mtr); mutex_enter(&(rseg->mutex)); @@ -609,10 +618,8 @@ trx_purge_truncate_history(void) trx_id_t limit_trx_no; undo_no_t limit_undo_no; - ut_ad(mutex_own(&(purge_sys->mutex))); - - trx_purge_arr_get_biggest(purge_sys->arr, &limit_trx_no, - &limit_undo_no); + trx_purge_arr_get_biggest( + purge_sys->arr, &limit_trx_no, &limit_undo_no); if (limit_trx_no == 0) { @@ -630,34 +637,29 @@ trx_purge_truncate_history(void) ut_ad(limit_trx_no <= purge_sys->view->low_limit_no); - rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list); + for (rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list); + rseg != NULL; + rseg = UT_LIST_GET_NEXT(rseg_list, rseg)) { - while (rseg) { - trx_purge_truncate_rseg_history(rseg, limit_trx_no, - limit_undo_no); - rseg = UT_LIST_GET_NEXT(rseg_list, rseg); + trx_purge_truncate_rseg_history( + rseg, limit_trx_no, limit_undo_no); } } /********************************************************************//** Does a truncate if the purge array is empty. NOTE that when this function is -called, the caller must not have any latches on undo log pages! -@return TRUE if array empty */ +called, the caller must not have any latches on undo log pages! */ UNIV_INLINE -ibool +void trx_purge_truncate_if_arr_empty(void) /*=================================*/ { - ut_ad(mutex_own(&(purge_sys->mutex))); + static ulint count; - if (purge_sys->arr->n_used == 0) { + if (!(++count % TRX_SYS_N_RSEGS) && purge_sys->arr->n_used == 0) { trx_purge_truncate_history(); - - return(TRUE); } - - return(FALSE); } /***********************************************************************//** @@ -675,8 +677,8 @@ trx_purge_rseg_get_next_history_log( trx_id_t trx_no; ibool del_marks; mtr_t mtr; - - ut_ad(mutex_own(&(purge_sys->mutex))); + rseg_queue_t rseg_queue; + const void* ptr; mutex_enter(&(rseg->mutex)); @@ -688,8 +690,9 @@ trx_purge_rseg_get_next_history_log( mtr_start(&mtr); - undo_page = trx_undo_page_get_s_latched(rseg->space, rseg->zip_size, - rseg->last_page_no, &mtr); + undo_page = trx_undo_page_get_s_latched( + rseg->space, rseg->zip_size, rseg->last_page_no, &mtr); + log_hdr = undo_page + rseg->last_offset; /* Increase the purge page count by one for every handled log */ @@ -698,6 +701,7 @@ trx_purge_rseg_get_next_history_log( 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 */ @@ -712,11 +716,11 @@ trx_purge_rseg_get_next_history_log( 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 + We assume that purge truncates the history list in large size pieces, and if we here reach the head of the list, the - list cannot be longer than 20 000 undo logs now. */ + list cannot be longer than 2000 000 undo logs now. */ - if (trx_sys->rseg_history_len > 20000) { + if (trx_sys->rseg_history_len > 2000000) { ut_print_timestamp(stderr); fprintf(stderr, " InnoDB: Warning: purge reached the" @@ -756,105 +760,150 @@ trx_purge_rseg_get_next_history_log( rseg->last_trx_no = trx_no; rseg->last_del_marks = del_marks; + rseg_queue.rseg = rseg; + rseg_queue.trx_no = rseg->last_trx_no; + + /* Purge can also produce events, however these are already ordered + in the rollback segment and any user generated event will be greater + than the events that Purge produces. ie. Purge can never produce + events from an empty rollback segment. */ + + mutex_enter(&purge_sys->bh_mutex); + + ptr = ib_bh_push(purge_sys->ib_bh, &rseg_queue); + ut_a(ptr != NULL); + + mutex_exit(&purge_sys->bh_mutex); + mutex_exit(&(rseg->mutex)); } /***********************************************************************//** -Chooses the next undo log to purge and updates the info in purge_sys. This -function is used to initialize purge_sys when the next record to purge is -not known, and also to update the purge system info on the next record when -purge has handled the whole undo log for a transaction. */ +Chooses the rollback segment with the smallest trx_id. +@return zip_size if log is for a compressed table, ULINT_UNDEFINED if + no rollback segments to purge, 0 for non compressed tables. */ static -void -trx_purge_choose_next_log(void) -/*===========================*/ +ulint +trx_purge_get_rseg_with_min_trx_id( +/*===============================*/ + trx_purge_t* purge_sys) /*!< in/out: purge instance */ + { - trx_undo_rec_t* rec; - trx_rseg_t* rseg; - trx_rseg_t* min_rseg; - trx_id_t min_trx_no; - ulint space = 0; /* remove warning (??? bug ???) */ ulint zip_size = 0; - ulint page_no = 0; /* remove warning (??? bug ???) */ - ulint offset = 0; /* remove warning (??? bug ???) */ - mtr_t mtr; - ut_ad(mutex_own(&(purge_sys->mutex))); - ut_ad(purge_sys->next_stored == FALSE); + mutex_enter(&purge_sys->bh_mutex); - rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list); + /* Only purge consumes events from the binary heap, user + threads only produce the events. */ - min_trx_no = IB_ULONGLONG_MAX; + if (!ib_bh_is_empty(purge_sys->ib_bh)) { + trx_rseg_t* rseg; - min_rseg = NULL; + rseg = ((rseg_queue_t*) ib_bh_first(purge_sys->ib_bh))->rseg; + ib_bh_pop(purge_sys->ib_bh); - while (rseg) { - mutex_enter(&(rseg->mutex)); + mutex_exit(&purge_sys->bh_mutex); - if (rseg->last_page_no != FIL_NULL) { + purge_sys->rseg = rseg; + } else { + mutex_exit(&purge_sys->bh_mutex); - if (min_rseg == NULL - || min_trx_no > rseg->last_trx_no) { + purge_sys->rseg = NULL; - min_rseg = rseg; - min_trx_no = rseg->last_trx_no; - space = rseg->space; - zip_size = rseg->zip_size; - 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; - } - } + return(ULINT_UNDEFINED); + } - mutex_exit(&(rseg->mutex)); + ut_a(purge_sys->rseg != NULL); - rseg = UT_LIST_GET_NEXT(rseg_list, rseg); - } + mutex_enter(&purge_sys->rseg->mutex); - if (min_rseg == NULL) { + ut_a(purge_sys->rseg->last_page_no != FIL_NULL); - return; - } + /* We assume in purge of externally stored fields + that space id == 0 */ + ut_a(purge_sys->rseg->space == 0); - mtr_start(&mtr); + zip_size = purge_sys->rseg->zip_size; - if (!min_rseg->last_del_marks) { - /* No need to purge this log */ + ut_a(purge_sys->purge_trx_no <= purge_sys->rseg->last_trx_no); - rec = &trx_purge_dummy_rec; - } else { - rec = trx_undo_get_first_rec(space, zip_size, page_no, offset, - RW_S_LATCH, &mtr); - if (rec == NULL) { - /* Undo log empty */ + purge_sys->purge_trx_no = purge_sys->rseg->last_trx_no; + + purge_sys->hdr_offset = purge_sys->rseg->last_offset; + + purge_sys->hdr_page_no = purge_sys->rseg->last_page_no; + + mutex_exit(&purge_sys->rseg->mutex); + + return(zip_size); +} + +/***********************************************************************//** +Position the purge sys "iterator" on the undo record to use for purging. */ +static +void +trx_purge_read_undo_rec( +/*====================*/ + trx_purge_t* purge_sys, /*!< in/out: purge instance */ + ulint zip_size) /*!< in: block size or 0 */ +{ + ulint page_no; + ulint offset = 0; + ib_uint64_t undo_no = 0; + + purge_sys->hdr_offset = purge_sys->rseg->last_offset; + page_no = purge_sys->hdr_page_no = purge_sys->rseg->last_page_no; + + if (purge_sys->rseg->last_del_marks) { + mtr_t mtr; + trx_undo_rec_t* undo_rec; - rec = &trx_purge_dummy_rec; + mtr_start(&mtr); + + undo_rec = trx_undo_get_first_rec( + 0 /* System space id */, zip_size, + purge_sys->hdr_page_no, + purge_sys->hdr_offset, RW_S_LATCH, &mtr); + + if (undo_rec != NULL) { + offset = page_offset(undo_rec); + undo_no = trx_undo_rec_get_undo_no(undo_rec); + page_no = page_get_page_no(page_align(undo_rec)); } + + mtr_commit(&mtr); } + purge_sys->offset = offset; + purge_sys->page_no = page_no; + purge_sys->purge_undo_no = undo_no; + purge_sys->next_stored = TRUE; - purge_sys->rseg = min_rseg; +} - purge_sys->hdr_page_no = page_no; - purge_sys->hdr_offset = offset; +/***********************************************************************//** +Chooses the next undo log to purge and updates the info in purge_sys. This +function is used to initialize purge_sys when the next record to purge is +not known, and also to update the purge system info on the next record when +purge has handled the whole undo log for a transaction. */ +static +void +trx_purge_choose_next_log(void) +/*===========================*/ +{ + ulint zip_size; - purge_sys->purge_trx_no = min_trx_no; + ut_ad(purge_sys->next_stored == FALSE); - if (rec == &trx_purge_dummy_rec) { + zip_size = trx_purge_get_rseg_with_min_trx_id(purge_sys); - purge_sys->purge_undo_no = 0; - purge_sys->page_no = page_no; - purge_sys->offset = 0; - } else { - purge_sys->purge_undo_no = trx_undo_rec_get_undo_no(rec); + if (purge_sys->rseg != NULL) { - purge_sys->page_no = page_get_page_no(page_align(rec)); - purge_sys->offset = page_offset(rec); + trx_purge_read_undo_rec(purge_sys, zip_size); + } else { + /* There is nothing to do yet. */ + os_thread_yield(); } - - mtr_commit(&mtr); } /***********************************************************************//** @@ -880,7 +929,6 @@ trx_purge_get_next_rec( ulint cmpl_info; mtr_t mtr; - ut_ad(mutex_own(&(purge_sys->mutex))); ut_ad(purge_sys->next_stored); space = purge_sys->rseg->space; @@ -903,8 +951,8 @@ trx_purge_get_next_rec( mtr_start(&mtr); - undo_page = trx_undo_page_get_s_latched(space, zip_size, - page_no, &mtr); + undo_page = trx_undo_page_get_s_latched(space, zip_size, page_no, &mtr); + rec = undo_page + offset; rec2 = rec; @@ -913,9 +961,9 @@ trx_purge_get_next_rec( /* 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); + 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, @@ -995,17 +1043,12 @@ trx_purge_fetch_next_rec( { 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) { + } else if (!purge_sys->next_stored) { trx_purge_choose_next_log(); if (!purge_sys->next_stored) { @@ -1020,8 +1063,6 @@ trx_purge_fetch_next_rec( (ulong) purge_sys->n_pages_handled); } - mutex_exit(&(purge_sys->mutex)); - return(NULL); } } @@ -1032,18 +1073,12 @@ trx_purge_fetch_next_rec( trx_purge_truncate_if_arr_empty(); - mutex_exit(&(purge_sys->mutex)); - return(NULL); - } - - if (purge_sys->purge_trx_no >= purge_sys->view->low_limit_no) { + } else if (purge_sys->purge_trx_no >= purge_sys->view->low_limit_no) { purge_sys->state = TRX_STOP_PURGE; trx_purge_truncate_if_arr_empty(); - mutex_exit(&(purge_sys->mutex)); - return(NULL); } @@ -1052,12 +1087,13 @@ trx_purge_fetch_next_rec( (ullint) purge_sys->purge_trx_no, (ullint) 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); + *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(purge_sys->purge_trx_no < purge_sys->view->low_limit_no); @@ -1066,8 +1102,6 @@ trx_purge_fetch_next_rec( undo_rec = trx_purge_get_next_rec(heap); - mutex_exit(&(purge_sys->mutex)); - return(undo_rec); } @@ -1079,11 +1113,7 @@ trx_purge_rec_release( /*==================*/ trx_undo_inf_t* cell) /*!< in: storage cell */ { - mutex_enter(&(purge_sys->mutex)); - trx_purge_arr_remove_info(cell); - - mutex_exit(&(purge_sys->mutex)); } /*******************************************************************//** @@ -1097,23 +1127,11 @@ trx_purge( purge in one 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) { + ut_a(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)); + rw_lock_x_lock(&purge_sys->latch); mutex_enter(&kernel_mutex); @@ -1147,8 +1165,9 @@ trx_purge( } } - purge_sys->view = read_view_oldest_copy_or_open_new(0, - purge_sys->heap); + purge_sys->view = read_view_oldest_copy_or_open_new( + 0, purge_sys->heap); + mutex_exit(&kernel_mutex); rw_lock_x_unlock(&(purge_sys->latch)); @@ -1159,7 +1178,6 @@ trx_purge( old_pages_handled = purge_sys->n_pages_handled; - mutex_exit(&(purge_sys->mutex)); mutex_enter(&kernel_mutex); @@ -1167,15 +1185,8 @@ trx_purge( 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); |