go redigo执行lua脚本 实现原子操作

大二小的宝 · · 1356 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

Lua

Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
Lua可以在很多地方使用比如游戏开发、独立应用脚本、Web应用脚本、扩招和数据库插件等等。今天记录的是在redis中执行lua脚本实现原子操作。如果不是专业些lua脚本我们可以看下菜鸟联盟的简单教程,已经够我们平时简单使用了。

使用场景

在redis中使用lua的场景很多,这次记录的使用场景是:开发需要用到redis记录一个有顺序的数据,但是又不能重复添加。所以准备先用一个set记录list存在的值用来判断是否重复(set在redis中是不允许重复的),再用list数据类型进行保存。但是如果分开执行这些语句在高并发的情况下可能会遇到问题,在某种情况下可能会加入重复数据。这个时候就可以用到lua脚本来原子的执行这两条语句。

代码

package main

import (
    "fmt"
    "time"
    "github.com/gomodule/redigo/redis"
)

const(
    SCRIPT_INCR = `
local num = redis.call('sismember', KEYS[1], ARGV[1])
if num > 0
then
    return -1
else
    redis.call('sadd',KEYS[1],ARGV[1])
    return redis.call('lpush',KEYS[2],ARGV[1])
end
`
)

func init()(){
    initRedis()
}

var redisCoon redis.Conn

//初始化redis连接,redis在本地docker中运行
func initRedis()(){
    redisClient := &redis.Pool{
        // 最大空闲链接
        MaxIdle: 10,
        // 最大激活链接
        MaxActive: 10,
        // 最大空闲链接等待时间
        IdleTimeout: 5 * time.Second,
        Dial: func() (redis.Conn, error) {
            rc, err := redis.Dial("tcp", "127.0.0.1:6379")
            if err != nil {
                return nil, err
            }
            rc.Do("SELECT", 0)
            fmt.Println("USE DB", 0)

            return rc, nil
        },
    }
    redisCoon = redisClient.Get()
}

func AddCode(id,code string)(){
    lua := redis.NewScript(2, SCRIPT_INCR)
    in,err :=redis.Int(lua.Do(redisCoon, "u:"+id, "u:set:"+id, code))
    fmt.Println(in,"--",err)
}

func main() {
    AddCode("1","60001")
    AddCode("1","60001")
    AddCode("1","60002")
}

//执行结果
//1 -- <nil>
//-1 -- <nil>
//2 -- <nil>
//redis查询结果
//127.0.0.1:6379> lrange u:set:1 0 -1
//1) "60002"
//2) "60001"
//127.0.0.1:6379> smembers u:1
//1) "60001"
//2) "60002"
在go中使用的是redigo的包来操作redis。redigo自带redis连接池可以方便的定义做大连接数,最大空闲数,最大空闲连接等和超时时间等。我们可以很方便控制这些参数也可以很方便的执行lua脚本。执行步骤如下:
  1. 第一步需要新建一个脚本对象,第一个参数为keycount,标志你的lua脚本参数中key的个数 第二个参数为脚本字符串
  2. 第二步 执行这个脚本,把定义时候的2个key传递过去,之后的参数为脚本中的ARGV
  3. 第三部 使用redis.Int 获取返回值,如果没有返回值直接使用Do即可
脚本接受一个set的key和一个list的key 先判断set是否存在保证没有重复。之后再去往list中添加,避免高并发中加入重复数据。如果已经存在则返回-1表示。如果没有则往set,list中都添加值。

有疑问加站长微信联系(非本文作者)

本文来自:Segmentfault

感谢作者:大二小的宝

查看原文:go redigo执行lua脚本 实现原子操作

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1356 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传