summaryrefslogtreecommitdiff
path: root/storage/innobase/lock
diff options
context:
space:
mode:
authorsensssz <hjmsens@gmail.com>2016-10-19 01:37:52 -0400
committersensssz <hjmsens@gmail.com>2016-10-19 01:42:10 -0400
commit0e60f89c296a626e3c6ff8385ada8e21613f92d0 (patch)
tree834384dd571bb196a28eafa9716ee273510ab66a /storage/innobase/lock
parent03b3425d31a0af66f65e194f10b8325e9acebec5 (diff)
downloadmariadb-git-0e60f89c296a626e3c6ff8385ada8e21613f92d0.tar.gz
Change VATS implementation.
Diffstat (limited to 'storage/innobase/lock')
-rw-r--r--storage/innobase/lock/lock0lock.cc489
1 files changed, 322 insertions, 167 deletions
diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc
index 7db94b143be..534b2bde114 100644
--- a/storage/innobase/lock/lock0lock.cc
+++ b/storage/innobase/lock/lock0lock.cc
@@ -83,6 +83,17 @@ lock_rec_has_to_wait_in_queue(
/*==========================*/
const lock_t* wait_lock); /*!< in: waiting record lock */
+/*************************************************************//**
+Grants a lock to a waiting lock request and releases the waiting transaction.
+The caller must hold lock_sys->mutex. */
+static
+void
+lock_grant(
+/*=======*/
+ lock_t* lock, /*!< in/out: waiting lock request */
+ bool owns_trx_mutex); /*!< in: whether lock->trx->mutex is owned */
+
+
/* Buffer to collect THDs to report waits for. */
struct thd_wait_reports {
struct thd_wait_reports *next; /*!< List link */
@@ -1724,6 +1735,149 @@ RecLock::lock_alloc(
return(lock);
}
+/*********************************************************************//**
+Check if lock1 has higher priority than lock2.
+NULL has lowest priority.
+If either is a high priority transaction, the lock has higher priority.
+If neither of them is wait lock, the first one has higher priority.
+If only one of them is a wait lock, it has lower priority.
+Otherwise, the one with an older transaction has higher priority.
+@returns true if lock1 has higher priority, false otherwise. */
+bool
+has_higher_priority(
+ lock_t *lock1,
+ lock_t *lock2)
+{
+ if (lock1 == NULL) {
+ return false;
+ } else if (lock2 == NULL) {
+ return true;
+ }
+ // Ask the upper server layer if any of the two trx should be prefered.
+ int preference = thd_deadlock_victim_preference(lock1->trx->mysql_thd, lock2->trx->mysql_thd);
+ if (preference == -1) {
+ // lock1 is preferred as a victim, so lock2 has higher priority
+ return false;
+ } else if (preference == 1) {
+ // lock2 is preferred as a victim, so lock1 has higher priority
+ return true;
+ }
+ if (trx_is_high_priority(lock1->trx)) {
+ return true;
+ }
+ if (trx_is_high_priority(lock2->trx)) {
+ return false;
+ }
+ // No preference. Compre them by wait mode and trx age.
+ if (!lock_get_wait(lock1)) {
+ return true;
+ } else if (!lock_get_wait(lock2)) {
+ return false;
+ }
+ return lock1->trx->start_time < lock2->trx->start_time;
+}
+
+/*********************************************************************//**
+Insert a lock to the hash list according to the mode (whether it is a wait
+lock) and the age of the transaction the it is associated with.
+If the lock is not a wait lock, insert it to the head of the hash list.
+Otherwise, insert it to the middle of the wait locks according to the age of
+the transaciton. */
+static
+void
+lock_rec_insert_by_trx_age(
+ lock_t *in_lock) /*!< in: lock to be insert */{
+ ulint space;
+ ulint page_no;
+ ulint rec_fold;
+ hash_table_t* hash;
+ hash_cell_t* cell;
+ lock_t* node;
+ lock_t* next;
+
+ space = in_lock->un_member.rec_lock.space;
+ page_no = in_lock->un_member.rec_lock.page_no;
+ rec_fold = lock_rec_fold(space, page_no);
+ hash = lock_hash_get(in_lock->type_mode);
+ cell = hash_get_nth_cell(hash,
+ hash_calc_hash(rec_fold, hash));
+
+ node = (lock_t *) cell->node;
+ // If in_lock is not a wait lock, we insert it to the head of the list.
+ if (node == NULL || !lock_get_wait(in_lock) || has_higher_priority(in_lock, node)) {
+ cell->node = in_lock;
+ in_lock->hash = node;
+ return;
+ }
+ while (node != NULL && has_higher_priority((lock_t *) node->hash,
+ in_lock)) {
+ node = (lock_t *) node->hash;
+ }
+ next = (lock_t *) node->hash;
+ node->hash = in_lock;
+ in_lock->hash = next;
+}
+
+static
+bool
+lock_queue_validate(
+ const lock_t *in_lock) /*!< in: lock whose hash list is to be validated */
+{
+ ulint space;
+ ulint page_no;
+ ulint rec_fold;
+ hash_table_t* hash;
+ hash_cell_t* cell;
+ lock_t* next;
+ bool wait_lock = false;
+
+ if (in_lock == NULL) {
+ return true;
+ }
+
+ space = in_lock->un_member.rec_lock.space;
+ page_no = in_lock->un_member.rec_lock.page_no;
+ rec_fold = lock_rec_fold(space, page_no);
+ hash = lock_hash_get(in_lock->type_mode);
+ cell = hash_get_nth_cell(hash,
+ hash_calc_hash(rec_fold, hash));
+ next = (lock_t *) cell->node;
+ while (next != NULL) {
+ // If this is a granted lock, check that there's no wait lock before it.
+ if (!lock_get_wait(next)) {
+ ut_ad(!wait_lock);
+ } else {
+ wait_lock = true;
+ }
+ next = next->hash;
+ }
+ return true;
+}
+
+static
+void
+lock_rec_insert_to_head(
+ lock_t *in_lock, /*!< in: lock to be insert */
+ ulint rec_fold) /*!< in: rec_fold of the page */
+{
+ hash_table_t* hash;
+ hash_cell_t* cell;
+ lock_t* node;
+
+ if (in_lock == NULL) {
+ return;
+ }
+
+ hash = lock_hash_get(in_lock->type_mode);
+ cell = hash_get_nth_cell(hash,
+ hash_calc_hash(rec_fold, hash));
+ node = (lock_t *) cell->node;
+ if (node != in_lock) {
+ cell->node = in_lock;
+ in_lock->hash = node;
+ }
+}
+
/**
Add the lock to the record lock hash and the transaction's lock list
@param[in,out] lock Newly created record lock to add to the rec hash
@@ -1733,16 +1887,28 @@ RecLock::lock_add(lock_t* lock, bool add_to_hash)
{
ut_ad(lock_mutex_own());
ut_ad(trx_mutex_own(lock->trx));
+
+ bool wait_lock = m_mode & LOCK_WAIT;
if (add_to_hash) {
ulint key = m_rec_id.fold();
+ hash_table_t *lock_hash = lock_hash_get(m_mode);
++lock->index->table->n_rec_locks;
-
- HASH_INSERT(lock_t, hash, lock_hash_get(m_mode), key, lock);
+
+ if (innodb_lock_schedule_algorithm == INNODB_LOCK_SCHEDULE_ALGORITHM_VATS
+ && !is_slave_replication) {
+ if (wait_lock) {
+ HASH_INSERT(lock_t, hash, lock_hash, key, lock);
+ } else {
+ lock_rec_insert_to_head(lock, m_rec_id.fold());
+ }
+ } else {
+ HASH_INSERT(lock_t, hash, lock_hash, key, lock);
+ }
}
- if (m_mode & LOCK_WAIT) {
+ if (wait_lock) {
lock_set_lock_and_trx_wait(lock, lock->trx);
}
@@ -1892,85 +2058,6 @@ RecLock::create(
return(lock);
}
-/*********************************************************************//**
-Check if lock1 has higher priority than lock2.
-NULL has lowest priority.
-If either is a high priority transaction, the lock has higher priority.
-If neither of them is wait lock, the first one has higher priority.
-If only one of them is a wait lock, it has lower priority.
-Otherwise, the one with an older transaction has higher priority.
-@returns true if lock1 has higher priority, false otherwise. */
-bool
-has_higher_priority(
- lock_t *lock1,
- lock_t *lock2)
-{
- if (lock1 == NULL) {
- return false;
- } else if (lock2 == NULL) {
- return true;
- }
- // Ask the upper server layer if any of the two trx should be prefered.
- int preference = thd_deadlock_victim_preference(lock1->trx->mysql_thd, lock2->trx->mysql_thd);
- if (preference == -1) {
- // lock1 is preferred as a victim, so lock2 has higher priority
- return false;
- } else if (preference == 1) {
- // lock2 is preferred as a victim, so lock1 has higher priority
- return true;
- }
- // No preference. Compre them by wait mode and trx age.
- if (!lock_get_wait(lock1)) {
- return true;
- } else if (!lock_get_wait(lock2)) {
- return false;
- }
- return lock1->trx->start_time < lock2->trx->start_time;
-}
-
-/*********************************************************************//**
-Insert a lock to the hash list according to the mode (whether it is a wait
-lock) and the age of the transaction the it is associated with.
-If the lock is not a wait lock, insert it to the head of the hash list.
-Otherwise, insert it to the middle of the wait locks according to the age of
-the transaciton. */
-static
-void
-lock_rec_insert_by_trx_age(
- lock_t *in_lock, /*!< in: lock to be insert */
- bool wait) /*!< in: whether it's a wait lock */
-{
- ulint space;
- ulint page_no;
- ulint rec_fold;
- hash_table_t* hash;
- hash_cell_t* cell;
- lock_t* node;
- lock_t* next;
-
- space = in_lock->un_member.rec_lock.space;
- page_no = in_lock->un_member.rec_lock.page_no;
- rec_fold = lock_rec_fold(space, page_no);
- hash = lock_hash_get(in_lock->type_mode);
- cell = hash_get_nth_cell(hash,
- hash_calc_hash(rec_fold, hash));
-
- node = (lock_t *) cell->node;
- // If in_lock is not a wait lock, we insert it to the head of the list.
- if (node == NULL || !wait || has_higher_priority(in_lock, node)) {
- cell->node = in_lock;
- in_lock->hash = node;
- return;
- }
- while (node != NULL && has_higher_priority((lock_t *) node->hash,
- in_lock)) {
- node = (lock_t *) node->hash;
- }
- next = (lock_t *) node->hash;
- node->hash = in_lock;
- in_lock->hash = next;
-}
-
/**
Check the outcome of the deadlock check
@param[in,out] victim_trx Transaction selected for rollback
@@ -2176,6 +2263,23 @@ RecLock::add_to_waitq(const lock_t* wait_for, const lock_prdt_t* prdt)
dberr_t err = deadlock_check(lock);
ut_ad(trx_mutex_own(m_trx));
+
+ // Move it only when it does not cause a deadlock.
+ if (err != DB_DEADLOCK
+ && innodb_lock_schedule_algorithm
+ == INNODB_LOCK_SCHEDULE_ALGORITHM_VATS
+ && !is_slave_replication
+ && !trx_is_high_priority(lock->trx)) {
+
+ HASH_DELETE(lock_t, hash, lock_hash_get(lock->type_mode),
+ m_rec_id.fold(), lock);
+ lock_rec_insert_by_trx_age(lock);
+ if (lock_get_wait(lock) && !lock_rec_has_to_wait_in_queue(lock)) {
+ lock_grant(lock, true);
+ return DB_SUCCESS_LOCKED_REC;
+ }
+ }
+
return(err);
}
@@ -2590,13 +2694,16 @@ static
void
lock_grant(
/*=======*/
- lock_t* lock) /*!< in/out: waiting lock request */
+ lock_t* lock, /*!< in/out: waiting lock request */
+ bool owns_trx_mutex) /*!< in: whether lock->trx->mutex is owned */
{
ut_ad(lock_mutex_own());
lock_reset_lock_and_trx_wait(lock);
- trx_mutex_enter(lock->trx);
+ if (!owns_trx_mutex) {
+ trx_mutex_enter(lock->trx);
+ }
if (lock_get_mode(lock) == LOCK_AUTO_INC) {
dict_table_t* table = lock->un_member.tab_lock.table;
@@ -2629,7 +2736,9 @@ lock_grant(
}
}
- trx_mutex_exit(lock->trx);
+ if (!owns_trx_mutex) {
+ trx_mutex_exit(lock->trx);
+ }
}
/**
@@ -2893,30 +3002,64 @@ lock_rec_cancel(
trx_mutex_exit(lock->trx);
}
-/*************************************************************//**
-Move the lock to the head of the hash list. */
static
void
-lock_rec_move_to_front(
- lock_t *lock_to_move, /*!< in: lock to be moved */
- ulint rec_fold) /*!< in: rec fold of the lock */
-{
- hash_table_t* lock_hash;
- hash_cell_t* cell;
- lock_t* next;
+lock_grant_and_move_on_page(
+ hash_table_t *lock_hash,
+ ulint space,
+ ulint page_no)
+{
+ lock_t* lock;
+ lock_t* previous;
+ ulint rec_fold = lock_rec_fold(space, page_no);
- if (lock_to_move != NULL)
- {
- lock_hash = lock_hash_get(lock_to_move->type_mode);
- // Move the target lock to the head of the list
- cell = hash_get_nth_cell(lock_hash,
- hash_calc_hash(rec_fold, lock_hash));
- if (lock_to_move != cell->node) {
- next = (lock_t *) cell->node;
- cell->node = lock_to_move;
- lock_to_move->hash = next;
- }
- }
+ previous = (lock_t *) hash_get_nth_cell(lock_hash,
+ hash_calc_hash(rec_fold, lock_hash))->node;
+ /* Grant locks if there are no conflicting locks ahead.
+ Move granted locks to the head of the list. */
+ for (lock = lock_rec_get_first_on_page_addr(lock_hash, space,
+ page_no);
+ lock != NULL;) {
+
+ /* If the lock is a wait lock on this page, and it does not need to wait. */
+ if ((lock->un_member.rec_lock.space == space)
+ && (lock->un_member.rec_lock.page_no == page_no)
+ && lock_get_wait(lock)
+ && !lock_rec_has_to_wait_in_queue(lock)) {
+
+ /* Grant the lock */
+ ut_ad(lock->trx != in_lock->trx);
+
+ bool exit_trx_mutex = false;
+
+ if (lock->trx->abort_type != TRX_SERVER_ABORT) {
+ ut_ad(trx_mutex_own(lock->trx));
+ trx_mutex_exit(lock->trx);
+ exit_trx_mutex = true;
+ }
+
+ lock_grant(lock, false);
+
+ if (exit_trx_mutex) {
+ ut_ad(!trx_mutex_own(lock->trx));
+ trx_mutex_enter(lock->trx);
+ }
+
+ if (previous != NULL) {
+ /* Move the lock to the head of the list. */
+ HASH_GET_NEXT(hash, previous) = HASH_GET_NEXT(hash, lock);
+ lock_rec_insert_to_head(lock, rec_fold);
+ } else {
+ /* Already at the head of the list. */
+ previous = lock;
+ }
+ /* Move on to the next lock. */
+ lock = static_cast<lock_t *>(HASH_GET_NEXT(hash, previous));
+ } else {
+ previous = lock;
+ lock = static_cast<lock_t *>(HASH_GET_NEXT(hash, lock));
+ }
+ }
}
/*************************************************************//**
@@ -2935,8 +3078,6 @@ lock_rec_dequeue_from_page(
{
ulint space;
ulint page_no;
- ulint rec_fold;
- lock_t* previous = NULL;
lock_t* lock;
trx_lock_t* trx_lock;
hash_table_t* lock_hash;
@@ -2949,7 +3090,6 @@ lock_rec_dequeue_from_page(
space = in_lock->un_member.rec_lock.space;
page_no = in_lock->un_member.rec_lock.page_no;
- rec_fold = lock_rec_fold(space, page_no);
ut_ad(in_lock->index->table->n_rec_locks > 0);
in_lock->index->table->n_rec_locks--;
@@ -2965,7 +3105,7 @@ lock_rec_dequeue_from_page(
MONITOR_DEC(MONITOR_NUM_RECLOCK);
if (innodb_lock_schedule_algorithm
- == INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS) {
+ == INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS || is_slave_replication) {
/* Check if waiting locks in the queue can now be granted:
grant locks if there are no conflicting locks ahead. Stop at
@@ -2990,7 +3130,7 @@ lock_rec_dequeue_from_page(
exit_trx_mutex = true;
}
- lock_grant(lock);
+ lock_grant(lock, false);
if (exit_trx_mutex) {
ut_ad(!trx_mutex_own(lock->trx));
@@ -2999,50 +3139,7 @@ lock_rec_dequeue_from_page(
}
}
} else {
- /* Grant locks if there are no conflicting locks ahead.
- Move granted locks to the head of the list. */
- for (lock = lock_rec_get_first_on_page_addr(lock_hash, space, page_no);
- lock != NULL;) {
-
- /* If the lock is a wait lock on this page, and it does not need to wait. */
- if ((lock->un_member.rec_lock.space == space)
- && (lock->un_member.rec_lock.page_no == page_no)
- && lock_get_wait(lock)
- && !lock_rec_has_to_wait_in_queue(lock)) {
-
- /* Grant the lock */
- ut_ad(lock->trx != in_lock->trx);
-
- bool exit_trx_mutex = false;
-
- if (lock->trx->abort_type != TRX_SERVER_ABORT) {
- ut_ad(trx_mutex_own(lock->trx));
- trx_mutex_exit(lock->trx);
- exit_trx_mutex = true;
- }
-
- lock_grant(lock);
-
- if (exit_trx_mutex) {
- ut_ad(!trx_mutex_own(lock->trx));
- trx_mutex_enter(lock->trx);
- }
-
- if (previous != NULL) {
- /* Move the lock to the head of the list. */
- HASH_GET_NEXT(hash, previous) = HASH_GET_NEXT(hash, lock);
- lock_rec_move_to_front(lock, rec_fold);
- } else {
- /* Already at the head of the list. */
- previous = lock;
- }
- /* Move on to the next lock. */
- lock = static_cast<lock_t *>(HASH_GET_NEXT(hash, previous));
- } else {
- previous = lock;
- lock = static_cast<lock_t *>(HASH_GET_NEXT(hash, lock));
- }
- }
+ lock_grant_and_move_on_page(lock_hash, space, page_no);
}
}
@@ -4793,7 +4890,7 @@ lock_table_dequeue(
/* Grant the lock */
ut_ad(in_lock->trx != lock->trx);
- lock_grant(lock);
+ lock_grant(lock, false);
}
}
}
@@ -4877,6 +4974,54 @@ run_again:
}
/*=========================== LOCK RELEASE ==============================*/
+static
+void
+lock_grant_and_move_on_rec(
+ hash_table_t *lock_hash,
+ lock_t *first_lock,
+ ulint heap_no)
+{
+ lock_t* lock;
+ lock_t* previous;
+ ulint space;
+ ulint page_no;
+ ulint rec_fold;
+
+ space = first_lock->un_member.rec_lock.space;
+ page_no = first_lock->un_member.rec_lock.page_no;
+ rec_fold = lock_rec_fold(space, page_no);
+
+ previous = (lock_t *) hash_get_nth_cell(lock_hash,
+ hash_calc_hash(rec_fold, lock_hash))->node;
+ /* Grant locks if there are no conflicting locks ahead.
+ Move granted locks to the head of the list. */
+ for (lock = first_lock; lock != NULL;) {
+
+ /* If the lock is a wait lock on this page, and it does not need to wait. */
+ if (lock->un_member.rec_lock.space == space
+ && lock->un_member.rec_lock.page_no == page_no
+ && lock_rec_get_nth_bit(lock, heap_no)
+ && lock_get_wait(lock)
+ && !lock_rec_has_to_wait_in_queue(lock)) {
+
+ lock_grant(lock, false);
+
+ if (previous != NULL) {
+ /* Move the lock to the head of the list. */
+ HASH_GET_NEXT(hash, previous) = HASH_GET_NEXT(hash, lock);
+ lock_rec_insert_to_head(lock, rec_fold);
+ } else {
+ /* Already at the head of the list. */
+ previous = lock;
+ }
+ /* Move on to the next lock. */
+ lock = static_cast<lock_t *>(HASH_GET_NEXT(hash, previous));
+ } else {
+ previous = lock;
+ lock = static_cast<lock_t *>(HASH_GET_NEXT(hash, lock));
+ }
+ }
+}
/*************************************************************//**
Removes a granted record lock of a transaction from the queue and grants
@@ -4937,19 +5082,25 @@ lock_rec_unlock(
released:
ut_a(!lock_get_wait(lock));
lock_rec_reset_nth_bit(lock, heap_no);
+
+ if (innodb_lock_schedule_algorithm
+ == INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS || is_slave_replication) {
- /* Check if we can now grant waiting lock requests */
+ /* Check if we can now grant waiting lock requests */
- for (lock = first_lock; lock != NULL;
- lock = lock_rec_get_next(heap_no, lock)) {
- if (lock_get_wait(lock)
- && !lock_rec_has_to_wait_in_queue(lock)) {
+ for (lock = first_lock; lock != NULL;
+ lock = lock_rec_get_next(heap_no, lock)) {
+ if (lock_get_wait(lock)
+ && !lock_rec_has_to_wait_in_queue(lock)) {
- /* Grant the lock */
- ut_ad(trx != lock->trx);
- lock_grant(lock);
- }
- }
+ /* Grant the lock */
+ ut_ad(trx != lock->trx);
+ lock_grant(lock, false);
+ }
+ }
+ } else {
+ lock_grant_and_move_on_rec(lock_sys->rec_hash, first_lock, heap_no);
+ }
lock_mutex_exit();
trx_mutex_exit(trx);
@@ -6182,6 +6333,9 @@ lock_rec_queue_validate(
ut_a(lock_rec_has_to_wait_in_queue(lock));
}
}
+
+ ut_ad(innodb_lock_schedule_algorithm == INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS ||
+ lock_queue_validate(lock));
func_exit:
if (!locked_lock_trx_sys) {
@@ -7974,7 +8128,8 @@ DeadlockChecker::get_first_lock(ulint* heap_no) const
ut_a(lock != NULL);
ut_a(lock != m_wait_lock ||
(innodb_lock_schedule_algorithm
- == INNODB_LOCK_SCHEDULE_ALGORITHM_VATS));
+ == INNODB_LOCK_SCHEDULE_ALGORITHM_VATS
+ && !is_slave_replication));
/* Check that the lock type doesn't change. */
ut_ad(lock_get_type_low(lock) == lock_get_type_low(m_wait_lock));