锁机制是用来管理多个请求对同一数据进行操作时可能出现的冲突和数据一致性问题。主要分为乐观锁和悲观锁两种类型。
悲观锁假设最坏的情况,即在数据处理过程中,总是假设会发生冲突,因此在数据处理之前先加锁。这种锁通常由数据库自身提供支持,如MySQL的SELECT FOR UPDATE
。
他的主要使用场景是:
乐观锁采用一种宽松的加锁机制,它假设多个事务在大多数时间不会同时修改同一数据。通常是通过版本号或时间戳来实现。每次数据更新过程中,检查版本号或时间戳是否发生变化,如果没有变化,则进行更新;如果已经变化,则放弃更新或重试。
他的主要使用场景是:
乐观锁适用于读操作频繁,写操作相对较少的场景。悲观锁适用于写操作较为频繁,且并发写入的概率较高的场景。
乐观锁和悲观锁还有个区别:乐观锁因为比较乐观,所以一般是先做业务逻辑操作,比如参数处理,内存中进行模型组装调整,然后再去更新数据库。悲观锁因为比较悲观,所以会先尝试加锁,然后再去做业务逻辑操作。
也就是说,乐观锁是先干活,后加锁。悲观锁是先加锁,再干活。
而高并发的写操作时,你干了一大堆活,把模型都组装好了,内存计算也都做完了,结果最后去数据库那更新的时候发现版本号变了。这不是大冤种吗?
所以,应该是先尝试获取锁,如果获取锁成功,再进行业务操作,否则就直接返回失败。这样可以做fail-fast。
综上,在高并发场景中,一般来说并发写入的冲突较为频繁,所以建议优先考虑悲观锁。即在做并发操作前,先尝试获取锁,如果获取锁成功,在进行业务操作,否则就直接返回失败。
分布式锁是为了控制分布式系统中多个进程间的执行顺序,用于保护跨多个系统的共享资源。其实,我们前面提到的数据库的Select For Update,也是分布式锁的实现方式的一种。而因为Redis有更好的性能,所以更适合用来做分布式锁。
分布式锁的主要使用场景:
也就是说,用数据库悲观锁的地方,都可以用Redis分布式锁。当然Redis也支持乐观锁。但是一般用的比较少而已。
什么时候用数据库的悲观锁,什么时候用Redis的分布式锁呢?
1、如果是单体应用,在做数据库操作的时候,建议直接基于数据库的悲观锁来做并发控制。如果是分布式应用,二者都可以。
2、如果不想额外引入Redis,也可以直接基于数据库做悲观锁控制。
除了以上两种情况以外,在做并发控制的时候,建议用Redis的分布式锁,而不是数据库的悲观锁(分布式锁)。主要有几个原因:
1、Redis更快,性能更好,可以有更快的响应。
2、Redis实现的分布式锁功能更多,比如可重入,续期等等,这些都是数据库的悲观锁不支持的。
3、数据库悲观锁可能会锁表,影响整体性能。
4、数据库的链接资源要比Redis更加珍贵,建议把这些非业务逻辑操作放到Redis中去抗,而不是用数据库抗。