Mysql locates next key lock in one minute. You need a few minutes

Keywords: MySQL Session

Connections and threads

View the connection information show processlist

+----+------+------------------+------+---------+------+----------+------------------+
| Id | User | Host             | db   | Command | Time | State    | Info             |
+----+------+------------------+------+---------+------+----------+------------------+
| 3  | root | 172.17.0.1:60542 | test | Query   | 0    | starting | show processlist |
| 5  | root | 172.17.0.1:60546 | test | Sleep   | 4168 |          | <null>           |
| 8  | root | 172.17.0.1:60552 | test | Sleep   | 4170 |          | <null>           |
+----+------+------------------+------+---------+------+----------+------------------+

mysql non enterprise version only supports one thread and one link

View the thread model show variables like 'thread'handling'

+-----------------------------------------+---------------------------+
| Variable_name                           | Value                     |
+-----------------------------------------+---------------------------+
| thread_handling                         | one-thread-per-connection |
+-----------------------------------------+---------------------------+

[transaction submission strategy]
There are two hidden transaction submission time points to note. The first one is the autocommit variable at the level of autocommit=1 Mysql session. All the transaction submission controls in the ORM framework will be affected by this field. By default, the current statement will be automatically submitted. However, if the start transaction is displayed, it needs to be manually submitted by itself. Sometimes the ORM framework will set autocommit to 0 according to some settings or policies.

The second is that the current transaction will be implicitly committed before DDL operation. Some scripts mix DML and DDL together, so there will be consistency problems. DDL automatically commits the current transaction. Because DDL does not support transaction policy operation before 5.7. (MySQL 8.0 already supports DDL transactional)

Next key lock troubleshooting

Next key lock only occurs under RR (repeatable read) isolation level.

There are many types of lock pairs in Mysql, including table lock, record lock, gap lock, intention sharing / exclusive lock, insert intention lock, metadata lock, auto incr auto increment lock. After excluding metadata lock and auto incr auto increment lock, the remaining lock combinations are mostly used at RR isolation level.

RR isolation level is the default transaction isolation level, which is also one of Mysql's strengths. Under RR isolation level, transactions have the maximum throughput, and there is no unreal reading problem. Next key lock is to solve this problem. Simply speaking, record lock+gap lock is next key lock.

_The fundamental problem of unreal reading lies in the boundary value of records. For example, we count the number of people over 30 years old: select count (1) people where age>30 may get different result sets every time we query, because as long as the records that meet age > 30 are entered into our people table, they will be hit by query criteria.

So in order to solve the problem of unreal reading, not only the gap between records is not allowed to be inserted into records, but also the records are prevented from being modified twice, because if the two records are modified, the interval will become larger, and unreal reading will appear.

Let's take an example.

 CREATE TABLE `peoples` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_peoples_age` (`age`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4
+----+-----+
| id | age |
+----+-----+
| 1  | 20  |
| 2  | 30  |
| 3  | 35  |
| 4  | 40  |
+----+-----+

To facilitate debugging, increase the timeout for innodb to acquire locks

show variables like '%innodb_lock_wait%'
set innodb_lock_wait_timeout=600

Open two sessions.

session A id=8:
begin
select count(1) from peoples where age>30 for update;
session B id=5:
begin
insert into peoples(age) values(31)

show processlist finds the id of the connection.

***************************[ 1. row ]***************************
Id      | 3
User    | root
Host    | 172.17.0.1:60542
db      | test
Command | Query
Time    | 0
State   | starting
Info    | show processlist
***************************[ 2. row ]***************************
Id      | 5
User    | root
Host    | 172.17.0.1:60546
db      | test
Command | Query
Time    | 394
State   | update
Info    | insert into peoples(age) values(31)
***************************[ 3. row ]***************************
Id      | 8
User    | root
Host    | 172.17.0.1:60552
db      | test
Command | Sleep
Time    | 396
State   |
Info    | <null>
  • affair

Select * from information? Schema. InnoDB? TRX \ g to view transaction execution.

***************************[ 1. row ]***************************
trx_id                     | 457240
trx_state                  | LOCK WAIT
trx_started                | 2020-01-27 06:08:12
trx_requested_lock_id      | 457240:131:4:4
trx_wait_started           | 2020-01-27 06:09:25
trx_weight                 | 6
trx_mysql_thread_id        | 5
trx_query                  | insert into peoples(age) values(31)
trx_operation_state        | inserting
trx_tables_in_use          | 1
trx_tables_locked          | 1
trx_lock_structs           | 5
trx_lock_memory_bytes      | 1136
trx_rows_locked            | 4
trx_rows_modified          | 1
trx_concurrency_tickets    | 0
trx_isolation_level        | REPEATABLE READ
trx_unique_checks          | 1
trx_foreign_key_checks     | 1
trx_last_foreign_key_error | <null>
trx_adaptive_hash_latched  | 0
trx_adaptive_hash_timeout  | 0
trx_is_read_only           | 0
trx_autocommit_non_locking | 0
***************************[ 2. row ]***************************
trx_id                     | 457239
trx_state                  | RUNNING
trx_started                | 2020-01-27 06:07:59
trx_requested_lock_id      | <null>
trx_wait_started           | <null>
trx_weight                 | 3
trx_mysql_thread_id        | 8
trx_query                  | <null>
trx_operation_state        | <null>
trx_tables_in_use          | 0
trx_tables_locked          | 1
trx_lock_structs           | 3
trx_lock_memory_bytes      | 1136
trx_rows_locked            | 5
trx_rows_modified          | 0
trx_concurrency_tickets    | 0
trx_isolation_level        | REPEATABLE READ
trx_unique_checks          | 1
trx_foreign_key_checks     | 1
trx_last_foreign_key_error | <null>
trx_adaptive_hash_latched  | 0
trx_adaptive_hash_timeout  | 0
trx_is_read_only           | 0
trx_autocommit_non_locking | 0

457240 transaction status is LOCK WAIT waiting for lock, 457239 transaction status is RUNNING, waiting for transaction commit.

  • lock

Select * from information menu schema. InnoDB menu locks \ g to view the lock usage.

***************************[ 1. row ]***************************
lock_id     | 457240:131:4:4
lock_trx_id | 457240
lock_mode   | X,GAP
lock_type   | RECORD
lock_table  | `test`.`peoples`
lock_index  | idx_peoples_age
lock_space  | 131
lock_page   | 4
lock_rec    | 4
lock_data   | 35, 7
***************************[ 2. row ]***************************
lock_id     | 457239:131:4:4
lock_trx_id | 457239
lock_mode   | X
lock_type   | RECORD
lock_table  | `test`.`peoples`
lock_index  | idx_peoples_age
lock_space  | 131
lock_page   | 4
lock_rec    | 4
lock_data   | 35, 7

The InnoDB [locks table contains the acquired lock information and the requested lock information. The lock [index] field indicates the locked index. record locks are all based on the index.

According to the above transaction 457240, the status is to acquire the lock, lock_data| 35, 7, indicating the requested data. Transaction 457239 occupies the current X lock.

  • Lock wait

Select * from information menu schema.innodb menu lock menu waits to view lock waiting information.

***************************[ 1. row ]***************************
requesting_trx_id | 457240
requested_lock_id | 457240:131:4:4
blocking_trx_id   | 457239
blocking_lock_id  | 457239:131:4:4

457240 transaction needs to acquire 131:4:4 lock. 457239 transaction occupies 131:4:4 lock.

  • innodb monitor
    show engine innodb status
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 422032240994144, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 457240, ACTIVE 394 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 5 lock struct(s), heap size 1136, 7 row lock(s), undo log entries 1
MySQL thread id 5, OS thread handle 140556966967040, query id 105 172.17.0.1 root update
insert into peoples(age) values(31)
------- TRX HAS BEEN WAITING 165 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 131 page no 4 n bits 72 index idx_peoples_age of table `test`.`peoples` trx id 457240 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 4 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 80000023; asc    #;;
 1: len 4; hex 00000007; asc     ;;

------------------
---TRANSACTION 457239, ACTIVE 407 sec
3 lock struct(s), heap size 1136, 5 row lock(s)
MySQL thread id 8, OS thread handle 140556966696704, query id 104 172.17.0.1 root

MySQL thread id 5 is preparing to insert an intention lock. The intention lock is essentially a gap lock. It is to ensure the maximum concurrent insert, and unrelated row inserts are not mutually exclusive. thread id 5 needs to ensure that a gap lock is added before insertion, mainly to prevent the consistency problems caused by concurrent insertion.

session 5 and session 8 do not operate on records with id=3 and age=35, but they are locked by X+Gap Lock. Only in this way can the problem of unreal reading be solved.

Author: Wang Qingpei (funny headline Tech Leader)

Posted by bigbstanley on Tue, 28 Jan 2020 04:38:47 -0800