django--redis分布式锁
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
Redis的分布式锁和事务是常用的并发控制机制可以有效地避免多个客户端同时对同一资源进行修改或操作时出现的数据竞争问题。
分布式锁
分布式锁的作用是确保在分布式系统中对同一资源的操作只有一个客户端在执行避免出现并发冲突的情况。在Redis中可以通过setnx命令set if not exists实现分布式锁。当一个客户端想要获得锁时它会尝试通过setnx命令向Redis服务器发送一个写入请求。如果返回值是1则说明该客户端成功获取了锁如果返回值是0则说明该锁已被其他客户端占用该客户端获取锁失败。
为了保证分布式锁的正确性需要在 Redis 中开启事务并对 acquire() 和 release() 方法使用事务。
事务
事务的作用是保证一系列操作的原子性即这些操作要么全部执行成功要么全部不执行避免中间出现异常或错误导致部分操作执行而部分不执行的情况。在Redis中可以通过multi/exec命令实现事务。在执行multi命令之后Redis会将客户端发送的所有命令缓存起来而不是立即执行。直到执行exec命令时Redis才会按照客户端发送的命令顺序依次执行这些命令。
实现逻辑
在Redis中可以使用pipeline方法创建管道使用watch方法监控分布式锁的key。如果发现锁已被其他客户端占用当前客户端会通过continue语句重新尝试获取锁。如果当前客户端成功获取了锁就会在事务中执行一系列操作包括更新数据库、保存缓存等操作。在事务执行完成后当前客户端会释放锁并删除缓存中的相关数据。
以下是一个使用Redis分布式锁和事务的示例代码使用 Redis 分布式锁来确保同一时刻只有一个请求能够执行关键代码并且在执行关键代码之前检查锁的状态以避免竞争条件
# 创建redis客户端
redis_client = redis.StrictRedis(host='localhost', port=6379, db=0)
# 加锁
lock_key = f"borrow_lock_{book_id}"
is_locked = redis_client.setnx(lock_key, 1)
if not is_locked:
# 获取锁失败
return HttpResponse('borrowing in progress')
try:
with redis_client.pipeline() as pipe:
while True:
try:
# 开启事务
pipe.watch(lock_key)
if not pipe.exists(lock_key):
continue
# 尝试获取锁
pipe.multi()
pipe.expire(lock_key, 1)
pipe.execute()
redis_lock = redis_client.lock(lock_key, timeout=1)
if redis_lock.acquire():
# 加锁成功执行代码
pass
break
except redis.WatchError:
continue
except Exception as e:
print(e)
finally:
# 释放锁
redis_client.delete(lock_key)
示例的代码解析如下
1.创建 Redis 客户端连接到 Redis 服务器。
2.定义一个锁的 key由 book_id 加上固定前缀 borrow_lock_ 组成。
3.使用 setnx() 方法尝试获取锁。如果获取失败则说明有其他请求已经获取了锁这里直接返回 HttpResponse(‘borrowing in progress’)。
4.如果获取锁成功则使用 Redis 事务来执行关键代码。在事务开始之前首先使用 watch() 方法监视锁的状态如果锁已经被其他请求释放则事务会回滚并重试。
5.在事务中首先使用 exists() 方法检查锁是否仍然存在如果不存在则重试。
6.如果锁仍然存在则使用 multi() 方法开启事务。然后设置锁的过期时间为 1 秒并执行事务。
7.在事务执行成功后使用 lock() 方法创建一个 Redis 锁设置超时时间为 1 秒并尝试获取锁。如果获取成功则说明当前请求获得了锁可以执行关键代码。如果获取失败则说明锁已经被其他请求获取当前请求需要重试。
8.在成功获取锁之后执行关键代码。在这里首先查询 book_id 对应的 Book 对象然后根据该 Book 对象创建一个 BorrowRecord 对象并将其保存到数据库中。接着将 book_numbers 减 1并保存到数据库中。
9.在完成关键代码的执行后查询数据表并返回给客户端。
10.在 finally 语句块中释放锁这里使用 delete() 方法删除锁的 key。
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |