Lock and transaction model of MySQL_InnoDB

If you want to implement a large database application system with high throughput and high reliability; if you want to migrate from other databases to MySQL; if you want to tune MySQL performance; then it is very useful to learn and master the lock and transaction model of InnoDB.


14.7.1 Locks in InnoDB

This section describes the various types of locks in InnoDB.

Friends passing by, please help me to support with Star: https://github.com/cncounter/translation/

InnoDB's lock and transaction model - Github release

Shared locks and exclusive locks

InnoDB implements standard row-level locking, including two types: shared lock (S lock for short) and exclusive lock (X lock for short). [Exclusive lock] is sometimes also called [mutual exclusion lock].

  • Shared lock (S): Allows the transaction holding the lock to read this row.
  • Exclusive lock (X): Allows the transaction holding the lock to update or delete this row.

If a transaction T1holds ra shared lock ( S) on row , then another transaction T2requests ra lock on row , as follows:

  • If a lock T2is requested S, it can be granted immediately. The result is: T1and T2both hold the lock ron row S.
  • If a lock T2is requested X, it cannot be granted immediately and needs to be queued to wait.

If a transaction holds an exclusive lock ( ) T1on row , any request for a lock on row by another transaction cannot be granted immediately. At this point, the transaction must wait for the transaction to release the lock on the row.rXT2rT2T1r

intent lock

InnoDB supports multi-granularity locks ( multiple granularity locking), allowing row locks and table locks to coexist.
For example, LOCK TABLES ... WRITEthe statement sets an exclusive lock (lock) on the specified table X.
In order to make locks at multiple granularity levels practical, InnoDB uses [Intent Lock] ( intention lock), or [Intent Lock].
The intention lock is a table-level lock (table-level lock), indicating which type of lock the current transaction will later perform on the rows in the table (whether you want a shared lock or an exclusive lock).
There are two types of intent locks:

  • 共享意向锁( IS, intention shared lock): Indicates that the transaction intends to set shared locks on certain rows in the table.
  • 排他意向锁( IX, intention exclusive lock): Indicates that the transaction intends to set exclusive locks on certain rows in the table.

For example, a lock SELECT ... LOCK IN SHARE MODEis set ISand a lock SELECT ... FOR UPDATEis set IX.

The protocol of the intent lock is as follows:

  • Before a transaction can acquire a shared lock on a row in a table, it must first acquire ISa lock on that table, or a more restrictive lock.
  • Before a transaction can acquire an exclusive lock on a row in a table, it must first acquire IXa lock on that table.

The compatibility of intent locks with other locks is summarized as follows:

X IX S IS
X conflict conflict conflict conflict
IX conflict 兼容 conflict 兼容
S conflict conflict 兼容 兼容
IS conflict 兼容 兼容 兼容

If the requested lock is compatible with an existing lock, the requested transaction is granted immediately;
but if it conflicts with an existing lock, the lock is not granted. The requesting transaction needs to wait until the conflicting lock is released.
If a lock request conflicts with an existing lock and cannot be granted because it would cause a "deadlock", an error will be reported directly.

LOCK TABLES ... WRITEIntent locks do not block any other requests except (for example) full table requests . The main purpose of an intent lock is to indicate that a transaction is locking a row in a table, or is about to lock a row in a table.

In SHOW ENGINE INNODB STATUSthe statement and InnoDB monitoroutput transaction information, the intent lock looks like this:

TABLE LOCK table `test`.`t` trx id 10080 lock mode IX

record lock

Record lock (Record Lock), which is also a lock on the index record (index record). For example, SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;the statement prevents other transactions from inserting/updating/or deleting rows t.c1with a value of 10.

A record lock always locks an index record, even for tables with no indexes defined. For tables that do not have an index set, InnoDB will automatically create a hidden clustered index ( clustered index, also known as a clustered index), and use this index to perform record locks. See Section 14.6.2.1, “Clustered and Secondary Indexes” for details .

In SHOW ENGINE INNODB STATUSstatement and InnoDB monitoroutput transaction information, record locks look like this:

RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t`
trx id 10078 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;
 1: len 6; hex 00000000274f; asc     ''O;;
 2: len 7; hex b60000019d0110; asc        ;;

gap lock

Gap Lock:

  • is to lock the gap between index records,
  • Or lock the gap before the first index record,
  • Or lock the gap after the last index record.

For example, SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;other transactions are prevented from 15inserting this value into t.c1the column, regardless of whether the value exists in the column, because the gaps between all values ​​​​in this range are locked.

A gap may span a single index value, multiple index values, or even be empty.

Gap locks are a trade-off between performance and concurrency, and are only used in certain transaction isolation levels.

For statements that use unique indexes to lock unique rows, gap locks are not required. (If it is a unique index composed of multiple columns, and only some columns are included in the search condition, a gap lock will also be generated.)
For example, if idthe column has a unique index, the following SQL statement will only id = 100use the record lock for the row, Regardless of whether other sessions insert newlines in the preceding gap:

SELECT * FROM child WHERE id = 100;

If idthe column does not have an index, or is not a unique index, the statement will lock the previous gap together.

It is worth noting that different transactions can hold conflicting locks on the same gap. For example, transaction A can hold a shared gap lock (gap S-lock) on a gap, while transaction B can hold an exclusive gap lock (gap X-lock) on the same gap. The reason conflicting gap locks are allowed is that if a record is purged from the index, gap locks held by different transactions on that record must be merged.

Gap locks in InnoDB are "purely inhibitory", the sole purpose of which is to prevent other transactions from inserting into the gap. Gap locks can co-exist. A gap lock held by one transaction does not prevent other transactions from locking the same gap. There is also no difference between shared and exclusive gap locks. They do not conflict with each other and have the same effect.

Gap locks can be explicitly disabled. This happens when the transaction isolation level is set to READ COMMITTED, or the system variable is enabled (obsolete). innodb_locks_unsafe_for_binlogGap locks are disabled during searches and index scans, and are only used for foreign key constraint checks and duplicate key checks.

There are other effects when using READ COMMITTEDisolation-level or enabling . innodb_locks_unsafe_for_binlogMySQL WHEREwill immediately release the record locks of unmatched rows after the condition calculation is completed. For UPDATEthe statement, InnoDB performs a "semi-consistent read (semi-consistent)", returning the latest committed version to MySQL so that MySQL can determine whether the row matches the condition UPDATEof WHERE.

Pro key lock

Next-Key Lock is a combination of index record locks and previous gap locks.

The way InnoDB row-level locks are executed is that when searching or scanning indexes, shared locks or mutex locks are set on the index records encountered. Therefore, row-level locks are essentially index record locks. A temporary key lock on an index record also affects the "gap" preceding that index record. That is: Pro key lock = record lock + gap lock.
If a session (session) Rholds a shared temporary key lock or an exclusive temporary key lock on an index record, other sessions cannot Rinsert new index records in the previous gap according to the sorting direction of the index.

Suppose an index contains 10, 11, 13, and 20these 4 values; at this time, the key lock of the index can include these intervals (interval):

(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)

Among them, parentheses indicate open intervals, excluding endpoints; square brackets indicate closed intervals, including endpoints.

The last group of intervals, the range of the key lock is: greater than the current maximum value, until positive infinity (positive infinity). Infinity does not contain real index records, in fact, this temporary key lock only locks the gap after the current maximum index value.

By default, InnoDB REPEATABLE READruns under the transaction isolation level. At this isolation level, InnoDB uses key locks for searches and index scans to prevent phantom rows.

In the transaction information output by SHOW ENGINE INNODB STATUSthe statement and the temporary key lock looks like this:InnoDB monitor

RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t`
trx id 10080 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;
 1: len 6; hex 00000000274f; asc     ''O;;
 2: len 7; hex b60000019d0110; asc        ;;

insert intent lock

INSERTInsert Intention Lock (Insert Intention Lock) is a gap lock set by the operation before inserting a new row . This lock indicates the intention signal of insertion, and the execution method is: if multiple transactions want to insert records in the same gap, as long as they are not in the same position, there is no need to block or wait.
For example, there are 2 values ​​in the index as 4and 7. Suppose there are two transactions, when inserting 5and 6respectively, before acquiring the exclusive lock of the row to be inserted, each transaction uses the insertion intent lock to lock the gap between 4and 7, but they will not block each other, because the specific row is not conflicting.

The following example demonstrates that a transaction obtains an insert intent lock before obtaining an exclusive lock for inserting records. This example involves two clients: A and B.

AThe client creates a table containing two index records ( 90and 102), then starts a transaction, and places an exclusive lock on the index record with an ID greater than 100. The exclusive lock includes a gap lock before record 102:

# A客户端
mysql> CREATE TABLE child (id int(11) NOT NULL, PRIMARY KEY(id)) ENGINE=InnoDB;
mysql> INSERT INTO child (id) values (90),(102);

mysql> START TRANSACTION;
mysql> SELECT * FROM child WHERE id > 100 FOR UPDATE;
+-----+
| id  |
+-----+
| 102 |
+-----+

After client B starts the transaction, it tries to insert record 101 into the gap. While waiting to acquire an exclusive lock, the insert intent lock will be acquired first.

# B客户端
mysql> START TRANSACTION;
mysql> INSERT INTO child (id) VALUES (101);

In SHOW ENGINE INNODB STATUSthe statement and InnoDB monitoroutput transaction information, insert an intent lock like this:

RECORD LOCKS space id 31 page no 3 n bits 72 index `PRIMARY` of table `test`.`child`
trx id 8731 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 80000066; asc    f;;
 1: len 6; hex 000000002215; asc     "" ;;
 2: len 7; hex 9000000172011c; asc     r  ;;...

self-increasing lock

Self-increasing lock ( AUTO-INCLock) is a special table-level lock, AUTO_INCREMENTa table with columns, acquired by a transaction that needs to insert a record. In the simplest scenario, if a transaction is inserting values ​​into the table, other transactions must wait for him to complete before inserting new values ​​into the table, so that the rows inserted by the first transaction get continuous auto-increment primary key values .

innodb_autoinc_lock_modeOption, used to control the self-increasing algorithm. Allow us to weigh ourselves, whether to choose a predictable auto-increment sequence, or choose the high concurrency performance of the insert operation.

For more information, please refer to: Section 14.6.1.6, “AUTO_INCREMENT Handling in InnoDB” .

Predicate locks used by spatial indexes

InnoDB supports indexes on geospatial columns SPATIAL. Please refer to Section 11.4.8, “Optimizing Spatial Analysis” for details .

When locking index records, temporary key locks do not support or transaction isolation levels SPATIALwell . Because there is no absolute sort order in cubes, there is no way to tell who is the "next" key.REPEATABLE READSERIALIZABLE

In order to support tables with indexes in the transaction isolation level SPATIAL, InnoDB uses predicate locks (Predicate Lock).
SPATIALIndex records contain MBR values ​​(minimum bounding rectangle, minimum bounding rectangle), so InnoDB sets predicate locks on index records that match MBR values ​​to enforce consistent reads on the index. Other transactions cannot insert or modify rows matching the query criteria.

14.7.2 InnoDB Transaction Model

InnoDB's transaction model aims to combine the best properties of a multi-versioning database with traditional two-phase locking.
By default, InnoDB uses row-level locks and executes queries in a nonlocking consistent read manner, similar to an Oracle database.
Lock information in InnoDB is stored in a space-saving manner, so no lock escalation is required.
Supports multiple users locking each row in an InnoDB table, or any number of rows, without running out of InnoDB memory.

14.7.2.1 Transaction isolation levels

Transaction isolation is a fundamental feature of databases. Isolation is ACIDmedium I; the isolation level is a configurable item used to adjust performance, reliability, consistency, and reproducibility when multiple transactions are concurrently modified and concurrently queried ) balance between.

InnoDB supports four transaction isolation levels defined in the "SQL:1992 Standard":

  • READ UNCOMMITTED(read uncommitted),
  • READ COMMITTED(read committed),
  • REPEATABLE READ(repeatable read), InnoDB's default isolation level.
  • SERIALIZABLE(serialization).

Users can change the isolation level of the current session (control the visibility of their own session), and also change the isolation level of all subsequent connections, SET TRANSACTIONjust use the statement.
To set the server's default isolation level, use --transaction-isolationthe option on the command line or in a configuration file. For details on setting the isolation level, refer to Section 13.3.6, “SET TRANSACTION Statement” .

InnoDB uses different locking strategies for each transaction isolation level.

  • The default REPEATABLE READlevel can be used to achieve consistency, such as ACIDcritical data processing where the specification is important.
  • In scenarios such as batch reporting, you can use READ COMMITTEDor even READ UNCOMMITTEDto relax consistency constraints. At this time, accurate consistency and repeatable results are relatively less important than reducing the overhead of locks.
  • SERIALIZABLEis more restrictive than REPEATABLE READ, and is mainly used in special cases, such as XAtransactions, or scenarios such as troubleshooting and troubleshooting concurrency and deadlock problems.

The various transaction isolation levels of MySQL are described in detail below. We cover the most commonly used isolation levels first and the least used isolation levels last.

REPEATABLE READ

[Repeatable Read] is the default isolation level of InnoDB. Repeatable read isolation level, consistent read in the same transaction, using the snapshot created on the first read.
This means that if multiple normal SELECTstatements (nonlocking) are executed in the same transaction, the consistency between these SELECTstatements can be guaranteed.
For details, see 14.7.2.3 Non-locking Consistent Read .

For UPDATEstatements, DELETEstatements, and locking reads (locking read, that is, SELECT ... FOR UPDATEor SELECT ... LOCK IN SHARE MODEstatements), the lock used is determined according to whether the filter condition uses a unique index or uses a range condition:

  • For unique query conditions that use a unique index, InnoDB only locks the found index records, not the previous gaps.

  • For other query conditions, InnoDB will lock the scanned index range to prevent other sessions from inserting new values ​​​​in this range through gap locks or key locks. For information about gap locks and adjacent key locks, please refer to the previous content: 14.7.1 Locks in InnoDB .

READ COMMITTED

Under the [read committed] isolation level, even within the same transaction, each consistent read will set and read its own new snapshot. For information on consistent reads, refer to 14.7.2.3 Non-locking consistent reads .

For locking read( SELECT ... FOR UPDATEor SELECT ... LOCK IN SHARE MODE) UPDATEstatements and DELETEstatements, InnoDB only locks the index records at this time, not the gaps between them, so other transactions can insert new records next to the locked records. At this time, gap locks are only used for foreign key constraint checks and duplicate key checks.

With gap locks disabled, there is a possibility of a phantom problem, as other sessions may insert new rows in the gap. For information on phantom reads, refer to 14.7.4 Phantom Rows .

READ COMMITTEDThe isolation level only supports row-based bin-log. If used READ COMMITTEDtogether binlog_format=MIXEDwith , the server automatically switches to a row-based bin-log.

Using READ COMMITTEDalso has other effects:

For UPDATEAND DELETEstatements, InnoDB only holds locks for rows that need to be updated or deleted. After MySQL has calculated WHEREthe condition, it releases the record locks for the unmatched rows. This greatly reduces the possibility of deadlocks, but it can still happen.

For UPDATEthe statement, if a row is locked, InnoDB will perform a semi-consistent read ("semi-consistent" read), return the latest committed version to MySQL, and let MySQL determine whether the row meets the UPDATEconditions WHERE. If the row matches (indicating an update is required), MySQL reads the row again, and this time InnoDB either locks it or waits for the above lock to be released first.

See the example below, let's start with this table:

CREATE TABLE t (a INT NOT NULL, b INT) ENGINE = InnoDB;
INSERT INTO t VALUES (1,2),(2,3),(3,2),(4,3),(5,2);
COMMIT;

In this case, because there is no index, the hidden clustered index will be used as a record lock during query and index scan.

Suppose a session A performs an update operation through the following statement:

# 会话A
START TRANSACTION;
UPDATE t SET b = 5 WHERE b = 3;

At this time, session A has not committed the transaction, and then the second session B performs the update operation through the following statement:

# 会话B
UPDATE t SET b = 4 WHERE b = 2;

When InnoDB executes UPDATE, it will first set an exclusive lock (exclusive lock) for each row it reads, and then determine whether it needs to be modified. If InnoDB does not require modification, the lock on the row is released. Otherwise, InnoDB will hold the row lock until the end of the transaction. This affects the processing of transactions as shown below.

Assuming the default REPEATABLE READisolation level is used, the first one UPDATEwill first set an X lock on each row it scans and will not release any of them:

# REPEATABLE READ 隔离级别; 会话A
x-lock(1,2); retain x-lock
x-lock(2,3); update(2,3) to (2,5); retain x-lock
x-lock(3,2); retain x-lock
x-lock(4,3); update(4,3) to (4,5); retain x-lock
x-lock(5,2); retain x-lock

Because the first UPDATEholds locks on all rows, the second UPDATEblocks immediately trying to acquire any of the locks, and UPDATEcannot proceed until after the first commits or rolls back:

# REPEATABLE READ 隔离级别; 会话B
x-lock(1,2); block and wait for first UPDATE to commit or roll back

If READ COMMITTEDisolation levels are used, the first UPDATEacquires X locks on each row read by the scan, and then releases X locks on rows that do not need to be modified:

# READ COMMITTED 隔离级别; 会话A
x-lock(1,2); unlock(1,2)
x-lock(2,3); update(2,3) to (2,5); retain x-lock
x-lock(3,2); unlock(3,2)
x-lock(4,3); update(4,3) to (4,5); retain x-lock
x-lock(5,2); unlock(5,2)

For the second UPDATE, InnoDB will perform a semi-consistent read ("semi-consistent" read), return the latest committed version to MySQL, and let MySQL determine whether the row meets the conditions UPDATEof WHERE:

# READ COMMITTED 隔离级别; 会话B
x-lock(1,2); update(1,2) to (1,4); retain x-lock
x-lock(2,3); unlock(2,3)
x-lock(3,2); update(3,2) to (3,4); retain x-lock
x-lock(4,3); unlock(4,3)
x-lock(5,2); update(5,2) to (5,4); retain x-lock

However, if WHEREan indexed column is included in the condition, and InnoDB uses the index, only the indexed column is considered when acquiring and retaining the record lock.
In the following example, the first acquires and holds an X lock UPDATEon all b = 2rows.
The second UPDATEwill block when trying to acquire an X lock on the same record, because the index defined on the b column is also used.

CREATE TABLE t (a INT NOT NULL, b INT, c INT, INDEX (b)) ENGINE = InnoDB;
INSERT INTO t VALUES (1,2,3),(2,2,4);
COMMIT;

# 会话 A
START TRANSACTION;
UPDATE t SET b = 3 WHERE b = 2 AND c = 3;

# 会话 B
UPDATE t SET b = 4 WHERE b = 2 AND c = 4;

Using READ COMMITTEDthe isolation level innodb_locks_unsafe_for_binlogis basically the same as setting the option [this option is obsolete], but there are some differences:

  • innodb_locks_unsafe_for_binlogIs a global setting that affects all sessions, and the isolation level can be set globally for all sessions or individually for each session.
  • innodb_locks_unsafe_for_binlogCan only be set at server startup, while the isolation level can be set at startup or changed during runtime.

Therefore, READ COMMITTEDit innodb_locks_unsafe_for_binlogis more convenient and flexible than .

READ UNCOMMITTED

[Read Uncommitted] Under the isolation level, SELECTthe statement is executed in a non-locking manner, but an earlier version of a row may be used. Therefore, when using this isolation level, the consistency of reading cannot be guaranteed. This phenomenon is called dirty read (dirty read). Otherwise, this isolation level is similar to READ COMMITTED.

SERIALIZABLE

[Serialization] This isolation level is similar REPEATABLE READ, if disabled autocommit, InnoDB will implicitly SELECTconvert all ordinary statements to SELECT ... LOCK IN SHARE MODE.

If autocommit ( autocommit) is enabled, then SELECTis alone in a transaction. Therefore considered read-only, serialization can be achieved without blocking other transactions if performed as a consistent non-locking read.

SELECTDisable if you want to force ordinary statements to block and wait while other transactions modify the selected rows autocommit.

14.7.2.2 autocommit, commit and rollback

In InnoDB, all user activities are performed within transactions. If autocommit mode ( autocommit) is enabled, each SQL statement forms a transaction by itself.
Each new session connection in MySQL is automatically committed by default. If a SQL statement does not generate an error, it will be automatically committed after its execution.
If a certain SQL statement returns an error, it is decided whether to commit or rollback according to the specific error. For details, refer to Section 14.22.4, “InnoDB Error Handling” .

Sessions with autocommit enabled can also execute multi-statement transactions, beginning with an explicit START TRANSACTIONOR BEGINstatement and ending with an COMMITOR ROLLBACKstatement. For details, please refer to Section 13.3.1, “START TRANSACTION, COMMIT, and ROLLBACK Statements” .

If SET autocommit = 0autocommit mode is disabled via , the session will always have an open transaction. COMMITor ROLLBACKstatement will end the current transaction and start a new one.

For a session with autocommit mode disabled, MySQL will perform a transaction rollback if the connection is disconnected or the session ends without explicitly committing the transaction.

Some statements end the transaction implicitly, as if a COMMITstatement was automatically added before such a statement. For details, refer to Section 13.3.3, “Statements That Cause an Implicit Commit” .

COMMITIndicates that the changes made by the current transaction need to be made permanent and visible to other sessions. The statement ROLLBACKcancels the modification in the current transaction. COMMITand ROLLBACKboth release all InnoDB locks set during the current transaction.

DML operation groups and transactions

The client connection of the MySQL database, the automatic submission mode is enabled by default, and each SQL statement will be automatically submitted after execution. Users who have used other database systems may not be used to this mode of operation, because their more common way is to execute a series of DML statements, and then submit or roll back together.

To use multi-statement transactions:

  • The auto-commit mode can be turned off by the statement, and end the transaction with or SET autocommit = 0at the appropriate time .COMMITROLLBACK
  • In auto-commit state, you can START TRANSACTIONstart a transaction with and end with COMMITor ROLLBACK.

The following examples demonstrate two transactions. Among them, the first transaction is committed; the second transaction is rolled back.

# 连接数据库
shell> mysql test

Executed SQL:

-- 建表
mysql> CREATE TABLE customer (a INT, b CHAR (20), INDEX (a));
Query OK, 0 rows affected (0.00 sec)
mysql> -- 在 autocommit 开启的状态下, 启动事务:
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO customer VALUES (10, 'Heikki');
Query OK, 1 row affected (0.00 sec)
mysql> COMMIT;
Query OK, 0 rows affected (0.00 sec)
mysql> -- 关闭 autocommit 状态; 启动事务:
mysql> SET autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO customer VALUES (15, 'John');
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO customer VALUES (20, 'Paul');
Query OK, 1 row affected (0.00 sec)
mysql> DELETE FROM customer WHERE b = 'Heikki';
Query OK, 1 row affected (0.00 sec)
mysql> -- 回滚事务:
mysql> ROLLBACK;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT * FROM customer;
+------+--------+
| a    | b      |
+------+--------+
|   10 | Heikki |
+------+--------+
1 row in set (0.00 sec)
mysql>
Transactions in Client Programming Languages

In the MySQL client API, such as PHP, Perl DBI, JDBC, ODBC, or the standard C calling interface, transaction control statements (such as ) can be sent COMMITto the MySQL server as strings, just like ordinary SQL statements ( SELECTand INSERT). Certain APIs also separately provide functions/methods for committing and rolling back transactions.

14.7.2.3 Nonlocking Consistent Reads

Consistent read means that InnoDB presents a database snapshot at a certain point in time for a query through multi-version technology. The query can see the changes of all committed transactions before this point in time, but cannot see the changes made by new transactions or uncommitted transactions after this point in time.
The exception is that queries can see changes made by previously executed statements within the same transaction. This exception causes some exceptions: if you update some rows in the table, you will SELECTsee the latest version of the row since it was updated, but other rows may still see the old version. If other sessions also updated this table, this exception means that we may see some kind of state that does not exist.

If it is the default REPEATABLE READisolation level, all consistent reads in the same transaction will read the snapshot created when the transaction was first read. You can commit the current transaction, and then execute a new query statement to get the latest data snapshot.

When using READ COMMITTEDthe isolation level, every consistent read in the transaction will set and read its own new snapshot.

Under READ COMMITTEDthe and REPEATABLE READisolation levels, consistent read is SELECTthe default mode for InnoDB to process statements.
Consistent reads do not set any locks on the tables being read, so other sessions are free to perform modifications to those tables while reading.

Using the default REPEATABLE READisolation level, when performing ordinary SELECTconsistent reads, InnoDB will specify a point in time for the current transaction, and determine which data can be seen by all queries in the transaction based on this point in time. If after this given point in time, another transaction deletes a row of data and commits, then the current transaction does not see that this row has been deleted. Insert and update operations are handled in the same way.

hint

The snapshot of the database state is applicable to the statement in the transaction SELECT, but not necessarily applicable to the DML statement (addition, deletion, modification). If some rows are inserted or modified, and the transaction is going to be committed later; then an OR statement executed
in another REPEATABLE READlevel of concurrent transaction may affect these just-committed rows, although that session may not be able to See these lines when reading. When a transaction updates or deletes rows committed by another transaction, those changes become visible to the current transaction.DELETEUPDATE

For example the following scenario:

# 场景1
SELECT COUNT(c1) FROM t1 WHERE c1 = 'xyz';
-- Returns 0: 没有匹配的行. ::: 查不到
DELETE FROM t1 WHERE c1 = 'xyz';
-- 删除了被另一个事务提交的某些行... ::: 但确实会删除

# 场景2
SELECT COUNT(c2) FROM t1 WHERE c2 = 'abc';
-- Returns 0: 没有匹配的行.::: 查不到
UPDATE t1 SET c2 = 'cba' WHERE c2 = 'abc';
-- Affects 10 rows: 比如, 另一个事务 txn2 刚刚提交了 10 行 'abc' 值. ::: 但确实更新了...
SELECT COUNT(c2) FROM t1 WHERE c2 = 'cba';
-- Returns 10: 本事务 txn1 这时候可以看到刚刚更新的行.

SELECTA transaction can be committed, and then another OR statement executed START TRANSACTION WITH CONSISTENT SNAPSHOTto advance the point in time.

This is called multi-versioned concurrency control.

In the following example, only after Session B commits the transaction and Session A itself commits the transaction, A can see the new row of B, because at this time, the time point of A will be newer than the commit of B.

             Session A              Session B

           SET autocommit=0;      SET autocommit=0;
时间序
|          SELECT * FROM t;
|          empty set
|                                 INSERT INTO t VALUES (1, 2);
|
v          SELECT * FROM t;
           empty set
                                  COMMIT;

           SELECT * FROM t;
           empty set

           COMMIT;

           SELECT * FROM t;
           ---------------------
           |    1    |    2    |
           ---------------------

If you want to view the latest state of the database (freshest), you can use READ COMMITTEDthe isolation level, or use locking read:

SELECT * FROM t LOCK IN SHARE MODE;

When using READ COMMITTEDthe isolation level, each consistent read within a transaction sets and reads its own new snapshot.
With LOCK IN SHARE MODEa SELECT statement, a locking read: SELECTmay block until the end of the transaction containing the latest row. Please refer to 14.7.2.4 Locking Read .

Consistent read does not support certain DDL statements:

  • Consistent read cannot DROP TABLEtake effect at time, because MySQL cannot use the deleted table, and InnoDB has destroyed this table.
  • Consistent read cannot ALTER TABLEtake effect on the operation, because this operation will create a temporary copy of the original table, and delete the original table after the temporary copy is built. When a consistent read is re-performed within a transaction, the data rows in the new table are not visible because these rows did not exist when the transaction took the snapshot. In this case, an error message will be returned: ER_TABLE_DEF_CHANGED, “Table definition has changed, please retry transaction”.

The behavior differs for various queries that do not specify FOR UPDATEor , such as , , and :LOCK IN SHARE MODEINSERT INTO ... SELECTUPDATE ... (SELECT)CREATE TABLE ... SELECT

  • By default, InnoDB uses stronger locks in these statements, while SELECTsome behave like READ COMMITTEDeach consistent read sets and reads its own new snapshot, even within the same transaction.
  • To perform non-locking reads in this case, enable innodb_locks_unsafe_for_binlogthe option and set the transaction isolation level to READ UNCOMMITTED, READ COMMITTED, or REPEATABLE READ, to avoid locking while reading data rows.

14.7.2.4 LOCKED READ

If in a transaction, the data is queried first, and then the related data is inserted or updated, the conventional SELECTstatement does not provide sufficient protection.
Other transactions can update or delete the rows we just looked up.
InnoDB supports two types of locking reads (Locking Read), which can provide additional security:

  • SELECT ... LOCK IN SHARE MODE

    Set shared locks on all rows read. Other sessions can read these rows, but none can be modified until the end of the current transaction. When querying, if some rows are modified by other transactions that have not yet been committed, the current query will be blocked until the end of those transactions, and then the latest values ​​will be used.

  • SELECT ... FOR UPDATE

    For index records found by the search, the data row and all associated index entries are locked as if the statement had been executed on those rows UPDATE. Other transactions will be blocked, including modification, use SELECT ... LOCK IN SHARE MODEto read, and even read operations at certain isolation levels. Consistent reads ignore any locks set on records in the read view. (Because the old version of the data row cannot be locked, it is reconstructed through the memory copy of the record plus undo logs).

This type of clause is very useful when dealing with tree-structured or graph-structured data, whether it is a single table or multiple tables. We can traverse it first, and then modify some of the records.

When the transaction is committed or rolled back, the locks set by LOCK IN SHARE MODEand will be released.FOR UPDATE

Notice

Locking reads are only possible if autocommit is disabled. (generally use START TRANSACTIONthe statement or setting autocommit=0to disable autocommit)

When executing a nested statement query, the locked read in the outer query will not lock the data rows of the subquery, unless the subquery also specifies a locked read. For example, the following statement does not lock t2rows in the table.

SELECT * FROM t1 WHERE c1 = (SELECT c1 FROM t2) FOR UPDATE;

To lock t2rows in the table, a locking read is also required in the subquery:

SELECT * FROM t1 WHERE c1 = (SELECT c1 FROM t2 FOR UPDATE) FOR UPDATE;
Locking Read Usage Example

Example 1:

Suppose you need to childinsert a new row into the table, but make sure that parentthere is a corresponding record in the table. In application code, referential integrity can be ensured by the following sequence of operations.

First, the table is queried using a consistent read parentto check if the parent record exists. Does this guarantee safe insertion of data into childthe table? No, because other sessions may delete the row of data in the table between SELECTand without our knowledge .INSERTparent

To avoid this potential bug, it can be LOCK IN SHARE MODEexecuted by SELECT:

SELECT * FROM parent WHERE NAME = 'Jones' LOCK IN SHARE MODE;

After LOCK IN SHARE MODEthe query returns the parent record 'Jones', it is safe to add the record to childthe table and then commit the transaction. If other transactions try to acquire parentthe exclusive lock on the corresponding data row in the table, they will be blocked and need to wait for us to complete the operation before continuing, that is, we need to wait for the data in the two tables to be in a consistent state.

Example 2:

For another example, CHILD_CODESthere is an integer counter_field field in the table, which is used to childassign a unique ID to each record in the table.
We cannot use consistent read or shared mode to read the current counter value, because then multiple clients will see the same value, and if two transactions try to add data with the same id, a duplicate key error will occur (duplicate-key error).

In this scenario, LOCK IN SHARE MODEit is not a good solution. If multiple users read the counter at the same time, at least one of them will be stuck in a deadlock state when updating the counter.

To read a counter and increment it, FOR UPDATEa locked read of the counter is performed before incrementing the counter. For example:

SELECT counter_field FROM child_codes FOR UPDATE;
UPDATE child_codes SET counter_field = counter_field + 1;

SELECT ... FOR UPDATEThe latest available data is read and an exclusive lock is set on each row read. Therefore, it sets UPDATEthe same lock as the statement sets.

This example is just to demonstrate SELECT ... FOR UPDATEhow works. In MySQL, the task of generating a unique identifier can actually be completed with one query:

UPDATE child_codes SET counter_field = LAST_INSERT_ID(counter_field + 1);
SELECT LAST_INSERT_ID();

SELECTThe statement will only query id information based on the current session. And without reading any of the tables.

14.7.3 Locks set by different SQL statements in InnoDB

Locking reads, UPDATEand DELETEstatements, typically set record locks on scanned index records.
Regardless of whether the SQL statement contains WHEREconditions or not, it will be locked. Because InnoDB does not record the exact WHERE condition, it only knows which index ranges it has scanned. Generally, a temporary key lock ( next-key lock) is used, so that other transactions can be blocked (block) from inserting new rows into the previous gap (gap). Of course, it is possible to explicitly disable gap locks ( gap locking), and then adjacent key locks will not be used. See the previous section 14.7.1 Locks in InnoDB for more information .
The transaction isolation level also affects the locks used; see Section 14.7.2.1, “Transaction Isolation Levels” .

If a secondary index is used in the search and an exclusive lock is to be set, InnoDB will also retrieve the corresponding clustered index record and lock it.

If no index is found, MySQL will perform a full table scan (scan the entire table), and each row in the table will be locked, thereby blocking all insertions into the table by other sessions. So it is very important to create a good index, so that you don't need to scan a lot of unnecessary rows when executing a query.

The locks set by InnoDB for various SQL statements are as follows:

  • SELECT ... FROM, consistent read, the read is a snapshot, generally not locked; only the transaction isolation level is SERIALIZABLElocked. For SERIALIZABLElevel, a shared temporary key lock is set on the searched index records. However, for SQL statements that use unique indexes to query unique rows, only one index record lock needs to be set.

  • For SELECT ... FOR UPDATEor SELECT ... LOCK IN SHARE MODE, the scanned rows will be locked, but the rows that are not in the result set will generally be released immediately (such as not meeting the WHEREfilter conditions in the clause). But in some cases the row lock may not be released immediately, because during the execution of the query, the connection between the result row and its original data source may be lost. For example, in UNIONa statement, the rows scanned (and locked) in the table may be inserted into a temporary table before calculating whether they meet the result set. In this case, the relationship between the rows in the temporary table and the rows in the original table is lost, so the row locks are not released until after the query has finished executing.

  • SELECT ... LOCK IN SHARE MODESets a shared temporary key lock on index records encountered by the search. But if you are retrieving unique rows through a unique index, you only need to lock a single index record.

  • SELECT ... FOR UPDATESets an exclusive keylock on each record found in the search. The exceptions are statements that search for unique rows via a unique index, requiring only one index record to be locked.

    For the index records encountered by the search, SELECT ... FOR UPDATEit will block other session execution SELECT ... LOCK IN SHARE MODE, and block certain isolation level transactions from reading data. A consistent read will ignore any locks set on the record in the read view.

  • UPDATE ... WHERE ...Sets an exclusive keylock on each record found in the search. The exceptions are statements that search for unique rows via a unique index, requiring only one index record to be locked.

  • When UPDATEa clustered index record is modified, an implicit lock is taken on the affected secondary index record. When performing a duplicate check before inserting a new secondary index record, and while inserting a new secondary index record, the UPDATEoperation also sets a shared lock on the affected secondary index record.

  • DELETE FROM ... WHERE ...Sets an exclusive keylock on each record found in the search. The exceptions are statements that search for unique rows via a unique index, requiring only one index record to be locked.

  • INSERTSet an exclusive lock on the inserted row. Index record locks, rather than temporary key locks (that is, no gap locks), do not prevent other sessions from inserting new rows into preceding gaps.

    Before inserting a new row, an insert intention gap lock is set. Signals an insert intent, if multiple transactions want to insert a new record in the same index gap, as long as it is not the same slot, there is no need to wait. 4Assume that the index records have values ​​of and , respectively 7. If there are two transactions that want to insert the two values ​​of 5 and 6 respectively, before obtaining the exclusive lock, each transaction will first set the insertion intention lock to lock the gap between 4 and 7, but there is no blocking between them, Because the lines do not conflict.

    If a duplicate-key error occurs, a shared lock is set on the duplicate index record. This shared lock can cause a deadlock if another session has acquired the exclusive lock and multiple sessions try to insert the same row. This happens when joining another session deletes the row. For example an InnoDB table t1has the following structure:

    CREATE TABLE t1 (i INT, PRIMARY KEY (i)) ENGINE = InnoDB;
    

    Suppose there are three sessions performing the following operations in sequence:

    Session 1:

    START TRANSACTION;
    INSERT INTO t1 VALUES(1);
    

    Session 2:

    START TRANSACTION;
    INSERT INTO t1 VALUES(1);
    

    Session 3:

    START TRANSACTION;
    INSERT INTO t1 VALUES(1);
    

    Session 1:

    ROLLBACK;
    

    The first operation of session 1 acquires an exclusive lock on the row. Both session 2 and session 3 operations generate duplicate key errors and request a shared lock on the row. When session 1 rolls back, the exclusive lock on the row is released, and the requests queued for the shared lock by sessions 2 and 3 are granted. At this time, session 2 and session 3 will have a deadlock: because the other party holds a shared lock, neither session can obtain the exclusive lock of the row.

    1A similar situation occurs if the table contains rows with key values ​​and the three sessions are executed in the following order:

    Session 1:

    START TRANSACTION;
    DELETE FROM t1 WHERE i = 1;
    

    Session 2:

    START TRANSACTION;
    INSERT INTO t1 VALUES(1);
    

    Session 3:

    START TRANSACTION;
    INSERT INTO t1 VALUES(1);
    

    Session 1:

    COMMIT;
    

    The first operation of session 1 acquires an exclusive lock on the row. Both session 2 and session 3 operations cause a duplicate key error, and then request a shared lock for the row. After session 1 commits, it releases the exclusive lock on the row and grants session 2 and session 3 shared locks on queued requests. At this time, session 2 and session 3 will have a deadlock: because the other party holds a shared lock, neither session can obtain the exclusive lock of the row.

  • INSERT ... ON DUPLICATE KEY UPDATEUnlike simple INSERTstatements, when a duplicate key error occurs, an exclusive rather than a shared lock is set on the row being updated. Exclusive index record locks are used for duplicate primary key values. Set an exclusive temporary key lock on duplicate unique key values.

  • If there are no conflicts on the unique key, then REPLACEis handled the INSERTsame as . If there is a conflict, an exclusive keylock is set on the line to be replaced.

  • INSERT INTO T SELECT ... FROM S WHERE ...TSet an exclusive record lock (without gap locks) on each row inserted . If the transaction isolation level is READ COMMITTED, or the transaction isolation level is not SERIALIZABLEbut enabled innodb_locks_unsafe_for_binlog, InnoDB searches the S table for consistent reads (no locks). In other cases, InnoDB will Sset a shared temporary key lock on the row. Why must the lock be set? The reason is that when using the statement-based bin-log for rollforward recovery, each must be executed in exactly the same way as the original operation. SQL statements.

    CREATE TABLE ... SELECT ...Use a shared adjacent key lock, or do it with a consistent read SELECT, similarly INSERT ... SELECT.

    When constructed SELECTwith REPLACE INTO t SELECT ... FROM s WHERE ...or UPDATE t ... WHERE col IN (SELECT ... FROM s ...), InnoDB ssets shared temporary key locks on the rows of the table.

  • When initializing data on a table with a specified AUTO_INCREMENTattribute column, InnoDB places an exclusive lock on the end of the associated index.

    In innodb_autoinc_lock_mode=0the case of , InnoDB uses a special table locking mode AUTO-INCin which access to auto-increment counters requires the lock to be acquired and held until the end of the current SQL statement (not the entire transaction). While a table lock is held AUTO-INC, other clients cannot insert into the table.
    And for innodb_autoinc_lock_mode=1bulk inserts of , the same behavior occurs.
    Table-level locks AUTO-INCand innodb_autoinc_lock_mode=2cannot be used together. See Section 14.6.1.6, “AUTO_INCREMENT Handling in InnoDB” for more information .

    InnoDB does not set any locks when retrieving previously initialized AUTO_INCREMENTcolumn values.

  • If constraints are defined FOREIGN KEY, then all inserts, updates, and deletes need to check the constraints and set shared record locks. InnoDB also sets the lock in the event of a constraint check failure.

  • LOCK TABLESSet a table lock, but set a higher-level MySQL lock than InnoDB. If it is the default value innodb_table_locks = 1, and autocommit = 0, InnoDB can perceive table locks, MySQL level will also perceive row-level locks.

    Otherwise, InnoDB's automatic deadlock detection cannot detect deadlocks involving such table locks. Similarly, in this case, the upper-layer MySQL does not perceive row-level locks, and may set table locks on tables that hold row-level locks for other sessions. But this does not affect transactional integrity, as described in 14.7.5.2 Deadlock Detection .

  • If default innodb_table_locks=1, LOCK TABLEStwo locks are acquired on each table. In addition to MySQL-level table locks, an InnoDB table lock is also acquired. Versions prior to MySQL 4.1.2 do not acquire InnoDB table locks; the behavior of older versions can be innodb_table_locks=0simulated by specifying . If the InnoDB table lock is not obtained, if the records in the table are locked by other transactions, LOCK TABLESthe execution will be successful.

    In MySQL 5.7, does not work innodb_table_locks=0for LOCK TABLES ... WRITEtables explicitly locked. But table locks in read mode, and read/write locks triggered implicitly by LOCK TABLES ... READ, (such as triggers) work.LOCK TABLES ... WRITE

  • When a transaction commits or aborts, all InnoDB locks it holds are released. So in autocommit=1the mode, LOCK TABLESit doesn't make sense to execute on the InnoDB table, because the acquired InnoDB table lock will be released immediately.

  • Other tables cannot be locked during transaction execution because an LOCK TABLESimplicit COMMIT' 和UNLOCK TABLES` is performed.

14.7.4 Phantom rows

In the same transaction, if the same query statement is executed at different time points, if different result sets are obtained, this phenomenon is called phantom reading (phantom problem).
Example: The same SELECTstatement is executed twice, but the second query returns one more row than the first query, then this row is a "phantom row (Phantom Row)".

Assuming that the column childof the table idhas an index, query all 100rows with an id value greater than and lock them for later update:

SELECT * FROM child WHERE id > 100 FOR UPDATE;

100This query scans the index starting from the first record with an id value greater than . If there are two rows of data in the table whose id values 90​​are sum 102, within the scan range, other sessions may insert a new row with an id value in the table if there is no lock ( 90to ) the gap. If the same statement is executed again in the same transaction , a new row with id will be seen in the result returned by the query , which is the phantom row​​. If such a row is considered a data item, then this new phantom data will violate the transaction isolation principle: the data that has been read cannot be modified during the execution of the transaction.102101SELECT101

In order to prevent phantom reading, InnoDB uses an algorithm called "next-key locking", which combines index-row locking and gap locking. The way InnoDB row-level locks are executed is that when searching or scanning an index, a shared lock or an exclusive lock is set on the index records encountered. Therefore, row-level locks are essentially index-record locks.
In addition, a temporary key lock on an index record also affects the "gap" preceding that index record. That is, the immediate key lock, is the index record lock, plus the gap lock before the index record. If a session Rsets a temporary key lock (shared lock or exclusive lock) on an index record, other sessions cannot Rinsert new index records in the immediately preceding gap, according to the sort order of the index.

When InnoDB scans an index, it may also lock the gap after the last record in the index. In the previous example, we demonstrated this situation: In order to prevent the insertion idof records greater than 100 in the table, the lock set by InnoDB includes idthe gap lock after 102.

We can also use temporary key locks to implement uniqueness checks: when reading data in shared mode, if no duplicates are seen in the row to be inserted, the row can be safely inserted because the temporary key lock set when reading , which prevents other sessions from inserting duplicates later. In fact, temporary key locks can "lock" content that does not exist in the table.

For how to disable gap locks, please refer to 14.7.1 Locks in InnoDB . But it may cause phantom reading problems, because after disabling the gap lock, other sessions may insert new rows in the gap.

14.7.5 Deadlocks in InnoDB

Deadlock refers to the situation that multiple transactions cannot continue to execute because they hold the locks that each other needs. Because they are all waiting for resources, no one will release the lock they hold.

For example, through statements such as UPDATEor SELECT ... FOR UPDATE, when locking multiple tables or multiple rows, if they are executed in reverse order, deadlock may occur.
If the SQL statement needs to lock the index range or lock the gap, due to timing problems, when each transaction only acquires a part of the lock, deadlock will also occur.
For an example of a deadlock, refer to the following subsection 14.7.5.1 InnoDB deadlock example .

To reduce the possibility of deadlocks:

  • Please use transactions, try not to use LOCK TABLESstatements.
  • Make the transaction that executes insert or update small enough so that the transaction opening time will not be too long;
  • When different transactions update multiple tables or a large range of rows, keep each transaction in the same order of operations;
  • Create indexes on the columns used in SELECT ... FOR UPDATEthe and statements.UPDATE ... WHERE

The possibility of deadlock is not affected by the isolation level, because the isolation level only changes the behavior of the read operation, while the deadlock occurs due to the write operation.
For how to avoid deadlocks and how to recover from deadlock conditions, please refer to 14.7.5.3 Reducing deadlocks and deadlock handling methods .

When deadlock detection is enabled (default enabled), InnoDB will automatically detect where a deadlock occurs and automatically roll back one of the transactions (called the victim, victim). If innodb_deadlock_detectautomatic deadlock detection is disabled with the option, InnoDB can only innodb_lock_wait_timeoutrollback transactions by the specified timeout period. Even if the application logic is completely correct, situations such as transaction retries need to be handled. We can use SHOW ENGINE INNODB STATUSthe command to view the most recent deadlocked transactions. If deadlocks occur frequently, transaction structure adjustments are required, or error handling is required, you can mysqldspecify the option for the startup parameter innodb_print_all_deadlocksto print all deadlock-related information to the error log.

For information on how to perform automatic deadlock detection and handling, refer to 14.7.5.2 Deadlock Detection .

14.7.5.1 InnoDB deadlock example

The following example demonstrates what happens when a deadlock occurs. There are two clients involved in this example: A and B.

First, client A creates a table, inserts a piece of data, and then starts a transaction. In the transaction, client A obtains Sthe lock of the row by querying in shared mode:

# 客户端A
mysql> CREATE TABLE t (i INT) ENGINE = InnoDB;
Query OK, 0 rows affected (1.07 sec)

mysql> INSERT INTO t (i) VALUES(1);
Query OK, 1 row affected (0.09 sec)

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM t WHERE i = 1 LOCK IN SHARE MODE;
+------+
| i    |
+------+
|    1 |
+------+

Next, client B starts a transaction and tries to delete this row from the table:

# 客户端B
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

mysql> DELETE FROM t WHERE i = 1;

A delete operation requires the acquisition of Xa lock. However, because the lock is incompatible with the lock Xheld by client A , the authorization cannot be obtained immediately, and the lock request that needs to be added to the row is queued for queuing, so client B is blocked.S

Client A then also tries to delete the row from the table:

# 客户端A
mysql> DELETE FROM t WHERE i = 1;
ERROR 1213 (40001): Deadlock found when trying to get lock;
try restarting transaction

It can be seen that a deadlock has occurred here, because client A needs to acquire Xthe lock before deleting the row. But since client B requested Xthe lock and is waiting for client A to release Sthe lock, client A's Xlock request cannot be granted.
Moreover, it is the lock requested by client B first X, so the lock held by A Scannot be upgraded to Xa lock. The result is that InnoDB causes one of the clients to generate an error and release the lock it holds. The error message returned by the client is similar to this:

ERROR 1213 (40001): Deadlock found when trying to get lock;
try restarting transaction

Then, another client's lock request is granted and executed, deleting the row from the table.

14.7.5.2 Deadlock Detection

InnoDB will enable deadlock detection ( deadlock detection) by default, which can automatically detect the "deadlock" generated by the transaction, and automatically roll back one or more transactions to break the deadlock state.
InnoDB will try to choose to roll back smaller transactions. As for the size of the transaction, it depends on the number of rows that have been inserted, updated, and deleted.

By default, innodb_table_locks = 1if autocommit = 0InnoDB perceives table locks, the upper layer MySQL can also perceive row-level locks. Otherwise, InnoDB cannot automatically detect deadlocks if table locks set by MySQL statements or locks set by other storage engines are
involved . This kind of situation can only be resolved by the timeout period set by LOCK TABLESthe system variable .innodb_lock_wait_timeout

If the InnoDB Monitor output LATEST DETECTED DEADLOCKsection contains this information: "TOO DEEP OR LONG SEARCH IN THE LOCK TABLE WAITS-FOR GRAPH, WE WILL ROLL BACK FOLLOWING TRANSACTION," it means that the number of transactions in the waiting list has reached 200 limits. A wait list of more than 200 transactions will be considered a deadlock, and transactions attempting to check the wait list will be rolled back. The same error may also occur if the transactions in the wait list hold more than 1 million locks, and there are lock threads to check.

For techniques on how to perform database operations to avoid deadlocks, please refer to the previous 14.7.5 Deadlocks in InnoDB .

Disable deadlock detection

innodb_deadlock_detectDeadlock detection can be disabled using the option.

In a high-concurrency system, when multiple threads wait for the same lock, deadlock detection can lead to slow response. Sometimes innodb_lock_wait_timeoutit may be more efficient to rely on specified timeouts for transaction rollbacks than automatic deadlock detection.

14.7.5.3 Reducing Deadlock Probability, Handling Deadlock Errors

Based on the concepts introduced in the previous section 14.7.5.2 Deadlock Detection . Here's how to organize our database operations to minimize deadlocks, and how to follow up on deadlock errors in the application.

Deadlock is a typical problem in transactional relational databases, but deadlock is not terrible unless it occurs frequently and some transactions cannot be executed.
Usually, when a transaction is rolled back due to a deadlock error, our application needs to re-execute the transaction [some businesses can be manually triggered].

InnoDB uses automatic row-level locking. Even in a transaction that inserts or deletes a single row of data, a deadlock can occur. Because these operations are not really "atomic" operations; when inserting or deleting rows corresponding to (one or more) index records, the database will be automatically locked.

The technical means described below can be used to deal with deadlocks and reduce the possibility of deadlocks:

  • Use SHOW ENGINE INNODB STATUSthe command at any time to see the cause of a recent deadlock. Can help us tune the application to avoid deadlocks.

  • If deadlock warnings occur frequently, enable innodb_print_all_deadlocksthe configure option to collect more DEBUG information. Output information about each deadlock in the MySQL "error log". After debugging, remember to disable this option.

  • Be sure to re-execute the transaction if it fails due to a deadlock. Deadlock is not terrible, generally just try again.

  • Keep transactions small and allow transactions to last for a shorter period of time to reduce the chance of conflicts.

  • After making database changes, commit transactions in a timely manner to reduce the possibility of conflicts. In particular, do not leave long interactive mysqlsessions open without committing transactions.

  • If using locking reads ( SELECT ... FOR UPDATEor SELECT ... LOCK IN SHARE MODE), try switching to a lower isolation level, eg READ COMMITTED.

  • If you modify multiple tables, or multiple sets of data, in one transaction, perform these operations in a consistent order each time. This way transactions can form well-defined queues and will not deadlock. For example, encapsulate database operations into specific functions or service methods, or call save services instead of writing scattered INSERT, UPDATE, and DELETE statements in multiple places.

  • Add indexes appropriately. In this way, our SQL query only needs to scan a few index records, and there are fewer locked records. You can use EXPLAIN SELECTto determine which index the MySQL server will use by default to execute SQL queries.

  • Lock less. If the data can be read from the old version of the snapshot, there is no need to use the FOR UPDATEor LOCK IN SHARE MODEclause. If deadlocks often occur, READ COMMITTEDit is better to use the isolation level, because every consistent read in the same transaction is read from its own new snapshot.

  • If nothing else, use table-level locks to serialize our transactions. In a storage engine that supports transactions such as InnoDB, LOCK TABLESthe correct method to use is: first pass SET autocommit = 0(instead of START TRANSACTION) open the transaction, then execute in the transaction LOCK TABLES, and then call it after the transaction is explicitly committed UNLOCK TABLES. For example, if you need to t2read data from a table and write it to a table t1, you can execute it in the following order:

    SET autocommit=0;
    LOCK TABLES t1 WRITE, t2 READ;
    ... do something with tables t1 and t2 here ...
    COMMIT;
    UNLOCK TABLES;
    

    Table-level locks can prevent other sessions from concurrently updating this table, which avoids deadlocks, but at the cost of slower response times for high-load systems.

  • Another way to serialize transactions is to create an auxiliary "semaphore" table that contains only one row of data. Each transaction updates this row of data before reading or writing to other tables. This also ensures that all transactions are executed serially. Note that in this case, InnoDB's deadlock detection algorithm will also take effect, because this serialization operation corresponds to row-level locks. When using MySQL table lock, the deadlock problem can only be solved by timeout.

Related Links

If you see the friends here, please help me to support with Star: https://github.com/cncounter/translation/

Guess you like

Origin blog.csdn.net/renfufei/article/details/110355839