高并发-Redis分布式锁setnx,setex连用

Lua在Redis中的使用方式

​ redis中内嵌了Lua脚本的解释器,并提供了执行Lua脚本的入口“eval”命令,

​ 格式为 EVAL script numkeys key [key ...] arg [arg...] .

​ 其中eval 为命令, script为执行的命令脚本, numkeys 为脚本中共涉及到的key的数量,后续接收若干个key的输入和若干个arg的输入.

整个脚本中使用KEYS[index],和ARGS[index]来获取实际的输入有点类似于SQL的占位符。另外一层原因由于Redis集群的固有模式导致EVAL在集群中涉及多个KEY的操作时要求所有的KEY都在同一个Hash Solt上,集群环境中调用EVAL Redis会对脚本先做一个的校验。
KEYS[1] KEYS[2],是要操作的键,可以指定多个,在lua脚本中通过KEYS[1], KEYS[2]获取。【特别注意】这些键要现在redis中存在,不然就获取不到对应的值。
ARGV[1] ARGV[2],参数在lua脚本中通过ARGV[1], ARGV[2]获取。

redis 执行Lua的保证

​ Redis中保证对一个Lua脚本执行的完整性,也就是说一个Lua脚本的执行只会有成功和失败,且保证在Redis Server端同时只会有一个Lua脚本在运行,这样就意味着Lua脚本中的操作是一个完整的原子操作,不会伴随中间状态和资源竞争,同时也意味着在Lua脚本中不适合进行一些耗时长的操作.由于有以上的保证,使用Redis来进行一些复杂的原子操作就在合适不过了,setNx方法的局限性也被Redis Lua进行了弥补.

​ Redis对嵌入的Lua做了若干的限制,包保证脚本不对Redis 造成破坏.不提供访问系统状态的库,禁止使用loadfile函数,禁止带有随机性质的命令或者带有副作用的命令, 对随机读命令的结果进行排序,替换math原有的random方法,不允许定义函数,不允许声明全局变量等等.

Copy Highlighter-hljs
要注意的是Lua中 0 为 true

在脚本中调用redis命令
在脚本中可以使用redis.call函数调用Redis命令

Copy Highlighter-hljs
redis.call('set', 'foo', 'bar') local value=redis.call('get', 'foo') --value的值为bar

redis.call函数的返回值就是Redis命令的执行结果

Redis命令的返回值有5种类型,redis.call函数会将这5种类型的回复转换成对应的Lua的数据类型。

Redis Setnx(SET if Not eXists) 命令在指定的 key 不存在时,为 key 设置指定的值。

Redis分布式锁#

  • 分布锁满足两个条件,一个是加有效时间的锁,一个是高性能解锁

  • 采用redis命令setnx(set if not exist)、setex(set expire value)实现

【千万记住】解锁流程不能遗漏,否则导致任务执行一次就永不过期

分布式锁setnx、setex的缺陷,在setnx和setex中间发生了服务down机#

  • 从Redis宕机讲解分布式锁执行的异常场景流程

  • 从Server服务宕机讲解分布式锁执行的异常场景流程

  • 在setnx和setex中间发生了服务down机 那么key将没有超时时间 会一直存在,新的请求永远进不来

解决方案:

由于setnx与setex是分步进行,那么我们将两步合成一步,放在同一个原子中即可

  • 怎么一次性执行过一条命令而不会出现问题,采用Lua脚本

  • Redis从2.6之后支持setnx、setex连用

Lua简介#

  从 Redis 2.6.0 版本开始,通过内置的 Lua 解释器,可以使用 EVAL 命令对 Lua 脚本进行求值。
  * Redis 使用单个 Lua 解释器去运行所有脚本,并且, Redis 也保证脚本会以原子性(atomic)的方式执行:当某个脚本正在运行的时候,不会有其他脚本或 Redis 命令被执行。这和使用 MULTI / EXEC 包围的事务很类似。在其他别的客户端看来,脚本的效果(effect)要么是不可见的(not visible),要么就是已完成的(already completed)。
在 Lua 脚本中,可以使用redis.call()来执行 Redis 命令

Lua脚本配置流程#

  1、在resource目录下面新增一个后缀名为.lua结尾的文件
  2、编写lua脚本

Copy Highlighter-hljs
local lockKey = KEYS[1] local lockTime = KEYS[2] local lockValue = KEYS[3] -- setnx info local result_1 = redis.call('SETNX', lockKey, lockValue) if result_1 == 1 then local result_2= redis.call('SETEX', lockKey,lockTime, lockValue) return result_2 else return 'faild' end

3、传入lua脚本的key和arg
4、调用redisTemplate.execute方法执行脚本

posted @   caibaotimes  阅读(1632)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示
CONTENTS