Redis 分布式锁存在什么问题 ?如何解决 ?

目录

1. 如何实现分布式锁

2. Redis 分布式锁存在什么问题

2.1 解决死锁问题

2.2 解决锁误删问题


1. 如何实现分布式锁

Redis 天生就可以作为一个分布式系统来使用,所以它实现的锁都是分布式锁。

Redis 可以通过 setnx(set if not exists)命令实现分布式锁~

  • setnx mylock true  -  加锁
  • del mylock  -  释放锁

通过执行结果是否为 1 可以判断是否成功获取到锁~

2. Redis 分布式锁存在什么问题

Redis 分布式锁存在两个问题:

1.死锁问题:未设置过期时间锁忘记释放加锁后还没来的及释放锁就宕机了都会导致死锁问题.

2. 锁误删问题:设置了超时时间,但是线程执行超过超时时间后锁误删问题.

2.1 解决死锁问题

        MySQL 中解决死锁问题是通过设置超时时间,Redis 也是如此,但是问题来了,第一步先加锁,然后再设置超时时间,那么就不满足原子性了,那么怎么办 ?

        官方在 Redis 2.6.12 版本之后,新增了一个功能,我们可以使用一条命令既执行加锁操作,又设置超时时间:setnx expire

  • 第一条命令成功加锁,并设置 30 s 过期时间
  • 第二条命令跟在第一条命令后,还没有超过 30s,所以获取失败

2.2 解决锁误删问题

锁误删问题是解决死锁问题带来的问题,如何理解 ?

两个线程同时获取锁,线程 1 获取到锁了,锁的过期时间为 10s,而线程 1 需要执行 12s,第 10s 的时候,线程 2 获取到锁了(并发执行),线程 2 在执行过程中,2s 后,线程 1 执行完,执行力释放锁的动作,导致把线程 2 的锁给释放了。

既然知道了什么是锁误删问题,那么如何解决 ?

答:可以通过添加锁标识来解决.

        前面我们使用 set 命令的时候,只使用到了 key,那么可以给 value 设置一个标识,表示当前锁归属于那个线程,例如 value=thread1,value=thread2.....

但是这样解决依然存在问题,因为新增锁标识之后,线程在释放锁的时候,需要执行两步操作了:

  1. 判断锁是否属于自己
  2. 如果是,就删除锁

这样就不能保证原子性了,那该怎么办?

【解决方案】

  1. 使用 lua 脚本来解决 (Redis 本身就能保证 lua 脚本里面所有命令都是原子性操作)
  2. 使用 Redisson 框架来解决(主流)

那么 Redisson 如何实现分布式锁呢 ?

【代码示例】

1.引入 Redisson 依赖

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.23.2</version>
</dependency>

2.创建 RedissonClient 对象

@Configuration
public class RedissonConfig {
    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        // 如果有密码需要设置密码
        return Redisson.create(config);
    }
}

3.调用分布式锁

@RestController
public class LockController {
    @Resource
    private RedissonClient redissonClient;

    @RequestMapping("/lock")
    public String lockResource() throws InterruptedException {
        String lockKey = "myLock";
        // 获取锁
        RLock lock = redissonClient.getLock(lockKey);
        try {
            // 超时时间 10s, [tryLock 获取成功才需要释放锁,获取失败不需要释放锁]
            boolean isLocked = lock.tryLock(20, TimeUnit.SECONDS);
            if(isLocked) {
                // 成功获取到锁
                try {
                    TimeUnit.SECONDS.sleep(5);
                    return "成功获取到锁,并执行业务代码";
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    // 释放锁
                    lock.unlock();
                }
            } else {
                // 获取锁失败
                return "获取锁失败";
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "获取锁成功";
    }
}

启动项目,使用 8080 端口访问接口:


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Redis分布式锁的实现可以使用Redis的SETNX命令(SET if Not eXists)来完成。SETNX命令在键不存在时设置键的值,如果键已经存在则不执行任何操作。 以下是使用SETNX命令实现Redis分布式锁的示例代码(使用Python语言): ```python import redis def acquire_lock(redis_conn, lock_key, expire_time): # 使用SETNX命令尝试获取锁 lock_acquired = redis_conn.setnx(lock_key, 1) if lock_acquired: # 设置锁的过期时间 redis_conn.expire(lock_key, expire_time) return True else: return False def release_lock(redis_conn, lock_key): # 释放锁,删除键 redis_conn.delete(lock_key) # 创建Redis连接 redis_conn = redis.Redis(host='localhost', port=6379, db=0) # 获取锁 if acquire_lock(redis_conn, 'mylock', 10): try: # 执行业务逻辑 print("Lock acquired. Do something here...") finally: # 释放锁 release_lock(redis_conn, 'mylock') else: print("Failed to acquire lock. Another process holds the lock.") ``` 以上代码中,`acquire_lock`函数尝试获取锁并设置过期时间,如果成功获取到锁则返回True,否则返回False。`release_lock`函数用于释放锁,即删除键。 在使用分布式锁时,可以根据具体业务需求设置合适的锁键(`lock_key`)和过期时间(`expire_time`)。确保在获取到锁之后,执行业务逻辑的代码在适当的位置调用`release_lock`函数释放锁,避免锁一直被占用而无法释放。 需要注意的是,Redis的SETNX命令是原子操作,可以确保在并发情况下只有一个客户端能够成功获取到锁。同时,设置合适的过期时间能够避免因为异常情况导致锁一直被占用而无法释放。 这只是一个简单的示例,实际使用中还需要考虑异常处理、加锁失败的重试策略以及其他线程安全的因素,以确保分布式锁的可靠性和性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Master_hl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值