55

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

 5 years ago
source link: https://studygolang.com/articles/19741?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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中都添加值。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK