Go-redis:执行 Lua 脚本

go-redis (github.com/redis/go-redis) 支持 Lua 脚本 redis.Script,本文在这里简单展示其在秒杀场景中使用的代码片段。

秒杀场景

在秒杀场景中,一个商品的库存对应了两个信息,分别是总库存量和已秒杀量。可以使用一个 Hash 类型的键值对来保存库存的这两个信息,如下所示:

key: product_id

value: {total: N, ordered: M}

其中,product_id 是商品的编号,total 是总库存量,ordered 是已秒杀量。

因为库存查验 和库存扣减这两个操作要保证一起执行,一个直接的方法就是使用 Redis 的原子操作。

原子操作可以是 Redis 自身提供的原子命令,也可以是 Lua 脚本。因为库存查验和库存扣减是两个操作,无法用一条命令来完成,所以需要使用 Lua 脚本原子性地执行这两个操作。

go 复制代码
package main

import (
 "context"
 "fmt"
 "github.com/redis/go-redis/v9"
)

var stockHIncrBy = redis.NewScript(`
  local key = KEYS[1]
  local change = ARGV[1]

  local values = redis.call("HMGET", key, "total", "ordered")

  local total = tonumber(values[1])
  local ordered = tonumber(values[2])

  if( ordered + change <= total ) then
   redis.call("HINCRBY", key, "ordered", change)
   return change
  end
  return 0
 `)

func main() {
 rdb := redis.NewClient(&redis.Options{
  Addr:     "localhost:6379",
  Password: "",
  DB:       0,
 })

 key := "product_id:10001"                                    // id 为 10001 的商品 key
 value := map[string]interface{}{"total": 100, "ordered": 10} // 插入数据 total 总库存量 ordered 已秒杀量

 ctx := context.Background()
 rdb.HMSet(ctx, key, value) // 设置初始库存

 change := 20 // 库存扣减数量

 keys := []string{key}
 values := []interface{}{change}

 for i := 0; i < 5; i++ { // 尝试扣减库存 5 次
  result, err := stockHIncrBy.Run(ctx, rdb, keys, values...).Int()
  if err != nil {
   // ...
  }
  fmt.Println(result) // 如果返回值是当前请求的库存量,就是成功;如果是 0,就是失败。
 }
}

Lua 脚本的逻辑如下:

  1. 获取商品的 total(总库存量) 和 ordered(已秒杀量) 值。
  2. 检查 ordered(已秒杀量) + change(库存扣减量) 是否小于等于 total(总库存量)。
  • 是:执行 HINCRBY 更新 ordered,返回成功的数量。
  • 否:返回 0,表示库存不足,秒杀失败。

如果当前请求的库存扣减量加上已被秒杀的库存量仍然小于总库存量,就可以更新已秒杀的库存量。根据脚本的返回值,来确定秒杀是成功还是失败了。如果返回值是当前请求的库存量,就是成功了;如果是 0,就是失败。


References

redis.uptrace.dev/zh/guide/lu...

推荐阅读

相关推荐
苍何3 小时前
开源微信流 Windows,微信聊天记录,可以直接给 Codex 和 Obsidan 了
后端
代码什么用5 小时前
Spring基础使用
java·后端·spring
明月_清风5 小时前
Deno 终局来了:从挑战 Node 到被 Cloudflare 收编
前端·后端·node.js
蜗牛互联网6 小时前
Java 17 HttpClient调用文件转写API的超时与失败回退
java·人工智能·后端
用户69371750013848 小时前
2026,程序员的时代拐点到了
android·前端·后端
惜鸟8 小时前
从源码看 pi 的上下文管理:原始数据永久保留,模型视角按需裁剪
后端
量化分析码农8 小时前
【Python量化策略评估体系 #05】极端行情扛得住吗?2008/2015/2020 三场景压力测试
后端
十年Java程序媛8 小时前
Java 接口和抽象类对比|Java8 新特性,抛弃老旧八股,正确选型
java·spring boot·后端
IT_陈寒8 小时前
SpringBoot自动配置差点让我加班到凌晨
前端·人工智能·后端
用户8356290780518 小时前
使用 Python 对 Excel 工作表进行排序
后端·python