zdpgo_redis_v2 Go语言连接Redis的另一个版本,支持上下文操作,封装了一些便捷的处理操作

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

  • 初始代码
相关推荐
起飞的风筝1 小时前
【redis】—— 环境搭建教程
数据库·redis·缓存
古人诚不我欺3 小时前
Redis设置密码认证,以及不重启服务情况下设置临时密码
数据库·redis·缓存
__AtYou__4 小时前
Golang | Leetcode Golang题解之第563题二叉树的坡度
leetcode·golang·题解
凡人的AI工具箱12 小时前
15分钟学 Go 第 49 天 :复杂项目开发
开发语言·人工智能·后端·算法·golang
ac.char12 小时前
在 Ubuntu 上安装 Redis 并为其设置登录密码
linux·redis·ubuntu
杜杜的man13 小时前
【go从零单排】Random Numbers、Number Parsing
开发语言·python·golang
aiee15 小时前
Golang时间函数
开发语言·后端·golang
还是转转15 小时前
Go开发指南- Goroutine
开发语言·golang
蜗牛沐雨15 小时前
Go语言中的`io.Pipe`:实现进程间通信的利器
开发语言·后端·golang·进程通信·pipe
杜杜的man15 小时前
【go从零单排】泛型(Generics)、链表
开发语言·链表·golang