diff --git a/storage/innobase/include/trx0sys.h b/storage/innobase/include/trx0sys.h index 2910ade..e5b7ef5 100644 --- a/storage/innobase/include/trx0sys.h +++ b/storage/innobase/include/trx0sys.h @@ -394,15 +394,18 @@ struct trx_sys_t { TrxSysMutex mutex; /*!< mutex protecting most fields in this structure except when noted otherwise */ + TrxSysMutex serial_mutex; + /*!< mutex to protect serialisation_list */ MVCC *mvcc; /*!< Multi version concurrency control manager */ - volatile trx_id_t max_trx_id; /*!< The smallest number not yet + std::atomic max_trx_id; /*!< The smallest number not yet assigned as a transaction id or transaction number. This is declared volatile because it can be accessed without holding any mutex during AC-NL-RO view creation. */ + std::atomic min_trx_no; std::atomic min_active_id; /*!< Minimal transaction id which is still in active state. */ diff --git a/storage/innobase/include/trx0sys.ic b/storage/innobase/include/trx0sys.ic index b473e20..cd7022f 100644 --- a/storage/innobase/include/trx0sys.ic +++ b/storage/innobase/include/trx0sys.ic @@ -335,7 +335,6 @@ trx_t *trx_rw_is_active(trx_id_t trx_id, /*!< in: trx id of the transaction */ @return new, allocated trx id */ UNIV_INLINE trx_id_t trx_sys_get_new_trx_id() { - ut_ad(trx_sys_mutex_own()); /* VERY important: after the database is started, max_trx_id value is divisible by TRX_SYS_TRX_ID_WRITE_MARGIN, and the following if @@ -356,24 +355,7 @@ trx_id_t trx_sys_get_new_trx_id() { next call to trx_sys_get_new_trx_id() */ UNIV_INLINE trx_id_t trx_sys_get_max_trx_id(void) { - ut_ad(!trx_sys_mutex_own()); - -#if UNIV_WORD_SIZE < DATA_TRX_ID_LEN - /* Avoid torn reads. */ - - trx_sys_mutex_enter(); - - trx_id_t max_trx_id = trx_sys->max_trx_id; - - trx_sys_mutex_exit(); - - return (max_trx_id); -#else - /* Perform a dirty read. Callers should be prepared for stale - values, and we know that the value fits in a machine word, so - that it will be read and written atomically. */ - return (trx_sys->max_trx_id); -#endif /* UNIV_WORD_SIZE < DATA_TRX_ID_LEN */ + return(trx_sys->max_trx_id.load()); } /** Determine if there are incomplete transactions in the system. diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h index 9c2a0cc..69210df 100644 --- a/storage/innobase/include/trx0trx.h +++ b/storage/innobase/include/trx0trx.h @@ -406,6 +406,8 @@ Check if redo/noredo rseg is modified for insert/update. UNIV_INLINE bool trx_is_rseg_updated(const trx_t *trx); +trx_id_t trx_get_serial_no(); + /** Transactions that aren't started by the MySQL server don't set the trx_t::mysql_thd field. For such transactions we set the lock diff --git a/storage/innobase/read/read0read.cc b/storage/innobase/read/read0read.cc index 68b2154..a63f42e 100644 --- a/storage/innobase/read/read0read.cc +++ b/storage/innobase/read/read0read.cc @@ -434,14 +434,10 @@ void ReadView::prepare(trx_id_t id) { m_ids.clear(); } - if (UT_LIST_GET_LEN(trx_sys->serialisation_list) > 0) { - const trx_t *trx; + trx_id_t min_no = trx_get_serial_no(); - trx = UT_LIST_GET_FIRST(trx_sys->serialisation_list); - - if (trx->no < m_low_limit_no) { - m_low_limit_no = trx->no; - } + if (min_no < m_low_limit_no) { + m_low_limit_no = min_no; } } diff --git a/storage/innobase/trx/trx0sys.cc b/storage/innobase/trx/trx0sys.cc index 7b4adde..39a72ba 100644 --- a/storage/innobase/trx/trx0sys.cc +++ b/storage/innobase/trx/trx0sys.cc @@ -385,6 +385,7 @@ void trx_sys_create(void) { trx_sys = static_cast(ut_zalloc_nokey(sizeof(*trx_sys))); mutex_create(LATCH_ID_TRX_SYS, &trx_sys->mutex); + mutex_create(LATCH_ID_TRX_SYS, &trx_sys->serial_mutex); UT_LIST_INIT(trx_sys->serialisation_list, &trx_t::no_list); UT_LIST_INIT(trx_sys->rw_trx_list, &trx_t::trx_list); @@ -392,6 +393,8 @@ void trx_sys_create(void) { trx_sys->mvcc = UT_NEW_NOKEY(MVCC(1024)); + trx_sys->min_trx_no = TRX_ID_MAX; + trx_sys->min_active_id = 0; new (&trx_sys->rw_trx_ids) @@ -492,6 +495,7 @@ void trx_sys_close(void) { /* We used placement new to create this mutex. Call the destructor. */ mutex_free(&trx_sys->mutex); + mutex_free(&trx_sys->serial_mutex); trx_sys->rw_trx_ids.~trx_ids_t(); diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc index 1055b56..9293e7f 100644 --- a/storage/innobase/trx/trx0trx.cc +++ b/storage/innobase/trx/trx0trx.cc @@ -1319,6 +1319,53 @@ static void trx_start_low( MONITOR_INC(MONITOR_TRX_ACTIVE); } +bool trx_add_serial_list(trx_t* trx) +{ + mutex_enter(&trx_sys->serial_mutex); + trx->no = trx_sys_get_new_trx_id(); + + if (trx->read_only) { + return (false); + } + + UT_LIST_ADD_LAST(trx_sys->serialisation_list, trx); + + if (UT_LIST_GET_LEN(trx_sys->serialisation_list) == 1) { + trx_sys->min_trx_no.store(trx->no); + } + + return (true); +} + +trx_id_t +trx_get_serial_no(void) +{ + trx_id_t trx_no = trx_sys->min_trx_no.load(); + + if (trx_no == TRX_ID_MAX) { + mutex_enter(&trx_sys->serial_mutex); + trx_no = trx_sys->min_trx_no.load(); + mutex_exit(&trx_sys->serial_mutex); + } + + return (trx_no); +} + +void +trx_erase_serial_list(trx_t* trx) +{ + mutex_enter(&trx_sys->serial_mutex); + + UT_LIST_REMOVE(trx_sys->serialisation_list, trx); + if (UT_LIST_GET_LEN(trx_sys->serialisation_list) > 0) { + trx_sys->min_trx_no.store(UT_LIST_GET_FIRST(trx_sys->serialisation_list)->no); + } else { + trx_sys->min_trx_no.store(TRX_ID_MAX); + } + + mutex_exit(&trx_sys->serial_mutex); +} + /** Set the transaction serialisation number. @return true if the transaction number was added to the serialisation_list. */ static bool trx_serialisation_number_get( @@ -1344,17 +1391,7 @@ static bool trx_serialisation_number_get( temp_rseg = temp_rseg_undo_ptr->rseg; } - trx_sys_mutex_enter(); - - trx->no = trx_sys_get_new_trx_id(); - - /* Track the minimum serialisation number. */ - if (!trx->read_only) { - UT_LIST_ADD_LAST(trx_sys->serialisation_list, trx); - added_trx_no = true; - } else { - added_trx_no = false; - } + added_trx_no = trx_add_serial_list(trx); /* If the rollack segment is not empty then the new trx_t::no can't be less than any trx_t::no @@ -1379,13 +1416,13 @@ static bool trx_serialisation_number_get( difference because this code path is only taken when the rbs is empty. */ - trx_sys_mutex_exit(); + mutex_exit(&trx_sys->serial_mutex); purge_sys->purge_queue->push(elem); mutex_exit(&purge_sys->pq_mutex); } else { - trx_sys_mutex_exit(); + mutex_exit(&trx_sys->serial_mutex); } return (added_trx_no); @@ -1669,10 +1706,6 @@ static void trx_erase_lists(trx_t *trx, bool serialised) { ut_ad(trx->id > 0); trx_sys_mutex_enter(); - if (serialised) { - UT_LIST_REMOVE(trx_sys->serialisation_list, trx); - } - trx_ids_t::iterator it = std::lower_bound(trx_sys->rw_trx_ids.begin(), trx_sys->rw_trx_ids.end(), trx->id); ut_ad(*it == trx->id); @@ -1693,12 +1726,16 @@ static void trx_erase_lists(trx_t *trx, bool serialised) { trx_sys->rw_trx_set.erase(TrxTrack(trx->id)); /* Set minimal active trx id. */ - trx_id_t min_id = trx_sys->rw_trx_ids.empty() ? trx_sys->max_trx_id + trx_id_t min_id = trx_sys->rw_trx_ids.empty() ? trx_sys_get_max_trx_id() : trx_sys->rw_trx_ids.front(); trx_sys->min_active_id.store(min_id); trx_sys_mutex_exit(); + + if (serialised) { + trx_erase_serial_list(trx); + } } /** Commits a transaction in memory. */