zdpgo_redis_v2
Go语言连接Redis的另一个版本,支持上下文操作,封装了一些便捷的处理操作
安装
bash
github.com/zhangdapeng520/zdpgo_redis_v2
使用教程
快速入门
go
package main
import (
"context"
"fmt"
redis "github.com/zhangdapeng520/zdpgo_redis_v2"
)
var ctx = context.Background()
func main() {
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
err := rdb.Set(ctx, "key", "value", 0).Err()
if err != nil {
panic(err)
}
val, err := rdb.Get(ctx, "key").Result()
if err != nil {
panic(err)
}
fmt.Println("key", val)
val2, err := rdb.Get(ctx, "key2").Result()
if err == redis.Nil {
fmt.Println("key2 does not exist")
} else if err != nil {
panic(err)
} else {
fmt.Println("key2", val2)
}
// Output: key value
// key2 does not exist
}
通过URL地址连接Redis
go
package main
import (
"context"
"fmt"
redis "github.com/zhangdapeng520/zdpgo_redis_v2"
)
var ctx = context.Background()
func main() {
//url := "redis://user:password@localhost:6379/0?protocol=3"
url := "redis://:@localhost:6379/0?protocol=3"
opts, err := redis.ParseURL(url)
if err != nil {
panic(err)
}
rdb := redis.NewClient(opts)
err = rdb.Set(ctx, "key", "value", 0).Err()
if err != nil {
panic(err)
}
val, err := rdb.Get(ctx, "key").Result()
if err != nil {
panic(err)
}
fmt.Println("key", val)
val2, err := rdb.Get(ctx, "key2").Result()
if err == redis.Nil {
fmt.Println("key2 does not exist")
} else if err != nil {
panic(err)
} else {
fmt.Println("key2", val2)
}
// Output: key value
// key2 does not exist
}
删除key
go
package main
import (
"context"
"fmt"
"sync"
"time"
redis "github.com/zhangdapeng520/zdpgo_redis_v2"
)
func main() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: ":6379",
})
_ = rdb.Set(ctx, "key_with_ttl", "bar", time.Minute).Err()
_ = rdb.Set(ctx, "key_without_ttl_1", "", 0).Err()
_ = rdb.Set(ctx, "key_without_ttl_2", "", 0).Err()
checker := NewKeyChecker(rdb, 100)
start := time.Now()
checker.Start(ctx)
iter := rdb.Scan(ctx, 0, "", 0).Iterator()
for iter.Next(ctx) {
checker.Add(iter.Val())
}
if err := iter.Err(); err != nil {
panic(err)
}
deleted := checker.Stop()
fmt.Println("deleted", deleted, "keys", "in", time.Since(start))
}
type KeyChecker struct {
rdb *redis.Client
batchSize int
ch chan string
delCh chan string
wg sync.WaitGroup
deleted int
}
func NewKeyChecker(rdb *redis.Client, batchSize int) *KeyChecker {
return &KeyChecker{
rdb: rdb,
batchSize: batchSize,
ch: make(chan string, batchSize),
delCh: make(chan string, batchSize),
}
}
func (c *KeyChecker) Add(key string) {
c.ch <- key
}
func (c *KeyChecker) Start(ctx context.Context) {
c.wg.Add(1)
go func() {
defer c.wg.Done()
if err := c.del(ctx); err != nil {
panic(err)
}
}()
c.wg.Add(1)
go func() {
defer c.wg.Done()
defer close(c.delCh)
keys := make([]string, 0, c.batchSize)
for key := range c.ch {
keys = append(keys, key)
if len(keys) < cap(keys) {
continue
}
if err := c.checkKeys(ctx, keys); err != nil {
fmt.Println("checkKeys failed", err)
}
keys = keys[:0]
}
if len(keys) > 0 {
if err := c.checkKeys(ctx, keys); err != nil {
fmt.Println("checkKeys failed", err)
}
keys = nil
}
}()
}
func (c *KeyChecker) Stop() int {
close(c.ch)
c.wg.Wait()
return c.deleted
}
func (c *KeyChecker) checkKeys(ctx context.Context, keys []string) error {
cmds, err := c.rdb.Pipelined(ctx, func(pipe redis.Pipeliner) error {
for _, key := range keys {
pipe.TTL(ctx, key)
}
return nil
})
if err != nil {
return err
}
for i, cmd := range cmds {
d, err := cmd.(*redis.DurationCmd).Result()
if err != nil {
return err
}
if d == -1 {
c.delCh <- keys[i]
}
}
return nil
}
func (c *KeyChecker) del(ctx context.Context) error {
pipe := c.rdb.Pipeline()
for key := range c.delCh {
fmt.Printf("deleting %s...\n", key)
pipe.Del(ctx, key)
c.deleted++
if pipe.Len() < c.batchSize {
continue
}
if _, err := pipe.Exec(ctx); err != nil {
return err
}
}
if _, err := pipe.Exec(ctx); err != nil {
return err
}
return nil
}
hll
go
package main
import (
"context"
"fmt"
redis "github.com/zhangdapeng520/zdpgo_redis_v2"
)
func main() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: ":6379",
})
_ = rdb.FlushDB(ctx).Err()
for i := 0; i < 10; i++ {
if err := rdb.PFAdd(ctx, "myset", fmt.Sprint(i)).Err(); err != nil {
panic(err)
}
}
card, err := rdb.PFCount(ctx, "myset").Result()
if err != nil {
panic(err)
}
fmt.Println("set cardinality", card)
}
执行LUA脚本
go
package main
import (
"context"
"fmt"
redis "github.com/zhangdapeng520/zdpgo_redis_v2"
)
func main() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: ":6379",
})
_ = rdb.FlushDB(ctx).Err()
fmt.Printf("# INCR BY\n")
for _, change := range []int{+1, +5, 0} {
num, err := incrBy.Run(ctx, rdb, []string{"my_counter"}, change).Int()
if err != nil {
panic(err)
}
fmt.Printf("incr by %d: %d\n", change, num)
}
fmt.Printf("\n# SUM\n")
sum, err := sum.Run(ctx, rdb, []string{"my_sum"}, 1, 2, 3).Int()
if err != nil {
panic(err)
}
fmt.Printf("sum is: %d\n", sum)
}
var incrBy = redis.NewScript(`
local key = KEYS[1]
local change = ARGV[1]
local value = redis.call("GET", key)
if not value then
value = 0
end
value = value + change
redis.call("SET", key, value)
return value
`)
var sum = redis.NewScript(`
local key = KEYS[1]
local sum = redis.call("GET", key)
if not sum then
sum = 0
end
local num_arg = #ARGV
for i = 1, num_arg do
sum = sum + ARGV[i]
end
redis.call("SET", key, sum)
return sum
`)
将结果转换为结构体
go
package main
import (
"context"
redis "github.com/zhangdapeng520/zdpgo_redis_v2"
"github.com/zhangdapeng520/zdpgo_redis_v2/spew"
)
type Model struct {
Str1 string `redis:"str1"`
Str2 string `redis:"str2"`
Bytes []byte `redis:"bytes"`
Int int `redis:"int"`
Bool bool `redis:"bool"`
Ignored struct{} `redis:"-"`
}
func main() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: ":6379",
})
_ = rdb.FlushDB(ctx).Err()
// Set some fields.
if _, err := rdb.Pipelined(ctx, func(rdb redis.Pipeliner) error {
rdb.HSet(ctx, "key", "str1", "hello")
rdb.HSet(ctx, "key", "str2", "world")
rdb.HSet(ctx, "key", "int", 123)
rdb.HSet(ctx, "key", "bool", 1)
rdb.HSet(ctx, "key", "bytes", []byte("this is bytes !"))
return nil
}); err != nil {
panic(err)
}
var model1, model2 Model
// Scan all fields into the model.
if err := rdb.HGetAll(ctx, "key").Scan(&model1); err != nil {
panic(err)
}
// Or scan a subset of the fields.
if err := rdb.HMGet(ctx, "key", "str1", "int").Scan(&model2); err != nil {
panic(err)
}
spew.Dump(model1)
// Output:
// (main.Model) {
// Str1: (string) (len=5) "hello",
// Str2: (string) (len=5) "world",
// Bytes: ([]uint8) (len=15 cap=16) {
// 00000000 74 68 69 73 20 69 73 20 62 79 74 65 73 20 21 |this is bytes !|
// },
// Int: (int) 123,
// Bool: (bool) true,
// Ignored: (struct {}) {
// }
// }
spew.Dump(model2)
// Output:
// (main.Model) {
// Str1: (string) (len=5) "hello",
// Str2: (string) "",
// Bytes: ([]uint8) <nil>,
// Int: (int) 123,
// Bool: (bool) false,
// Ignored: (struct {}) {
// }
// }
}
版本历史
v0.1.0
- 初始代码