应用使用Redis实现多实例进程锁

2 篇文章 0 订阅
1 篇文章 0 订阅

1. redis加锁分类

redis常用的的加锁命令分别是INCRSETNXHSETNXSET

2. 第一种锁命令INCR

这种加锁的思路是, key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作进行加一。
然后其它线程再执行 INCR 操作进行加一时,如果返回的数大于 1 ,说明这个锁正在被使用当中。

1、 客户端A请求服务器获取key的值为1表示获取了锁
2、 客户端B也去请求服务器获取key的值为2表示获取锁失败
3、 客户端A执行代码完成,删除锁
4、 客户端B在等待一段时间后在去请求的时候获取key的值为1表示获取锁成功
5、 客户端B执行代码完成,删除锁

$redis->incr $key
$redis->expire $key $ttl //设置过期时间

3. 第二种锁SETNX

这种加锁的思路是,如果 key 不存在,将 key 设置为 value
如果 key 已存在,则 SETNX 不做任何动作

1、 客户端A请求服务器设置key的值,如果设置成功就表示加锁成功
2、 客户端B也去请求服务器设置key的值,如果返回失败,那么就代表加锁失败
3、 客户端A执行代码完成,删除锁
4、 客户端B在等待一段时间后在去请求设置key的值,设置成功
5、 客户端B执行代码完成,删除锁

$redis->SETNX $key $value
$redis->EXPIRE $key $ttl  //设置过期时间

4. 第三种锁HSETNX

这种加锁的思路是,如果 key下的field 不存在,将 key下的field 设置为 value
如果 key 已存在,则 HSETNX 不做任何动作

1、 客户端A请求服务器设置key下的field的值,如果设置成功就表示加锁成功
2、 客户端B也去请求服务器设置key下的field的值,如果返回失败,那么就代表加锁失败
3、 客户端A执行代码完成,删除锁
4、 客户端B在等待一段时间后在去请求设置key下的field的值,设置成功
5、 客户端B执行代码完成,删除锁

$redis->HSETNX $key $field $value
$redis->EXPIRE $key $ttl  //设置过期时间

5. 第四种锁SET

上面两种方法都有一个问题,会发现,都需要设置 key 过期。那么为什么要设置key过期呢?如果请求执行因为某些原因意外退出了,导致创建了锁但是没有删除锁,那么这个锁将一直存在,以至于以后缓存再也得不到更新。于是乎我们需要给锁加一个过期时间以防不测。
但是借助 Expire 来设置就不是原子性操作了。所以还可以通过事务来确保原子性,但是还是有些问题,所以官方就引用了另外一个,使用 SET 命令本身已经从版本 2.6.12 开始包含了设置过期时间的功能。

1、 客户端A请求服务器设置key的值,如果设置成功就表示加锁成功
2、 客户端B也去请求服务器设置key的值,如果返回失败,那么就代表加锁失败
3、 客户端A执行代码完成,删除锁
4、 客户端B在等待一段时间后在去请求设置key的值,设置成功
5、 客户端B执行代码完成,删除锁

$redis->set($key, $value, array('nx', 'ex' => $ttl));  //ex表示秒

6. 问题

过期问题
锁提前过期后,客户端A还没执行完,然后客户端B获取到了锁,这时候客户端A执行完了,会不会在删锁的时候把B的锁给删掉?

解决办法
在加锁的时候存入的key的value是随机的。这样的话,每次在删除key的时候判断下存入的key里的value和自己存的是否一样再决定是否删除

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
使用Redis实现分布式的常见方法是通过SET命令来获取。下面是一个简单的示例代码,演示了如何使用Redis实现分布式: ```python import redis import time class RedisLock: def __init__(self, redis_client, lock_key, expire_time=10): self.redis_client = redis_client self.lock_key = lock_key self.expire_time = expire_time def acquire_lock(self): # 使用SET命令尝试获取设置过期时间防止死 result = self.redis_client.set(self.lock_key, "locked", nx=True, ex=self.expire_time) return result is not None def release_lock(self): # 使用DEL命令释放 self.redis_client.delete(self.lock_key) # 创建Redis客户端 redis_client = redis.Redis(host='localhost', port=6379, db=0) # 创建分布式实例 lock = RedisLock(redis_client, "my_lock") # 尝试获取 if lock.acquire_lock(): try: # 在内执行需要保护的代码块 print("Do something...") time.sleep(5) # 模拟执行耗时操作 finally: # 释放 lock.release_lock() else: print("Failed to acquire lock") ``` 在上述示例中,`RedisLock`类封装了获取和释放的逻辑。`acquire_lock`方法尝试使用`SET`命令将一个特定的键值对写入Redis,如果写入成功则表示获取到。`release_lock`方法使用`DEL`命令将的键值对从Redis中删除,释放。 需要注意的是,获取之后,需要在适当的时候释放,以防止被其他进程一直持有,导致死。在示例中,使用了`try-finally`语句确保无论代码块是否抛出异常,都会释放。 此外,还可以考虑使用Redlock算法来实现更可靠的分布式。Redlock算法是Redis官方提供的一种分布式算法,可以在多个Redis实例之间实现互斥访问。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值