Redisson分布式锁
Redisson分布式锁
介绍
Redisson是一个在Redis的基础上实现的Java驻留(in-memory)数据网格(In-Memory Data Grid)。它不仅仅是一个客户端,而是一个完整的Redis服务Java实现。Redisson提供了许多分布式Java对象和服务,包括分布式锁、分布式集合、分布式执行服务、发布/订阅模式等。这些服务都使用了Redis作为底层的数据存储和消息传递机制。
Redisson通过Java实现了Redis的大部分命令,并且提供了丰富的API来操作Redis数据。它支持Redis的集群模式,可以在多个Redis节点之间自动进行数据的分片、复制和故障转移,从而实现高可用性和可扩展性。
使用Redisson,开发者可以更加方便地在Java应用中使用Redis的各种特性,而无需关心底层的Redis通信细节。同时,Redisson还提供了许多高级的分布式功能,使得开发者可以更加轻松地构建分布式应用。
请注意,虽然Redisson是一个强大的Redis Java客户端库,但它并不是Redis的官方Java客户端。Redis的官方Java客户端是Jedis。然而,Jedis只提供了Redis的基础功能,而Redisson则提供了更多高级的分布式功能。因此,在选择Redis的Java客户端时,需要根据项目的实际需求来决定使用哪个库。
分布式锁
Redisson分布式锁可以解决多个场景中的问题,主要体现在以下几个方面:
- 资源互斥访问:在分布式应用程序中,当多个节点或线程需要访问共享资源时,如果不加以控制,可能会导致数据不一致或其他并发问题。Redisson分布式锁可以确保在同一时间只有一个线程或节点可以访问和修改资源,从而防止多个节点同时修改共享数据。
- 临界区保护:对于某些关键的代码段或操作,需要确保在执行过程中不会被其他线程或节点打断。使用Redisson分布式锁可以保护这些临界区,确保在锁释放之前,没有其他线程或节点能够进入并执行这些操作。
- 分布式任务调度与事务:在分布式系统中,经常需要协调多个节点或任务以完成某项工作。Redisson分布式锁可以用于同步这些任务或事务的执行,确保它们在正确的顺序和时机下执行,避免出现冲突或不一致的情况。
- 并发控制:对于高并发的场景,Redisson分布式锁可以有效地控制并发访问的数量,确保系统能够平稳地处理大量的请求。通过合理地分配锁和释放锁,可以避免资源过度消耗或系统崩溃的情况。
- 减少线程阻塞和唤醒开销:在某些短时间内竞争激烈的场景中,使用Redisson分布式锁可以减少线程阻塞和唤醒的开销,提高系统的响应速度和吞吐量。
总之,Redisson分布式锁适用于需要确保资源互斥访问、保护临界区、协调分布式任务调度与事务、控制并发访问以及减少线程阻塞和唤醒开销的场景。通过使用Redisson分布式锁,可以大大提高分布式系统的稳定性和可靠性。
Demo
商城项目中一般要对购买的商品进行减库存的操作,如何保证多个线程对同一个商品进行减库存操作时商品的库存数不会出现超卖的现象呢?我们可以使用Redisson分布式锁解决商品超卖问题!简单几行代码搞定…
引入Redisson依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.23.5</version>
</dependency>
测试
测试前商品库存为20

使用jmeter工具测试
我们定义了100个线程数去进行减库存的操作。

未加锁
public void decreaseStock(long id) {
//查询商品库存
Goods goods = baseMapper.selectById(id);
if(goods.getNum() > 0) {
baseMapper.updateWithConstom(id);
log.info("减库存成功");
}else{
log.info("减库存失败");
}
}
出现超卖现象

加锁
public void decreaseStock(long id) {
/**
* getLock()使用的是Redisson的普通分布式锁(RLock),
* 它在竞争锁时没有保证公平性。这意味着多个客户端同时请求锁时,
* 任何一个客户端都有可能获得锁,这可能会导致某些客户端长时间等待。
*/
RLock clientLock = redissonClient.getLock("goods:" + id);
/**
* getFairLock()使用的是Redisson的公平分布式锁(FairLock),
* 它在竞争锁时保证了锁的公平性。这意味着按照客户端请求锁的顺序来分配锁,
* 先请求锁的客户端会先获得锁,这样可以避免某些客户端长时间等待。
* 需要注意的是,公平锁在性能上通常会略低于普通锁,因为它需要维护请求锁的队列和顺序,
* 所以在选择锁机制时需要根据实际需求进行权衡。
*/
// RLock clientLock = redissonClient.getFairLock("goods:" + id);
try {
//加锁
clientLock.lock();
//查询商品库存
Goods goods = baseMapper.selectById(id);
if(goods.getNum() > 0) {
baseMapper.updateWithConstom(id);
log.info("减库存成功");
}else{
log.info("减库存失败");
}
}finally {
//释放自己的锁
if(clientLock.isHeldByCurrentThread()) {
clientLock.unlock();
}
}
}
未出现超卖现象

疑问?
是否已经彻底解决商品超卖问题了呢?
虽然Redisson的分布式锁可以在很大程度上减少或避免商品超卖问题的发生,但并不能完全“彻底”解决超卖问题。这是因为在实际应用中,超卖问题可能受到多种因素的影响,包括但不限于:
- 锁粒度:锁的粒度决定了它所能提供的并发控制级别。在上述代码中,锁是以商品ID为键的,这意味着对于同一件商品的所有并发请求都会被同一个锁控制。但是,如果锁的粒度设置得过细或过粗,都可能影响并发控制的效果。
- 网络延迟:网络延迟可能导致锁的请求和释放操作不是立即生效的。这种情况下,即使有锁机制,仍然有可能发生超卖。
- 数据库并发控制:分布式锁通常用于保护对共享资源的访问,但资源本身(如数据库)也可能有自己的并发控制机制(如事务隔离级别)。如果数据库层面的并发控制不当,也可能导致超卖。
- 锁释放失败:在
finally
块中释放锁是一个很好的做法,但如果在释放锁的过程中出现异常(比如网络问题或Redis服务器故障),那么锁可能不会被正确释放,从而导致其他线程或进程无法获得锁,进而影响业务的正常进行。 - 代码逻辑错误:如果代码中存在逻辑错误,比如查询库存和更新库存不是原子的,那么即使使用了锁,也可能出现超卖的情况。