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...

推荐阅读

相关推荐
小码哥_常4 小时前
一套自己部署的 IM,客户端是同一份 Flutter
前端·后端
学长毕设6 小时前
基于SpringBoot的实验室资产管理系统
java·spring boot·后端
努力的小雨9 小时前
给 AI 眼镜开发应用有多难?我从零做了一个陪练,还上架了
后端
excel10 小时前
双 Token 与前端使用 Crypto 公私钥鉴权换 Token,哪种方案更好?
前端·后端
AI入职记11 小时前
AI 代码审查召回率实测:20 个骗过单元测试的变异体,抓到 13 个
后端
浮链序11 小时前
别让 AI 客服把用户当猴耍:百来行 Python 把「转人工」做成一条高可用链路
后端
属于自己的天空11 小时前
Claude Code 插件完全指南:官方市场怎么用,怎么自己写(v2.1.269+)
后端
Tokenfree11 小时前
给大模型调用加一个熔断器:60 行代码,三个阈值
后端
Jason_chen11 小时前
Linux 进程调度器深入解析与实时性优化指南(基于 Linux 5.10+)
后端
1360967572311 小时前
三个 Agent 干同一个活,职责边界到底该怎么划
后端