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