目录
1、Mysql锁机制
1.1、乐观锁和悲观锁
1.1.1、乐观锁
1.1.2、悲观锁
1.2、共享锁与排他锁
1.2.1、共享锁
1.2.2、排他锁
1.3、行锁与表锁
1.3.1、行锁
1.3.2、表锁
1.4、间隙锁
1.4.1、生活中的间隙锁
1.4.2、Mysql中的间隙锁
1.4.3、间隙是怎么划分的?
1.4.3、间隙锁锁定的区域
1.4.4、间隙锁的作用范围
1.4.5、next-key锁
2、MyISAM与InnoDB
3、总结
MySQL死锁系列-常见加锁场景分析 - 孙龙-程序员 - 博客园
|--表级锁(锁定整个表)
|--页级锁(锁定一页)
|--行级锁(锁定一行)
|--共享锁(S锁,MyISAM 叫做读锁)
|--排他锁(X锁,MyISAM 叫做写锁)
|--悲观锁(抽象性,不真实存在这个锁)
|--乐观锁(抽象性,不真实存在这个锁)
对mysql乐观锁、悲观锁、共享锁、排它锁、行锁、表锁概念的理解_水中加点糖-CSDN博客
乐观锁和悲观锁都是针对读(select)来说的。
通常实现是这样的:在表中的数据进行更新时,先给数据表加一个版本(version)字段,每操作一次,将那条记录的版本号加1。
举例:下单操作包括3步骤:
select (status,status,version) from t_goods where id=#{id}
update t_goods set status=2,version=version+1 where id=#{id} and version=#{version};
除了自己手动实现乐观锁之外,现在网上许多框架已经封装好了乐观锁的实现,如hibernate,需要时,可能自行搜索"hiberate 乐观锁"试试看。
悲观锁就是在操作数据时,认为此操作会出现数据冲突,所以在进行每次操作时都要通过获取锁才能进行对相同数据的操作,这点跟java中的synchronized很相似,所以悲观锁需要耗费较多的时间。
数据库的增删改操作默认都会加排他锁,而查询不会加任何锁。
悲观锁一般数据库已经实现了,共享锁也属于悲观锁的一种,那么共享锁在mysql中是通过什么命令来调用呢。
在执行语句后面加上 lock in share mode就代表对某些资源加上共享锁了。
select * from table where id=1 lock in share mode
update、insert、delete 语句会自动加排它锁。
在执行的语句后面加上 for update
select * from table where id=1 for update
mysql锁机制分为表级锁和行级锁。mysql行级锁包括共享锁与排他锁:
mysql InnoDB引擎默认的修改数据语句,update,delete,insert都会自动给涉及到的数据加上排他锁,select语句默认不会加任何锁类型。
innodb的 表锁,分成意向共享锁 和意向拍他锁,表锁是innodb引擎⾃动加的,不⽤你⾃⼰去加。
mysql共享锁与排他锁 - java攻城狮 - 博客园
innodb从来不会⾃⼰主动加个共享锁的,除⾮你⽤下⾯的语句⾃⼰⼿动加个锁:
对⼀⾏数据,如果有⼈在修改会加个排他锁,然后你不能修改只能等着获取这把锁,但这时可以随便select,查询你的事务开始之前那⾏数据的某个版本。然后如果你修改某⾏数据,会同时拿这个表的排他锁,但是如果不同的事务修改不同的⾏,会拿不同⾏的⾏级排他锁,但⼤家都会拿⼀个表的排他锁,实际上 innodb 的表级排他锁可以随便拿,这个是没冲突的。
mysql innodb 存储引擎的默认锁模式,其实还挺不错的。相当于就是⼀⾏数据,同⼀个时刻只能⼀个⼈在修改,但是别⼈修改,你可以随便读,读是读某个版本的,⾛mvcc 机制。
mySQL数据库间隙锁(mysql是如何解决幻读的)_sinat_27143551的博客-CSDN博客_mysql间隙锁解决幻读
MySQL 中锁的面试题总结 - IT酸菜鱼 - 博客园
其实innodb下的记录锁(也叫行锁),间隙锁,next-key锁统统属于排他锁。
编程的思想源于生活,生活中的例子能帮助我们更好的理解一些编程中的思想。生活中排队的场景,小明,小红,小花三个人依次站成一排,此时,如何让新来的小刚不能站在小红旁边,这时候只要将小红和她前面的小明之间的空隙封锁,将小红和她后面的小花之间的空隙封锁,那么小刚就不能站到小红的旁边。这里的小红,小明,小花,小刚就是数据库的一条条记录。
他们之间的空隙也就是间隙,而封锁他们之间距离的锁,叫做间隙锁。
表中id为主键,number字段上有非唯一索引的二级索引,有什么方式可以让该表不能再插入number=5的记录?注意:此时按照二级索引建索引表,即插入时按照number顺序排序。
只要控制几个点,number=5之前不能插入记录,number=5现有的记录之间不能再插入新的记录,number=5之后不能插入新的记录,那么新的number=5的记录将不能被插入进来。
那么,mysql是如何控制number=5之前,之中,之后不能有新的记录插入呢(防止幻读)?
答案是用间隙锁,在RR级别下,mysql通过间隙锁可以实现锁定number=5之前的间隙,number=5记录之间的间隙,number=5之后的间隙,从而使的新的记录无法被插入进来。
注:我们规定(id=A,number=B)代表一条字段id=A,字段number=B的记录,(C,D)代表一个区间,代表C-D这个区间范围。
图中根据number列,我们分为几个区间:(无穷小,2)(2,4)(4,5)(5,5)(5,11)(11,无穷大)。
只要这些区间对应的两个临界记录中间可以插入记录,就认为区间对应的记录之间有间隙。
例如:区间(2,4)分别对应的临界记录是(id=1,number=2),(id=3,number=4),这两条记录中间可以插入(id=2,number=3)等记录,那么就认为(id=1,number=2)与(id=3,number=4)之间存在间隙。
很多人会问,那记录(id=6,number=5)与(id=8,number=5)之间有间隙吗?
答案是有的,(id=6,number=5)与(id=8,number=5)之间可以插入记录(id=7,number=5),因此(id=6,number=5)与(id=8,number=5)之间有间隙的,
根据检索条件向左寻找最靠近检索条件的记录值A,作为左区间,向右寻找最靠近检索条件的记录值B作为右区间,即锁定的间隙为(A,B)。
图一中,where number=5的话,那么间隙锁的区间范围为(4,11);
间隙锁的目的是为了防止幻读,其主要通过两个方面实现这个目的:
innodb自动使用间隙锁的条件:
案例一:
````
session 1:
start transaction ;
select * from news where number=4 for update ;
session 2:
start transaction ;
insert into news value(2,4);#(阻塞)
insert into news value(2,2);#(阻塞)
insert into news value(4,4);#(阻塞)
insert into news value(4,5);#(阻塞)
insert into news value(7,5);#(执行成功)
insert into news value(9,5);#(执行成功)
insert into news value(11,5);#(执行成功)
````
检索条件number=4,向左取得最靠近的值2作为左区间,向右取得最靠近的5作为右区间,因此,session 1的间隙锁的范围(2,4),(4,5),如下图所示:
间隙锁锁定的区间为(2,4)(4,5),即记录(id=1,number=2)和记录(id=3,number=4)之间间隙会被锁定,记录(id=3,number=4)和记录(id=6,number=5)之间间隙被锁定。
因此记录(id=2,number=4),(id=2,number=2),(id=4,number=4),(id=4,number=5)正好处在(id=3,number=4)和(id=6,number=5)之间,所以插入不了,需要等待锁的释放,而记录(id=7,number=5),(id=9,number=5),(id=11,number=5)不在上述锁定的范围内,因此都会插入成功。
案例二:
````
session 1:
start transaction ;
select * from news where number=13 for update ;
session 2:
start transaction ;
insert into news value(11,5);#(执行成功)
insert into news value(12,11);#(执行成功)
insert into news value(14,11);#(阻塞)
insert into news value(15,12);#(阻塞)
update news set id=14 where number=11;#(阻塞)
update news set id=11 where number=11;#(执行成功)
````
检索条件number=13,向左取得最靠近的值11作为左区间,向右由于没有记录因此取得无穷大作为右区间,因此,session 1的间隙锁的范围(11,无穷大),如下图所示:
此表中没有number=13的记录的,innodb依然会为该记录左右两侧加间隙锁,间隙锁的范围(11,无穷大)。有人会问,为啥update news set id=14 where number=11会阻塞,但是update news set id=11 where number=11却执行成功呢?
间隙锁采用在指定记录的前面和后面以及中间的间隙上加间隙锁的方式避免数据被插入
此图间隙锁锁定区域(11,无穷大),即记录(id=13,number=11)之后不能再插入记录,
update news set id=14 where number=11
这条语句如果执行的话,将会被插入到(id=13,number=11)的后面,也就是在区间(11,无穷大)之间,由于该区间被间隙锁锁定,所以只能阻塞等待,而
update news set id=11 where number=11
执行后是会被插入到(id=13,number=11)的记录前面,也就不在(11,无穷大)的范围内,所以无需等待,执行成功。
案例三:
````
session 1:
start transaction ;
select * from news where number=5 for update;
session 2:
start transaction ;
insert into news value(4,4);#(阻塞)
insert into news value(4,5);#(阻塞)
insert into news value(5,5);#(阻塞)
insert into news value(7,11);#(阻塞)
insert into news value(9,12);#(执行成功)
insert into news value(12,11);#(阻塞)
update news set number=5 where id=1;#(阻塞)
update news set id=11 where number=11;#(阻塞)
update news set id=2 where number=4 ;#(执行成功)
update news set id=4 where number=4 ;#(阻塞)
````
检索条件number=5,向左取得最靠近的值4作为左区间,向右取得11为右区间,因此,session 1的间隙锁的范围(4,5),(5,11),如下图所示:
有人会问,为啥insert into news value(9,12)会执行成功?间隙锁采用在指定记录的前面和后面以及中间的间隙上加间隙锁的方式避免数据被插入,(id=9,number=12)很明显在记录(13,11)的后面,因此不再锁定的间隙范围内。
为啥update news set number=5 where id=1会阻塞?
number=5的记录的前面,后面包括中间都被封锁了,你这个update news set number=5 where id=1根本没法执行,因为innodb已经把你可以存放的位置都锁定了,因为只能等待。
同理,update news set id=11 where number=11
由于记录(id=10,number=5)与记录(id=13,number=11)中间的间隙被封锁了,你这句sql也没法执行,必须等待,因为存放的位置被封锁了。
案例四:
session 1:
start transaction;
select * from news where number>4 for update;
session 2:
start transaction;
update news set id=2 where number=4 ;#(执行成功)
update news set id=4 where number=4 ;#(阻塞)
update news set id=5 where number=5 ;#(阻塞)
insert into news value(2,3);#(执行成功)
insert into news value(null,13);#(阻塞)
检索条件number>4,向左取得最靠近的值4作为左区间,向右取无穷大,因此,session 1的间隙锁的范围(4,无穷大),如下图所示:
session2中之所以有些阻塞,有些执行成功,其实就是因为插入的区域被锁定,从而阻塞。
next-key锁其实包含了记录锁和间隙锁,即锁定一个范围,并且锁定记录本身,InnoDB默认加锁方式是next-key 锁。
上面的案例一session 1中的sql是:
select * from news where number=4 for update ;
next-key锁锁定的范围为间隙锁+记录锁,即区间(2,4)(4,5)加间隙锁,同时number=4的记录加记录锁。
链接:https://www.jianshu.com/p/bf862c37c4c9
https://blog.csdn.net/localhost01/article/details/78720727
对于以上,可以看得出来乐观锁和悲观锁的区别:
浅谈Mysql共享锁、排他锁、悲观锁、乐观锁及其使用场景_localhost01-CSDN博客_排他锁
也就是一句话:读用乐观锁,写用悲观锁。