MySQL gap lock


In the case of high concurrency, insert deadlock often occurs in the system. After investigation, it is the gap lock that is at fault!


MySQL InnoDB supports three row locking methods:

Row lock (Record Lock) : Also called record lock, the lock is directly added to the index record.

Gap Lock : The lock is added to the free space that does not exist, which can be between two index records, or the space before the first index record or after the last index record.

Next-Key Lock: The combination of row lock and gap lock is called Next-Key Lock.

By default, InnoDB works under the Repeatable Read isolation level. The primary key index and the unique index are locked by record lock, and the ordinary index is locked by Next-Key Lock. If a gap is locked by a transaction, other transactions cannot insert records in this gap, which can effectively prevent the occurrence of phantom reads.

If you want to disable the gap lock, you can reduce the isolation level to Read Commited or enable the parameter innodb_locks_unsafe_for_binlog (the default value is OFF, that is, the gap lock is enabled, if it is set to true, the gap lock is not enabled), but modifying this parameter will affect the master-slave Replication and disaster recovery, so only modifying the code logic is a better solution.

The emergence of gap locks is mainly concentrated in the case of delete first and then insert in the same thing. When we delete a record through a parameter condition, if the parameter condition exists, then an ordinary row lock is generated at this time to lock the record. , then delete, then release the row lock. If the parameter condition record does not exist, at this time, the delete statement acquires a gap lock, and then the database scans to the left to the first value smaller than the current parameter, and scans to the right to the first value larger than the given parameter. Then use this as a boundary to lock the data in the entire area, and such a gap lock that is particularly prone to deadlock was born.

Example:

table student

id      grade

1      79

2     80

15   90

30   98

Start a client session:

sql>set autocommit=0; (cancel autocommit)

sql>delete from student  where grade=90;

sql>insert into student  values(20,100);

In the case of no concurrency or very little concurrency, this may be executed normally. In Mysql, the actual transaction is executed through the execution. In the case of high concurrency, the execution order is very likely to change, and it will look like the following :

sql>delete from student  where grade=90;

sql>delete from student  where grade=91;

sql>insert into student  values(15,90);

sql>insert into student  values(25,99);

At this time, the last statement: insert into student values( 25,99 ); will cause a deadlock error when executed. Because when the record of grade=91 was deleted, all 90--98 were locked, and they all obtained the shared lock of this data segment, so a deadlock occurred when acquiring the exclusive lock of this data segment.


The solution to this problem: As mentioned earlier, lowering the isolation level will affect master-slave replication and disaster recovery, so it is recommended to modify the code logic and delete it only if it exists, and try not to delete records that do not exist.








Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324645682&siteId=291194637