[MYSQL articles] detailed explanation of related locks and MVCC in mysql


foreword

The database lock is a mechanism used to ensure data stability and consistency in the case of high multi-threaded concurrency. MySQL supports different granularities and implementation mechanisms for locks depending on the underlying storage engine. MyISAM only supports table locks, and InnoDB supports row locks and table locks. At present, the default storage engine of MySQL is InnoDB, here mainly introduces the lock of InnoDB. In the previous article, we elaborated on MySQL transactions in detail, and also mentioned MVCC by the way. Before introducing locks, let's talk about MVCC first.

MVCC

To keep the data read twice before and after a transaction consistent, then we can create a backup or snapshot for it when modifying the data, and then read the snapshot later.

Question: When was this snapshot created? When reading data, how can we ensure that we can read this snapshot instead of the latest data? How can this be achieved?

The core idea of ​​MVCC is: I can find the data that existed before my transaction started, even if it was modified or deleted later. I can't find the data added after my transaction.

InnoDB implements two hidden fields for each row:

DB_TRX_ID : The transaction ID of the last transaction that inserts or updates a row. The transaction number is automatically incremented (we understand it as the creation version number, and record the current transaction ID when data is added or modified to new data).

DB_ROLL_PTR : rollback pointer (we understand it as the deletion version number, when data is deleted or recorded as old data, record the current transaction ID).

Next, let's talk about the control of these two version numbers in detail to ensure that the data read twice by a transaction is consistent.

1. The first transaction

Initialization data.

Transaction 1
begin; 
insert into user values(NULL,'mayun') ; 
insert into user values(NULL,'tory') ; 
commit;

For the data at this time, the creation version is the current transaction ID, and the deletion version is empty:

id name create version delete version
1 witch 1 undefined
2 tory 1 undefined

2. The second transaction

Execute the first query and read two pieces of raw data. At this time, the transaction ID is 2:

Transaction 2 
begin; 
select * from user; -- (1) 第一次查询

3. The third business

Insert data:

Transaction 3 
begin; 
insert into user values(NULL,'tom') ; 
commit;

In the data at this time, there is one more tom, and its creation version number is the current transaction number: 3.

id name create version delete version
1 witch 1 undefined
2 tory 1 undefined
3 tom 3 undefined

The second transaction executes the second query:

Transaction 2 
select * from user; --(2)第二次查询

MVCC's search rules: only data whose creation time is less than or equal to the current transaction ID can be found, and rows whose deletion time is greater than the current transaction ID (or not deleted).

That is to say, the data inserted after my transaction started cannot be found. Tom's creation ID is greater than 2, so only two data can still be found.

4. The fourth transaction

Delete data, delete id=2 ,name = torythis record.

Transaction 4 
begin; 
delete from user where id=2;
commit;

The deleted version of the data at this time toryis recorded as the current transaction ID, 4, and other data remains unchanged.

id name create version delete version
1 witch 1 undefined
2 tory 1 4
3 tom 3 undefined

In the second transaction, execute the 3rd query:

Transaction 2 
select * from user; --(3)第三次查询

Search rules: Only data whose creation time is less than or equal to the current transaction ID can be found, and rows whose deletion time is greater than the current transaction ID (or not deleted).

That is, the data deleted after my transaction started, so it can torystill be found out. So it's still these two data.

5. The fifth business

Perform update operation, this transaction transaction ID is 5.

Transaction 5 
begin; 
update user set name ='xiaoming' where id=1; 
commit;

For the data at this time, when the data is updated, the deleted version of the old data is recorded as the current transaction ID 5 (undo),

A new piece of data is generated, and the creation ID is current transaction ID 5.

id name create version delete version
1 witch 1 5
2 tory 1 4
3 tom 3 undefined
1 xiaoming 5 undefined

The second transaction executes the 4th query:

Transaction 2 
select * from user; --(4) 第四次查询

Search rules: Only data whose creation time is less than or equal to the current transaction ID can be found, and rows whose deletion time is greater than the current transaction ID (or not deleted).

Because the updated data xiaomingcreation version is greater than 2, it means that it was added after the transaction and cannot be found out.

However, if the deleted version mayunof is greater than 2, it means that it was deleted after the transaction and can be found out.

Through the above demonstration, we can see that through the control of the version number, no matter whether other transactions are inserted, modified, or deleted, the data queried by the second transaction will not change.

Several lock mechanisms common to InnoDB

shared lock

Shared Locks (shared lock): After we acquire a read lock of a row of data, it can be used to read the data, so it is also called a read lock. And multiple transactions can share a read lock.

So how to add a read lock to a row of data?

We can manually add a read lock in the select …… lock in share mode; same way.

There are two ways to release the lock. As long as the transaction ends, the lock will automatically execute the transaction, including committing the transaction and ending the transaction.

Verify to see if the shared lock can be acquired repeatedly.

Transaction 1
begin;
select * from user where id = 1 lock in share mode;

Transaction 2
begin;
select * from user where id = 1 lock in share mode;  --ok

exclusive lock

Exclusive Locks (exclusive lock): It is used to manipulate data, so it is also called a write lock. As long as a transaction acquires an exclusive lock on a row of data, other transactions can no longer acquire shared and exclusive locks on this row of data.

There are two ways to lock exclusive locks. The first one is to automatically add exclusive locks, which may not be noticed by students: when we operate data, including adding, deleting, and modifying, an exclusive lock will be added by default.

There is also a manual lock. We use a FOR UPDATEto add an exclusive lock to a row of data. This is commonly used in our code or in tools for manipulating data.

The way to release the lock is the same as before.

Verification of exclusive locks:

Transaction 1
begin;
update user set name = 'xiaoming' where id=1;

Transaction 2
begin;
select * from user where id = 1 lock in share mode; -- BLOCKED
select * from user where id=1 FOR UPDATE; -- BLOCKED
delete  from user where id=1 ; -- BLOCKED

intent lock

Before we add a shared lock to a row of data, the database will automatically add an intention shared lock to this table.

Before we add an exclusive lock to a row of data, the database will automatically add an intention exclusive lock to this table.

Conversely: If there is at least one intentional shared lock on a table, it means that other transactions have added shared locks to some of the data rows. If there is at least one intentional exclusive lock on a table, it means that other transactions have added exclusive locks to some of the data rows.

Intention locks will not lock anything, unless there is a full table request operation, it will not lock any data. The meaning of existence is only used to indicate that a transaction is locking the data of a certain row, or will lock the data of a certain row.

record lock

Record Locks: Lock a certain row. If the table has an index, the record lock is locked on the index. If the table does not have an index, InnoDB will create a hidden clustered index to lock. So when we query, we try to use indexes to query, which can reduce the conflict of locks.

When we use an equivalent query for a unique index (including a unique index and a primary key index) to accurately match a record, we use a record lock at this time.

For example where id = 1 4 7 10 .

We use different keys to lock, there will be no conflict, it only locks this record.

gap lock

Gap Locks: Gap locks are locks that have gaps between record rows or before the first row of records or after the last row of records. Gap locks may occupy single-line, multi-line or empty records.

The usual situation is that when we use range search, for example, in the student performance management system, if there are students with grades of 60, 72, 80, and 95 at this time, a teacher wants to check the information of all students whose grades are greater than 72. The statement used Yes select * from student where grade > 72 for update, at this time, InnoDB locks not only 80, 95, but all records above 72-80, 80-95, and 95. Why is this so?

In fact, if these rows are not locked, if another transaction inserts a record with a score greater than 72 at this time, it will cause the results of the first transaction to be different from the two queries, and a phantom read will occur. Therefore, in order to meet the transaction isolation level, it is necessary to lock all rows that meet the conditions.

Pro key lock

Next-Key Locks: NK is a combination lock of record lock and gap lock. Locks both rows and gaps. And adopt the principle of left open and right close. InnoDB uses this kind of lock for queries.

To illustrate with an example:

Now there is a table a, as follows:

image-20220127093631641

We do the following:

# T1
START TRANSACTION WITH CONSISTENT SNAPSHOT; //1

SELECT * FROM a WHERE uid = 6 for UPDATE; //2

COMMIT;  //5


# T2
START TRANSACTION WITH CONSISTENT SNAPSHOT;  //3

INSERT INto a(uid) VALUES(11);
INSERT INto a(uid) VALUES(5);  //4
INSERT INto a(uid) VALUES(7);
INSERT INto a(uid) VALUES(8);
INSERT INto a(uid) VALUES(9);

SELECT * FROM a WHERE uid = 6 for UPDATE;

COMMIT;

ROLLBACK;

Execute in the order of 1, 2, 3, and 4 above, and you will find that step 4 is blocked, and you must complete step 5 to insert successfully. Here we will be very surprised that the row with uid=6 is obviously locked, why can't 5 be inserted? The reason is that the next-key algorithm is used here, and the entire interval (3,10) is locked.

image-20220127093853903

summary

The above is a summary of some knowledge about mysql MVCC and InnoDB common locks. I hope you can try it out while watching it, so that you can experience it better and understand it more deeply.

Guess you like

Origin blog.csdn.net/jiang_wang01/article/details/131296521