这篇文章主要介绍MySQL中锁解决幻读问题的方法,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!
什么是锁
锁是一种用于保证在并发场景下每个事务仍能以一致性的方式读取和修改数据的方式,当一个事务对某一条数据上锁之后,其他事务就不能修改或者只能阻塞等待锁的释放,所以锁的粒度大小一定程度上可以影响到访问数据库的性能。
从锁的粒度上来说,我们可以将锁分为表锁和行锁。
表锁
顾名思议,表锁就是直接锁表,在MyISAM引擎中就只有表锁。
表锁的加锁方式为:
锁表的表名;阅读——锁定后表只读 打开表;--解锁复制代码
行锁
行锁,从名字上来看,就是锁住一行数据,然而,行锁的实际实现算法会相对复杂,有时候并不仅仅只是锁住某一条数据,这个后面再展开。
正常的思路是:锁住一行数据之后,其他事务就不能来访问这条数据了,那么我们想象,假如事务A访问了一条数据,只是拿出来读一下,并不想去修改,正好事务B也来访问这条数据,也仅仅只是想拿出来读一下,并不想去修改,这时候如果因此阻塞了,就有点浪费性能了。所以为了优化这种读数据的场景,我们又把行锁分为了两大类型:共享锁和排他锁。
共享锁
共享锁,Shared Lock,又称之为读锁,S锁,就是说一条数据被加了S锁之后,其他事务也能来读数据,可以共享一把锁。
我们可以通过如下语句加共享锁:
select * from test where id=1 LOCK IN SHARE MODE;复制代码
加锁之后,直到加锁的事务结束(提交或者回滚)就会释放锁。
排他锁
排他锁,Exclusive Lock,又称之为写锁,X锁。就是说一条数据被加了X锁之后,其他事务想来访问这条数据只能阻塞等待锁的释放,具有排他性。
当我们在修改数据,如:insert,update,delete的时候MySQL就会自动加上排他锁,同样的,我们可以通过如下sql语句手动加上排他锁:
select * from test where id=1 for update;复制代码
在InnoDB引擎中,是允许行锁和表锁共存的。
但是这样就会有一个问题,假如事务A给t表其中一行数据上锁了,这时候事务B想给t表上一个表锁,这时候怎么办呢?事务B怎么知道t表有没有行锁的存在,如果采用全表遍历的情况,当表中的数据很大的话,加锁都要加半天,所以MySQL中就又引入了意向锁。
意向锁
意向锁为表锁,分为两种类型,分为:意向共享锁(Intention Shared Lock)和意向排他锁(Intention Exclusive Lock),这两种锁又分别可以简称为IS锁和IX锁。
意向锁是MySQL自己维护的,用户无法手动加意向。
意向锁有两大加锁规则:
- 当需要给一行数据加上S锁的时候,MySQL会先给这张表加上IS锁。
- 当需要给一行数据加上X锁的时候,MySQL会先给这张表加上IX锁。
这样的话上面的问题就迎刃而解了,当需要给一张表上表锁的时候,只需要看这张表是否有对应的意向锁就可以了,无需遍历整张表。
各种锁的兼容关系
下面这张图是各种锁的兼容关系,参考自官网:
XIXSISX
互斥
互斥
互斥
互斥
IX互斥
共享
冲突
共享
S互斥
互斥
共享
共享
IS互斥
共享
共享
共享
锁到底锁的是什么
建立以下两张表,并初始化5条数据,注意test表有2个索引而test2没有索引:
CREATE TABLE `test` ( “id”int(11)不是零, “名字”varchar(50)默认为空, 主键(“id”), 关键“NAME_INDEX”(“名字”) )引擎=InnoDB默认字符集=utf8; 插入测试值(& # 39;张1 & # 39;); 插入测试值(5 & # 39;张5 & # 39;); 插入测试值(8 & # 39;张8 & # 39;); 插入测试值(10日& # 39;& # 39;10张); 插入测试值(20日& # 39;张20 & # 39;); 创建表的test2 ( “id”varchar(32)非空, “名字”varchar(32)默认为空 )引擎=InnoDB默认字符集=utf8; 插入test2值(& # 39;张1 & # 39;); 插入test2值(5 & # 39;张5 & # 39;); 插入test2值(8 & # 39;张8 & # 39;); 插入test2值(10日& # 39;& # 39;10张); nullMySQL中锁解决幻读问题的方法