对于事物在这就不多做概念性的介绍,本人能够去搜寻。

对于Redis的事务简略阐明一下

  • 事务作为一个整体被执行,执行期间不会被其它客户端申请打断
  • 事务中的多个命令打包执行,当做一个整体执行,不会被打断
  • 事务在执行的中途遇到谬误,不会回滚,而是继续执行后续命令
  • 事务中能够交叉查问,依据后果执行不同操作

咱们能够用lua脚本来实现事务处理

在go中应用lua脚本拜访redis时以下几点要留神

  • 脚本返回nil时,Go中失去的是err = redis.Nil(与Get找不到值雷同)
  • 脚本返回false时,Go中失去的是nil,脚本返回true时,Go中失去的是int64类型的1
  • 脚本返回number类型时,Go中失去的是int64类型
  • 传入脚本的KEYS/ARGV中的值一律为string类型,要转换为数字类型应用to_number
  • 脚本返回{"ok": ...}时,Go中失去的是redis的status类型(true/false)
  • 脚本返回{"err": ...}时,Go中失去的是err值,也能够通过return redis.error_reply("My Error")达成

测试代码

cache.go

package rdsimport (    "fmt"    "time"    rds "github.com/gomodule/redigo/redis")type Cache struct {    pool *rds.Pool}func InitCache(rdsAddress string, pwd string) *Cache {    p := &rds.Pool{        MaxIdle:     5,        MaxActive:   30,        IdleTimeout: 240 * time.Second,        Dial: func() (rds.Conn, error) {            return rds.Dial("tcp", rdsAddress, rds.DialPassword(pwd))        },    }    return &Cache{        pool: p,    }}func (cache *Cache) GetCon() rds.Conn {    return cache.pool.Get()}func (cache *Cache) Close() {    cache.pool.Close()}func (cache *Cache) GetAndAdd(roomId string, usrId string) (usrAccPoint string, err error) {    script := rds.NewScript(2, `        local key1 = "usr:accpoint:" .. KEYS[2]        local accPoint = redis.call("get", key1)        key1 = "room:accpoints:" .. KEYS[1]        redis.call("sadd", key1, accPoint)        key1 = "room:accpoint:" .. KEYS[1]        key1 = key1 .. ":" .. accPoint        redis.call("sadd", key1, KEYS[2])        return accPoint    `)    con := cache.GetCon()    defer con.Close()    usrAccPoint, err = rds.String(script.Do(con, roomId, usrId))    if err != nil {        fmt.Println(err.Error())        return    }    return}func (cache *Cache) GetAndRem(roomId string, usrId string) (cnt int64, err error) {    script := rds.NewScript(2, `        local key1 = "usr:accpoint:" .. KEYS[2]        local accPoint = redis.call("get", key1)        key1 = "room:accpoint:" .. KEYS[1] .. ":" .. accPoint        redis.call("srem", key1, KEYS[2])        local cnt = redis.call("scard", key1)        if (cnt == 0) then            key1 = "room:accpoints:" .. KEYS[1]            redis.call("srem", key1, accPoint)        end        return cnt    `)    con := cache.GetCon()    defer con.Close()    accPointCnt, err := rds.Int64(script.Do(con, roomId, usrId))    if err != nil {        fmt.Println(err.Error())        return -1, err    }    fmt.Printf("Room:%s, remains %d accPoint\n", roomId, accPointCnt)    return accPointCnt, nil}

main.go

package mainimport (    "fmt"    cache "jianghu.com/godemo/026redislua/rds")const (    REDIS_ADDR string = "127.0.0.1:6379")func main() {    c := cache.InitCache(REDIS_ADDR, "")    defer c.Close()    v, err := c.GetAndAdd("1", "100")    if err != nil {        fmt.Printf("Error:%s\n", err.Error())    }    fmt.Printf("Redis return v:%s\n", v)    cnt, err := c.GetAndRem("1", "100")    if err != nil {        fmt.Printf("Error:%s\n", err.Error())    }    fmt.Printf("Cnt:%d\n", cnt)}

有几个要害的办法阐明一下

(1) 生成脚本

  • func rds.NewScript(keyCount int, src string) *rds.Script
    keyCount传入参数的个数,对应lua脚本中KEYS[1],KEYS[2]...KEYS[keyCount]

(2)执行脚本,如果脚本没有加载会主动调用加载解决

  • func (*rds.Script).Do(c rds.Conn, keysAndArgs ...interface{}) (interface{}, error)
    返回参数须要依据类型调用相应的转换解决