MySQL的锁Block
所谓锁,就是锁定住某一部分,其他人都不许动,从类别上来讲分为三种,不同的引擎支持不一样
- 表锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
- 行锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
- 页锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。
引擎 | 支持的锁 |
---|---|
MyISAM | 表级锁 |
BDB | 表锁/页锁 |
InnoDB | 表锁/页锁/行锁 |
乐观锁与悲观锁
定义
乐观锁(Optimistic Lock): 就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。
悲观锁(Pessimistic Lock): 就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。
悲观锁使用
如果总觉得有刁民想害朕,就是用悲观锁,使用悲观锁有两个前提条件
- 必须是处于事务状态或者数据库配置关闭了Autocommit
- 使用特殊的SELECT语句
1 | SELECT ..... FOR UPDATE |
在SELECT后加上 FOR UPDATE 就会进行加悲观锁,一旦加了悲观锁,其它事务的SELECT语句就会被刮起,直到加锁事务commit或者rollback结束事务
乐观锁的使用
乐观锁的使用的前提条件同悲观锁,但是特殊的SELECT语句不同
1 | SELECT ..... IN SHARE MODE |
在SELECT后加 IN SHARE MODE 就会进行加乐观锁
为什么要使用乐观锁和悲观锁
在我们的业务逻辑中一般存在两种更新
- 状态的更新
- 数目的更新
我们以淘宝为例,常见的商品有三个状态:原价/半价/秒杀 以及商品的剩余数量,作为店家我操作商品会出现以下情况
状态的更新
不管我对商品做了几次修改,当前商品都以最后一次修改为准,不关心之前修改了几次,这种是状态的更新
数目的更新
而对于商品数量
- 如果0.01秒内有3个人同时买我的商品
- 我的商品还剩下2个
- 按照逻辑肯定有一个人会购买不成功
所以说我3个人购买前都要确认下商品是否还有剩余,因为0.01秒对于电脑来说可能根本分不出先后,如果不确认还有没有,很可能3个人都购买成功了,你却没货。
两种更新的操作
状态的更新
因为状态更新,只有你店家一个人操作,不太可能出现在0.01秒内切换3次状态的情况,所以说我们的SQL语句只需要
1 | //先看看当前的商品状态是什么 |
数目的更新
而商品数量的变化,很可能在你秒杀时有很多人再买。出现了只有2个存货,结果3个人同时买的情况。
1 | //第一个人:查看当前的商品数量还有2个 |
以上是应该执行的正确顺序,但是由于0.01秒时间实在是太短,很有可能数据库实际发生的情况是
1 | //第一个人:查看当前的商品数量还有2个 |
可以发现 当第三个人查询商品剩余数量时,第二个人的更新还没有完成,所以会导致业务层认为还有剩余
所以要使用乐观锁和悲观锁
根据上文分析
- 在状态更新时不管发生几次改变,都以最后一次为准,但是要记录改变的次数
- 在数量更新时第三个人的SELECT确保在第二个人UPDATE之后执行
那么 乐观锁 和 悲观锁 就是为了这样的两个需求而诞生的,但是不要忘记,前提是这两个锁都要 配合事务 进行操作