Understanding MySQL InnoDB Locks, Latches, and Intention Locks with Source‑Code Analysis
This article explains MySQL 8.0.17 InnoDB locking mechanisms—including latches, row and table locks, lock modes, intention locks, their compatibility matrix, and detailed source‑code examples that illustrate how IX and X locks are acquired during record updates.
Preparation
MySQL kernel version: 8.0.17
Understanding lock and latch
Latch
Latch is a lock placed on B‑tree pages when locating a record; it is usually released immediately after the record is accessed or modified, and its scope is much smaller than a lock.
Lock
Lock is the transactional lock used by MySQL, applied to tables or rows.
Lock mode
Lock modes include Share (S) and Exclusive (X) (corresponding to LOCK_S and LOCK_X in the source). Gap modes include Record lock, Gap lock, and Next‑key lock (corresponding to LOCK_REC_NOT_GAP , LOCK_GAP , LOCK_ORDINARY ). An insert‑intention lock, represented by (LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION) , is mutually exclusive with Gap/Next‑key locks; it is set when a transaction intends to insert a new record into a gap and must wait for any existing Gap/Next‑key lock to be released.
Lock types
Intention Locks – InnoDB supports multiple‑granularity locking, allowing row and table locks to coexist.
Record Locks – A lock on an index record.
Gap Locks – A lock on the gap between index records (or before the first/after the last record).
Next‑Key Locks – A combination of a record lock and a gap lock on the preceding gap.
…
Intention locks
Table‑level lock compatibility matrix:
X
IX
S
IS
X
Conflict
Conflict
Conflict
Conflict
IX
Conflict
Compatible
Conflict
Compatible
S
Conflict
Conflict
Compatible
Compatible
IS
Conflict
Compatible
Compatible
Compatible
The X, IX, S, and IS entries in the matrix are table‑level locks and do not represent row‑level locks.
Lock meanings:
X : exclusive lock IX : intention exclusive lock S : shared lock IS : intention shared lock
Within a transaction ( trx_t ), lock information is stored in trx_lock_t , which includes trx->lock.trx_locks (row locks) and trx->lock.table_locks (table locks).
MySQL introduces intention locks to allow them to coexist with row locks. For example, SELECT ... FOR SHARE sets an IS lock, while SELECT ... FOR UPDATE sets an IX lock. The locking rules are:
When a transaction requests a shared row lock, it must first acquire an IS (intention shared) table lock.
When a transaction requests an exclusive row lock, it must first acquire an IX (intention exclusive) table lock.
IX and IS are table‑level locks; they do not conflict with row‑level X or S locks, only with other table‑level X or S locks. Row‑level X and S follow the usual shared/exclusive rules. Intention locks avoid scanning the entire lock hash when a table‑level X lock is needed.
Source‑code analysis
The following excerpt shows how an IX lock is created when updating a record.
/* storage/innobase/lock/lock0lock.cc */
/** Creates a table lock object and adds it as the last in the lock queue of the table.
Does NOT check for deadlocks or lock compatibility.
@return own: new lock object */
UNIV_INLINE
lock_t *lock_table_create(dict_table_t *table, ulint type_mode, trx_t *trx)
{
lock_t *lock;
ut_ad(table && trx);
ut_ad(lock_mutex_own());
ut_ad(trx_mutex_own(trx));
/* check transaction state */
check_trx_state(trx);
++table->count_by_mode[type_mode & LOCK_MODE_MASK];
if (type_mode == LOCK_AUTO_INC) {
lock = table->autoinc_lock;
table->autoinc_trx = trx;
ib_vector_push(trx->autoinc_locks, &lock);
} else if (trx->lock.table_cached < trx->lock.table_pool.size()) {
lock = trx->lock.table_pool[trx->lock.table_cached++];
} else {
lock = static_cast
(mem_heap_alloc(trx->lock.lock_heap, sizeof(*lock)));
}
lock->type_mode = ib_uint32_t(type_mode | LOCK_TABLE);
lock->trx = trx;
lock->un_member.tab_lock.table = table;
ut_ad(table->n_ref_count > 0 || !table->can_be_evicted);
UT_LIST_ADD_LAST(trx->lock.trx_locks, lock);
ut_list_append(table->locks, lock, TableLockGetNode());
if (type_mode & LOCK_WAIT) {
lock_set_lock_and_trx_wait(lock, trx);
}
lock->trx->lock.table_locks.push_back(lock);
MONITOR_INC(MONITOR_TABLELOCK_CREATED);
MONITOR_INC(MONITOR_NUM_TABLELOCK);
return(lock);
}
/** Sets the wait flag of a lock and the back pointer in trx to lock.
@param[in] lock The lock on which a transaction is waiting */
UNIV_INLINE
void lock_set_lock_and_trx_wait(lock_t *lock)
{
auto trx = lock->trx;
ut_a(trx->lock.wait_lock == NULL);
ut_ad(lock_mutex_own());
ut_ad(trx_mutex_own(trx));
trx->lock.wait_lock = lock;
trx->lock.wait_lock_type = lock_get_type_low(lock);
lock->type_mode |= LOCK_WAIT;
}After acquiring the IX lock, row_upd_step() calls row_upd_clust_step() , which eventually invokes lock_clust_rec_modify_check_and_lock() to request an X lock on the target record.
----------------
| row_upd_step() | /* request IX lock */
----------------
|
| ----------------
-> | ... |
----------------
|
| ----------------------
-> | row_upd_clust_step() |
----------------------
|
| ----------------------------------------
-> | lock_clust_rec_modify_check_and_lock() /* request X lock */ |
----------------------------------------If a user issues a LOCK TABLE statement, the system calls lock_table_other_has_incompatible() to check table‑level lock compatibility; a conflict puts the thread into a waiting state.
/** Checks if other transactions have an incompatible mode lock request in the lock queue.
@return lock or NULL */
UNIV_INLINE
const lock_t *lock_table_other_has_incompatible(const trx_t *trx, ulint wait,
const dict_table_t *table, lock_mode mode)
{
const lock_t *lock;
ut_ad(lock_mutex_own());
// Fast‑path: if no S or X locks exist, return NULL.
if ((mode == LOCK_IS || mode == LOCK_IX) &&
table->count_by_mode[LOCK_S] == 0 && table->count_by_mode[LOCK_X] == 0) {
return NULL;
}
for (lock = UT_LIST_GET_LAST(table->locks); lock != NULL;
lock = UT_LIST_GET_PREV(tab_lock.locks, lock)) {
if (lock->trx != trx && !lock_mode_compatible(lock_get_mode(lock), mode) &&
(wait || !lock_get_wait(lock))) {
return lock;
}
}
return NULL;
}Summary
MySQL intention locks do not conflict with each other; only IS is compatible with S, while intention locks are mutually exclusive with shared and exclusive locks.
IX and IS are table‑level locks and do not conflict with row‑level X or S locks.
References
https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html#innodb-intention-locks
https://zhuanlan.zhihu.com/p/412358771
政采云技术
ZCY Technology Team (Zero), based in Hangzhou, is a growth-oriented team passionate about technology and craftsmanship. With around 500 members, we are building comprehensive engineering, project management, and talent development systems. We are committed to innovation and creating a cloud service ecosystem for government and enterprise procurement. We look forward to your joining us.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.