悲观锁和乐观锁

概念不多说,直接上例子:

背景:某商品库存量为1,现在又两个人几乎同时下单。

锁这种东西,就是在这种场景下,避免两个人都下单成功,而实际上后台只有一份资源的情况。

而乐观锁、悲观锁这两个概念,其实也只是两种思维模式,而非某种具体的功能一类的东西。以上场景下,乐观锁每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。

1
2
3
4
5
6
$goods = Goods::where('id', 1)->first();
if ($goods->left_count > 0) {
// 判断该类产品剩余量大于0,可以被购买,执行购买程序,并讲库存量 -1;
} else {
// 购买失败
}

而悲观锁每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

1
2
// 带条件执行,执行结果,只有库存量大于0的时候才卖得出去,result 可以得到执行结果从而返回是否购买成功。
$result = Goods::where('id', 1)->where('left_count', '>', 0)->decrement('left_count');

容易看到,一但发生并发事件,在乐观锁中两个进程同时读取了剩余量 N,并同时卖出了1份产品,各自执行了一次 left_count = N-1 的更新操作,实际上卖出了两份但数据结果更新只减少了一份。这就会导致数据发生异常。所以悲观锁可以避免此类情况的发生,尤其是在写入频次较高、并发很有可能出现的场景中,有必要使用悲观锁。

但是如果对应业务在实际使用场景中确实不会发生并发类的情况,写入频次低而读取频次高,那么使用乐观锁可以减少加锁操作带来的额外消耗,从而相对的提高数据读取的效率。

所以两个锁的概念,并不存在真正的孰优孰劣,只能是看情况选择~