diff options
author | Sergey Vojtovich <svoj@mariadb.org> | 2018-01-30 20:59:42 +0400 |
---|---|---|
committer | Sergey Vojtovich <svoj@mariadb.org> | 2018-01-31 20:13:34 +0400 |
commit | bc7a1dc1fbd27e6064d3b40443fe242397668af7 (patch) | |
tree | 51de97bcd55f59957436067920a662084df230dd | |
parent | c0d5d7c0efb6d2b9961da7fb813ff000a5d3e4c5 (diff) | |
download | mariadb-git-bc7a1dc1fbd27e6064d3b40443fe242397668af7.tar.gz |
MDEV-15104 - Optimise MVCC snapshot
With trx_sys_t::rw_trx_ids removal, MVCC snapshot overhead became
slightly higher. That is instead of copying an array we now have to
iterate LF_HASH. All this done under trx_sys.mutex protection.
This patch moves MVCC snapshot out of trx_sys.mutex.
Clean-ups:
Removed MVCC: doesn't make too much sense to keep it in a separate class
anymore.
Refactored ReadView so that it now calls register()/deregister() routines
(it was vice versa before).
ReadView doesn't have friends anymore. :(
Even less trx_sys.mutex references.
-rw-r--r-- | storage/innobase/handler/ha_innodb.cc | 8 | ||||
-rw-r--r-- | storage/innobase/handler/handler0alter.cc | 2 | ||||
-rw-r--r-- | storage/innobase/include/lock0lock.ic | 1 | ||||
-rw-r--r-- | storage/innobase/include/read0read.h | 75 | ||||
-rw-r--r-- | storage/innobase/include/read0types.h | 222 | ||||
-rw-r--r-- | storage/innobase/include/trx0sys.h | 63 | ||||
-rw-r--r-- | storage/innobase/include/trx0trx.ic | 2 | ||||
-rw-r--r-- | storage/innobase/read/read0read.cc | 244 | ||||
-rw-r--r-- | storage/innobase/row/row0row.cc | 1 | ||||
-rw-r--r-- | storage/innobase/row/row0sel.cc | 8 | ||||
-rw-r--r-- | storage/innobase/row/row0vers.cc | 1 | ||||
-rw-r--r-- | storage/innobase/srv/srv0srv.cc | 3 | ||||
-rw-r--r-- | storage/innobase/trx/trx0purge.cc | 3 | ||||
-rw-r--r-- | storage/innobase/trx/trx0rec.cc | 1 | ||||
-rw-r--r-- | storage/innobase/trx/trx0roll.cc | 1 | ||||
-rw-r--r-- | storage/innobase/trx/trx0sys.cc | 10 | ||||
-rw-r--r-- | storage/innobase/trx/trx0trx.cc | 9 |
17 files changed, 324 insertions, 330 deletions
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 2b8b0f3bb91..f2e71564945 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -3499,7 +3499,7 @@ ha_innobase::init_table_handle_for_HANDLER(void) /* Assign a read view if the transaction does not have it yet */ - trx_sys.mvcc.view_open(m_prebuilt->trx); + m_prebuilt->trx->read_view.open(m_prebuilt->trx); innobase_register_trx(ht, m_user_thd, m_prebuilt->trx); @@ -4386,7 +4386,7 @@ innobase_start_trx_and_assign_read_view( thd_get_trx_isolation(thd)); if (trx->isolation_level == TRX_ISO_REPEATABLE_READ) { - trx_sys.mvcc.view_open(trx); + trx->read_view.open(trx); } else { push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, HA_ERR_UNSUPPORTED, @@ -16063,7 +16063,7 @@ ha_innobase::external_lock( } } else if (trx->isolation_level <= TRX_ISO_READ_COMMITTED) { - trx_sys.mvcc.view_close(trx->read_view); + trx->read_view.close(); } } @@ -16728,7 +16728,7 @@ ha_innobase::store_lock( /* At low transaction isolation levels we let each consistent read set its own snapshot */ - trx_sys.mvcc.view_close(trx->read_view); + trx->read_view.close(); } } diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 7c47340fae7..35623197106 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -5562,7 +5562,7 @@ error_handling_drop_uncached: if (ctx->online && ctx->num_to_add_index) { /* Assign a consistent read view for row_merge_read_clustered_index(). */ - trx_sys.mvcc.view_open(ctx->prebuilt->trx); + ctx->prebuilt->trx->read_view.open(ctx->prebuilt->trx); } if (fts_index) { diff --git a/storage/innobase/include/lock0lock.ic b/storage/innobase/include/lock0lock.ic index b73843e7a1f..76c491a8721 100644 --- a/storage/innobase/include/lock0lock.ic +++ b/storage/innobase/include/lock0lock.ic @@ -35,7 +35,6 @@ Created 5/7/1996 Heikki Tuuri #include "row0vers.h" #include "que0que.h" #include "btr0cur.h" -#include "read0read.h" #include "log0recv.h" /*********************************************************************//** diff --git a/storage/innobase/include/read0read.h b/storage/innobase/include/read0read.h deleted file mode 100644 index cd129434560..00000000000 --- a/storage/innobase/include/read0read.h +++ /dev/null @@ -1,75 +0,0 @@ -/***************************************************************************** - -Copyright (c) 1997, 2013, Oracle and/or its affiliates. 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 -Foundation; version 2 of the License. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along with -this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA - -*****************************************************************************/ - -/**************************************************//** -@file include/read0read.h -Cursor read - -Created 2/16/1997 Heikki Tuuri -*******************************************************/ - -#ifndef read0read_h -#define read0read_h - -#include "univ.i" - -#include "read0types.h" - -/** The MVCC read view manager */ -class MVCC -{ - /** Active views. */ - UT_LIST_BASE_NODE_T(ReadView) m_views; - - - /** Validates a read view list. */ - bool validate() const; -public: - MVCC() { UT_LIST_INIT(m_views, &ReadView::m_view_list); } - ~MVCC() { ut_ad(UT_LIST_GET_LEN(m_views) == 0); } - - - /** - Allocate and create a view. - @param trx transaction creating the view - */ - void view_open(trx_t *trx); - - - /** - Close a view created by the above function. - @param view view allocated by view_open. - */ - void view_close(ReadView &view); - - - /** - Clones the oldest view and stores it in view. No need to - call view_close(). The caller owns the view that is passed in. - This function is called by Purge to create it view. - - @param view Preallocated view, owned by the caller - */ - void clone_oldest_view(ReadView *view); - - - /** @return the number of active views */ - size_t size() const; -}; - -#endif /* read0read_h */ diff --git a/storage/innobase/include/read0types.h b/storage/innobase/include/read0types.h index 848cb33dbca..f647826e6ee 100644 --- a/storage/innobase/include/read0types.h +++ b/storage/innobase/include/read0types.h @@ -32,16 +32,154 @@ Created 2/16/1997 Heikki Tuuri #include "trx0types.h" -// Friend declaration -class MVCC; -/** Read view lists the trx ids of those transactions for which a consistent -read should not see the modifications to the database. */ +/** View is not in MVCC and not visible to purge thread. */ +#define READ_VIEW_STATE_CLOSED 0 + +/** View is in MVCC, but not visible to purge thread. */ +#define READ_VIEW_STATE_REGISTERED 1 + +/** View is in MVCC, purge thread must wait for READ_VIEW_STATE_OPEN. */ +#define READ_VIEW_STATE_SNAPSHOT 2 + +/** View is in MVCC and is visible to purge thread. */ +#define READ_VIEW_STATE_OPEN 3 + + +/** + Read view lists the trx ids of those transactions for which a consistent read + should not see the modifications to the database. +*/ +class ReadView +{ + /** + View state. + + It is not defined as enum as it has to be updated using atomic operations. + Possible values are READ_VIEW_STATE_CLOSED, READ_VIEW_STATE_REGISTERED, + READ_VIEW_STATE_SNAPSHOT and READ_VIEW_STATE_OPEN. + + Possible state transfers... + + Opening view for the first time: + READ_VIEW_STATE_CLOSED -> READ_VIEW_STATE_SNAPSHOT (non-atomic) + + Complete first time open or reopen: + READ_VIEW_STATE_SNAPSHOT -> READ_VIEW_STATE_OPEN (atomic) + + Close view but keep it in list: + READ_VIEW_STATE_OPEN -> READ_VIEW_STATE_REGISTERED (atomic) + + Close view and remove it from list: + READ_VIEW_STATE_OPEN -> READ_VIEW_STATE_CLOSED (non-atomic) + + Reusing view: + READ_VIEW_STATE_REGISTERED -> READ_VIEW_STATE_SNAPSHOT (atomic) + + Removing closed view from list: + READ_VIEW_STATE_REGISTERED -> READ_VIEW_STATE_CLOSED (non-atomic) + */ + int32_t m_state; + -class ReadView { public: - ReadView() : m_creator_trx_id(TRX_ID_MAX), m_ids(), - m_registered(false) {} + ReadView(): m_state(READ_VIEW_STATE_CLOSED) {} + + + /** + Copy state from another view. + + @param other view to copy from + */ + void copy(const ReadView &other) + { + ut_ad(&other != this); + m_ids= other.m_ids; + m_up_limit_id= other.m_up_limit_id; + m_low_limit_no= other.m_low_limit_no; + m_low_limit_id= other.m_low_limit_id; + } + + + /** + Opens a read view where exactly the transactions serialized before this + point in time are seen in the view. + + View becomes visible to purge thread via trx_sys.m_views. + + @param[in,out] trx transaction + */ + void open(trx_t *trx); + + + /** + Closes the view. + + View becomes not visible to purge thread via trx_sys.m_views. + */ + void close(); + + + /** + Marks view unused. + + View is still in trx_sys.m_views list, but is not visible to purge threads. + */ + void unuse() + { + ut_ad(m_state == READ_VIEW_STATE_CLOSED || + m_state == READ_VIEW_STATE_REGISTERED || + m_state == READ_VIEW_STATE_OPEN); + if (m_state == READ_VIEW_STATE_OPEN) + my_atomic_store32_explicit(&m_state, READ_VIEW_STATE_REGISTERED, + MY_MEMORY_ORDER_RELAXED); + } + + + /** m_state getter for trx_sys::clone_oldest_view() trx_sys::size(). */ + int32_t get_state() const + { + return my_atomic_load32_explicit(const_cast<int32*>(&m_state), + MY_MEMORY_ORDER_ACQUIRE); + } + + + /** + Returns true if view is open. + + Only used by view owner thread, thus we can omit atomic operations. + */ + bool is_open() const + { + ut_ad(m_state == READ_VIEW_STATE_OPEN || + m_state == READ_VIEW_STATE_CLOSED || + m_state == READ_VIEW_STATE_REGISTERED); + return m_state == READ_VIEW_STATE_OPEN; + } + + + /** + Creates a snapshot where exactly the transactions serialized before this + point in time are seen in the view. + + @param[in,out] trx transaction + */ + void snapshot(trx_t *trx); + + + /** + Sets the creator transaction id. + + This should be set only for views created by RW transactions. + */ + void set_creator_trx_id(trx_id_t id) + { + ut_ad(id > 0); + ut_ad(m_creator_trx_id == 0); + m_creator_trx_id= id; + } + + /** Check whether transaction id is valid. @param[in] id transaction id to check @param[in] name table name */ @@ -86,25 +224,6 @@ public: } /** - Mark the view as closed */ - void close() - { - set_creator_trx_id(TRX_ID_MAX); - } - - bool is_open() const - { - return static_cast<trx_id_t>(my_atomic_load64_explicit( - const_cast<int64*>( - reinterpret_cast<const int64*>( - &m_creator_trx_id)), - MY_MEMORY_ORDER_RELAXED)) != TRX_ID_MAX; - } - - bool is_registered() const { return(m_registered); } - void set_registered(bool registered) { m_registered= registered; } - - /** Write the limits to the file. @param file file to write to */ void print_limits(FILE* file) const @@ -129,54 +248,8 @@ public: return(m_low_limit_id); } - /** - @return true if there are no transaction ids in the snapshot */ - bool empty() const - { - return(m_ids.empty()); - } - - /** - Set the creator transaction id, existing id must be 0. - Note: This shouldbe set only for views created by RW - transactions. */ - void set_creator_trx_id(trx_id_t id) - { - my_atomic_store64_explicit( - reinterpret_cast<int64*>(&m_creator_trx_id), - id, MY_MEMORY_ORDER_RELAXED); - } - -#ifdef UNIV_DEBUG - /** - @param rhs view to compare with - @return truen if this view is less than or equal rhs */ - bool le(const ReadView* rhs) const - { - return(m_low_limit_no <= rhs->m_low_limit_no); - } - - trx_id_t up_limit_id() const - { - return(m_up_limit_id); - } -#endif /* UNIV_DEBUG */ private: - /** - Opens a read view where exactly the transactions serialized before this - point in time are seen in the view. - - @param[in,out] trx transaction */ - void open(trx_t *trx); - - /** - Copy state from another view. - @param other view to copy from */ - inline void copy(const ReadView& other); - - friend class MVCC; - /** The read should not see any transaction with trx id >= this value. In other words, this is the "high water mark". */ trx_id_t m_low_limit_id; @@ -199,11 +272,8 @@ private: they can be removed in purge if not needed by other views */ trx_id_t m_low_limit_no; - /** true if transaction is in MVCC::m_views. Only thread that owns - this view may access it. */ - bool m_registered; - byte pad1[CACHE_LINE_SIZE]; +public: UT_LIST_NODE_T(ReadView) m_view_list; }; diff --git a/storage/innobase/include/trx0sys.h b/storage/innobase/include/trx0sys.h index fc591c04724..c8013ba4e71 100644 --- a/storage/innobase/include/trx0sys.h +++ b/storage/innobase/include/trx0sys.h @@ -810,17 +810,17 @@ private: /** Solves race condition between register_rw() and snapshot_ids(). */ MY_ALIGNED(CACHE_LINE_SIZE) trx_id_t m_rw_trx_hash_version; + + /** Active views. */ + MY_ALIGNED(CACHE_LINE_SIZE) UT_LIST_BASE_NODE_T(ReadView) m_views; + bool m_initialised; public: - MY_ALIGNED(CACHE_LINE_SIZE) + MY_ALIGNED(CACHE_LINE_SIZE) mutable TrxSysMutex mutex; /*!< mutex protecting most fields in this structure except when noted otherwise */ - - MY_ALIGNED(CACHE_LINE_SIZE) - MVCC mvcc; /*!< Multi version concurrency control - manager */ MY_ALIGNED(CACHE_LINE_SIZE) trx_ut_list_t mysql_trx_list; /*!< List of transactions created for MySQL. All user transactions are @@ -1032,6 +1032,59 @@ public: } + /** + Registers view in MVCC. + + @param view view owned by the caller + */ + void register_view(ReadView *view) + { + mutex_enter(&mutex); + UT_LIST_ADD_FIRST(m_views, view); + mutex_exit(&mutex); + } + + + /** + Deregisters view in MVCC. + + @param view view owned by the caller + */ + void deregister_view(ReadView *view) + { + mutex_enter(&mutex); + UT_LIST_REMOVE(m_views, view); + mutex_exit(&mutex); + } + + + /** + Clones the oldest view and stores it in view. + + No need to call ReadView::close(). The caller owns the view that is passed + in. This function is called by purge thread to determine whether it should + purge the delete marked record or not. + */ + void clone_oldest_view(); + + + /** @return the number of active views */ + size_t view_count() const + { + size_t count= 0; + + mutex_enter(&mutex); + for (const ReadView* view= UT_LIST_GET_FIRST(m_views); view; + view= UT_LIST_GET_NEXT(m_view_list, view)) + { + if (view->get_state() == READ_VIEW_STATE_OPEN) + ++count; + } + mutex_exit(&mutex); + return count; + } + + private: static my_bool get_min_trx_id_callback(rw_trx_hash_element_t *element, trx_id_t *id) diff --git a/storage/innobase/include/trx0trx.ic b/storage/innobase/include/trx0trx.ic index d84a4299cfb..6372a02db17 100644 --- a/storage/innobase/include/trx0trx.ic +++ b/storage/innobase/include/trx0trx.ic @@ -24,8 +24,6 @@ The transaction Created 3/26/1996 Heikki Tuuri *******************************************************/ -#include "read0read.h" - /**********************************************************************//** Determines if a transaction is in the given state. The caller must hold trx_sys.mutex, or it must be the thread diff --git a/storage/innobase/read/read0read.cc b/storage/innobase/read/read0read.cc index 985aa000443..607ea027e96 100644 --- a/storage/innobase/read/read0read.cc +++ b/storage/innobase/read/read0read.cc @@ -24,10 +24,11 @@ Cursor read Created 2/16/1997 Heikki Tuuri *******************************************************/ -#include "read0read.h" +#include "read0types.h" #include "srv0srv.h" #include "trx0sys.h" +#include "trx0purge.h" /* ------------------------------------------------------------------------------- @@ -172,52 +173,16 @@ RW transaction can commit or rollback (or free views). AC-NL-RO transactions will mark their views as closed but not actually free their views. */ -#ifdef UNIV_DEBUG -/** Functor to validate the view list. */ -struct ViewCheck { - - ViewCheck() : m_prev_view() { } - - void operator()(const ReadView* view) - { - ut_ad(view->is_registered()); - ut_a(m_prev_view == NULL - || !view->is_open() - || view->le(m_prev_view)); - - m_prev_view = view; - } - - const ReadView* m_prev_view; -}; /** -Validates a read view list. */ - -bool -MVCC::validate() const -{ - ViewCheck check; - - ut_ad(mutex_own(&trx_sys.mutex)); - - ut_list_map(m_views, check); - - return(true); -} -#endif /* UNIV_DEBUG */ - - -/** - Opens a read view where exactly the transactions serialized before this + Creates a snapshot where exactly the transactions serialized before this point in time are seen in the view. @param[in,out] trx transaction */ - -void ReadView::open(trx_t *trx) +void ReadView::snapshot(trx_t *trx) { - ut_ad(mutex_own(&trx_sys.mutex)); + ut_ad(!mutex_own(&trx_sys.mutex)); trx_sys.snapshot_ids(trx, &m_ids, &m_low_limit_id, &m_low_limit_no); m_up_limit_id= m_ids.empty() ? m_low_limit_id : m_ids.front(); ut_ad(m_up_limit_id <= m_low_limit_id); @@ -225,42 +190,33 @@ void ReadView::open(trx_t *trx) /** - Create a view. + Opens a read view where exactly the transactions serialized before this + point in time are seen in the view. - Assigns a read view for a consistent read query. All the consistent reads - within the same transaction will get the same read view, which is created - when this function is first called for a new started transaction. + View becomes visible to purge thread via trx_sys.m_views. - @param trx transaction instance of caller + @param[in,out] trx transaction */ - -void MVCC::view_open(trx_t* trx) +void ReadView::open(trx_t *trx) { - if (srv_read_only_mode) + ut_ad(this == &trx->read_view); + switch (m_state) { - ut_ad(!trx->read_view.is_open()); - return; - } - else if (trx->read_view.is_open()) + case READ_VIEW_STATE_OPEN: + ut_ad(!srv_read_only_mode); return; - - /* - Reuse closed view if there were no read-write transactions since (and at) it's - creation time. - */ - if (trx->read_view.is_registered() && - trx_is_autocommit_non_locking(trx) && - trx->read_view.empty() && - trx->read_view.m_low_limit_id == trx_sys.get_max_trx_id()) - { + case READ_VIEW_STATE_REGISTERED: + ut_ad(!srv_read_only_mode); /* + Reuse closed view if there were no read-write transactions since (and at) + its creation time. + Original comment states: there is an inherent race here between purge and this thread. To avoid this race we should've checked trx_sys.get_max_trx_id() and - do trx->read_view.set_creator_trx_id(trx->id) atomically under - trx_sys.mutex protection. But we're cutting edges to achieve great - scalability. + set state to READ_VIEW_STATE_OPEN atomically under trx_sys.mutex + protection. But we're cutting edges to achieve great scalability. There're at least two types of concurrent threads interested in this value: purge coordinator thread (see MVCC::clone_oldest_view()) and @@ -280,108 +236,108 @@ void MVCC::view_open(trx_t* trx) Second, scary things start when there's a read-write transaction starting concurrently. - Speculative execution may reorder set_creator_trx_id() before - get_max_trx_id(). In this case purge thread has short gap to clone - outdated view. Which is probably not that bad: it just won't be able to - purge things that it was actually allowed to purge for a short while. + Speculative execution may reorder state change before get_max_trx_id(). + In this case purge thread has short gap to clone outdated view. Which is + probably not that bad: it just won't be able to purge things that it was + actually allowed to purge for a short while. This thread may as well get suspended after trx_sys.get_max_trx_id() and - before trx->read_view.set_creator_trx_id(trx->id). New read-write - transaction may get started, committed and purged meanwhile. It is - acceptable as well, since this view doesn't see it. + before state is set to READ_VIEW_STATE_OPEN. New read-write transaction + may get started, committed and purged meanwhile. It is acceptable as + well, since this view doesn't see it. */ - trx->read_view.set_creator_trx_id(trx->id); - return; - } + if (trx_is_autocommit_non_locking(trx) && m_ids.empty() && + m_low_limit_id == trx_sys.get_max_trx_id()) + goto reopen; - mutex_enter(&trx_sys.mutex); - trx->read_view.open(trx); - if (trx->read_view.is_registered()) - UT_LIST_REMOVE(m_views, &trx->read_view); - else - trx->read_view.set_registered(true); - trx->read_view.set_creator_trx_id(trx->id); - UT_LIST_ADD_FIRST(m_views, &trx->read_view); - ut_ad(validate()); - mutex_exit(&trx_sys.mutex); -} + /* + Can't reuse view, take new snapshot. + Alas this empty critical section is simplest way to make sure concurrent + purge thread completed snapshot copy. Of course purge thread may come + again and try to copy once again after we release this mutex, but in + this case it is guaranteed to see READ_VIEW_STATE_REGISTERED and thus + it'll skip this view. -void MVCC::view_close(ReadView &view) -{ - view.close(); - if (view.is_registered()) - { + This critical section can be replaced with new state, which purge thread + would set to inform us to wait until it completes snapshot. However it'd + complicate m_state even further. + */ mutex_enter(&trx_sys.mutex); - view.set_registered(false); - UT_LIST_REMOVE(m_views, &view); - ut_ad(validate()); mutex_exit(&trx_sys.mutex); + my_atomic_store32_explicit(&m_state, READ_VIEW_STATE_SNAPSHOT, + MY_MEMORY_ORDER_RELAXED); + break; + case READ_VIEW_STATE_CLOSED: + if (srv_read_only_mode) + return; + m_state= READ_VIEW_STATE_SNAPSHOT; + trx_sys.register_view(this); + break; + default: + ut_ad(0); } + + snapshot(trx); +reopen: + m_creator_trx_id= trx->id; + my_atomic_store32_explicit(&m_state, READ_VIEW_STATE_OPEN, + MY_MEMORY_ORDER_RELEASE); } /** -Copy state from another view. -@param other view to copy from */ + Closes the view. -void -ReadView::copy(const ReadView& other) + The view will become invisible to purge (deregistered from trx_sys). +*/ +void ReadView::close() { - ut_ad(&other != this); - m_ids= other.m_ids; - m_up_limit_id = other.m_up_limit_id; - m_low_limit_no = other.m_low_limit_no; - m_low_limit_id = other.m_low_limit_id; + ut_ad(m_state == READ_VIEW_STATE_OPEN || + m_state == READ_VIEW_STATE_REGISTERED || + m_state == READ_VIEW_STATE_CLOSED); + if (m_state != READ_VIEW_STATE_CLOSED) + { + trx_sys.deregister_view(this); + m_state= READ_VIEW_STATE_CLOSED; + } } -/** Clones the oldest view and stores it in view. No need to -call view_close(). The caller owns the view that is passed in. -This function is called by Purge to determine whether it should -purge the delete marked record or not. -@param view Preallocated view, owned by the caller */ - -void -MVCC::clone_oldest_view(ReadView* view) -{ - mutex_enter(&trx_sys.mutex); - /* Find oldest view. */ - for (const ReadView *oldest_view = UT_LIST_GET_LAST(m_views); - oldest_view != NULL; - oldest_view = UT_LIST_GET_PREV(m_view_list, oldest_view)) - { - if (oldest_view->is_open()) - { - view->copy(*oldest_view); - mutex_exit(&trx_sys.mutex); - return; - } - } - /* No views in the list: snapshot current state. */ - view->open(0); - mutex_exit(&trx_sys.mutex); -} /** -@return the number of active views */ + Clones the oldest view and stores it in view. -size_t -MVCC::size() const -{ - mutex_enter(&trx_sys.mutex); + No need to call ReadView::close(). The caller owns the view that is passed + in. This function is called by purge thread to determine whether it should + purge the delete marked record or not. - size_t size = 0; + Since foreign views are accessed under the mutex protection, the only + possible state transfers are + READ_VIEW_STATE_SNAPSHOT -> READ_VIEW_STATE_OPEN + READ_VIEW_STATE_OPEN -> READ_VIEW_STATE_REGISTERED + All other state transfers are eliminated by the mutex. +*/ +void trx_sys_t::clone_oldest_view() +{ + const ReadView *oldest_view= &purge_sys->view; - for (const ReadView* view = UT_LIST_GET_FIRST(m_views); - view != NULL; - view = UT_LIST_GET_NEXT(m_view_list, view)) { + purge_sys->view.snapshot(0); - if (view->is_open()) { - ++size; - } - } + mutex_enter(&mutex); + /* Find oldest view. */ + for (const ReadView *v= UT_LIST_GET_FIRST(m_views); v; + v= UT_LIST_GET_NEXT(m_view_list, v)) + { + int32_t state; - mutex_exit(&trx_sys.mutex); + while ((state= v->get_state()) == READ_VIEW_STATE_SNAPSHOT) + ut_delay(1); - return(size); + if (state == READ_VIEW_STATE_OPEN && + v->low_limit_no() < oldest_view->low_limit_no()) + oldest_view= v; + } + if (oldest_view != &purge_sys->view) + purge_sys->view.copy(*oldest_view); + mutex_exit(&mutex); } diff --git a/storage/innobase/row/row0row.cc b/storage/innobase/row/row0row.cc index a3248dc19ee..024c54cfe22 100644 --- a/storage/innobase/row/row0row.cc +++ b/storage/innobase/row/row0row.cc @@ -41,7 +41,6 @@ Created 4/20/1996 Heikki Tuuri #include "row0ext.h" #include "row0upd.h" #include "rem0cmp.h" -#include "read0read.h" #include "ut0mem.h" #include "gis0geo.h" #include "row0mysql.h" diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index 4888a4e7500..ccc2ab0c14c 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -51,7 +51,6 @@ Created 12/19/1997 Heikki Tuuri #include "pars0sym.h" #include "pars0pars.h" #include "row0mysql.h" -#include "read0read.h" #include "buf0lru.h" #include "srv0srv.h" #include "ha_prototypes.h" @@ -2270,7 +2269,7 @@ row_sel_step( if (node->consistent_read) { trx_t *trx = thr_get_trx(thr); /* Assign a read view for the query */ - trx_sys.mvcc.view_open(trx); + trx->read_view.open(trx); node->read_view = trx->read_view.is_open() ? &trx->read_view : NULL; } else { @@ -4426,7 +4425,7 @@ row_search_mvcc( /* Assign a read view for the query */ trx_start_if_not_started(trx, false); - trx_sys.mvcc.view_open(trx); + trx->read_view.open(trx); prebuilt->sql_stat_start = FALSE; } else { @@ -5878,8 +5877,7 @@ row_search_check_if_query_cache_permitted( transaction if it does not yet have one */ if (trx->isolation_level >= TRX_ISO_REPEATABLE_READ) { - - trx_sys.mvcc.view_open(trx); + trx->read_view.open(trx); } } diff --git a/storage/innobase/row/row0vers.cc b/storage/innobase/row/row0vers.cc index 11624068d2b..c5663c0c828 100644 --- a/storage/innobase/row/row0vers.cc +++ b/storage/innobase/row/row0vers.cc @@ -41,7 +41,6 @@ Created 2/6/1997 Heikki Tuuri #include "row0row.h" #include "row0upd.h" #include "rem0cmp.h" -#include "read0read.h" #include "lock0lock.h" #include "row0mysql.h" diff --git a/storage/innobase/srv/srv0srv.cc b/storage/innobase/srv/srv0srv.cc index af0ff2d3168..ad246a231f8 100644 --- a/storage/innobase/srv/srv0srv.cc +++ b/storage/innobase/srv/srv0srv.cc @@ -1365,9 +1365,8 @@ srv_printf_innodb_monitor( srv_conc_get_active_threads(), srv_conc_get_waiting_threads()); - /* This is a dirty read, without holding trx_sys.mutex. */ fprintf(file, ULINTPF " read views open inside InnoDB\n", - trx_sys.mvcc.size()); + trx_sys.view_count()); n_reserved = fil_space_get_n_reserved_extents(0); if (n_reserved > 0) { diff --git a/storage/innobase/trx/trx0purge.cc b/storage/innobase/trx/trx0purge.cc index a7c0f350217..0fc61b32292 100644 --- a/storage/innobase/trx/trx0purge.cc +++ b/storage/innobase/trx/trx0purge.cc @@ -33,7 +33,6 @@ Created 3/26/1996 Heikki Tuuri #include "mtr0log.h" #include "os0thread.h" #include "que0que.h" -#include "read0read.h" #include "row0purge.h" #include "row0upd.h" #include "srv0mon.h" @@ -1615,7 +1614,7 @@ trx_purge( ut_a(purge_sys->n_submitted == purge_sys->n_completed); rw_lock_x_lock(&purge_sys->latch); - trx_sys.mvcc.clone_oldest_view(&purge_sys->view); + trx_sys.clone_oldest_view(); rw_lock_x_unlock(&purge_sys->latch); #ifdef UNIV_DEBUG diff --git a/storage/innobase/trx/trx0rec.cc b/storage/innobase/trx/trx0rec.cc index a9f4cef6579..d119a469e47 100644 --- a/storage/innobase/trx/trx0rec.cc +++ b/storage/innobase/trx/trx0rec.cc @@ -31,7 +31,6 @@ Created 3/26/1996 Heikki Tuuri #include "mtr0log.h" #include "dict0dict.h" #include "ut0mem.h" -#include "read0read.h" #include "row0ext.h" #include "row0upd.h" #include "que0que.h" diff --git a/storage/innobase/trx/trx0roll.cc b/storage/innobase/trx/trx0roll.cc index 7e7870f53ce..54e3a77e7be 100644 --- a/storage/innobase/trx/trx0roll.cc +++ b/storage/innobase/trx/trx0roll.cc @@ -37,7 +37,6 @@ Created 3/26/1996 Heikki Tuuri #include "mach0data.h" #include "pars0pars.h" #include "que0que.h" -#include "read0read.h" #include "row0mysql.h" #include "row0undo.h" #include "srv0mon.h" diff --git a/storage/innobase/trx/trx0sys.cc b/storage/innobase/trx/trx0sys.cc index 1d9b1dcb8c3..877531f833b 100644 --- a/storage/innobase/trx/trx0sys.cc +++ b/storage/innobase/trx/trx0sys.cc @@ -42,7 +42,6 @@ Created 3/26/1996 Heikki Tuuri #include "log0log.h" #include "log0recv.h" #include "os0file.h" -#include "read0read.h" #include "fsp0sysspace.h" #include <mysql/service_wsrep.h> @@ -402,6 +401,8 @@ trx_sys_t::create() m_initialised = true; mutex_create(LATCH_ID_TRX_SYS, &mutex); UT_LIST_INIT(mysql_trx_list, &trx_t::mysql_trx_list); + UT_LIST_INIT(m_views, &ReadView::m_view_list); + rw_trx_hash.init(); } @@ -507,7 +508,7 @@ trx_sys_t::close() return; } - if (ulint size = mvcc.size()) { + if (size_t size = view_count()) { ib::error() << "All read views were not closed before" " shutdown: " << size << " read views open"; } @@ -532,6 +533,7 @@ trx_sys_t::close() } ut_a(UT_LIST_GET_LEN(mysql_trx_list) == 0); + ut_ad(UT_LIST_GET_LEN(m_views) == 0); /* We used placement new to create this mutex. Call the destructor. */ mutex_free(&mutex); @@ -564,7 +566,7 @@ ulint trx_sys_t::any_active_transactions() reinterpret_cast<my_hash_walk_action> (active_count_callback), &total_trx); - mutex_enter(&trx_sys.mutex); + mutex_enter(&mutex); for (trx_t* trx = UT_LIST_GET_FIRST(trx_sys.mysql_trx_list); trx != NULL; trx = UT_LIST_GET_NEXT(mysql_trx_list, trx)) { @@ -572,7 +574,7 @@ ulint trx_sys_t::any_active_transactions() total_trx++; } } - mutex_exit(&trx_sys.mutex); + mutex_exit(&mutex); return(total_trx); } diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc index 6a555737ef2..b6e2b0067e8 100644 --- a/storage/innobase/trx/trx0trx.cc +++ b/storage/innobase/trx/trx0trx.cc @@ -39,7 +39,6 @@ Created 3/26/1996 Heikki Tuuri #include "log0log.h" #include "os0proc.h" #include "que0que.h" -#include "read0read.h" #include "srv0mon.h" #include "srv0srv.h" #include "fsp0sysspace.h" @@ -511,7 +510,7 @@ trx_free(trx_t*& trx) trx->mod_tables.clear(); ut_ad(!trx->read_view.is_open()); - trx_sys.mvcc.view_close(trx->read_view); + trx->read_view.close(); /* trx locking state should have been reset before returning trx to pool */ @@ -673,7 +672,7 @@ trx_disconnect_from_mysql( trx_t* trx, bool prepared) { - trx_sys.mvcc.view_close(trx->read_view); + trx->read_view.close(); mutex_enter(&trx_sys.mutex); @@ -972,7 +971,7 @@ trx_lists_init_at_db_start() ib::info() << "Trx id counter is " << trx_sys.get_max_trx_id(); } - trx_sys.mvcc.clone_oldest_view(&purge_sys->view); + trx_sys.clone_oldest_view(); } /** Assign a persistent rollback segment in a round-robin fashion, @@ -1512,7 +1511,7 @@ trx_commit_in_memory( the transaction did not modify anything */ { trx->must_flush_log_later = false; - trx->read_view.close(); + trx->read_view.unuse(); if (trx_is_autocommit_non_locking(trx)) { ut_ad(trx->id == 0); |