redis的opsForValue 作为锁实现功能和Redisson区别

一、前言
分布式锁是一种用于协调分布式系统中多个节点之间对共享资源进行访问控制的机制。它可以确保在分布式环境下,同一时间只有一个节点能够获取到锁,并且其他节点需要等待释放锁后才能获取。

以下是使用分布式锁的几个常见场景和原因:

避免资源冲突:当多个节点需要同时对共享资源进行读写操作时,使用分布式锁可以确保同一时间只有一个节点能够执行写操作,避免数据冲突和一致性问题。

防止重复处理:在某些业务场景中,可能会出现重复处理的问题,例如订单支付、秒杀等。使用分布式锁可以确保同一时间只有一个节点能够处理该任务,避免重复处理和产生脏数据。

控制资源并发:某些资源的并发操作会导致性能问题,如数据库的并发写操作。使用分布式锁可以限制对资源的并发访问,提高系统的稳定性和性能。

避免死锁:在分布式环境下,由于网络延迟等原因,可能会发生死锁的情况。使用分布式锁可以避免死锁问题的发生,确保资源的正确释放。
二、使用redisTemplate.opsForValue().setIfAbsent
1获取锁:

客户端通过在Redis中设置一个特定的键,作为锁的标识,并设置过期时间以避免死锁情况。这个操作可以通过Redis的SETNX命令来实现。如果SETNX命令返回1,表示锁获取成功,客户端可以继续执行相应的业务逻辑;如果返回0,表示锁已经被其他客户端持有,客户端需要等待或进行重试操作。
可以通过Redis的SET命令设置锁的过期时间,以防止锁一直被持有而导致死锁。设置过期时间可以保证即使持有锁的客户端发生异常退出,锁也会在过期时间后自动释放。
2释放锁:

客户端完成业务操作后,通过DEL命令删除锁的键,即可释放锁。只有持有锁的客户端才能删除锁,以避免误删其他客户端的锁。
下面是一个示例:

@Component
public class DistributedLock {
    private static final String LOCK_KEY = "my_lock";
    private static final long EXPIRE_TIME = 30000; // 锁的过期时间,单位为毫秒
    private static final long WAIT_TIME = 1000; // 获取锁时的等待时间,单位为毫秒
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    public boolean acquireLock() throws InterruptedException {
        long start = System.currentTimeMillis();
        while (true) {
            Boolean success = redisTemplate.opsForValue().setIfAbsent(LOCK_KEY, "locked", EXPIRE_TIME, TimeUnit.MILLISECONDS);
            if (success != null && success) {
                return true;
            }
            
            long current = System.currentTimeMillis();
            if (current - start > WAIT_TIME) {
                return false;
            }
            
            Thread.sleep(100); // 等待一段时间后进行重试
        }
    }
    
    public void releaseLock() {
        redisTemplate.delete(LOCK_KEY);
    }
}

在上述代码中,acquireLock方法尝试获取分布式锁,如果成功获取,则返回true;如果超过等待时间仍未获取到锁,则返回false。releaseLock方法用于释放锁。
使用setIfAbsent的缺点
使用redisTemplate.opsForValue().setIfAbsent()方法实现分布式锁存在以下缺点:

可靠性问题:使用redisTemplate.opsForValue().setIfAbsent()方法实现分布式锁时,需要手动编写代码来处理锁的获取和释放,容易出现人为的错误,如忘记释放锁、锁的过期时间设置不正确等。而Redisson框架提供了更加可靠的分布式锁实现,内部封装了各种功能的锁,并提供了易于使用的API,能够确保锁的可靠性和正确性。

功能限制:redisTemplate.opsForValue().setIfAbsent()方法只能实现简单的锁功能,无法支持更复杂的功能,如可重入锁、公平锁、红锁和读写锁等。而Redisson框架提供了丰富的分布式锁实现方式,可以根据实际需求选择适用的锁类型。

性能问题:redisTemplate.opsForValue().setIfAbsent()方法实现分布式锁时,每次都需要与Redis服务器进行通信,可能会造成较高的网络开销和延迟。而Redisson框架通过内部的优化和封装,能够提供更高效的分布式锁实现,减少与Redis服务器的通信次数和网络开销。

可拓展性问题:使用redisTemplate.opsForValue().setIfAbsent()方法实现分布式锁时,随着业务的发展和变化,可能需要添加更多的功能和特性,而手动编写的代码可能无法满足新的需求。而Redisson框架提供了丰富的锁实现,同时也支持自定义锁的扩展,能够更好地适应业务的变化和拓展。

三、使用redisson实现分布式锁
通过Redisson框架可以方便地实现分布式锁。Redisson是一个基于Redis的分布式Java对象和服务框架,提供了丰富的分布式锁的实现方式。

要使用Redisson实现分布式锁,需要完成以下步骤:

1 引入Redisson依赖:在项目的pom.xml文件中添加Redisson的依赖。

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.14.0</version>
</dependency>

2 创建RedissonClient对象:在Spring Boot中,可以通过Redisson的Spring支持来创建RedissonClient对象。

@Configuration
public class RedissonConfig {
    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://localhost:6379");
        return Redisson.create(config);
    }
}

在上述代码中,创建了一个RedissonClient对象,并配置了连接Redis的地址。

3 实现分布式锁:
使用RedissonClient对象获取RLock对象,RLock是Redisson提供的分布式锁接口。
通过RLock对象的lock方法来获取锁,并在获取锁成功后执行业务逻辑。
通过RLock对象的unlock方法来释放锁。
下面是一个示例:

@Service
public class DistributedLockService {
    @Autowired
    private RedissonClient redissonClient;
    
    public void executeWithLock() {
        RLock lock = redissonClient.getLock("my_lock");
        try {
            lock.lock();
            // 执行业务逻辑...博客原文:https://blog.csdn.net/qq_27471405/article/details/134109185
        } finally {
            lock.unlock();
        }
    }
}

在上述代码中,executeWithLock方法通过redissonClient获取了一个名为"my_lock"的锁,并通过lock方法获取锁。在获取锁成功后,可以执行业务逻辑。最后,通过unlock方法释放锁。

Redisson还提供了其他一些功能强大的分布式锁实现方式,如可重入锁、公平锁、红锁、读写锁等。这些锁的实现方式更加灵活和强大,可以根据实际需求进行选择和使用。

使用Redisson实现分布式锁时,需要确保Redis服务器的可用性和稳定性,以避免单点故障导致的锁失效或锁的不稳定情况。此外,还需要根据具体的应用场景和需求,合理设置锁的过期时间,避免锁的长时间占用。

  1. 可重入锁(Reentrant Lock):
    可重入锁是指同一个线程可以多次获得同一个锁,而不会发生死锁。Redisson的可重入锁实现是基于Redis的分布式锁的一种特例。
@Service
public class ReentrantLockService {
    @Autowired
    private RedissonClient redissonClient;
    
    public void executeWithReentrantLock() {
        RLock lock = redissonClient.getLock("my_lock");
        try {
            lock.lock();
            // 执行业务逻辑...小小鱼儿小小林的博客测试
            executeWithReentrantLock();
        } finally {
            lock.unlock();
        }
    }
}

在上述代码中,使用redissonClient获取了一个名为"my_lock"的可重入锁,并通过lock方法获取锁。在获取锁成功后,可以执行业务逻辑,包括递归调用executeWithReentrantLock方法。最后,通过unlock方法释放锁。

  1. 公平锁(Fair Lock):
    公平锁是指按照线程请求锁的顺序来分配锁。Redisson的公平锁实现可以保证多个线程按照先后顺序获取锁。
@Service
public class FairLockService {
    @Autowired
    private RedissonClient redissonClient;
    
    public void executeWithFairLock() {
        RLock lock = redissonClient.getFairLock("my_lock");
        try {
            lock.lock();
            // 执行业务逻辑...小小鱼儿小小林的博客测试
        } finally {
            lock.unlock();
        }
    }
}

在上述代码中,使用redissonClient获取了一个名为"my_lock"的公平锁,并通过lock方法获取锁。在获取锁成功后,可以执行业务逻辑。最后,通过unlock方法释放锁。

  1. 红锁(Red Lock):
    红锁是指在多个Redis节点上获取锁,以提高分布式系统的可靠性和容错性。Redisson的红锁实现是基于Redis的分布式锁的一种优化方式。
@Service
public class RedLockService {
    @Autowired
    private RedissonClient redissonClient;
    
    public void executeWithRedLock() {
        RLock lock1 = redissonClient.getLock("lock1");
        RLock lock2 = redissonClient.getLock("lock2");
        RLock lock3 = redissonClient.getLock("lock3");
        
        RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);
        try {
            redLock.lock();
            // 执行业务逻辑...
        } finally {
            redLock.unlock();
        }
    }
}

在上述代码中,使用redissonClient分别获取了名为"lock1"、"lock2"和"lock3"的锁,并通过RedissonRedLock将这些锁组合成红锁。在获取红锁成功后,可以执行业务逻辑。最后,通过unlock方法释放红锁。

  1. 读写锁(ReadWrite Lock):
    读写锁是指在多线程环境下,对于读操作可以并行进行,对于写操作必须互斥进行。Redisson的读写锁实现提供了读锁和写锁两种操作。
@Service
public class ReadWriteLockService {
    @Autowired
    private RedissonClient redissonClient;
    
    public void readWithReadWriteLock() {
        RReadWriteLock rwLock = redissonClient.getReadWriteLock("my_lock");
        RLock readLock = rwLock.readLock();
        try {
            readLock.lock();
            // 执行读操作...
        } finally {
            readLock.unlock();
        }
    }
    
    public void writeWithReadWriteLock() {
        RReadWriteLock rwLock = redissonClient.getReadWriteLock("my_lock");
        RLock writeLock = rwLock.writeLock();
        try {
            writeLock.lock();
            // 执行写操作...
        } finally {
            writeLock.unlock();
        }
    }
}

在上述代码中,使用redissonClient获取了一个名为"my_lock"的读写锁,并通过readLock方法获取读锁,通过writeLock方法获取写锁。在获取锁成功后,可以执行相应的读操作或写操作。最后,通过unlock方法释放锁。

四、遇到redis单点故障怎么办
当使用Redisson实现分布式锁时,如果遇到Redis服务器的单点故障,可以采取以下解决方案:

1 Redis Sentinel(哨兵模式):Redis Sentinel是Redis官方提供的高可用性解决方案,它通过监控Redis主节点和从节点的状态,实现自动故障转移和故障恢复。在使用Redisson时,可以配置Redis Sentinel来实现高可用性的Redis集群,在主节点故障时,Redis Sentinel会自动将从节点切换为主节点,从而保证分布式锁的可用性。

2 Redis Cluster(集群模式):Redis Cluster是Redis官方提供的分布式解决方案,通过将数据分散到多个节点上进行存储和访问,实现高可用性和横向扩展。使用Redisson时,可以配置Redis Cluster来搭建分布式锁的集群,当某个节点出现故障时,其他节点仍然可以正常工作,确保分布式锁的可用性。

3 使用RedLock算法:RedLock算法是由Redis官方提出的一种多实例锁机制,通过在多个独立的Redis实例之间获取锁,确保锁的可靠性。在使用Redisson时,可以使用RedLock算法来实现分布式锁,通过协调多个Redis实例之间的锁获取和释放,即使部分实例发生故障,仍然可以保证锁的可用性。

4 引入其他高可用的中间件:除了Redis本身的高可用性解决方案,也可以考虑引入其他高可用的中间件,如ZooKeeper、etcd等。在使用Redisson时,可以将这些中间件作为分布式锁的协调中心,用于进行锁的获取和释放操作,以保证分布式锁的可用性。

以上解决方案都需要在配置和部署时做相应的工作,如正确配置Redis Sentinel或Redis Cluster、合理设计Redis实例的数量和分布、选择合适的RedLock算法实现等。同时,还需要在代码实现中考虑异常处理和重试机制,以应对可能出现的故障和异常情况。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容