gpt4 book ai didi

Redis使用lua脚本实现库存扣减

转载 作者:我是一只小鸟 更新时间:2023-05-16 14:31:32 27 4
gpt4 key购买 nike

为什么使用Lua脚本为什么能合并多个原子操作?

Redis官方文档: https://redis.io/docs/manual/programmability/eval-intro/ 。

  。

Redis 保证脚本的原子执行。在执行脚本时,所有服务器活动在其整个运行期间都被阻止。这些语义意味着脚本的所有效果要么尚未发生,要么已经发生.

脚本提供了几个在许多情况下都很有价值的属性。这些包括:

  • 通过在数据所在的地方执行逻辑来提供局部性。数据局部性减少了整体延迟并节省了网络资源。
  • 确保脚本原子执行的阻塞语义。
  • 启用 Redis 中缺少的或过于小众的简单功能的组合。

Lua 允许您在 Redis 中运行部分应用程序逻辑。这样的脚本可以跨多个键执行条件更新,可能以原子方式组合几种不同的数据类型.

  。

命令行应用Lua

Redis Eval 命令使用 Lua 解释器执行脚本.

这里能帮我们实现 Redis 执行 Lua 脚本的命令有两个,一个是 EVAL,另一个是 EVALSHA.

redis Eval 命令基本语法如下:

                          EVAL script numkeys key [key ...] arg [arg ...] 
                        

其中 EVAL 是命令,script 是我们 Lua 脚本的字符串形式,numkeys 是我们要传入的参数数量,key 是我们的入参,可以传入多个,arg 是额外的入参.

以下尝试演示脚本KEYS和ARGV运行时全局变量之间输入参数的分布:

                          redis> EVAL 
                          
                            "
                          
                          
                            return { KEYS[1], KEYS[2], ARGV[1], ARGV[2], ARGV[3] }
                          
                          
                            "
                          
                          
                            2
                          
                          
                             key1 key2 arg1 arg2 arg3

                          
                          
                            1
                          
                          ) 
                          
                            "
                          
                          
                            key1
                          
                          
                            "
                          
                          
                            2
                          
                          ) 
                          
                            "
                          
                          
                            key2
                          
                          
                            "
                          
                          
                            3
                          
                          ) 
                          
                            "
                          
                          
                            arg1
                          
                          
                            "
                          
                          
                            4
                          
                          ) 
                          
                            "
                          
                          
                            arg2
                          
                          
                            "
                          
                          
                            5
                          
                          ) 
                          
                            "
                          
                          
                            arg3
                          
                          
                            "
                          
                        

  。

但这种方式需要每次都传入 Lua 脚本字符串,不仅浪费网络开销,同时 Redis 需要每次重新编译 Lua 脚本,对于我们追求性能极限的系统来说,不是很完美。所以这里就要说到另一个命令 EVALSHA 了,原生语法如下:

  。

                          EVALSHA sha1 numkeys key [key ...] arg [arg ...]
                        

  。

不同的是这里传入的不是脚本字符串,而是一个加密串 sha1。这个 sha1 是从哪来的呢?它是通过另一个命令 SCRIPT LOAD 返回的,该命令是预加载脚本用的.

 

从脚本与 Redis 交互

可以通过 redis.call() 或从 Lua 脚本调用 Redis 命令 redis.pcall() .

两者几乎一模一样。两者都执行 Redis 命令及其提供的参数(如果这些参数表示格式正确的命令)。但是,这两个函数之间的区别在于处理运行时错误(例如语法错误)的方式。调用函数引发的错误 redis.call() 直接返回给执行它的客户端。相反,调用 redis.pcall() 函数时遇到的错误将返回到脚本的执行上下文,而不是进行可能的处理.

Java客户端应用Lua实例

注意Lua 脚本并不会自动帮你完成回滚操作,如果你的脚本逻辑中包含两步写操作,需要自己去做回滚。好在我们库存扣减的逻辑针对 Redis 的命令就两种,一个读一个写,并且写命令在最后,这样就不存在需要回滚的问题了.

以库存扣减核心操作为例,完成核心 Lua 脚本的编写。其主要实现的功能就是查询库存并判断库存是否充足,如果充足,则做相应的扣减操作,脚本内容如下:

                          -- 调用Redis的get指令,查询活动库存,其中KEYS[
                          
                            1
                          
                          
                            ]为传入的参数1,即库存key
local c_s 
                          
                          = redis.call(
                          
                            '
                          
                          
                            get
                          
                          
                            '
                          
                          , KEYS[
                          
                            1
                          
                          
                            ])

                          
                          -- 判断活动库存是否充足,其中KEYS[
                          
                            2
                          
                          
                            ]为传入的参数2,即当前抢购数量

                          
                          
                            if
                          
                           not c_s or tonumber(c_s) < tonumber(KEYS[
                          
                            2
                          
                          
                            ]) then
   
                          
                          
                            return
                          
                          
                            0
                          
                          
                            
end

                          
                          -- 如果活动库存充足,则进行扣减操作。其中KEYS[
                          
                            2
                          
                          
                            ]为传入的参数2,即当前抢购数量
redis.call(
                          
                          
                            '
                          
                          
                            decrby
                          
                          
                            '
                          
                          ,KEYS[
                          
                            1
                          
                          ], KEYS[
                          
                            2
                          
                          ])
                        

  。

然后我们将 Lua 脚本转成字符串,并添加脚本预加载机制.

预加载可以有多种实现方式,一个是外部预加载好,生成了 sha1 然后配置到配置中心,这样 Java 代码从配置中心拉取最新 sha1 即可.

另一种方式是在服务启动时,来完成脚本的预加载,并生成单机全局变量 sha1。我们这里先采取第二种方式,代码结构如下图所示:

 

  。

以上是将 Lua 脚本转成字符串形式,并通过 @PostConstruct 完成脚本的预加载。然后新增 EVALSHA 方法,如下图所示:

  。

方法入参为活动商品库存 key 以及单次抢购数量,并在内部调用 Lua 脚本执行库存扣减操作。看起来是不是很简单?在写完底层核心方法之后,我们只需要在下单之前,调用该方法即可,具体如下图所示:

   。

脚本缓存

到目前为止,我们已经使用EVAL命令来运行我们的脚本.

每当我们调用时EVAL,我们还会在请求中包含脚本的源代码。重复调用EVAL执行同一套参数化脚本,既浪费网络带宽,也对 Redis 有一定的开销。当然,节省网络和计算资源是关键,因此,Redis 为脚本提供了一种缓存机制.

您执行的每个脚本都EVAL存储在服务器保留的专用缓存中。缓存的内容由脚本的 SHA1 摘要总和组织,因此脚本的 SHA1 摘要总和在缓存中唯一标识它。您可以通过运行EVAL并随后调用来验证此行为INFO.

  。

SCRIPT FLUSH 。

从脚本缓存中移除所有脚本.

SCRIPT LOAD script 。

将脚本 script 添加到脚本缓存中,但并不立即执行这个脚本.

最后此篇关于Redis使用lua脚本实现库存扣减的文章就讲到这里了,如果你想了解更多关于Redis使用lua脚本实现库存扣减的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com