Mysql transaction and lock knowledge (2) Mysql lock

1 The basic types of MySQL InnoDB locks

1.1 Lock granularity

The lock types supported by InnoDB and MylSAM are different. MylSAM only supports table locks, use lock table syntax to lock.

lock tables xxx read;
lock tables xxx write;
unlock tables ;

InnoDB supports both table locks and row locks. At that time, we had a doubt in our hearts, why support row locks will become the advantage of InnoDB? What is the difference between table lock and row lock?

  • Locking granularity: table lock> row lock
  • Locking efficiency: table lock> row lock
  • Conflict probability: table lock> row lock
  • Concurrency performance: table lock <row lock

1.2 Classification of locks

  • Table lock

    • Intent lock
  • Row lock

    • Shared locks (read locks) Shared Locks
    • Exclusive Locks (write lock) Exclusive Locks
  • Gap lock

  • Insertion intention lock: It is a special gap lock. The gap lock does not allow data to be inserted, but the insert intention lock allows multiple transactions to insert data into the same range at the same time. For example (4,7), one transaction inserts 5, one transaction inserts 6, and lock waits will not occur.

  • Self-increment lock: It is a special table lock used to prevent the repetition of self-increment fields, and the data will be released after the data is inserted. It does not need to wait until the transaction is committed to release.

1.3 Shared locks (read locks) Shared Locks

The first row-level lock is Shared Locks. After we acquire the read lock for a row of data, it can be used to read data, so it is also called a read lock. Be careful not to write after adding a read lock Data, otherwise deadlock may occur. And multiple transactions can share a read lock.

The role of shared locks: Because shared locks block the modification of other transactions, it can be used in situations where other transactions are not allowed to modify data.

How to add a read lock to a row of data?
We can select ..... lock in share mode;manually add a read lock by using.
There are two ways to release the lock. As long as the transaction ends, the lock will be automatically released, including committing the transaction and ending the transaction.

Let's verify and see if the shared lock can be acquired repeatedly.

transaction 1 Transaction 2
begin;
SELECT * FROM user WHERE id=1 LOCK IN SHARE MODE;
begin;
SELECT * FROM user WHERE id=1 LOCK IN SHARE MODE; // ok

1.4 Exclusive Locks (write lock) Exclusive Locks

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

There are two ways to lock exclusive locks,

  • The first is to automatically add an exclusive lock: when we manipulate data, including additions, deletions, and changes, we will add an exclusive lock by default.
  • The second is manual locking. We use FOR UPDATEan exclusive lock to add an exclusive lock to a row of data. This is more commonly used whether in code or in tools that manipulate data.

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

Verification of exclusive lock:

transaction 1 Transaction 2
begin;
UPDATE user SET name ='Second Battalion Commander' WHERE id=1;
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

1.5 Intent lock

What is intent lock? We seem to have never heard of it, and never used it. In fact, they are maintained by the database itself. In other words, when we add a shared lock to a row of data, the database will automatically add an intent shared lock to this table . Before we add an exclusive lock to a row of data, the database will automatically add an intent exclusive lock to this table .

Conversely: If there is at least one intent 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 intent exclusive lock on a table, it means that other transactions have added exclusive locks to some of the data rows.

Intent locks and intent locks do not conflict, and intent locks and row locks do not conflict. So what is the significance of the existence of these two table-level locks?

If there is no intention lock, when we are going to add a table lock to a table, what do we do first? Is it necessary to determine whether some other transactions have locked some of the rows? If there are any, table locks must not be added. So at this time we have to scan the entire table to determine whether we can successfully add a table lock. If the amount of data is very large, such as tens of millions of data, is the efficiency of adding table locks very low?

But after we introduced the intention lock, it was different. I only need to judge whether there is an intention lock on this table, and if there is, I just return to failure. If not, you can lock successfully. So the table lock in InnoDB, we can understand it as a sign. Just like the lights in the toilets on the train that are used by no one, you don't have to push the doors, which are used to improve the efficiency of locking.

transaction 1 Transaction 2
begin;
SELECT * FROM user where id=1 FOR UPDATE;
begin;
LOCK TABLES student WRITE; // BLOCKED
UNLOCK TABLES; // Ways to release table locks

What is the function of the lock? It is the same as the lock in Java. It is to solve the problem of resource competition. The resources in Java are objects, and the resources of the database are data tables or data rows.
Therefore, locks are used to solve the problem of concurrent access to data by transactions.
So, what does the lock lock?
When a transaction locks a row of data, other transactions cannot manipulate this row of data, so does it lock this row of data, or does it lock this field, or lock something else?

2. The principle of row lock

2.1 Tables without indexes (assuming that records are locked)

First, we have three tables, one with no index t1, one with primary key index t2, and one with unique index t3.
We first assume that InnoDB's row lock locks a row of data or a record.
Let's first look at the table structure of t1. It has two fields, an id of int type and a name of varchar type.
There are 4 pieces of data, 1, 2, 3, 4.

transaction 1 Transaction 2
begin;
SELECT * FROM t1 WHERE id =1 FOR UPDATE;
select * from t1 where id=3 for update; //blocked
INSERT INTO t1 (id, name) VALUES (5, ‘5’); //blocked

We manually start two transactions in two sessions.
In the first transaction, we lock the first row of data through where id =1.
In the second transaction, we try to lock the row of data with id = 3. Can it succeed?
Unfortunately, we saw the red light turned on and the lock operation was blocked. This is a bit strange. The first transaction locked the row of data with id = 1. Why can't I manipulate the data with id = 3?
Let's operate on a piece of non-existent data, insert id=5. It is also blocked. In fact, the entire table is locked here. Therefore, our first conjecture was overturned. InnoDB's row lock should not lock Record.

Then why is the entire table locked when there is no index or no index is used? Let's leave this question here.
We continue to watch the second demo.

2.2 Tables with primary key indexes

Let's look at the table structure of t2. The fields are the same, but the difference is that a primary key index is created on the id. The data inside is 1, 4, 7, 10.

transaction 1 Transaction 2
begin;
select * from t2 where id=1 for update;
select * from t2 where id=1 for update; // blocked
select * from t2 where id=4 for update; // OK

In the first case, using the same id value to lock, conflict; using different id to lock, you can lock successfully. So, since a row of data is not locked, is it possible that this field of id is locked?
We continue to verify.

2.3 Unique index (assuming to lock the field)

Let's take a look at the table structure of t3. The fields are still the same, a primary key index is created on id, and a unique index is created on name. The data inside is 1, 4, 7, 10.

transaction 1 Transaction 2
begin;
select * from t3 where name= ‘4’ for update;
select * from t3 where name = ‘4’ for update; // blocked
select * from t3 where id = 4 for update; // blocked

In the first transaction, we lock the row of data with a value of 4 through the name field.
In the second transaction, trying to acquire the same exclusive lock will definitely fail. There is no doubt about this.
Here we suspect that InnoDB's row lock locks the field, so this time I change a field and use id=4 to lock this row of data. Can it succeed?
Unfortunately, it is blocked again, indicating that the speculation that the row lock is locked by the field is also wrong, otherwise it will not happen that the first transaction locks the name and the second field fails to lock the id.

既然锁住的不是record,也不是column, InnoDB的行锁锁住的到底是什么呢?在这三个案例里面,我们要去分析一下他们的差异在哪里,也就是这三张表的结构,是什 么区别导致了加锁的行为的差异?其实答案就是索引。InnoDB的行锁,就是通过锁住索引来实现的

那么我们还有两个问题没有解决:

  1. 为什么表里面没有索引的时候,锁住一行数据会导致锁表?或者说,如果锁住的是索引,一张表没有索引怎么办?所以,一张表有没有可能没有索引?

    • 如果我们定义了主键(PRIMARY KEY),那么InnoDB会选择主键作为聚集索引。
    • 如果没有显式定义主键,则InnoDB会选择第一个不包含有NULL值的唯一索引作为主键索引。
    • 如果也没有这样的唯一索引,则InnoDB会选择内置6字节长的ROWID作为隐藏的聚集索引,它会随着行记录的写入而递增。
    • 所以,为什么锁表,是因为查询没有使用索引,会进行全表扫描,然后把每一个隐
      藏的聚集索引都锁住了。
  2. 为什么通过唯一索引给数据行加锁,主键索引也会被锁住?
    在InnoDB中,在辅助索引里面,索引存储的是二级索引和主键的值。比如name=4,存储的是name的索引和主键id的值4。
    而主键索引里面除了索引之外,还存储了完整的数据。所以我们通过辅助索引锁定一行数据的时候,它跟我们检索数据的步骤是一样的,会通过主键值找到主键索引,然后也锁定。本质上是因为锁定的是同一行数据,是相互冲突的。

在这里插入图片描述

3. 锁的算法

我们先来看一下我们测试用的表,t2表,这张表有一个主键索引,前面我们已经见过了。我们插入了4行数据,主键id分别是1、4、7、10。

因为我们用主键索引加锁,我们这里的划分标准就是主键索引的值。

在这里插入图片描述

  • Record 记录: 这些数据库里面存在的主键值,我们把它叫做Record,记录,那么这里我们就有4
    个 Record。

  • Gap间隙 : 根据主键,这些存在的Record隔开的数据不存在的区间,我们把它叫做Gap,间隙,它是一个左开右开的区间。

  • Next-key 临键锁: 最后一个,间隙(Gap)连同它左边的记录(Record),我们把它叫做临键的区间,它是一个左开右闭的区间。

整型的主键索引,它是可以排序的,所以才有这种区间。如果我的主键索引不是整形,是字符怎么办呢? 事实上,字符也是有顺序的。
在这里插入图片描述

3.1 记录锁

第一种情况,当我们对于唯一性的索引(包括唯一索引和主键索引)使用等值查询,精准匹配到一条记录的时候,这个时候使用的就是记录锁。

例如:

select * from user where id = 4 for update;

3.2 间隙锁

第二种情况,当我们査询的记录不存在,没有命中任何一个record,无论是用等值査询还是范围查询的时候,它使用的都是间隙锁。

示例:

transaction 1 Transaction 2
begin;
select * from t2 where id = 6 for update;
INSERT INTO ‘t2’ (‘id’, ‘name’) VALUES (5, ‘5’); // BLOCKED
INSERT INTO ‘t2’ (‘id’, ‘name’) VALUES (6, ‘6’); // BLOCKED
select * from t2 where id =6 for update; // OK
select * from t2 where id >20 for update;
INSERT INTO ‘t2’ (‘id’, ‘name’) VALUES (11, ‘11’); // BLOCKED

当查询记录不存在时,使用间隙锁
注意,间隙锁主要是阻塞插入inserto相同的间隙锁之间不冲突。

3.3 临键锁

第三种情况,当我们使用了范围査询,不仅仅命中了 Record记录,还包含了 Gap 间隙,在这种情况下我们使用的就是临键锁,它是MySQL里面默认的行锁算法,相当于记录锁加上间隙锁。唯一性索引,等值査询匹配到一条记录的时候,退化成记录锁。没有匹配到任何记录的时候,退化成间隙锁。

Next-key Lock = Record Lock + Gap Lock
比如我们使用>5 and <9 的条件,它包含了记录不存在的区间,也包含了一个Record 7。锁住区间(4,7] (7,10]

transaction 1 Transaction 2
begin;
select * from t2 where id >5 and id < 9 for update;
begin;
select * from t2 where id =4 for update; // OK
INSERT INTO ‘t2’ (‘id’, ‘name’) VALUES (6, ‘6’); // BLOCKED
INSERT INTO、t2’ (‘id’, ‘name’) VALUES (8, 8); // BLOCKED
select * from t2 where id =10 for update; // BLOCKED

临键锁,锁住最后一个key的下一个左开右闭的区间。

select * from t2 where id >5 and id <=7 for update;—锁住(4,7](7,10] 
select * from t2 where id >8 and id <=10 fbr update;—锁住(7,10], (10,+)

为什么要锁住下一个左开右闭的区间? ——就是为了解决幻读的问题。

3.4 小结

所以,我们再看下这张表格,为什么InnoDB的RR级别能够解决幻读的问题,就是用临键锁实现的。

事务隔离级别 P1(脏读) P2(不可重复读) P3(幻读)
READ UNCOMMITTED (读未提交) Possible Possible Possible
READ COMMITTED (读已提交) Not Possible Possible Possible
REPEATABLE READ (可重复读) Not Possible Not Possible 对InnoDB不可能
SERIALIZABLE (串行化) Not Possible Not Possible Not Possible

最后我们来总结一下四个事务隔离级别的实现:

  • READ UNCOMMITTED : RU隔离级别:不加锁。
  • SERIALIZABLE: Serializable所有的select语句都会被隐式的转化为select… in share mode,会和 updates delete 互斥。
  • REPEATABLE READ:
    RR隔离级别下,普通的select使用快照读(snapshot read),底层使用MVCC来实现。
    加锁的select(select … in share mode / select … for update)以及更新操作 update, delete等语句使用当前读(current read) ,底层使用记录锁、或者间隙锁、 临键锁
  • READ COMMITTED: RC隔离级别下,普通的select都是快照读,使用MVCC实现。
    加锁的select都使用记录锁,因为没有Gap Lock。
    除了两种特殊情况:外键约束检査(foreign-key constraint checking)以及重复键检査(duplicate-key checking)时会使用间隙锁封锁区间。所以RC会出现幻读的问题。

4. 事务隔离级别如何选择

RU和Serializable肯定不能用。为什么有些公司要用RC,或者说网上有些文章推荐用RC?
RC和RR主要有几个区别:

  • RR的间隙锁会导致锁定范围的扩大。
  • 条件列未使用到索引 RR锁表,RC锁行。
  • RC的"半一致性”(semi-consistent)读可以增加update操作的并发性。
    在RC中,一个update语句,如果读到一行已经加锁的记录,此时InnoDB返回记录最近提交的版本,由MySQL上层判断此版本是否满足update的where条件。若满足(需要更新),则MySQL会重新发起一次读操作,此时会读取行的最新版本(并加锁)。

实际上,如果能够正确地使用锁(避免不使用索引去加锁),只锁定需要的数据,用默认的RR级别就可以了。

Guess you like

Origin blog.csdn.net/nonage_bread/article/details/113092311