对于事物在这就不多做概念性的介绍,本人能够去搜寻。
对于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)
返回参数须要依据类型调用相应的转换解决