使用redis写一个简单的分布式锁

对于一个进程来说,内部的资源锁有很多的实现方式,也都很高效,对于同一个机器的不同进程做同步的话,也有很多的方式去实现,对于不同机器上部署的程序来说,假如要进行同步,则比较麻烦。

最简单的方式可能就是使用redis了,redis的实现是所有的操作放入单线程中处理的,所以对于并发的请求,都有先后顺序,那么对于实现分布式锁来说有着得天独到的优势。

其实所谓的分布式锁,就是在某个共享节点上做一个资源的标记,当标记不存在的时候,则表示没有其它的请求在操作这个资源,那么就可以获取这个资源,并进行操作,当其余的请求发现这个标记存在的时候,则等待,直到前一个请求将这个标记清楚,则整个流程就走通了。

redis中的SET命令有一个NX支持,就是在不存在的时候,该操作才会成功,于是我们就可以利用这一点去实现上述的想法。

当一个请求想要获取某个锁的时候,我们可以

//  to generate the unique key
u, err := uuid.NewUUID()
if nil != err {
    return err
}
s.lockValue = u.String()
s.lockKey = keyName

//  try to lock
rpl, err := redis.String(conn.Do("SET", keyName, s.lockValue, "NX", "PX", timeout))
if nil != err {
    return err
}
if rpl != "OK" {
    return ErrSingleLockOperationFailed
}

return nil

我们为每个locker分配了一个uuid,并使用SET NX命令来获取锁,当成功后,则该locker就获得了该锁。

解锁也很简单

//  try to unlock
//  avoid to unlock a lock not belongs to the locker
lockValue, err := redis.String(conn.Do("GET", s.lockKey))
if nil != err {
    return err
}
if lockValue != s.lockValue {
    return ErrSingleLockInvalidLockValue
}

rpl, err := redis.Int(conn.Do("DEL", s.lockKey))
if nil != err {
    return err
}

if rpl != 1 {
    return ErrSingleLockLockIsUnlocked
}

return nil

这些就是大概的代码实现。

然而实现其实是很简单的,但是其实里面做了点小工作来避免一个问题。

假如某个locker获得了锁,然而在timeout的时间内没有DEL该锁,那么这个锁就过期自动释放了,也就是其它的请求可以获得这个锁,问题就出现了,假设第二个获取的锁还没有释放,当第一个请求在timeout的事件后释放了该锁,那么第三个请求就可以获得新的锁了,这是个很大的问题,这样就有3个连接获得了锁,这是个很大的问题。

于是在上述代码中,每一个locker都有一个uuid,并且当uuid相等的时候才会去删除该锁,避免了锁被另外一个locker释放的问题。

github

使用Redis实现分布式锁的具体步骤如下: 1. 使用Redis的SETNX命令获取锁。SETNX的作用是设置一个键值对,如果该键不存在,则设置成功并返回1,否则设置失败并返回0。 2. 如果SETNX命令返回1,则表示获取锁成功,可以执行业务代码;否则设置失败,需要等待一段时间后重试。 3. 在业务代码执行完成后,使用Redis的DEL命令释放锁。 4. 为了避免死锁问题,需要设置锁的超时时间,超时时间到了之后锁会自动释放。可以使用Redis的EXPIRE命令来设置锁的超时时间。 下面是一个使用Redis实现分布式锁的示例代码: ``` import redis import time class RedisLock: def __init__(self, redis_client, lock_name, acquire_timeout=10, expire_time=10): self.redis_client = redis_client self.lock_name = f"lock:{lock_name}" self.acquire_timeout = acquire_timeout self.expire_time = expire_time def acquire(self): start_time = time.time() while time.time() - start_time < self.acquire_timeout: if self.redis_client.setnx(self.lock_name, 1): self.redis_client.expire(self.lock_name, self.expire_time) return True time.sleep(0.1) return False def release(self): self.redis_client.delete(self.lock_name) ``` 在使用时,可以创建一个RedisLock对象,然后调用acquire方法获取锁,执行业务代码,最后调用release方法释放锁。 ``` redis_client = redis.Redis(host='localhost', port=6379, db=0) lock = RedisLock(redis_client, 'my_lock') if lock.acquire(): try: # 执行业务代码 pass finally: lock.release() else: print('获取锁失败') ``` 需要注意的是,上面的示例代码只是一个简单的实现,实际应用中需要根据具体需求进行优化和改进。比如,可以使用RedLock算法来解决Redis的单点故障问题,提高分布式锁的稳定性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值