宝塔服务器面板,一键全能部署及管理,送你10850元礼包,点我领取

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方法,不允许定义函数,不允许声明全局变量等等.

要注意的是Lua中 0 为 true。

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

  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)实现

高并发-Redis分布式锁setnx,setex连用-冯金伟博客园

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

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

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

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

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

高并发-Redis分布式锁setnx,setex连用-冯金伟博客园

解决方案:

由于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脚本

      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方法执行脚本