Locking is a key feature that distinguishes database systems from file systems. Databases use locks to support concurrent access to shared resources, providing data integrity and consistency. In addition, isolation of database transactions is also achieved through locks. InnoDB has consistently outperformed other database engines in this respect. InnoDB locks table data at the row level, while MyISAM locks only at the table level.

Locks in InnoDB storage engine

InnoDB storage engine implements the following two standard row-level locks:

  • A shared Lock (S Lock) allows a transaction to read a row
  • An X Lock that allows a transaction to delete or update a row of data

If one transaction T1 has acquired the shared lock on row R, then another transaction T2 can immediately acquire the shared lock on row R because the read does not change the data and can be read concurrently. However, if other transaction T3 wants to acquire the exclusive lock on row R, it must wait for transaction T1 and T2 to release the shared lock on row R before it can continue. Because the exclusive lock is generally acquired to change data, it cannot be read or other write operations at the same time.

X S
X Are not compatible Are not compatible
S Are not compatible Compatible with

InnoDB storage engine supports multi-granularity locking, which allows transaction row-level locks and table-level locks to exist simultaneously. To support locking at different granularity, InnoDB storage engine supports a type of locking called intentional locking. Intent locking is to divide the locked object into multiple layers. Intent locking means that the transaction wants to be locked at a finer granularity.

InnoDB storage engine intent locks are table level locks. The main purpose is to reveal the type of lock that will be requested for the next row in a transaction. It supports two types of intent locks:

  • A transaction wants to acquire a shared Lock for rows in a table
  • A transaction wants to acquire an exclusive Lock for rows in a table

Note that intent locks are table level locks and do not conflict with row-level X and S locks. It only collides with table level X and S. The following table shows the compatibility between table level intent locks and table level locks.

IS IX S X
IS Compatible with Compatible with Compatible with Are not compatible
IX Compatible with Compatible with Are not compatible Are not compatible
S Compatible with Are not compatible Compatible with Are not compatible
X Are not compatible Are not compatible Are not compatible Are not compatible

When adding a table-level X LOCK to a TABLE (perform ALTER TABLE, DROP TABLE, LOCK TABLES, etc.), if there is no intended LOCK, you need to traverse the entire TABLE to determine whether there is a row LOCK to avoid conflicts. If you have an intent lock, you only need to determine whether the intent lock is compatible with the table level lock to be added. Because the presence of an intent lock indicates the presence or imminent presence of a row-level lock, the result can be obtained without traversing the entire table.

If you treat the locked object as a tree, then locking the lowest level of the object, that is, the finest-grained object, requires locking the coarse-grained object first. As shown in the figure above, if you want to lock X on record m in table 1, then you need to lock IX on table 1 and X on record M. If any of these parts cause a wait, the operation needs to wait for the coarse-grained lock to complete.

InnoDB lock status query

Users can use the INNODB_TRX, INNODB_LOCKS, and INNODB_LOCK_WAITS tables under the INFOMATION_SCHEMA library to monitor current transactions and analyze possible lock problems. INNODB_TRX is defined in the following table and consists of eight fields.

The field name instructions
trx_id InnoDB stores the unique transaction ID inside the engine
trx_state Status of the current transaction
trx_started Start time of transaction
trx_request_lock_id ID of the lock waiting for the transaction. If trX_STATE is in LOCK WAIT state, this field represents the LOCK resource ID occupied by the transaction prior to the current transaction waiting
trx_wait_started The time the transaction waits
trx_weight The weight of a transaction reflects the number of rows modified and locked by a transaction. When a deadlock occurs and a rollback is required, the smallest value is selected for rollback
trx_mysql_thread_id Thread ID, SHOW PROCESSLIST results displayed
trx_query The SQL statement in which the transaction runs
mysql> SELECT * FROM information_schema.INNODB_TRX\G;
************************************* 1.row *********************************************
trx_id:  7311F4
trx_state: LOCK WAIT
trx_started: 2010-01-04 10:49:33
trx_requested_lock_id: 7311F4:96:3:2
trx_wait_started: 2010-01-04 10:49:33
trx_weight: 2
trx_mysql_thread_id: 471719
trx_query: select * from parent lock in share mode
Copy the code

The INNODB_TRX table only shows the current InnoDB transaction, and does not directly determine some cases of locking. If you need to view locks, you also need to access the table INNODB_LOCKS, whose field composition is shown in the following table.

The field name instructions
lock_id The ID of the lock
lock_trx_id The transaction ID
lock_mode The pattern of the lock
lock_type Type of lock, table lock or row lock
lock_table The table to lock
lock_index Locked index
lock_space Id of the locked space
lock_page Number of pages locked by a transaction, or NULL in the case of a table lock
lock_rec Number of rows locked by a transaction, or NULL in the case of a table lock
lock_data The primary key value of a transaction locked record, or NULL in the case of a table lock
mysql> SELECT * FROM information_schema.INNODB_LOCKS\G;
*************************************** 1.row *************************************
lock_id: 7311F4:96:3:2
lock_trx_id: 7311F4
lock_mode: S
lock_type: RECORD
lock_table: 'mytest'.'parent'
lock_index: 'PRIMARY'
lock_space: 96
lock_page: 3
lock_rec: 2
lock_data: 1
Copy the code

After the table INNODB_LOCKS looks at how locked each table is, the user can determine how many waits are caused by it. It is not so easy to judge when the transaction volume is very high and locks and waits occur frequently. However, INNODB_LOCK_WAITS visually reflects the current transaction waits. The table INNODB_LOCK_WAITS consists of four fields, as shown in the following table.

The field name instructions
requesting_trx_id ID of the transaction requesting the lock resource
requesting_lock_id ID of the lock being applied for
blocking_trx_id ID of the blocked transaction
blocking_lock_id ID of the blocking lock
mysql> SELECT * FROM information_schema.INNODB_LOCK_WAITS\G;
*******************************************1.row************************************
requesting_trx_id: 7311F4
requesting_lock_id: 7311F4:96:3:2
blocking_trx_id: 730FEE
blocking_lock_id: 730FEE:96:3:2
Copy the code

Using the SQL statement above, you can visually see which transaction blocks another transaction and then use the transaction ID and lock ID above to go to the INNODB_TRX and INNDOB_LOCKS tables for more detailed information.

Afterword.

We will continue to learn about InnoDB’s consistent non-locked read, please stay tuned.