请选择 进入手机版 | 继续访问电脑版

[Redis] Redis怎样 实现分布式锁

[复制链接]
查看47 | 回复9 | 2021-9-14 00:41:35 | 显示全部楼层 |阅读模式
目次

本日 我们来聊一聊分布式锁的那些事。

信赖 大家对锁已经不陌生 了,我们在多线程环境中,假如 必要 对同一个资源举行 操作,为了避免数据不划一 ,我们必要 在操作共享资源之进步 行加锁操作。在计算机科学中,锁(lock)或互斥(mutex)是一种同步机制,用于在有很多 实行 线程的环境中逼迫 对资源的访问限定 。

比如你去相亲,发现你和一大哥同时和一个女的相亲,那怎么行呢...,搞不好还要被揍一顿。

那什么是分布式锁呢。当多个客户端必要 争抢锁时,我们就必要 分布式锁。这把锁不能是某个客户端本地的锁,否则的话,别的 客户端是无法访问的。以是 分布式锁是必要 存储在共享存储体系 中的,比如Redis、Zookeeper等,可以被多个客户端共享访问和获取。本日 我们就来看一下怎样 使用 Redis来实现分布式锁。

一、前言

在正式开始之前,我们先来相识 两个Redis的下令 :

  1. SETNX key value
复制代码

这个定名 的含义是,当key存在时,不做任何赋值操作;当key不存在时,就创建key,并赋值成value,即(不存在即设置)。

  1. SET key value [EX seconds | PX milliseconds] NX
复制代码

SET后加NX选项,就和SETNX下令 雷同 了,也实现不存在即设置的功能。此外,这个下令 在实行 时,可以通过EX或者PX设置键值对的过期时间。

二、正文

开始之前,我们先引入一个场景:

假设要给某个商品举行秒杀活动,我们事先把库存数据100已经存入到了redis中,我们现在 必要 来举行 库存扣减。

如图所示,我们假设有1000个客户端来举行 库存扣减操作,那我们该怎样 做,才能保证库存扣减次序 划一 且不会超扣呢。

Redis怎样
实现分布式锁

我们起首 想到的就是加锁,在举行 库存扣减之前,我们先拿到锁,然后举行 扣减,末了 再开释 锁。在redis中我们创建一个key来代表一个锁变量,然后对应的值来表示锁变量的值。我们来看一下怎样 举行 加锁。

假设1000个客户端同时举行 加锁哀求 。由于 redis使用 单线程来处理哀求 ,以是 redis会串行实行 他们的哀求 操作。假设redis先处理客户端2的哀求 ,读取lock_key的值,发现lock_key为0,以是 客户端2就把lock_key的value设置成1,表示已经举行 了加锁操作。假如 此时客户端3被处理,发现lock_key的值已经为1了,以是 就返回加锁失败的信息。

Redis怎样
实现分布式锁

当拿到锁的客户端2处理完共享资源后,就要举行 开释 锁的操作,开释 锁很简单,就是将lock_key重新设置为0。

由于加锁操作包含了三个操作(读取锁变量、判断 锁变量的值以及把锁变量的值设置成1),而这三个操作在实行 的过程中必要 保证原子性。那怎么保证原子性呢?

我们可以使用 SETNX下令 来实现加锁操作,SETNX下令 表示key不存在时就创建,key存在时就不做任何赋值操作,当加锁时间 ,我们实行

  1. SETNX lock_key 1
复制代码

对于开释 锁操作来说,我们可以使用 DEL下令 来删除锁变量。比如客户端2举行 加锁,实行 SETNX lock_key 1,假如 lock_key不存在,则会创建lock_key,返回加锁成功,此时客户端2可以举行 共享资源的访问。假如 这时客户端1来发起哀求 加锁操作,而此时lock_key已经存在,SETNX lock_key 1不做任何赋值操作操作,返回加锁失败,以是 客户端1加锁失败。当客户端2实行 完共享资源访问后,实行 DEL下令 来开释 锁。此时当有别的 客户端再来访问时,lock_key已经不存在了,就可以举行 正常的加锁操作了。以是 ,我们可以使用 SETNX和DEL下令 组合来举行 加锁和开释 锁的操作。

不过这里有两个题目 :

1.当某个客户端实行 完SETNX下令 、加锁后,此时发生了非常 ,结果 不停 没有实行 DEL操作下令 来开释 锁。因此,这个客户端不停 占用着这个锁,别的 客户端无法拿到锁。

办理 这个题目 ,一个有效 的方法就是,给锁变量设置一个过期时间。如许 一来,即使持有锁的客户端发生了非常 ,无法自动 的开释 锁,Redis也会根据锁变量的过期时间把它删除。别的 客户端在锁变量过期后,就可以重新举行 加锁操作了。

2.假如 客户端1实行 了SETNX下令 加锁后。假如 此时客户端2实行 DEL下令 删除锁,这时,客户端A的锁就被误开释 了。这是我们不能担当 的。

为相识 决这个题目 ,我们必要 能区分来自不同客户端的锁操作。我们该怎样 做呢?我们可以给每个客户端天生 一个唯一值,在举行 加锁时,我们把锁变量赋值成这个唯一值。如许 在开释 锁的时间 ,客户端必要 判断 ,当前锁变量的值是否和本身 的唯一标知趣 等,在相称 的环境 下,才能开释 锁。

下面来看一下怎样 在Redis中举行 实现。我们可以使用 SET加EX/PX和NX选项,来举行 加锁操作。

  1. SET lock_key uuid NX PX 100
复制代码

 此中 lock_key是锁变量,uuid表示客户端的唯一标识,PX 100表示100ms过期。由于我们在开释 锁时必要 对比客户端的标识和锁变量的值是否划一 ,这包含了多个操作,为了保证原子性,我们必要 使用 lua脚本,下面是lua脚本的实现。

  1. if redis.call("get",KEYS[1]) == ARGV[1] then
  2. return redis.call("del",KEYS[1])
  3. else
  4. return 0
  5. end
复制代码

此中 KEY[1]表示lock_key,ARGV[1]表示当前客户端的唯一标识,这两个值是我们在实行 lua脚本时作为参数传入的。下面我们来看一下完备 的代码实现。

  1. import redis
  2. import traceback
  3. import uuid
  4. import time
  5. class Inventory(object):
  6. def __init__(self):
  7. pool = redis.ConnectionPool(host='localhost', port=6379, decode_responses=True)
  8. client = redis.StrictRedis(connection_pool=pool, max_connections=20)
  9. self.client=client
  10. self.uuid=str(uuid.uuid1())
  11. print(self.uuid)
  12. self.key="lock_key"
  13. self.inventory_key="inventory"
  14. def unlock(self):
  15. unlock_script="" \
  16. "if redis.call("get",KEYS[1]) == ARGV[1] then" \
  17. " return redis.call("del",KEYS[1])" \
  18. "else" \
  19. " return 0 " \
  20. "end"
  21. try:
  22. unlock_cmd=self.client.register_script(unlock_script)
  23. result=unlock_cmd(keys=[self.key],args=[self.uuid])
  24. if result==1:
  25. print("释放成功")
  26. else:
  27. print("释放出错")
  28. except:
  29. print(traceback.format_exc())
  30. def lock(self):
  31. try:
  32. while True:
  33. result=self.client.set(self.key,self.uuid,px=100,nx=True)
  34. print(result)
  35. if result==1:
  36. break
  37. print("sleep 1s")
  38. time.sleep(1)
  39. print("加锁成功")
  40. return True
  41. except:
  42. print(traceback.format_exc())
  43. def inventory(self):
  44. if self.lock():
  45. print("库存扣减")
  46. self.client.decr(self.inventory_key)
  47. print("扣减完成")
  48. self.unlock()
  49. inv=Inventory()
  50. inv.inventory()
复制代码

到此,我们就把Redis实现分布式锁就聊完了。既然都读到了这里,不妨给个「三连」吧,你的三连就是我最大的动力。

到此这篇关于Redis怎样 实现分布式锁的文章就先容 到这了,更多干系 Redis 分布式锁内容请搜索 脚本之家从前 的文章或继续欣赏 下面的干系 文章渴望 大家以后多多支持脚本之家!


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

avatar 陈辞滥调 | 2021-9-14 06:57:04 | 显示全部楼层
admin楼主是好人!
回复

使用道具 举报

avatar Mionsterv | 2021-10-2 15:33:35 | 显示全部楼层
admin楼主今年多大了?
回复

使用道具 举报

avatar Vonice | 2021-10-3 05:24:00 | 显示全部楼层
支持一下!
回复

使用道具 举报

avatar 劳心忉忉卫 | 2021-10-3 12:05:56 | 显示全部楼层
admin楼主你想太多了!
回复

使用道具 举报

avatar 马马虎虎770 | 2021-10-4 08:19:34 | 显示全部楼层
admin楼主又闹绯闻了!
回复

使用道具 举报

avatar 紫罗兰的叶栏 | 2021-10-6 15:57:59 | 显示全部楼层
楼上是GG还是MM啊?
回复

使用道具 举报

avatar 岳兄弟散养家鸡 | 2021-10-11 14:19:19 | 显示全部楼层
在这个版块混了这么久了,第一次看见这么给你的帖子!
回复

使用道具 举报

avatar 贺老师 | 2021-10-15 20:47:06 | 显示全部楼层
读了admin楼主的帖子,顿时马桶就通了。。。
回复

使用道具 举报

avatar 木易549 | 前天 07:13 | 显示全部楼层
今天的心情很不错啊
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则