1
0
mirror of https://github.com/MariaDB/server.git synced 2025-12-01 17:39:21 +03:00

MDEV-15019 - InnoDB: store ReadView on trx

This will allow us to reduce critical section protected by
trx_sys.mutex:
- no need to maintain global m_free list
- eliminate if (trx->read_view == NULL) condition.

On x86_64 sizeof(Readview) is 144 mostly due to padding, sizeof(trx_t)
with ReadView is 1200.

Also don't close ReadView for read-write transactions, just mark it
closed similarly to read-only.

Clean-up: removed n_prepared_recovered_trx and n_prepared_trx, which
accidentally re-appeared after some rebase.
This commit is contained in:
Sergey Vojtovich
2018-01-20 17:45:42 +04:00
parent ec32c05072
commit 4dc30f3c17
13 changed files with 180 additions and 305 deletions

View File

@@ -3503,7 +3503,7 @@ ha_innobase::init_table_handle_for_HANDLER(void)
/* Assign a read view if the transaction does not have it yet */ /* Assign a read view if the transaction does not have it yet */
trx_assign_read_view(m_prebuilt->trx); trx_sys.mvcc.view_open(m_prebuilt->trx);
innobase_register_trx(ht, m_user_thd, m_prebuilt->trx); innobase_register_trx(ht, m_user_thd, m_prebuilt->trx);
@@ -4394,7 +4394,7 @@ innobase_start_trx_and_assign_read_view(
thd_get_trx_isolation(thd)); thd_get_trx_isolation(thd));
if (trx->isolation_level == TRX_ISO_REPEATABLE_READ) { if (trx->isolation_level == TRX_ISO_REPEATABLE_READ) {
trx_assign_read_view(trx); trx_sys.mvcc.view_open(trx);
} else { } else {
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
HA_ERR_UNSUPPORTED, HA_ERR_UNSUPPORTED,
@@ -16220,7 +16220,7 @@ ha_innobase::external_lock(
} }
} else if (trx->isolation_level <= TRX_ISO_READ_COMMITTED } else if (trx->isolation_level <= TRX_ISO_READ_COMMITTED
&& MVCC::is_view_active(trx->read_view)) { && trx->read_view.is_open()) {
mutex_enter(&trx_sys.mutex); mutex_enter(&trx_sys.mutex);
trx_sys.mvcc.view_close(trx->read_view); trx_sys.mvcc.view_close(trx->read_view);
mutex_exit(&trx_sys.mutex); mutex_exit(&trx_sys.mutex);
@@ -16885,7 +16885,7 @@ ha_innobase::store_lock(
(enum_tx_isolation) thd_tx_isolation(thd)); (enum_tx_isolation) thd_tx_isolation(thd));
if (trx->isolation_level <= TRX_ISO_READ_COMMITTED if (trx->isolation_level <= TRX_ISO_READ_COMMITTED
&& MVCC::is_view_active(trx->read_view)) { && trx->read_view.is_open()) {
/* At low transaction isolation levels we let /* At low transaction isolation levels we let
each consistent read set its own snapshot */ each consistent read set its own snapshot */

View File

@@ -5562,7 +5562,7 @@ error_handling_drop_uncached:
if (ctx->online && ctx->num_to_add_index) { if (ctx->online && ctx->num_to_add_index) {
/* Assign a consistent read view for /* Assign a consistent read view for
row_merge_read_clustered_index(). */ row_merge_read_clustered_index(). */
trx_assign_read_view(ctx->prebuilt->trx); trx_sys.mvcc.view_open(ctx->prebuilt->trx);
} }
if (fts_index) { if (fts_index) {

View File

@@ -31,60 +31,51 @@ Created 2/16/1997 Heikki Tuuri
#include "read0types.h" #include "read0types.h"
/** The MVCC read view manager */ /** The MVCC read view manager */
class MVCC { class MVCC
{
/** Active views. */
UT_LIST_BASE_NODE_T(ReadView) m_views;
/** Validates a read view list. */
bool validate() const;
public: public:
/** Constructor MVCC() { UT_LIST_INIT(m_views, &ReadView::m_view_list); }
@param size Number of views to pre-allocate */ ~MVCC() { ut_ad(UT_LIST_GET_LEN(m_views) == 0); }
void create(ulint size);
/** Destructor.
Free all the views in the m_free list */
void close();
/** /**
Allocate and create a view. Allocate and create a view.
@param view view owned by this class created for the @param trx transaction creating the view
caller. Must be freed by calling close() */
@param trx transaction creating the view */ void view_open(trx_t *trx);
void view_open(ReadView*& view, trx_t* trx);
/**
Close a view created by the above function.
@param view view allocated by trx_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. Close a view created by the above function.
It will also move the closed views from the m_views list to the @param view view allocated by view_open.
m_free list. This function is called by Purge to create it view. */
@param view Preallocated view, owned by the caller */ void view_close(ReadView &view)
void clone_oldest_view(ReadView* view); {
view.close();
view.set_registered(false);
UT_LIST_REMOVE(m_views, &view);
ut_ad(validate());
}
/**
@return the number of active views */
ulint size() const;
/** /**
@return true if the view is active and valid */ Clones the oldest view and stores it in view. No need to
static bool is_view_active(ReadView* view) call view_close(). The caller owns the view that is passed in.
{ This function is called by Purge to create it view.
return view && !view->is_closed();
}
private: @param view Preallocated view, owned by the caller
*/
void clone_oldest_view(ReadView *view);
/**
Validates a read view list. */
bool validate() const;
typedef UT_LIST_BASE_NODE_T(ReadView) view_list_t; /** @return the number of active views */
size_t size() const;
/** Free views ready for reuse. */
view_list_t m_free;
/** Active and closed views, the closed views will have the
creator trx id set to TRX_ID_MAX */
view_list_t m_views;
}; };
#endif /* read0read_h */ #endif /* read0read_h */

View File

@@ -198,19 +198,13 @@ public:
{ {
ut_ad(m_creator_trx_id != TRX_ID_MAX); ut_ad(m_creator_trx_id != TRX_ID_MAX);
m_creator_trx_id = TRX_ID_MAX; m_creator_trx_id = TRX_ID_MAX;
set_open(false);
} }
/** bool is_open() const { return(m_open); }
@return true if the view is closed */ void set_open(bool open) { m_open= open; }
bool is_closed() const bool is_registered() const { return(m_registered); }
{ void set_registered(bool registered) { m_registered= registered; }
return(m_closed);
}
void set_closed(bool closed)
{
m_closed= closed;
}
/** /**
Write the limits to the file. Write the limits to the file.
@@ -325,14 +319,15 @@ private:
they can be removed in purge if not needed by other views */ they can be removed in purge if not needed by other views */
trx_id_t m_low_limit_no; trx_id_t m_low_limit_no;
/** AC-NL-RO transaction view that has been "closed". */ /** true if view is open. */
bool m_closed; bool m_open;
typedef UT_LIST_NODE_T(ReadView) node_t; /** true if transaction is in MVCC::m_views. Only thread that owns
this view may access it. */
bool m_registered;
/** List of read views in trx_sys */ byte pad1[CACHE_LINE_SIZE];
byte pad1[64 - sizeof(node_t)]; UT_LIST_NODE_T(ReadView) m_view_list;
node_t m_view_list;
}; };
#endif #endif

View File

@@ -883,17 +883,6 @@ public:
MY_ALIGNED(CACHE_LINE_SIZE) rw_trx_hash_t rw_trx_hash; MY_ALIGNED(CACHE_LINE_SIZE) rw_trx_hash_t rw_trx_hash;
ulint n_prepared_trx; /*!< Number of transactions currently
in the XA PREPARED state */
ulint n_prepared_recovered_trx; /*!< Number of transactions
currently in XA PREPARED state that are
also recovered. Such transactions cannot
be added during runtime. They can only
occur after recovery if mysqld crashed
while there were XA PREPARED
transactions. We disable query cache
if such transactions exist. */
/** /**
Constructor. Constructor.

View File

@@ -45,13 +45,11 @@ Created 3/26/1996 Heikki Tuuri
#include "ut0vec.h" #include "ut0vec.h"
#include "fts0fts.h" #include "fts0fts.h"
#include "srv0srv.h" #include "srv0srv.h"
#include "read0types.h"
// Forward declaration // Forward declaration
struct mtr_t; struct mtr_t;
// Forward declaration
class ReadView;
// Forward declaration // Forward declaration
class FlushObserver; class FlushObserver;
@@ -290,23 +288,6 @@ void
trx_mark_sql_stat_end( trx_mark_sql_stat_end(
/*==================*/ /*==================*/
trx_t* trx); /*!< in: trx handle */ trx_t* trx); /*!< in: trx handle */
/********************************************************************//**
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. */
ReadView*
trx_assign_read_view(
/*=================*/
trx_t* trx); /*!< in: active transaction */
/****************************************************************//**
@return the transaction's read view or NULL if one not assigned. */
UNIV_INLINE
ReadView*
trx_get_read_view(
/*==============*/
trx_t* trx);
/****************************************************************//** /****************************************************************//**
Prepares a transaction for commit/rollback. */ Prepares a transaction for commit/rollback. */
void void
@@ -571,7 +552,7 @@ Check transaction state */
ut_ad(trx_state_eq((t), TRX_STATE_NOT_STARTED) \ ut_ad(trx_state_eq((t), TRX_STATE_NOT_STARTED) \
|| trx_state_eq((t), TRX_STATE_FORCED_ROLLBACK)); \ || trx_state_eq((t), TRX_STATE_FORCED_ROLLBACK)); \
ut_ad(!trx->has_logged()); \ ut_ad(!trx->has_logged()); \
ut_ad(!MVCC::is_view_active((t)->read_view)); \ ut_ad(!(t)->read_view.is_open()); \
ut_ad((t)->lock.wait_thr == NULL); \ ut_ad((t)->lock.wait_thr == NULL); \
ut_ad(UT_LIST_GET_LEN((t)->lock.trx_locks) == 0); \ ut_ad(UT_LIST_GET_LEN((t)->lock.trx_locks) == 0); \
ut_ad((t)->dict_operation == TRX_DICT_OP_NONE); \ ut_ad((t)->dict_operation == TRX_DICT_OP_NONE); \
@@ -1000,7 +981,7 @@ public:
trx_state_t state; trx_state_t state;
ReadView* read_view; /*!< consistent read view used in the ReadView read_view; /*!< consistent read view used in the
transaction, or NULL if not yet set */ transaction, or NULL if not yet set */
UT_LIST_NODE_T(trx_t) UT_LIST_NODE_T(trx_t)

View File

@@ -211,17 +211,6 @@ ok:
trx->dict_operation = op; trx->dict_operation = op;
} }
/**
@param trx Get the active view for this transaction, if one exists
@return the transaction's read view or NULL if one not assigned. */
UNIV_INLINE
ReadView*
trx_get_read_view(
trx_t* trx)
{
return(!MVCC::is_view_active(trx->read_view) ? NULL : trx->read_view);
}
/** /**
@param[in] trx Transaction to check @param[in] trx Transaction to check
@return true if the transaction is a high priority transaction.*/ @return true if the transaction is a high priority transaction.*/

View File

@@ -415,7 +415,8 @@ lock_clust_rec_cons_read_sees(
operate on same temp-table and so read of temp-table is operate on same temp-table and so read of temp-table is
always consistent read. */ always consistent read. */
if (srv_read_only_mode || dict_table_is_temporary(index->table)) { if (srv_read_only_mode || dict_table_is_temporary(index->table)) {
ut_ad(view == 0 || dict_table_is_temporary(index->table)); ut_ad(!view->is_open()
|| dict_table_is_temporary(index->table));
return(true); return(true);
} }
@@ -5719,15 +5720,13 @@ lock_trx_print_wait_and_mvcc_state(
trx_print_latched(file, trx, 600); trx_print_latched(file, trx, 600);
/* Note: this read_view access is data race. Further /* Note: read_view->is_active() check is race condition. But it
read_view->is_active() check is race condition. But it should should "kind of work" because read_view is freed only at shutdown.
"kind of work" because read_view is freed only at shutdown.
Worst thing that may happen is that it'll get transferred to Worst thing that may happen is that it'll get transferred to
another thread and print wrong values. */ another thread and print wrong values. */
const ReadView* read_view = trx->read_view;
if (read_view != NULL && !read_view->is_closed()) { if (trx->read_view.is_open()) {
read_view->print_limits(file); trx->read_view.print_limits(file);
} }
if (trx->lock.que_state == TRX_QUE_LOCK_WAIT) { if (trx->lock.que_state == TRX_QUE_LOCK_WAIT) {

View File

@@ -182,8 +182,9 @@ struct ViewCheck {
void operator()(const ReadView* view) void operator()(const ReadView* view)
{ {
ut_ad(view->is_registered());
ut_a(m_prev_view == NULL ut_a(m_prev_view == NULL
|| view->is_closed() || !view->is_open()
|| view->le(m_prev_view)); || view->le(m_prev_view));
m_prev_view = view; m_prev_view = view;
@@ -325,7 +326,9 @@ ReadView::ReadView()
m_up_limit_id(), m_up_limit_id(),
m_creator_trx_id(), m_creator_trx_id(),
m_ids(), m_ids(),
m_low_limit_no() m_low_limit_no(),
m_open(false),
m_registered(false)
{ {
ut_d(::memset(&m_view_list, 0x0, sizeof(m_view_list))); ut_d(::memset(&m_view_list, 0x0, sizeof(m_view_list)));
} }
@@ -337,34 +340,6 @@ ReadView::~ReadView()
// Do nothing // Do nothing
} }
/** Constructor
@param size Number of views to pre-allocate */
void MVCC::create(ulint size)
{
UT_LIST_INIT(m_free, &ReadView::m_view_list);
UT_LIST_INIT(m_views, &ReadView::m_view_list);
for (ulint i = 0; i < size; ++i) {
ReadView* view = UT_NEW_NOKEY(ReadView());
UT_LIST_ADD_FIRST(m_free, view);
}
}
void MVCC::close()
{
for (ReadView* view = UT_LIST_GET_FIRST(m_free);
view != NULL;
view = UT_LIST_GET_FIRST(m_free)) {
UT_LIST_REMOVE(m_free, view);
UT_DELETE(view);
}
ut_a(UT_LIST_GET_LEN(m_views) == 0);
}
/** /**
Copy the transaction ids from the source vector */ Copy the transaction ids from the source vector */
@@ -488,76 +463,89 @@ ReadView::complete()
m_up_limit_id = !m_ids.empty() ? m_ids.front() : m_low_limit_id; m_up_limit_id = !m_ids.empty() ? m_ids.front() : m_low_limit_id;
ut_ad(m_up_limit_id <= m_low_limit_id); ut_ad(m_up_limit_id <= m_low_limit_id);
set_open(true);
m_closed = false;
} }
/** /**
Allocate and create a view. Create a view.
@param view view owned by this class created for the
caller. Must be freed by calling view_close() Assigns a read view for a consistent read query. All the consistent reads
@param trx transaction instance of caller */ within the same transaction will get the same read view, which is created
void when this function is first called for a new started transaction.
MVCC::view_open(ReadView*& view, trx_t* trx)
@param trx transaction instance of caller
*/
void MVCC::view_open(trx_t* trx)
{ {
ut_ad(!srv_read_only_mode); if (srv_read_only_mode)
{
ut_ad(!trx->read_view.is_open());
return;
}
else if (trx->read_view.is_open())
return;
/** If no new RW transaction has been started since the last view /*
was created then reuse the the existing view. */ Reuse closed view if there were no read-write transactions since (and at) it's
if (view != NULL) { 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())
{
/*
Original comment states: there is an inherent race here between purge
and this thread.
uintptr_t p = reinterpret_cast<uintptr_t>(view); To avoid this race we should've checked trx_sys.get_max_trx_id() and
do trx->read_view.set_open(true) atomically under trx_sys.mutex
protection. But we're cutting edges to achieve great scalability.
view = reinterpret_cast<ReadView*>(p & ~1); There're at least two types of concurrent threads interested in this
value: purge coordinator thread (see MVCC::clone_oldest_view()) and
InnoDB monitor thread (see lock_trx_print_wait_and_mvcc_state()).
ut_ad(view->m_closed); What bad things can happen because we allow this race?
/* NOTE: This can be optimised further, for now we only First, purge thread may be affected by this race condition only if this
resuse the view iff there are no active RW transactions. view is the oldest open view. In other words this view is either last in
m_views list or there're no open views beyond.
There is an inherent race here between purge and this In this case purge may not catch this view and clone some younger view
thread. Purge will skip views that are marked as closed. instead. It might be kind of alright, because there were no read-write
Therefore we must set the low limit id after we reset the transactions and there should be nothing to purge. Besides younger view
closed status after the check. */ must have exactly the same values.
if (trx_is_autocommit_non_locking(trx) && view->empty()) { Second, scary things start when there's a read-write transaction starting
concurrently.
view->m_closed = false; Speculative execution may reorder set_open() 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.
if (view->m_low_limit_id == trx_sys.get_max_trx_id()) { This thread may as well get suspended after trx_sys.get_max_trx_id() and
return; before trx->read_view.set_open(true). New read-write transaction may get
} else { started, committed and purged meanwhile. It is acceptable as well, since
view->m_closed = true; this view doesn't see it.
} */
} trx->read_view.set_open(true);
return;
}
mutex_enter(&trx_sys.mutex); mutex_enter(&trx_sys.mutex);
trx->read_view.prepare(trx->id);
UT_LIST_REMOVE(m_views, view); trx->read_view.complete();
if (trx->read_view.is_registered())
} else { UT_LIST_REMOVE(m_views, &trx->read_view);
mutex_enter(&trx_sys.mutex); else
if ((view = UT_LIST_GET_FIRST(m_free))) { trx->read_view.set_registered(true);
UT_LIST_REMOVE(m_free, view); UT_LIST_ADD_FIRST(m_views, &trx->read_view);
} else if (!(view = UT_NEW_NOKEY(ReadView()))) { ut_ad(validate());
ib::error() << "Failed to allocate MVCC view"; mutex_exit(&trx_sys.mutex);
}
}
if (view != NULL) {
view->prepare(trx->id);
view->complete();
UT_LIST_ADD_FIRST(m_views, view);
ut_ad(!view->is_closed());
ut_ad(validate());
}
mutex_exit(&trx_sys.mutex);
} }
/** /**
@@ -625,7 +613,7 @@ MVCC::clone_oldest_view(ReadView* view)
oldest_view != NULL; oldest_view != NULL;
oldest_view = UT_LIST_GET_PREV(m_view_list, oldest_view)) oldest_view = UT_LIST_GET_PREV(m_view_list, oldest_view))
{ {
if (!oldest_view->is_closed()) if (oldest_view->is_open())
{ {
view->copy_prepare(*oldest_view); view->copy_prepare(*oldest_view);
mutex_exit(&trx_sys.mutex); mutex_exit(&trx_sys.mutex);
@@ -642,18 +630,18 @@ MVCC::clone_oldest_view(ReadView* view)
/** /**
@return the number of active views */ @return the number of active views */
ulint size_t
MVCC::size() const MVCC::size() const
{ {
mutex_enter(&trx_sys.mutex); mutex_enter(&trx_sys.mutex);
ulint size = 0; size_t size = 0;
for (const ReadView* view = UT_LIST_GET_FIRST(m_views); for (const ReadView* view = UT_LIST_GET_FIRST(m_views);
view != NULL; view != NULL;
view = UT_LIST_GET_NEXT(m_view_list, view)) { view = UT_LIST_GET_NEXT(m_view_list, view)) {
if (!view->is_closed()) { if (view->is_open()) {
++size; ++size;
} }
} }
@@ -662,18 +650,3 @@ MVCC::size() const
return(size); return(size);
} }
/**
Close a view created by the above function.
@param view view allocated by trx_open. */
void
MVCC::view_close(ReadView*& view)
{
ut_ad(mutex_own(&trx_sys.mutex));
view->close();
UT_LIST_REMOVE(m_views, view);
UT_LIST_ADD_LAST(m_free, view);
ut_ad(validate());
view = NULL;
}

View File

@@ -2101,16 +2101,16 @@ end_of_index:
ONLINE_INDEX_COMPLETE state between the time ONLINE_INDEX_COMPLETE state between the time
the DML thread has updated the clustered index the DML thread has updated the clustered index
but has not yet accessed secondary index. */ but has not yet accessed secondary index. */
ut_ad(MVCC::is_view_active(trx->read_view)); ut_ad(trx->read_view.is_open());
ut_ad(rec_trx_id != trx->id); ut_ad(rec_trx_id != trx->id);
if (!trx->read_view->changes_visible( if (!trx->read_view.changes_visible(
rec_trx_id, old_table->name)) { rec_trx_id, old_table->name)) {
rec_t* old_vers; rec_t* old_vers;
row_vers_build_for_consistent_read( row_vers_build_for_consistent_read(
rec, &mtr, clust_index, &offsets, rec, &mtr, clust_index, &offsets,
trx->read_view, &row_heap, &trx->read_view, &row_heap,
row_heap, &old_vers, NULL); row_heap, &old_vers, NULL);
if (!old_vers) { if (!old_vers) {
@@ -4526,8 +4526,8 @@ row_merge_is_index_usable(
return(!dict_index_is_corrupted(index) return(!dict_index_is_corrupted(index)
&& (dict_table_is_temporary(index->table) && (dict_table_is_temporary(index->table)
|| index->trx_id == 0 || index->trx_id == 0
|| !MVCC::is_view_active(trx->read_view) || !trx->read_view.is_open()
|| trx->read_view->changes_visible( || trx->read_view.changes_visible(
index->trx_id, index->trx_id,
index->table->name))); index->table->name)));
} }

View File

@@ -2268,15 +2268,11 @@ row_sel_step(
plan_reset_cursor(sel_node_get_nth_plan(node, 0)); plan_reset_cursor(sel_node_get_nth_plan(node, 0));
if (node->consistent_read) { if (node->consistent_read) {
trx_t *trx = thr_get_trx(thr);
/* Assign a read view for the query */ /* Assign a read view for the query */
trx_assign_read_view(thr_get_trx(thr)); trx_sys.mvcc.view_open(trx);
node->read_view = trx->read_view.is_open() ?
if (thr_get_trx(thr)->read_view != NULL) { &trx->read_view : NULL;
node->read_view = thr_get_trx(thr)->read_view;
} else {
node->read_view = NULL;
}
} else { } else {
sym_node_t* table_node; sym_node_t* table_node;
lock_mode i_lock_mode; lock_mode i_lock_mode;
@@ -3440,12 +3436,12 @@ row_sel_get_clust_rec_for_mysql(
if (trx->isolation_level > TRX_ISO_READ_UNCOMMITTED if (trx->isolation_level > TRX_ISO_READ_UNCOMMITTED
&& !lock_clust_rec_cons_read_sees( && !lock_clust_rec_cons_read_sees(
clust_rec, clust_index, *offsets, clust_rec, clust_index, *offsets,
trx_get_read_view(trx))) { &trx->read_view)) {
/* The following call returns 'offsets' associated with /* The following call returns 'offsets' associated with
'old_vers' */ 'old_vers' */
err = row_sel_build_prev_vers_for_mysql( err = row_sel_build_prev_vers_for_mysql(
trx->read_view, clust_index, prebuilt, &trx->read_view, clust_index, prebuilt,
clust_rec, offsets, offset_heap, &old_vers, clust_rec, offsets, offset_heap, &old_vers,
vrow, mtr); vrow, mtr);
@@ -3864,8 +3860,8 @@ exhausted:
*offsets = rec_get_offsets(rec, index, *offsets, true, *offsets = rec_get_offsets(rec, index, *offsets, true,
ULINT_UNDEFINED, heap); ULINT_UNDEFINED, heap);
if (!lock_clust_rec_cons_read_sees( if (!lock_clust_rec_cons_read_sees(rec, index, *offsets,
rec, index, *offsets, trx_get_read_view(trx))) { &trx->read_view)) {
goto retry; goto retry;
} }
@@ -4269,7 +4265,7 @@ row_search_mvcc(
if (prebuilt->select_lock_type == LOCK_NONE if (prebuilt->select_lock_type == LOCK_NONE
&& trx->isolation_level > TRX_ISO_READ_UNCOMMITTED && trx->isolation_level > TRX_ISO_READ_UNCOMMITTED
&& MVCC::is_view_active(trx->read_view)) { && trx->read_view.is_open()) {
/* This is a SELECT query done as a consistent read, /* This is a SELECT query done as a consistent read,
and the read view has already been allocated: and the read view has already been allocated:
@@ -4367,7 +4363,7 @@ row_search_mvcc(
ut_ad(prebuilt->sql_stat_start ut_ad(prebuilt->sql_stat_start
|| prebuilt->select_lock_type != LOCK_NONE || prebuilt->select_lock_type != LOCK_NONE
|| MVCC::is_view_active(trx->read_view) || trx->read_view.is_open()
|| prebuilt->table->no_rollback() || prebuilt->table->no_rollback()
|| srv_read_only_mode); || srv_read_only_mode);
@@ -4414,7 +4410,7 @@ row_search_mvcc(
} else if (!prebuilt->sql_stat_start) { } else if (!prebuilt->sql_stat_start) {
/* No need to set an intention lock or assign a read view */ /* No need to set an intention lock or assign a read view */
if (!MVCC::is_view_active(trx->read_view) if (!trx->read_view.is_open()
&& !srv_read_only_mode && !srv_read_only_mode
&& prebuilt->select_lock_type == LOCK_NONE) { && prebuilt->select_lock_type == LOCK_NONE) {
@@ -4430,9 +4426,7 @@ row_search_mvcc(
/* Assign a read view for the query */ /* Assign a read view for the query */
trx_start_if_not_started(trx, false); trx_start_if_not_started(trx, false);
if (!srv_read_only_mode) { trx_sys.mvcc.view_open(trx);
trx_assign_read_view(trx);
}
prebuilt->sql_stat_start = FALSE; prebuilt->sql_stat_start = FALSE;
} else { } else {
@@ -5036,14 +5030,13 @@ no_gap_lock:
if (srv_force_recovery < 5 if (srv_force_recovery < 5
&& !lock_clust_rec_cons_read_sees( && !lock_clust_rec_cons_read_sees(
rec, index, offsets, rec, index, offsets, &trx->read_view)) {
trx_get_read_view(trx))) {
rec_t* old_vers; rec_t* old_vers;
/* The following call returns 'offsets' /* The following call returns 'offsets'
associated with 'old_vers' */ associated with 'old_vers' */
err = row_sel_build_prev_vers_for_mysql( err = row_sel_build_prev_vers_for_mysql(
trx->read_view, clust_index, &trx->read_view, clust_index,
prebuilt, rec, &offsets, &heap, prebuilt, rec, &offsets, &heap,
&old_vers, need_vrow ? &vrow : NULL, &old_vers, need_vrow ? &vrow : NULL,
&mtr); &mtr);
@@ -5073,7 +5066,7 @@ no_gap_lock:
if (!srv_read_only_mode if (!srv_read_only_mode
&& !lock_sec_rec_cons_read_sees( && !lock_sec_rec_cons_read_sees(
rec, index, trx->read_view)) { rec, index, &trx->read_view)) {
/* We should look at the clustered index. /* We should look at the clustered index.
However, as this is a non-locking read, However, as this is a non-locking read,
we can skip the clustered index lookup if we can skip the clustered index lookup if
@@ -5877,18 +5870,16 @@ row_search_check_if_query_cache_permitted(
const bool ret = lock_table_get_n_locks(table) == 0 const bool ret = lock_table_get_n_locks(table) == 0
&& ((trx->id != 0 && trx->id >= table->query_cache_inv_id) && ((trx->id != 0 && trx->id >= table->query_cache_inv_id)
|| !MVCC::is_view_active(trx->read_view) || !trx->read_view.is_open()
|| trx->read_view->low_limit_id() || trx->read_view.low_limit_id()
>= table->query_cache_inv_id); >= table->query_cache_inv_id);
if (ret) { if (ret) {
/* If the isolation level is high, assign a read view for the /* If the isolation level is high, assign a read view for the
transaction if it does not yet have one */ transaction if it does not yet have one */
if (trx->isolation_level >= TRX_ISO_REPEATABLE_READ if (trx->isolation_level >= TRX_ISO_REPEATABLE_READ) {
&& !srv_read_only_mode
&& !MVCC::is_view_active(trx->read_view)) {
trx_sys.mvcc.view_open(trx->read_view, trx); trx_sys.mvcc.view_open(trx);
} }
} }

View File

@@ -459,8 +459,6 @@ trx_sys_t::create()
UT_LIST_INIT(serialisation_list, &trx_t::no_list); UT_LIST_INIT(serialisation_list, &trx_t::no_list);
UT_LIST_INIT(mysql_trx_list, &trx_t::mysql_trx_list); UT_LIST_INIT(mysql_trx_list, &trx_t::mysql_trx_list);
mvcc.create(1024);
rw_trx_hash.init(); rw_trx_hash.init();
} }
@@ -590,8 +588,6 @@ trx_sys_t::close()
} }
} }
mvcc.close();
ut_a(UT_LIST_GET_LEN(mysql_trx_list) == 0); ut_a(UT_LIST_GET_LEN(mysql_trx_list) == 0);
ut_a(UT_LIST_GET_LEN(serialisation_list) == 0); ut_a(UT_LIST_GET_LEN(serialisation_list) == 0);

View File

@@ -192,7 +192,7 @@ trx_init(
trx->last_sql_stat_start.least_undo_no = 0; trx->last_sql_stat_start.least_undo_no = 0;
ut_ad(!MVCC::is_view_active(trx->read_view)); ut_ad(!trx->read_view.is_open());
trx->lock.rec_cached = 0; trx->lock.rec_cached = 0;
@@ -246,6 +246,7 @@ struct TrxFactory {
new(&trx->lock.table_locks) lock_pool_t(); new(&trx->lock.table_locks) lock_pool_t();
new(&trx->hit_list) hit_list_t(); new(&trx->hit_list) hit_list_t();
new(&trx->read_view) ReadView();
trx->rw_trx_hash_pins = 0; trx->rw_trx_hash_pins = 0;
trx_init(trx); trx_init(trx);
@@ -301,7 +302,7 @@ struct TrxFactory {
trx->mod_tables.~trx_mod_tables_t(); trx->mod_tables.~trx_mod_tables_t();
ut_ad(trx->read_view == NULL); ut_ad(!trx->read_view.is_open());
if (!trx->lock.rec_pool.empty()) { if (!trx->lock.rec_pool.empty()) {
@@ -508,7 +509,12 @@ trx_free(trx_t*& trx)
trx->mod_tables.clear(); trx->mod_tables.clear();
ut_ad(trx->read_view == NULL); ut_ad(!trx->read_view.is_open());
if (trx->read_view.is_registered()) {
mutex_enter(&trx_sys.mutex);
trx_sys.mvcc.view_close(trx->read_view);
mutex_exit(&trx_sys.mutex);
}
/* trx locking state should have been reset before returning trx /* trx locking state should have been reset before returning trx
to pool */ to pool */
@@ -677,7 +683,7 @@ trx_disconnect_from_mysql(
UT_LIST_REMOVE(trx_sys.mysql_trx_list, trx); UT_LIST_REMOVE(trx_sys.mysql_trx_list, trx);
if (trx->read_view != NULL) { if (trx->read_view.is_open()) {
trx_sys.mvcc.view_close(trx->read_view); trx_sys.mvcc.view_close(trx->read_view);
} }
@@ -1536,17 +1542,11 @@ trx_erase_lists(
{ {
ut_ad(trx->id > 0); ut_ad(trx->id > 0);
if (trx->rsegs.m_redo.rseg && trx->read_view) {
ut_ad(!trx->read_only);
mutex_enter(&trx_sys.mutex);
trx_sys.mvcc.view_close(trx->read_view);
} else {
mutex_enter(&trx_sys.mutex);
}
if (serialised) { if (serialised) {
mutex_enter(&trx_sys.mutex);
UT_LIST_REMOVE(trx_sys.serialisation_list, trx); UT_LIST_REMOVE(trx_sys.serialisation_list, trx);
} else {
mutex_enter(&trx_sys.mutex);
} }
trx_ids_t::iterator it = std::lower_bound( trx_ids_t::iterator it = std::lower_bound(
@@ -1574,6 +1574,8 @@ trx_commit_in_memory(
written */ written */
{ {
trx->must_flush_log_later = false; trx->must_flush_log_later = false;
trx->read_view.set_open(false);
if (trx_is_autocommit_non_locking(trx)) { if (trx_is_autocommit_non_locking(trx)) {
ut_ad(trx->id == 0); ut_ad(trx->id == 0);
@@ -1597,10 +1599,6 @@ trx_commit_in_memory(
ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE)); ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE));
if (trx->read_view != NULL) {
trx->read_view->set_closed(true);
}
MONITOR_INC(MONITOR_TRX_NL_RO_COMMIT); MONITOR_INC(MONITOR_TRX_NL_RO_COMMIT);
/* AC-NL-RO transactions can't be rolled back asynchronously. */ /* AC-NL-RO transactions can't be rolled back asynchronously. */
@@ -1629,9 +1627,6 @@ trx_commit_in_memory(
if (trx->read_only || trx->rsegs.m_redo.rseg == NULL) { if (trx->read_only || trx->rsegs.m_redo.rseg == NULL) {
MONITOR_INC(MONITOR_TRX_RO_COMMIT); MONITOR_INC(MONITOR_TRX_RO_COMMIT);
if (trx->read_view != NULL) {
trx->read_view->set_closed(true);
}
} else { } else {
trx_update_mod_tables_timestamp(trx); trx_update_mod_tables_timestamp(trx);
MONITOR_INC(MONITOR_TRX_RW_COMMIT); MONITOR_INC(MONITOR_TRX_RW_COMMIT);
@@ -1875,30 +1870,6 @@ trx_commit(
trx_commit_low(trx, mtr); trx_commit_low(trx, mtr);
} }
/********************************************************************//**
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.
@return consistent read view */
ReadView*
trx_assign_read_view(
/*=================*/
trx_t* trx) /*!< in/out: active transaction */
{
ut_ad(trx->state == TRX_STATE_ACTIVE);
if (srv_read_only_mode) {
ut_ad(trx->read_view == NULL);
return(NULL);
} else if (!MVCC::is_view_active(trx->read_view)) {
trx_sys.mvcc.view_open(trx->read_view, trx);
}
return(trx->read_view);
}
/****************************************************************//** /****************************************************************//**
Prepares a transaction for commit/rollback. */ Prepares a transaction for commit/rollback. */
void void
@@ -2754,8 +2725,8 @@ trx_set_rw_mode(
trx_sys.rw_trx_ids.push_back(trx->id); trx_sys.rw_trx_ids.push_back(trx->id);
/* So that we can see our own changes. */ /* So that we can see our own changes. */
if (MVCC::is_view_active(trx->read_view)) { if (trx->read_view.is_open()) {
trx->read_view->creator_trx_id(trx->id); trx->read_view.creator_trx_id(trx->id);
} }
mutex_exit(&trx_sys.mutex); mutex_exit(&trx_sys.mutex);
trx_sys.rw_trx_hash.insert(trx); trx_sys.rw_trx_hash.insert(trx);