首页 > 数据库 >go go-redis 使用lua保证操作的原子性

go go-redis 使用lua保证操作的原子性

时间:2024-05-25 16:58:48浏览次数:36  
标签:脚本 扣减 redis Redis lua go

   Redis是应对高并发的常用工具,在常用缓存技巧中讲过相关技巧。但有些业务场景,使用Redis会遇到问题,如电商里的秒杀、扣减库存等。

拿减库存举例,一般需要两步:

  • 先扣减库存,获取扣减后的库存值V

  • 如果V小于0,说明库存不够,需要将扣减的值再加回去;如果V大于等于0,则执行后续操作

但这两步是分开的,很可能扣减时成功,但增加回去时失败,导致库存不一致。

另一种方案是:

  • t1时刻,先查询库存,判断是否够用

  • t2时刻,再减库存

但这两步也是分开的,而且t1和t2有时间差,t2时刻扣减库存时,真正的库存和t1时刻已经不一致了。

那如何保证一致性呢?go-redis又如何来解决这个问题呢?

redis中lua基本介绍

   在Redis中通过lua脚本操作Redis,脚本会将多个命令和操作当成一个命令在Redis中执行,也就是说该脚本在执行的过程中,不会被任何其他脚本或命令打断干扰。正是因此这种原子性,lua脚本才可以代替multi和exec的事务功能。同时也是因此,在lua脚本中不宜进行过大的开销操作,避免影响后续的其他请求的正常执行。

go-redis中lua使用


     废话不多说,直接上代码!!!!!!!!

package main

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

var ctx = context.Background()

func main() {
    // 创建 Redis 客户端连接
    rdb := redis.NewClient(&redis.Options{
        Addr:     "localhost:6379", // Redis 服务器地址
        Password: "",               // Redis 访问密码,如果没有设置密码则为空字符串
        DB:       0,                // 选择数据库,默认为0
    })


    // 定义 Lua 脚本 
    luaScript := `
local key = KEYS[1]
local value = redis.call('GET', key)
if value then
    redis.call('INCR', key)
    return redis.call('GET', key)
else
    return redis.error_reply("key does not exist")
end
`

    // 初始化键的值
    rdb.Set(ctx, "mykey", 10, 0)

    // 执行 Lua 脚本
    result, err := rdb.Eval(ctx, luaScript, []string{"mykey"}).Result()
    if err != nil {
        fmt.Println("Lua script execution error:", err)
        return
    }

    // 输出 Lua 脚本执行结果
    fmt.Println("Script result:", result)
}

结论

    使用 go-redis 执行 Lua 脚本可以大大增强 Redis 的功能,确保数据操作的原子性和一致性,提高系统的性能和可靠性。通过这种方式,可以在 Redis 中实现更复杂的业务逻辑,同时简化客户端代码,提高系统的整体效率。

标签:脚本,扣减,redis,Redis,lua,go
From: https://blog.csdn.net/dddsy_it/article/details/139199053

相关文章

  • Django应用创建到启动的简单示例
    一、系统环境和前置安装Ubuntu系统192.168.2.101,客户端192.168.2.100python3及虚拟环境管理库python3-venv创建项目文件创建djangoweb项目配置ALLOW_HOSTS=['*']二、创建并注册app创建django-adminstartappapp1注册app修改项目配置文件settings.py,在INSTALLED_APPS......
  • Unity热更学习--Lua脚本使用C#中的事件、委托和协程
    [14]lua调用使用C#中的事件和委托C#脚本:继续在Student类中声明//声明委托和事件publicUnityActiondele;publiceventUnityActioneventAction;publicvoidDoDele(){if(dele!=null)dele();}publicvoidDoEvent(){if(eventAction!=null)......
  • [SCTF 2021]loginme go语言ssti漏洞
    今天做个新颖的题,go中的ssti问题。进来点击访问/admin/index?id=1发现空白,只有admin能看,看看源码main.go。点击查看代码packagemainimport( "html/template" "loginme/middleware" "loginme/route" "loginme/templates" "github.com/gin-gonic/gin&quo......
  • Redis 源码学习记录:集合 (set)
    无序集合Redis源码版本:Redis-6.0.9,本篇文章无序集合的代码均在intset.h/intset.c文件中。Redis通常使用字典结构保存用户集合数据,字典键存储集合元素,字典值为空。如果一个集合全是整数,则使用字典国语浪费内存。为此,Redis设计了intset数据结构,专门用来保存整数......
  • Redis常问八股
    1.sortedSet底层sortedset中的每一个元素都带有一个score属性可以基于score属性对元素进行排序,底层的实现是一个跳表加hash表。可排序元素不重复查询速度快2.NoSQL和SQLSQL1对数据的格式有严格的约定Table必须定义每个属性的数据格式每个Table必须含有主键只能存储Tab......
  • Redis Stream消息队列
    工具类部分内容packagecom.hwd.campus.common.redis.utils;importcom.hwd.campus.common.redis.constant.RedisKeyPrefixConst;importcom.hwd.campus.common.redis.service.RedisListSelect;importcom.hwd.campus.common.redis.service.RedisSelect;importlombok.AllA......
  • Golang:使用go-resty/resty发送http请求get和post
    Golang:使用go-resty/resty发送http请求get和post原创 吃个大西瓜 CodingBigTree 2024-05-2508:00 北京 听全文 go-resty/resty是一个简单的HTTP和REST客户端,受到Rubyrest-client的启发文档https://github.com/go-resty/resty/安装go get github......
  • Go实战全家桶之八:统一ES服务接口之通用查询嵌套查询之封装与增删改API
    开源 goweb:https://gitee.com/ichub/goweb/settings#index需求UML代码位置测试用例:func(this*TestPageEsRequestSuite)Test002_NestBoolQuery(){varreq1=Default()req1.EsShould().EsMatch("dept_name","olivere")req1.EsTerm(&q......
  • 探索Go语言的原子操作秘籍:sync/atomic.Value全解析
    引言​在并发编程的世界里,数据的一致性和线程安全是永恒的话题。Go语言以其独特的并发模型——goroutine和channel,简化了并发编程的复杂性。然而,在某些场景下,我们仍然需要一种机制来保证操作的原子性。这就是sync/atomic.Value发挥作用的地方。原子性:并发编程的基石​......
  • Django验证码配置与使用
    在用户注册、登录页面,为了防止暴力请求,可以加入验证码功能,如果验证码错误,则不需要继续处理,可以减轻一些服务器的压力使用验证码也是一种有效的防止crsf的方法需要安装扩展:pipinstallpillow验证码效果如下图:验证码视图新建viewsUtil.py,定义函数verifycode此段代码用到了PIL......