MySQL(三)锁 ----- 行锁和锁机制

InnoDB 锁机制

InnoDB存储引擎支持行级锁
其大类可以细分为共享锁和排它锁两类

  • 共享锁(S):允许拥有共享锁的事务读取该行数据。当一个事务拥有一行的共享锁时,另外的事务可以在同一行数据也获得共享锁,但另外的事务无法获得同一行数据上的排他锁

  • 排它锁(X):允许拥有排它锁的事务修改或删除该行数据。 当一个事务拥有一行的排他锁时,另外的事务在此行数据上无法获得共享锁和排它锁,只能等待第一个事务的锁释放

除了共享锁和排他锁之外, InnoDB也支持意图锁。该锁类型是属于表级锁,表明事务在后期会对该表的行施加共享锁或者排它锁。所以对意图锁也有两种类型:

  • 共享意图锁(IS): 事务将会对表的行施加共享锁
  • 排他意图锁(IX): 事务将会对表的行施加排它锁

举例来说select … for share mode语句就是施加了共享意图锁,而select … for update语句就是施加了排他意图锁

首先它俩都是表级别的锁,因为InnoDB是支持行锁的,当某些行已经上了X锁之后,再想对这个表上锁的话,就得确认当前表中没有任何X锁,在没有意向锁的情况下,就得一行一行去判断,这样效率会非常低下,在有了意向锁之后,就不需要一行一行判断了,举个例子:

select * from user where id=1 for update;
当一个事务对id=1这行数据上了X锁之后,就会对user表也加一个IX锁。

LOCK TABLES user READ;
这时想要给表加上读锁,但是发现表上有IX锁,所以会阻塞无法执行。类似的如果一个事务对一行数据加上共享锁

select * from user where id=1 lock in share mode;
就会给对应的表加上IS锁。这时候如果执行

LOCK TABLES user WRITE;
也会因为表上有IS锁而阻塞。

行级锁

行级锁是施加在索引行数据上的锁,比如SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE语句是在t.c1=10的索引行上增加锁 ,来阻止其他事务对对应索引行的insert/update/delete操作。

当一个InnoDB表没有任何索引时, 则行级锁会施加在隐含创建的聚簇索引上,所以说当一条sql没有走任何索引时,那么将会在每一条聚集索引后面加X(排它)锁,这个类似于表锁,但原理上和表锁应该是完全不同的

开启两个会话链接,链接同一个数据库,模拟排他锁

session1:
 
##update加排他锁
mysql> set autocommit=0;  #模拟开启一个事务会话
Query OK, 0 rows affected (0.00 sec)
mysql> update temp set name='aa' where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
 
session2:
##update等待第一个锁释放 
mysql> update temp set name='bb'where id=2;
 
session1
##commit释放锁
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
 
session2
等待结束释放锁
update操作才会成功
Query OK, 1 row affected (5.81 sec)
Rows matched: 1  Changed: 1  Warnings: 0

给temp表加一个主键索引在看看

session1:
update加排它锁
 
set autocommit=0;
update temp set name='a' where id=1;
 
session2:
update temp set name='b' where id=2;
update不需要等待第一个锁释放,直接执行成功
 
session1: 进行提交事务
commit;

结论:当一个InnoDB表没有任何索引时, 则行级锁会施加在隐含创建的聚簇索引上,所以说当一条sql没有走任何索引时,那么将会在每一条聚集索引后面加X(排它)锁,这个类似于表锁,但原理上和表锁应该是完全不同的

间隔锁

当 我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件 的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”, InnoDB也会对这个“间隙”加锁

间隔锁是施加在索引记录之间的间隔上的锁, 锁定一个范围的记录、但不包括记录本身,
比如SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE语句,尽管有可能对c1字段来说当前表里没有=15 的值,但还是会阻止=15的数据的插入操作,是因为间隔锁已经把索引查询范围内的间隔数据也都锁住了

  • 间隔锁的使用只在部分事务隔离级别才是生效的

  • 间隔锁只会阻止其他事务的插入操作

gap lock的前置条件

  • 事务隔离级别为REPEATABLE-READ, innodb_locks_unsafe_for_binlog参数为0,且sql走的索引为非唯一索引(无论是等值检索还是范围检索)
  • 事务隔离级别为REPEATABLE-READ, innodb_locks_unsafe_for_binlog参数为0,且sql是一个范围的当前读操作,这时即使不是非唯一索引也会加gap lock
update加排他锁和间隔锁
set autocommit=0;
mysql> update temp2 set name='abc' where id between 4 and 6;
Query OK, 0 rows affected (0.01 sec)
Rows matched: 0  Changed: 0  Warnings: 0
 
session2:
insert into temp2 values(4,'d');
会等待第一个锁释放
 
session1:
释放锁
commit;
 
session2:
Query OK, 1 row affected (7.65 sec)
等待结束释放锁 插入数据成功

例子如果链接1的update语句是update temp set name=‘abc’ where id >4; 而链接2的
插入数据的id=4时也会被阻止,是因为记录中的3~4之间也算是间隔

Next-key锁

在默认情况下, mysql的事务隔离级别是可重复读,并且innodb_locks_unsafe_for_binlog
参数为0,这时默认采用next-key locks。所谓Next-Key Locks, 就是记录锁和间隔锁的结合,即除了锁住记录本身,还要再锁住索引之间的间隙。

插入意图锁

插入意图锁是在插入数据时首先获得的一种间隔锁, 对这种间隔锁只要不同的事务插 入的数据位置是不一样的,虽然都是同一个间隔,也不会产生互斥关系

比如有一个索引有4和7两个值,如果两个事务分别插入5和6两个值时,虽然两个事务都会在索引4和7之间施加间隔锁,但由于后续插入的数值不一样,所以两者不会互斥

比如下例中事务A对索引>100的值施加了排他间隔锁,而事务B在插入数据之前就试图先施加插入意图锁而必须等待

CREATE TABLE child (id int(11) NOT NULL, PRIMARY KEY(id)) ENGINE=InnoDB;
INSERT INTO child (id) values (90),(102);
#开启事务A
START TRANSACTION;
#增加排它间隔锁
SELECT * FROM child WHERE id > 100 FOR UPDATE;
+-----+
| id  |
+-----+
| 102 |
+-----+
1 row in set (0.00 sec)
 
 
另一个session窗口,开启事务B:
START TRANSACTION;
INSERT INTO child (id) VALUES (101);
这时候会有锁等待

自增锁

自增锁是针对事务插入表中自增列时施加的一种特殊的表级锁,即当一个事务在插入自增数据时,另一个事务必须等待前一个事务完成插入,以便获得顺序的自增值


转自:https://blog.csdn.net/JavaTeachers/article/details/120722499

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值