InnoDB储存引擎中的锁

InnoDB存储引擎与MylSAM存储引擎一个差别就是InnoDB支持表锁和行锁,MylSAM存储引擎只实现了表锁。

InnoDB存储引擎实现了两种标准的行级锁:

  1. 共享锁(S LOCK),允许事务读一行数据。
  2. 排他锁(X LOCK),允许事务删除或者更新一行数据。

存在一行数据r,如果事务T1获取了数据行r的S锁,此时事务T2可以立即获取数据行r的S锁,因为读取并没有更改数据的内容,因此S锁之间是可以兼容的。如果此时事务T3想要获取数据行r的X锁,则必须等待事务T1,T2释放行r上的S锁,这种情况说明S锁与X锁不能在同一行数据上共存,因此S锁与X锁不兼容。

SX
S兼容不兼容
X不兼容不兼容

与此同时,InnoDB存储引擎支持多粒度锁定,这种锁定允许事务在行级上的锁和表级上的锁同时存在,为了支持多粒度锁定,InnoDB存储引擎支持一种额外的锁方式,称之为意向锁。意向锁是将锁定的对象分为多个层次。我们可以把上锁的记录看成一个倒立的金字塔,上方代表粗粒度锁,最下方看作细粒度锁,当我们需要对记录加细粒度锁时,我们需要提前获取该记录的粗粒度锁。

举个例子,一条记录存在于某个数据库中的某张表中,此时数据库锁–>表锁–>行级锁(锁粒度由粗到细),所以我们需要先获取数据库,表上的意向锁之后,才能获取记录上的行锁。

InnoDB存储引擎实现意向锁的方式比较简单,其意向锁即为表级的锁。设计目的主要是为了在一个事务中揭示下一行将被请求的锁类型,支持两种意向锁:

  1. 意向共享锁(IS LOCK):事务将要获取一张表中的某几行的共享锁。
  2. 意向排他锁(IX LOCK):事务将要获取一张表中的某几行的排他锁。

S锁,X锁,IS锁,IX锁的兼容性:

ISIXSX
IS兼容兼容兼容不兼容
IX兼容兼容不兼容不兼容
S兼容不兼容兼容不兼容
X不兼容不兼容不兼容不兼容

举个例子,在对记录r加X锁之前,记录r在表1中,发现有事务对表1加了S锁,因此表1上已存在S锁,之后事务想要对记录r加IX锁时,由于S锁与IX不兼容,所以该事务需要等待表锁操作的完成。
在这里插入图片描述

数据库为我们在INFORMATION_SCHEMA架构下添加了INNODB_TRX,INNODB_LOCKS,INNODB_LOCK_WAITS,通过这三张表,可以监控当前事务并分析可能存在的锁问题。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一致性非锁定读
非锁定读意味着,当我们读取行记录时,不需要等待行数据上的X锁释放。
例如,当我们读取的行正在执行update操作或者delete操作时,我们不需要等待行上的X锁释放,相反的,我们可以读取该行数据的一个快照版本。如图所示,某行数据存在多个快照版本。这种通过读取快照数据的方式极大的提高了数据库的并发,这种方式称为多版本并发控制(Multi Version Concurrency Control,MVCC)
在这里插入图片描述
在事务隔离级别为REPEATABLE READ和READ COMMITTED时,InnoDB存储引擎使用的是非锁定的一致性读,然而在不同的隔离级别时,读取的快照版本却不一样。

  1. READ COMMITTED:总是读取最新的快照版本。
  2. REPEATABLE READ:总是读取事务开始时的行数据版本。

查看目前的隔离级别:

mysql> show global variables like "%isol%";
+-----------------------+-----------------+
| Variable_name         | Value           |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set, 1 warning (0.00 sec)

此时我们开启会话A,读取id=1的行内容。

#sessionA
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from parent where id=1;
+----+
| id |
+----+
|  1 |
+----+
1 row in set (0.00 sec)

我们另开一个会话B,对id=1的记录进行修改。

#sessionB
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> update parent set id=3 where id=1;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

此时并没有提交记录,由于是更新操作,相当于对该记录加了X锁,这时如果
在会话A中对该记录的再次读取,按照之前所说的,X锁排斥任何形式的锁,但是在InnoDB存储引擎的特性下,REPEATABLE READ和READ COMMITTED的隔离级别下会使用一致性的非锁定读,因此,我们在会话A中读取该记录是可行的。

#session A
mysql> select * from parent where id=1;
+----+
| id |
+----+
|  1 |
+----+
1 row in set (0.00 sec)

因为该数据只被会话B更改过一次,所以也就只存在一个快照版本。此时提交会话B中的内容。

#sessionB
mysql> commit;
Query OK, 0 rows affected (0.01 sec)

此时不同的隔离级别读取的数据就体现出差异了,如上所述REPEATABLE READ总是读取事务开始时的行数据版本也就是id=1,而READ COMMITTED总是读取最新的快照版本也就是id=3。接下来证实一下。目前的隔离级别时REPEATABLE READ。预期id=1;

#session A
mysql> select * from parent where id=1;
+----+
| id |
+----+
|  1 |
+----+
1 row in set (0.00 sec)

当在READ COMMITTED级别时,总是读取最新的快照。因此有如下结果:

#sessionA
mysql> select * from parent where id=1;
Empty set (0.00 sec)

执行过程如下:
在这里插入图片描述

一致性锁定读:
在innoDB存储引擎下,对于读操作,默认使用了一致性非锁定读,即读取快照版本,在一些情况下,需要对读取操作加锁来保证数据的一致性,对于select操作,加锁方式如下:

  1. select … for update;
  2. select … lock in share mode;

第一种方式代表对行记录加X锁,第二种方式代表对行记录加S锁,其他事务可以对加了S锁的数据加S锁,如果对其加X锁,则需要阻塞。

总结一下:
InnoDB存储引擎实现了标准的行级锁:共享锁S,排他锁X。只有S锁与S锁可以共存,其他的组合都排斥,需要等到原有的锁释放。

InnoDB存储引擎支持多粒度锁,从数据分布来看,可以分为数据库–>表–>数据页—>行记录,锁粒度由粗到细进行加锁。

InnoDB存储引擎支持意向锁,分为意向共享锁,意向排他锁。意向锁为表级锁。

InnoDB存储引擎在READ COMMITTED和REPEATABLE READ隔离级别时采用一致性非锁定读,通过读取数据快照的形式读取数据。区别在于:
READ COMMITTED:总是读取最新的行记录快照。
REPEATABLE READ:总是读取事务开始时的行记录。

InnoDB存储引擎对读操作也提供了加S锁和X锁,执行语句分别为:
select … for update 加X锁。
select … lock in share mode 加S锁。

常用命令:

show global variables like "%isolation%";  //查看隔离级别

set global transaction_isolation ='read-committed';  //修改隔离级别
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值