数据库中的锁
1 锁
Java锁和数据库中的锁其实是一样的,
为了避免多个事务同时操作数据库导致数据异常,一般会通过锁机制解决。
加锁是为了避免并发导致数据出现异常,来保证数据一致
2-共享锁
在查询语句后面增加
LOCK IN SHARE MODE
,Mysql会对查询结果中的每行都加共享锁。
SELECT ... LOCK IN SHARE MODE;
其他线程也可以读取使
别急,慢慢来。
用了共享锁的表,而且这些线程读取的是同一个版本的数据。
当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请共享锁,否则会被阻塞。
3 -排他锁
就是我们对数据进行写操作的时候,要先获得写锁,
获得写锁的事务既可以写数据也可以读数 据。
但是如果数据库已经被别人增加了排他写锁,那么后面的事务是无法在获得该数据库的任何锁的。
也就是说,如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁。
获准排他锁的事务既能读数 据,又能修改数据。
用法
在查询语句后面增加
FOR UPDATE
,Mysql会对查询结果中的每行都加排他锁
SELECT ... FOR UPDATE;
当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请排他锁,否则会被阻塞。
写操作会被默认增加排他锁,不需要显示声明
读操作需要显式声明。
拿MySql的InnoDB引擎来说,对于
insert
、
update
、
delete
等操作。会自动给涉及的数据加排他锁;
对于一般的
select
语句,InnoDB不会加任何锁
,事务可以通过以下语句给显示加共享锁或排他锁。
共享锁:
SELECT ... LOCK IN SHARE MODE;
排他锁(悲观锁):
SELECT ... FOR UPDATE;
悲观锁
悲观锁:每次去取数据,很悲观,都觉得会被别人修改,所以在拿数据的时候都会上锁。简言之,共享资源每次都只给一 个线程使用,其他线程阻塞,等第一个线程用完后再把资源转让给其他线程。synchronized和ReentranLock等都是悲观 锁思想的体现。
优点:
悲观锁利用数据库中的锁机制来实现数据变化的顺序执行,这是最有效的办法
缺点:
一个事务用悲观锁对数据加锁之后,其他事务将不能对加锁的数据进行除了查询以外的所有操作,如果该事务执行时间很 长,那么其他事务将一直等待,那势必影响我们系统的吞吐量。
乐观锁
乐观锁:每次去取数据,都很乐观,觉得不会被被人修改。因此每次都不上锁,但是在更新的时候,就会看别人有没有在 这期间去更新这个数据,如果有更新就重新获取,再进行判断,一直循环,直到拿到没有被修改过的数据。 CAS(Compare and Swap 比较并交换)就是乐观锁的一种实现方式。
乐观锁
事务在从数据库中取数据时,会将该数据的版本也取出来(v1),当事务对数据变动完毕想要将其更新到表中时,会将之前取出的 版本v1与数据中最新的版本v2相对比,如果v1=v2,那么说明在数据变动期间,没有其他事务对数据进行修改,此时,就允许 事务对表中的数据进行修改,并且修改时version会加1,以此来表明数据已被变动。
如果,v1不等于v2,那么说明数据变动期间,数据被其他事务改动了,此时不允许数据更新到表中,一般的处理办法是通知用 户让其重新操作。不同于悲观锁,乐观锁是人为控制的。
优点:
乐观锁不在数据库上加锁,任何事务都可以对数据进行操作,在更新时才进行校验,这样就避免了悲观锁造成的吞吐量 下降的劣势。
缺点:
乐观锁因为是通过我们人为实现的,它仅仅适用于我们自己业务中,如果有外来事务插入,那么就可能发生错误。
74-乐观锁,悲观锁场景
如果悲观锁和乐观锁都可以使用,那么选择就要考虑竞争的激烈程度:
当竞争激烈(出现并发冲突的概率大)时,
悲观锁更有优势,因为乐观锁在执行更新时频繁失败,需要不断重试,浪费CPU资源。
当竞争不激烈 (出现并发冲突的概率小)时,
乐观锁更有优势,
因为悲观锁会锁住代码块或数据,其他线程无法同时访问,影响并 发,
而且加锁和释放锁都需要消耗额外的资源
悲观锁:因为悲观锁会影响系统吞吐的性能,所以适合应用在写为居多的场景下。
乐观锁:因为乐观锁就是为了避免悲观锁的弊端出现的,所以适合应用在读为居多的场景下。
121-几个锁概念
-
重量级锁
我们要进入一个同步、线程安全的方法时,是需要先获得这个方法的锁的,
退出这个方法时,则会释放锁。
如果获取不到这个锁的话,意味着有别的线程在执行这个方法,
这时我们就会马上进入阻塞的状态,等待那个持有锁的 线程释放锁,
然后再把我们从阻塞的状态唤醒,我们再去获取这个方法的锁。
这种获取不到锁就马上进入阻塞状态的锁,我们称之为重量级锁 -
轻量级锁
重量级锁之所以要加锁,是因为他们害怕自己在这个方法执行的时候,被别人偷偷进来了,
所以只能加锁,防止其他线程进来。
这就相当于,每次离开自己的房间,都要锁上门,人回来了再把锁解开。
这实在是太麻烦了,如果根本就没有线程来和他们竞争锁,
那他们不是白白上锁了?要知道,加锁这个过程是需要操作 系统这个大佬来帮忙的,是很消耗时间的,。
为了解决这种动不动就加锁带来的开销,轻量级锁出现了。
轻量级锁认为,当你在方法里面执行的时候,
其实是很少刚好有人也来执行这个方法的,
所以,当我们进入一个方法的 时候根本就不用加锁,
我们只需要做一个标记就可以了,也就是说,
我们可以用一个变量来记录此时该方法是否有人在 执行。
也就是说,如果这个方法没人在执行,当我们进入这个方法的时候,采用CAS机制,
把这个方法的状态标记为已 经有人在执行,
退出这个方法时,在把这个状态改为了没有人在执行了。