Skip to content

Commit 8389b45

Browse files
committed
MDEV-15059 - Misc small InnoDB scalability fixes
Moved mutex locking inside lock_rec_lock(). Moved monitor increment out of mutex. Moved assertions that don't require protection out of mutex. Removed duplicate assertions. Moved duplicate debug injections into lock_rec_lock(). Let monitor updates use relaxed memory order. Return directly without maintaining variables in lock_rec_lock_slow(). Moved lock_rec_lock_fast() body into lock_rec_lock(): saves at least one trx_mutex_enter(), one switch() plus some code was moved out of mutex.
1 parent ce04790 commit 8389b45

File tree

3 files changed

+71
-187
lines changed

3 files changed

+71
-187
lines changed

storage/innobase/include/lock0priv.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -745,7 +745,7 @@ class RecLock {
745745
#ifdef WITH_WSREP
746746
,lock_t* c_lock = NULL
747747
#endif /* WITH_WSREP */
748-
);
748+
) const;
749749

750750
/**
751751
Check of the lock is on m_rec_id.
@@ -838,7 +838,7 @@ class RecLock {
838838
@param[in,out] lock Newly created record lock to add to the
839839
rec hash and the transaction lock list
840840
@param[in] add_to_hash If the lock should be added to the hash table */
841-
void lock_add(lock_t* lock, bool add_to_hash);
841+
void lock_add(lock_t* lock, bool add_to_hash) const;
842842

843843
/**
844844
Check and resolve any deadlocks

storage/innobase/include/srv0mon.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -608,8 +608,9 @@ Use MONITOR_INC if appropriate mutex protection exists.
608608
#define MONITOR_ATOMIC_INC_LOW(monitor, enabled) \
609609
if (enabled) { \
610610
ib_uint64_t value; \
611-
value = my_atomic_add64( \
612-
(int64*) &MONITOR_VALUE(monitor), 1) + 1; \
611+
value = my_atomic_add64_explicit( \
612+
(int64*) &MONITOR_VALUE(monitor), 1, \
613+
MY_MEMORY_ORDER_RELAXED) + 1; \
613614
/* Note: This is not 100% accurate because of the \
614615
inherent race, we ignore it due to performance. */\
615616
if (value > (ib_uint64_t) MONITOR_MAX_VALUE(monitor)) { \
@@ -624,8 +625,9 @@ Use MONITOR_DEC if appropriate mutex protection exists.
624625
#define MONITOR_ATOMIC_DEC_LOW(monitor, enabled) \
625626
if (enabled) { \
626627
ib_uint64_t value; \
627-
value = my_atomic_add64( \
628-
(int64*) &MONITOR_VALUE(monitor), -1) - 1; \
628+
value = my_atomic_add64_explicit( \
629+
(int64*) &MONITOR_VALUE(monitor), -1, \
630+
MY_MEMORY_ORDER_RELAXED) - 1; \
629631
/* Note: This is not 100% accurate because of the \
630632
inherent race, we ignore it due to performance. */\
631633
if (value < (ib_uint64_t) MONITOR_MIN_VALUE(monitor)) { \

storage/innobase/lock/lock0lock.cc

Lines changed: 63 additions & 181 deletions
Original file line numberDiff line numberDiff line change
@@ -1877,7 +1877,7 @@ Add the lock to the record lock hash and the transaction's lock list
18771877
@param[in,out] lock Newly created record lock to add to the rec hash
18781878
@param[in] add_to_hash If the lock should be added to the hash table */
18791879
void
1880-
RecLock::lock_add(lock_t* lock, bool add_to_hash)
1880+
RecLock::lock_add(lock_t* lock, bool add_to_hash) const
18811881
{
18821882
ut_ad(lock_mutex_own());
18831883
ut_ad(trx_mutex_own(lock->trx));
@@ -1929,7 +1929,7 @@ RecLock::create(
19291929
#ifdef WITH_WSREP
19301930
,lock_t* c_lock
19311931
#endif /* WITH_WSREP */
1932-
)
1932+
) const
19331933
{
19341934
ut_ad(lock_mutex_own());
19351935
ut_ad(owns_trx_mutex == trx_mutex_own(trx));
@@ -2389,88 +2389,6 @@ lock_rec_add_to_queue(
23892389
rec_lock.create(trx, caller_owns_trx_mutex, true);
23902390
}
23912391

2392-
/*********************************************************************//**
2393-
This is a fast routine for locking a record in the most common cases:
2394-
there are no explicit locks on the page, or there is just one lock, owned
2395-
by this transaction, and of the right type_mode. This is a low-level function
2396-
which does NOT look at implicit locks! Checks lock compatibility within
2397-
explicit locks. This function sets a normal next-key lock, or in the case of
2398-
a page supremum record, a gap type lock.
2399-
@return whether the locking succeeded */
2400-
UNIV_INLINE
2401-
lock_rec_req_status
2402-
lock_rec_lock_fast(
2403-
/*===============*/
2404-
boolimpl,/*!< in: if TRUE, no lock is set
2405-
if no wait is necessary: we
2406-
assume that the caller will
2407-
set an implicit lock */
2408-
ulint mode,/*!< in: lock mode: LOCK_X or
2409-
LOCK_S possibly ORed to either
2410-
LOCK_GAP or LOCK_REC_NOT_GAP */
2411-
const buf_block_t* block,/*!< in: buffer block containing
2412-
the record */
2413-
ulint heap_no,/*!< in: heap number of record */
2414-
dict_index_t* index,/*!< in: index of record */
2415-
que_thr_t* thr)/*!< in: query thread */
2416-
{
2417-
ut_ad(lock_mutex_own());
2418-
ut_ad(!srv_read_only_mode);
2419-
ut_ad((LOCK_MODE_MASK & mode) != LOCK_S
2420-
|| lock_table_has(thr_get_trx(thr), index->table, LOCK_IS));
2421-
ut_ad((LOCK_MODE_MASK & mode) != LOCK_X
2422-
|| lock_table_has(thr_get_trx(thr), index->table, LOCK_IX)
2423-
|| srv_read_only_mode);
2424-
ut_ad((LOCK_MODE_MASK & mode) == LOCK_S
2425-
|| (LOCK_MODE_MASK & mode) == LOCK_X);
2426-
ut_ad(mode - (LOCK_MODE_MASK & mode) == LOCK_GAP
2427-
|| mode - (LOCK_MODE_MASK & mode) == 0
2428-
|| mode - (LOCK_MODE_MASK & mode) == LOCK_REC_NOT_GAP);
2429-
ut_ad(dict_index_is_clust(index) || !dict_index_is_online_ddl(index));
2430-
2431-
DBUG_EXECUTE_IF("innodb_report_deadlock", return(LOCK_REC_FAIL););
2432-
2433-
lock_t* lock = lock_rec_get_first_on_page(lock_sys->rec_hash, block);
2434-
2435-
trx_t* trx = thr_get_trx(thr);
2436-
2437-
lock_rec_req_status status = LOCK_REC_SUCCESS;
2438-
2439-
if (lock == NULL) {
2440-
2441-
if (!impl) {
2442-
RecLockrec_lock(index, block, heap_no, mode);
2443-
2444-
/* Note that we don't own the trx mutex. */
2445-
rec_lock.create(trx, false, true);
2446-
}
2447-
2448-
status = LOCK_REC_SUCCESS_CREATED;
2449-
} else {
2450-
trx_mutex_enter(trx);
2451-
2452-
if (lock_rec_get_next_on_page(lock)
2453-
|| lock->trx != trx
2454-
|| lock->type_mode != (mode | LOCK_REC)
2455-
|| lock_rec_get_n_bits(lock) <= heap_no) {
2456-
2457-
status = LOCK_REC_FAIL;
2458-
} else if (!impl) {
2459-
/* If the nth bit of the record lock is already set
2460-
then we do not set a new lock bit, otherwise we do
2461-
set */
2462-
if (!lock_rec_get_nth_bit(lock, heap_no)) {
2463-
lock_rec_set_nth_bit(lock, heap_no);
2464-
status = LOCK_REC_SUCCESS_CREATED;
2465-
}
2466-
}
2467-
2468-
trx_mutex_exit(trx);
2469-
}
2470-
2471-
return(status);
2472-
}
2473-
24742392
/*********************************************************************//**
24752393
This is the general, and slower, routine for locking a record. This is a
24762394
low-level function which does NOT look at implicit locks! Checks lock
@@ -2496,33 +2414,12 @@ lock_rec_lock_slow(
24962414
que_thr_t* thr)/*!< in: query thread */
24972415
{
24982416
ut_ad(lock_mutex_own());
2499-
ut_ad(!srv_read_only_mode);
2500-
ut_ad((LOCK_MODE_MASK & mode) != LOCK_S
2501-
|| lock_table_has(thr_get_trx(thr), index->table, LOCK_IS));
2502-
ut_ad((LOCK_MODE_MASK & mode) != LOCK_X
2503-
|| lock_table_has(thr_get_trx(thr), index->table, LOCK_IX));
2504-
ut_ad((LOCK_MODE_MASK & mode) == LOCK_S
2505-
|| (LOCK_MODE_MASK & mode) == LOCK_X);
2506-
ut_ad(mode - (LOCK_MODE_MASK & mode) == LOCK_GAP
2507-
|| mode - (LOCK_MODE_MASK & mode) == 0
2508-
|| mode - (LOCK_MODE_MASK & mode) == LOCK_REC_NOT_GAP);
2509-
ut_ad(dict_index_is_clust(index) || !dict_index_is_online_ddl(index));
2510-
2511-
DBUG_EXECUTE_IF("innodb_report_deadlock", return(DB_DEADLOCK););
25122417

2513-
dberr_terr;
25142418
trx_t* trx = thr_get_trx(thr);
2419+
ut_ad(trx_mutex_own(trx));
25152420

2516-
trx_mutex_enter(trx);
2517-
2518-
if (lock_rec_has_expl(mode, block, heap_no, trx)) {
2519-
2520-
/* The trx already has a strong enough lock on rec: do
2521-
nothing */
2522-
2523-
err = DB_SUCCESS;
2524-
2525-
} else {
2421+
/* Do nothing if the trx already has a strong enough lock on rec */
2422+
if (!lock_rec_has_expl(mode, block, heap_no, trx)) {
25262423
lock_t* wait_for = lock_rec_other_has_conflicting(
25272424
mode, block, heap_no, trx);
25282425

@@ -2535,7 +2432,7 @@ lock_rec_lock_slow(
25352432

25362433
RecLockrec_lock(thr, index, block, heap_no, mode);
25372434

2538-
err = rec_lock.add_to_waitq(wait_for);
2435+
return rec_lock.add_to_waitq(wait_for);
25392436

25402437
} else if (!impl) {
25412438

@@ -2546,15 +2443,10 @@ lock_rec_lock_slow(
25462443
LOCK_REC | mode, block, heap_no, index, trx,
25472444
true);
25482445

2549-
err = DB_SUCCESS_LOCKED_REC;
2550-
} else {
2551-
err = DB_SUCCESS;
2446+
return DB_SUCCESS_LOCKED_REC;
25522447
}
25532448
}
2554-
2555-
trx_mutex_exit(trx);
2556-
2557-
return(err);
2449+
return DB_SUCCESS;
25582450
}
25592451

25602452
/*********************************************************************//**
@@ -2582,33 +2474,61 @@ lock_rec_lock(
25822474
dict_index_t* index,/*!< in: index of record */
25832475
que_thr_t* thr)/*!< in: query thread */
25842476
{
2585-
ut_ad(lock_mutex_own());
2586-
ut_ad(!srv_read_only_mode);
2587-
ut_ad((LOCK_MODE_MASK & mode) != LOCK_S
2588-
|| lock_table_has(thr_get_trx(thr), index->table, LOCK_IS));
2589-
ut_ad((LOCK_MODE_MASK & mode) != LOCK_X
2590-
|| lock_table_has(thr_get_trx(thr), index->table, LOCK_IX));
2591-
ut_ad((LOCK_MODE_MASK & mode) == LOCK_S
2592-
|| (LOCK_MODE_MASK & mode) == LOCK_X);
2593-
ut_ad(mode - (LOCK_MODE_MASK & mode) == LOCK_GAP
2594-
|| mode - (LOCK_MODE_MASK & mode) == LOCK_REC_NOT_GAP
2595-
|| mode - (LOCK_MODE_MASK & mode) == 0);
2596-
ut_ad(dict_index_is_clust(index) || !dict_index_is_online_ddl(index));
2597-
2598-
/* We try a simplified and faster subroutine for the most
2599-
common cases */
2600-
switch (lock_rec_lock_fast(impl, mode, block, heap_no, index, thr)) {
2601-
case LOCK_REC_SUCCESS:
2602-
return(DB_SUCCESS);
2603-
case LOCK_REC_SUCCESS_CREATED:
2604-
return(DB_SUCCESS_LOCKED_REC);
2605-
case LOCK_REC_FAIL:
2606-
return(lock_rec_lock_slow(impl, mode, block,
2607-
heap_no, index, thr));
2608-
}
2609-
2610-
ut_error;
2611-
return(DB_ERROR);
2477+
trx_t *trx= thr_get_trx(thr);
2478+
dberr_t err= DB_SUCCESS;
2479+
2480+
ut_ad(!srv_read_only_mode);
2481+
ut_ad((LOCK_MODE_MASK & mode) == LOCK_S ||
2482+
(LOCK_MODE_MASK & mode) == LOCK_X);
2483+
ut_ad((mode & LOCK_TYPE_MASK) == LOCK_GAP ||
2484+
(mode & LOCK_TYPE_MASK) == LOCK_REC_NOT_GAP ||
2485+
(mode & LOCK_TYPE_MASK) == 0);
2486+
ut_ad(dict_index_is_clust(index) || !dict_index_is_online_ddl(index));
2487+
DBUG_EXECUTE_IF("innodb_report_deadlock", return DB_DEADLOCK;);
2488+
2489+
lock_mutex_enter();
2490+
ut_ad((LOCK_MODE_MASK & mode) != LOCK_S ||
2491+
lock_table_has(trx, index->table, LOCK_IS));
2492+
ut_ad((LOCK_MODE_MASK & mode) != LOCK_X ||
2493+
lock_table_has(trx, index->table, LOCK_IX));
2494+
2495+
if (lock_t *lock= lock_rec_get_first_on_page(lock_sys->rec_hash, block))
2496+
{
2497+
trx_mutex_enter(trx);
2498+
if (lock_rec_get_next_on_page(lock) ||
2499+
lock->trx != trx ||
2500+
lock->type_mode != (mode | LOCK_REC) ||
2501+
lock_rec_get_n_bits(lock) <= heap_no)
2502+
{
2503+
err= lock_rec_lock_slow(impl, mode, block, heap_no, index, thr);
2504+
}
2505+
else if (!impl)
2506+
{
2507+
/*
2508+
If the nth bit of the record lock is already set then we do not set
2509+
a new lock bit, otherwise we do set
2510+
*/
2511+
if (!lock_rec_get_nth_bit(lock, heap_no))
2512+
{
2513+
lock_rec_set_nth_bit(lock, heap_no);
2514+
err= DB_SUCCESS_LOCKED_REC;
2515+
}
2516+
}
2517+
trx_mutex_exit(trx);
2518+
}
2519+
else
2520+
{
2521+
/*
2522+
Simplified and faster path for the most common cases
2523+
Note that we don't own the trx mutex.
2524+
*/
2525+
if (!impl)
2526+
RecLock(index, block, heap_no, mode).create(trx, false, true);
2527+
err= DB_SUCCESS_LOCKED_REC;
2528+
}
2529+
lock_mutex_exit();
2530+
MONITOR_ATOMIC_INC(MONITOR_NUM_RECLOCK_REQ);
2531+
return err;
26122532
}
26132533

26142534
/*********************************************************************//**
@@ -6575,17 +6495,9 @@ lock_clust_rec_modify_check_and_lock(
65756495
lock_rec_convert_impl_to_expl(thr_get_trx(thr), block, rec, index,
65766496
offsets);
65776497

6578-
lock_mutex_enter();
6579-
6580-
ut_ad(lock_table_has(thr_get_trx(thr), index->table, LOCK_IX));
6581-
65826498
err = lock_rec_lock(TRUE, LOCK_X | LOCK_REC_NOT_GAP,
65836499
block, heap_no, index, thr);
65846500

6585-
MONITOR_INC(MONITOR_NUM_RECLOCK_REQ);
6586-
6587-
lock_mutex_exit();
6588-
65896501
ut_ad(lock_rec_queue_validate(FALSE, block, rec, index, offsets));
65906502

65916503
if (err == DB_SUCCESS_LOCKED_REC) {
@@ -6638,17 +6550,9 @@ lock_sec_rec_modify_check_and_lock(
66386550
index record, and this would not have been possible if another active
66396551
transaction had modified this secondary index record. */
66406552

6641-
lock_mutex_enter();
6642-
6643-
ut_ad(lock_table_has(thr_get_trx(thr), index->table, LOCK_IX));
6644-
66456553
err = lock_rec_lock(TRUE, LOCK_X | LOCK_REC_NOT_GAP,
66466554
block, heap_no, index, thr);
66476555

6648-
MONITOR_INC(MONITOR_NUM_RECLOCK_REQ);
6649-
6650-
lock_mutex_exit();
6651-
66526556
#ifdef UNIV_DEBUG
66536557
{
66546558
mem_heap_t* heap = NULL;
@@ -6740,20 +6644,9 @@ lock_sec_rec_read_check_and_lock(
67406644
index, offsets);
67416645
}
67426646

6743-
lock_mutex_enter();
6744-
6745-
ut_ad(mode != LOCK_X
6746-
|| lock_table_has(thr_get_trx(thr), index->table, LOCK_IX));
6747-
ut_ad(mode != LOCK_S
6748-
|| lock_table_has(thr_get_trx(thr), index->table, LOCK_IS));
6749-
67506647
err = lock_rec_lock(FALSE, mode | gap_mode,
67516648
block, heap_no, index, thr);
67526649

6753-
MONITOR_INC(MONITOR_NUM_RECLOCK_REQ);
6754-
6755-
lock_mutex_exit();
6756-
67576650
ut_ad(lock_rec_queue_validate(FALSE, block, rec, index, offsets));
67586651

67596652
return(err);
@@ -6816,19 +6709,8 @@ lock_clust_rec_read_check_and_lock(
68166709
index, offsets);
68176710
}
68186711

6819-
lock_mutex_enter();
6820-
6821-
ut_ad(mode != LOCK_X
6822-
|| lock_table_has(thr_get_trx(thr), index->table, LOCK_IX));
6823-
ut_ad(mode != LOCK_S
6824-
|| lock_table_has(thr_get_trx(thr), index->table, LOCK_IS));
6825-
68266712
err = lock_rec_lock(FALSE, mode | gap_mode, block, heap_no, index, thr);
68276713

6828-
MONITOR_INC(MONITOR_NUM_RECLOCK_REQ);
6829-
6830-
lock_mutex_exit();
6831-
68326714
ut_ad(lock_rec_queue_validate(FALSE, block, rec, index, offsets));
68336715

68346716
DEBUG_SYNC_C("after_lock_clust_rec_read_check_and_lock");

0 commit comments

Comments
 (0)