Go语言之 go-redis 基本使用

Go语言之 go-redis 基本使用

Redis 介绍

Redishttps://redis.io/

Redis 中文网https://www.redis.net.cn/

REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。

Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Map), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。

macOS 安装Redis

brew install redis

Windows 安装 Redis

下载地址https://github.com/dmajkic/redis/tags/

https://github.com/ServiceStack/redis-windows

https://github.com/microsoftarchive/redis/releases

go-redis 使用

安装 go-redis 库 https://github.com/redis/go-redis

go get github.com/redis/go-redis/v9

Go-Redis 中文文档https://redis.uptrace.dev/zh/

安装

go-redis 支持 2 个最新的 go 版本且依赖Go modules,如果 你还没有 go mod,你需要首先初始化:

go mod init github.com/my/repo

安装 go-redis/v9 (支持所有的 redis 版本):

go get github.com/redis/go-redis/v9

#连接到 Redis 服务器

连接到 Redis 服务器示例,更多配置参数,请参照 redis.Options:

import "github.com/redis/go-redis/v9"

rdb := redis.NewClient(&redis.Options{
	Addr:	  "localhost:6379",
	Password: "", // 没有密码,默认值
	DB:		  0,  // 默认DB 0
})

同时也支持另外一种常见的连接字符串:

opt, err := redis.ParseURL("redis://<user>:<pass>@localhost:6379/<db>")
if err != nil {
	panic(err)
}

rdb := redis.NewClient(opt)

#使用 TLS

你需要手动设置 tls.Config,你可以在 这里 了解相关 tls.Config更多的配置信息:

rdb := redis.NewClient(&redis.Options{
	TLSConfig: &tls.Config{
		MinVersion: tls.VersionTLS12,
		ServerName: "you domain",
		//Certificates: []tls.Certificate{cert}
	},
})

如果你使用的是域名连接,且遇到了类似 x509: cannot validate certificate for xxx.xxx.xxx.xxx because it doesn't contain any IP SANs的错误 ,应该在 ServerName 中指定你的域名:更多详情请参考本链接

rdb := redis.NewClient(&redis.Options{
	TLSConfig: &tls.Config{
		MinVersion: tls.VersionTLS12,
		ServerName: "你的域名",
	},
})

#SSH 方式

使用 SSH 协议连接:

sshConfig := &ssh.ClientConfig{
	User:			 "root",
	Auth:			 []ssh.AuthMethod{ssh.Password("password")},
	HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	Timeout:		 15 * time.Second,
}

sshClient, err := ssh.Dial("tcp", "remoteIP:22", sshConfig)
if err != nil {
	panic(err)
}

rdb := redis.NewClient(&redis.Options{
	Addr: net.JoinHostPort("127.0.0.1", "6379"),
	Dialer: func(ctx context.Context, network, addr string) (net.Conn, error) {
		return sshClient.Dial(network, addr)
	},
	// SSH不支持超时设置,在这里禁用
	ReadTimeout:  -1,
	WriteTimeout: -1,
})

#dial tcp: i/o timeout

当你遇到 dial tcp: i/o timeout 错误时,表示 go-redis 无法连接 Redis 服务器,比如 redis 服务器没有 正常运行或监听了其他端口,以及可能被防火墙拦截等。你可以使用一些网络命令排查问题,例如 telnet:

telnet localhost 6379
Trying 127.0.0.1...
telnet: Unable to connect to remote host: Connection refused

如果你使用 Docker、Kubernetes、Istio、Service Mesh、Sidecar 方式运行,应该确保服务在容器完全可用后启 动,你可以通过参考该地址、Readiness Gate、Istio holdApplicationUntilProxyStarts等。

Context 上下文

go-redis 支持 Context,你可以使用它控制 超时 或者传递一些数据, 也可以 监控 go-redis 性能。

ctx := context.Background()

go-redis 实操 https://pkg.go.dev/github.com/go-redis/redis

安装

Code/go/redis_demo via 🐹 v1.20.3 via 🅒 base 
➜ go get github.com/redis/go-redis/v9
go: downloading github.com/redis/go-redis/v9 v9.0.5
go: downloading github.com/redis/go-redis v6.15.9+incompatible
go: downloading github.com/cespare/xxhash/v2 v2.2.0
go: downloading github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f
go: added github.com/cespare/xxhash/v2 v2.2.0
go: added github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f
go: added github.com/redis/go-redis/v9 v9.0.5

Code/go/redis_demo via 🐹 v1.20.3 via 🅒 base took 2.4s 
➜ 

连接 Redis 服务器并初始化

package main

import (
	"context"
	"fmt"
	"github.com/redis/go-redis/v9"
)

// 声明一个全局的 rdb 变量
var rdb *redis.Client

// 初始化连接
func initRedisClient() (err error) {
	// NewClient将客户端返回给Options指定的Redis Server。
	// Options保留设置以建立redis连接。
	rdb = redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // 没有密码,默认值
		DB:       0,  // 默认DB 0 连接到服务器后要选择的数据库。
		PoolSize: 20, // 最大套接字连接数。 默认情况下,每个可用CPU有10个连接,由runtime.GOMAXPROCS报告。
	})

	// Background返回一个非空的Context。它永远不会被取消,没有值,也没有截止日期。
	// 它通常由main函数、初始化和测试使用,并作为传入请求的顶级上下文
	ctx := context.Background()

	_, err = rdb.Ping(ctx).Result()
	if err != nil {
		return err
	}
	return nil
}

func main() {
	if err := initRedisClient(); err != nil {
		fmt.Printf("initRedisClient failed: %v\n", err)
		return
	}
	fmt.Println("initRedisClient started successfully")
	defer rdb.Close() // Close 关闭客户端,释放所有打开的资源。关闭客户端是很少见的,因为客户端是长期存在的,并在许多例程之间共享。
}

运行

Code/go/redis_demo via 🐹 v1.20.3 via 🅒 base took 2.1s 
➜ go run main.go
initRedisClient started successfully

Code/go/redis_demo via 🐹 v1.20.3 via 🅒 base 
➜ 

执行 Redis 命令

执行 Redis 命令:

val, err := rdb.Get(ctx, "key").Result()
fmt.Println(val)

你也可以分别访问值和错误:

get := rdb.Get(ctx, "key")
fmt.Println(get.Val(), get.Err())

执行尚不支持的命令

可以使用 Do() 方法执行尚不支持或者任意命令:

val, err := rdb.Do(ctx, "get", "key").Result()
if err != nil {
	if err == redis.Nil {
		fmt.Println("key does not exists")
		return
	}
	panic(err)
}
fmt.Println(val.(string))

Do() 方法返回 Cmd 类型,你可以使用它获取你 想要的类型:

// Text is a shortcut for get.Val().(string) with proper error handling.
val, err := rdb.Do(ctx, "get", "key").Text()
fmt.Println(val, err)

方法列表:

s, err := cmd.Text()
flag, err := cmd.Bool()

num, err := cmd.Int()
num, err := cmd.Int64()
num, err := cmd.Uint64()
num, err := cmd.Float32()
num, err := cmd.Float64()

ss, err := cmd.StringSlice()
ns, err := cmd.Int64Slice()
ns, err := cmd.Uint64Slice()
fs, err := cmd.Float32Slice()
fs, err := cmd.Float64Slice()
bs, err := cmd.BoolSlice()

#redis.Nil

redis.Nil 是一种特殊的错误,严格意义上来说它并不是错误,而是代表一种状态,例如你使用 Get 命令获取 key 的值,当 key 不存在时,返回 redis.Nil。在其他比如 BLPOPZSCORE 也有类似的响应,你需要区 分错误:

val, err := rdb.Get(ctx, "key").Result()
switch {
case err == redis.Nil:
	fmt.Println("key不存在")
case err != nil:
	fmt.Println("错误", err)
case val == "":
	fmt.Println("值是空字符串")
}

Conn

redis.Conn 是从连接池中取出的单个连接,除非你有特殊的需要,否则尽量不要使用它。你可以使用它向 redis 发送任何数据并读取 redis 的响应,当你使用完毕时,应该把它返回给 go-redis,否则连接池会永远丢失一个连 接。

cn := rdb.Conn(ctx)
defer cn.Close()

if err := cn.ClientSetName(ctx, "myclient").Err(); err != nil {
	panic(err)
}

name, err := cn.ClientGetName(ctx).Result()
if err != nil {
	panic(err)
}
fmt.Println("client name", name)

基本使用 执行命令

package main

import (
	"context"
	"fmt"
	"github.com/redis/go-redis/v9"
	"time"
)

// 声明一个全局的 rdb 变量
var rdb *redis.Client

// 初始化连接
func initRedisClient() (err error) {
	// NewClient将客户端返回给Options指定的Redis Server。
	// Options保留设置以建立redis连接。
	rdb = redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // 没有密码,默认值
		DB:       0,  // 默认DB 0 连接到服务器后要选择的数据库。
		PoolSize: 20, // 最大套接字连接数。 默认情况下,每个可用CPU有10个连接,由runtime.GOMAXPROCS报告。
	})

	// Background返回一个非空的Context。它永远不会被取消,没有值,也没有截止日期。
	// 它通常由main函数、初始化和测试使用,并作为传入请求的顶级上下文
	ctx := context.Background()

	_, err = rdb.Ping(ctx).Result()
	if err != nil {
		return err
	}
	return nil
}

// doCommand go-redis基本使用示例
func redisCommand() {
	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel()

	// 获取Redis的“Get key”命令。它返回redis。当键不存在时出现Nil错误。
	val, err := rdb.Get(ctx, "key").Result()
	if err != nil {
		fmt.Printf("redis command failed: %v\n", err)
	}
	fmt.Printf("redis command get key %v\n", val)

	// 分别访问值和错误:
	get := rdb.Get(ctx, "key")
	fmt.Println("redis command get value: ", get.Val()) // 获取值
	fmt.Println("redis command get err: ", get.Err())   // 获取错误

	// 设置Redis ' Set key value [expiration] '命令。
	err = rdb.Set(ctx, "key", 10, time.Hour).Err()
	fmt.Printf("rdb set err: %v\n", err)

	// 获取Redis的“Get key”命令。它返回redis。当键不存在时出现Nil错误。
	value := rdb.Get(ctx, "key").Val()
	fmt.Printf("rdb get value: %v\n", value)
}

func main() {
	if err := initRedisClient(); err != nil {
		fmt.Printf("initRedisClient failed: %v\n", err)
		return
	}
	fmt.Println("initRedisClient started successfully")
	defer rdb.Close() // Close 关闭客户端,释放所有打开的资源。关闭客户端是很少见的,因为客户端是长期存在的,并在许多例程之间共享。

	redisCommand()
}

运行

Code/go/redis_demo via 🐹 v1.20.3 via 🅒 base 
➜ go run main.go
initRedisClient started successfully
redis command failed: redis: nil
redis command get key 
redis command get value:  
redis command get err:  redis: nil
rdb set err: <nil>
rdb get value: 10

Code/go/redis_demo via 🐹 v1.20.3 via 🅒 base 
➜ 

Go Redis 配置

Redis Client

type Options struct {
    // 连接网络类型,如: tcp、udp、unix等方式
    // 如果为空默认tcp
    Network string
	
    // redis服务器地址,ip:port格式,比如:192.168.1.100:6379
    // 默认为 :6379
    Addr string
    
    // ClientName 是对网络连接设置一个名字,使用 "CLIENT LIST" 命令
    // 可以查看redis服务器当前的网络连接列表
    // 如果设置了ClientName,go-redis对每个连接调用 `CLIENT SETNAME ClientName` 命令
    // 查看: https://redis.io/commands/client-setname/
    // 默认为空,不设置客户端名称
    ClientName string
	
    // 如果你想自定义连接网络的方式,可以自定义 `Dialer` 方法,
    // 如果不指定,将使用默认的方式进行网络连接 `redis.NewDialer`
    Dialer func(ctx context.Context, network, addr string) (net.Conn, error)
    
    // 建立了新连接时调用此函数
    // 默认为nil
    OnConnect func(ctx context.Context, cn *Conn) error
	
    // 当redis服务器版本在6.0以上时,作为ACL认证信息配合密码一起使用,
    // ACL是redis 6.0以上版本提供的认证功能,6.0以下版本仅支持密码认证。
    // 默认为空,不进行认证。
    Username string

    // 当redis服务器版本在6.0以上时,作为ACL认证信息配合密码一起使用,
    // 当redis服务器版本在6.0以下时,仅作为密码认证。
    // ACL是redis 6.0以上版本提供的认证功能,6.0以下版本仅支持密码认证。
    // 默认为空,不进行认证。
    Password string
	
    // 允许动态设置用户名和密码,go-redis在进行网络连接时会获取用户名和密码,
    // 这对一些认证鉴权有时效性的系统来说很有用,比如一些云服务商提供认证信息有效期为12小时。
    // 默认为nil
    CredentialsProvider func() (username string, password string)
    
    // redis DB 数据库,默认为0
    DB int
    
    // 命令最大重试次数, 默认为3
    MaxRetries int
	
    // 每次重试最小间隔时间
    // 默认 8 * time.Millisecond (8毫秒) ,设置-1为禁用
    MinRetryBackoff time.Duration

    // 每次重试最大间隔时间
    // 默认 512 * time.Millisecond (512毫秒) ,设置-1为禁用
    MaxRetryBackoff time.Duration
    
    // 建立新网络连接时的超时时间
    // 默认5秒
    DialTimeout time.Duration
	
    // 从网络连接中读取数据超时时间,可能的值:
    //  0 - 默认值,3秒
    // -1 - 无超时,无限期的阻塞
    // -2 - 不进行超时设置,不调用 SetReadDeadline 方法
    ReadTimeout time.Duration
	
    // 把数据写入网络连接的超时时间,可能的值:
    //  0 - 默认值,3秒
    // -1 - 无超时,无限期的阻塞
    // -2 - 不进行超时设置,不调用 SetWriteDeadline 方法
    WriteTimeout time.Duration
	
    // 是否使用context.Context的上下文截止时间,
    // 有些情况下,context.Context的超时可能带来问题。
    // 默认不使用
    ContextTimeoutEnabled bool

    // 连接池的类型,有 LIFO 和 FIFO 两种模式,
    // PoolFIFO 为 false 时使用 LIFO 模式,为 true 使用 FIFO 模式。
    // 当一个连接使用完毕时会把连接归还给连接池,连接池会把连接放入队尾,
    // LIFO 模式时,每次取空闲连接会从"队尾"取,就是刚放入队尾的空闲连接,
    // 也就是说 LIFO 每次使用的都是热连接,连接池有机会关闭"队头"的长期空闲连接,
    // 并且从概率上,刚放入的热连接健康状态会更好;
    // 而 FIFO 模式则相反,每次取空闲连接会从"队头"取,相比较于 LIFO 模式,
    // 会使整个连接池的连接使用更加平均,有点类似于负载均衡寻轮模式,会循环的使用
    // 连接池的所有连接,如果你使用 go-redis 当做代理让后端 redis 节点负载更平均的话,
    // FIFO 模式对你很有用。
    // 如果你不确定使用什么模式,请保持默认 PoolFIFO = false
    PoolFIFO bool

    // 连接池最大连接数量,注意:这里不包括 pub/sub,pub/sub 将使用独立的网络连接
    // 默认为 10 * runtime.GOMAXPROCS
    PoolSize int
	
    // PoolTimeout 代表如果连接池所有连接都在使用中,等待获取连接时间,超时将返回错误
    // 默认是 1秒+ReadTimeout
    PoolTimeout time.Duration
	
    // 连接池保持的最小空闲连接数,它受到PoolSize的限制
    // 默认为0,不保持
    MinIdleConns int
	
    // 连接池保持的最大空闲连接数,多余的空闲连接将被关闭
    // 默认为0,不限制
    MaxIdleConns int

    // ConnMaxIdleTime 是最大空闲时间,超过这个时间将被关闭。
    // 如果 ConnMaxIdleTime <= 0,则连接不会因为空闲而被关闭。
    // 默认值是30分钟,-1禁用
    ConnMaxIdleTime time.Duration

    // ConnMaxLifetime 是一个连接的生存时间,
    // 和 ConnMaxIdleTime 不同,ConnMaxLifetime 表示连接最大的存活时间
    // 如果 ConnMaxLifetime <= 0,则连接不会有使用时间限制
    // 默认值为0,代表连接没有时间限制
    ConnMaxLifetime time.Duration
    
    // 如果你的redis服务器需要TLS访问,可以在这里配置TLS证书等信息
    // 如果配置了证书信息,go-redis将使用TLS发起连接,
    // 如果你自定义了 `Dialer` 方法,你需要自己实现网络连接
    TLSConfig *tls.Config
    
    // 限流器的配置,参照 `Limiter` 接口
    Limiter Limiter
    
    // 设置启用在副本节点只读查询,默认为false不启用
    // 参照:https://redis.io/commands/readonly
    readOnly bool
}

获取 Redis 值 GET Key

package main

import (
	"context"
	"fmt"
	"github.com/redis/go-redis/v9"
	"time"
)

// 声明一个全局的 rdb 变量
var rdb *redis.Client

// 初始化连接
func initRedisClient() (err error) {
	// NewClient将客户端返回给Options指定的Redis Server。
	// Options保留设置以建立redis连接。
	rdb = redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // 没有密码,默认值
		DB:       0,  // 默认DB 0 连接到服务器后要选择的数据库。
		PoolSize: 20, // 最大套接字连接数。 默认情况下,每个可用CPU有10个连接,由runtime.GOMAXPROCS报告。
	})

	// Background返回一个非空的Context。它永远不会被取消,没有值,也没有截止日期。
	// 它通常由main函数、初始化和测试使用,并作为传入请求的顶级上下文
	ctx := context.Background()

	_, err = rdb.Ping(ctx).Result()
	if err != nil {
		return err
	}
	return nil
}


func redisGetKey(key string) (string, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel()

	val, err := rdb.Get(ctx, key).Result()
	if err != nil {
		if err == redis.Nil {
			return "", nil
			// DeadlineExceeded是Context返回的错误。当上下文的截止日期过去时发生错误。
		} else if err == context.DeadlineExceeded {
			return "", fmt.Errorf("获取值超时")
		} else {
			return "", fmt.Errorf("获取值失败: %v", err)
		}
	}

	if val == "" {
		return "", nil
	}

	return val, nil
}

func main() {
	if err := initRedisClient(); err != nil {
		fmt.Printf("initRedisClient failed: %v\n", err)
		return
	}
	fmt.Println("initRedisClient started successfully")
	defer rdb.Close() // Close 关闭客户端,释放所有打开的资源。关闭客户端是很少见的,因为客户端是长期存在的,并在许多例程之间共享。

	//redisCommand()

	// get key
	value, _ := redisGetKey("key")
	fmt.Printf("get key: %v\n", value)
}

运行

Code/go/redis_demo via 🐹 v1.20.3 via 🅒 base 
➜ go run main.go
initRedisClient started successfully
get key: 10

Code/go/redis_demo via 🐹 v1.20.3 via 🅒 base 
➜ 

设置值 SET

package main

import (
	"context"
	"fmt"
	"github.com/redis/go-redis/v9"
	"time"
)

// 声明一个全局的 rdb 变量
var rdb *redis.Client

// 初始化连接
func initRedisClient() (err error) {
	// NewClient将客户端返回给Options指定的Redis Server。
	// Options保留设置以建立redis连接。
	rdb = redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // 没有密码,默认值
		DB:       0,  // 默认DB 0 连接到服务器后要选择的数据库。
		PoolSize: 20, // 最大套接字连接数。 默认情况下,每个可用CPU有10个连接,由runtime.GOMAXPROCS报告。
	})

	// Background返回一个非空的Context。它永远不会被取消,没有值,也没有截止日期。
	// 它通常由main函数、初始化和测试使用,并作为传入请求的顶级上下文
	ctx := context.Background()

	_, err = rdb.Ping(ctx).Result()
	if err != nil {
		return err
	}
	return nil
}


func redisSetKey(key string, val string) error {
	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel()

	// 设置Redis ' Set key value [expiration] '命令。
	err := rdb.Set(ctx, key, val, time.Hour).Err()
	if err != nil {
		fmt.Printf("redis set failed, err: %v\n", err)
		return err
	}
	return nil

}

func main() {
	if err := initRedisClient(); err != nil {
		fmt.Printf("initRedisClient failed: %v\n", err)
		return
	}
	fmt.Println("initRedisClient started successfully")
	defer rdb.Close() // Close 关闭客户端,释放所有打开的资源。关闭客户端是很少见的,因为客户端是长期存在的,并在许多例程之间共享。

	// set value
	err := redisSetKey("name", "xia")
	if err != nil {
		fmt.Printf("redisSetKey failed: %v\n", err)
		return
	}
	fmt.Println("redisSetKey succeeded")
}

运行

Code/go/redis_demo via 🐹 v1.20.3 via 🅒 base 
➜ go run main.go
initRedisClient started successfully
redisSetKey succeeded

HGetAll HSET

127.0.0.1:6379> hset user name "lixia"
(integer) 1
127.0.0.1:6379> hgetall user
1) "name"
2) "lixia"
127.0.0.1:6379> hset user age 14
(integer) 1
127.0.0.1:6379> hgetall user
1) "name"
2) "lixia"
3) "age"
4) "14"
127.0.0.1:6379>

main.go

package main

import (
	"context"
	"fmt"
	"github.com/redis/go-redis/v9"
	"time"
)

// 声明一个全局的 rdb 变量
var rdb *redis.Client

// 初始化连接
func initRedisClient() (err error) {
	// NewClient将客户端返回给Options指定的Redis Server。
	// Options保留设置以建立redis连接。
	rdb = redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // 没有密码,默认值
		DB:       0,  // 默认DB 0 连接到服务器后要选择的数据库。
		PoolSize: 20, // 最大套接字连接数。 默认情况下,每个可用CPU有10个连接,由runtime.GOMAXPROCS报告。
	})

	// Background返回一个非空的Context。它永远不会被取消,没有值,也没有截止日期。
	// 它通常由main函数、初始化和测试使用,并作为传入请求的顶级上下文
	ctx := context.Background()

	_, err = rdb.Ping(ctx).Result()
	if err != nil {
		return err
	}
	return nil
}


func hGetDemo(key string) (map[string]string, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel()

	val, err := rdb.HGetAll(ctx, key).Result()
	if err != nil {
		// redis.Nil
		// 其它错误
		fmt.Printf("hgetall failed, err: %v\n", err)
		return nil, err
	}
	return val, nil
}

func main() {
	if err := initRedisClient(); err != nil {
		fmt.Printf("initRedisClient failed: %v\n", err)
		return
	}
	fmt.Println("initRedisClient started successfully")
	defer rdb.Close() // Close 关闭客户端,释放所有打开的资源。关闭客户端是很少见的,因为客户端是长期存在的,并在许多例程之间共享。

	// hgetall()
	value, err := hGetDemo("user")
	if err != nil {
		fmt.Printf("hGetDem failed with error: %v\n", err)
		return
	}
	fmt.Printf("hgetall successful, value: %v\n", value)
}

运行

Code/go/redis_demo via 🐹 v1.20.3 via 🅒 base took 3.8s 
➜ go run main.go 
initRedisClient started successfully
hgetall successful, value: map[age:14 name:lixia]

Code/go/redis_demo via 🐹 v1.20.3 via 🅒 base 
➜ 

HMGet HGet

package main

import (
	"context"
	"fmt"
	"github.com/redis/go-redis/v9"
	"time"
)

// 声明一个全局的 rdb 变量
var rdb *redis.Client

// 初始化连接
func initRedisClient() (err error) {
	// NewClient将客户端返回给Options指定的Redis Server。
	// Options保留设置以建立redis连接。
	rdb = redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // 没有密码,默认值
		DB:       0,  // 默认DB 0 连接到服务器后要选择的数据库。
		PoolSize: 20, // 最大套接字连接数。 默认情况下,每个可用CPU有10个连接,由runtime.GOMAXPROCS报告。
	})

	// Background返回一个非空的Context。它永远不会被取消,没有值,也没有截止日期。
	// 它通常由main函数、初始化和测试使用,并作为传入请求的顶级上下文
	ctx := context.Background()

	_, err = rdb.Ping(ctx).Result()
	if err != nil {
		return err
	}
	return nil
}


func hMGetDemo() {
	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel()

	val := rdb.HMGet(ctx, "user", "name", "age").Val()
	fmt.Printf("redis HMGet %v\n", val)

	value := rdb.HGet(ctx, "user", "age").Val()
	fmt.Printf("redis HGet value: %v\n", value)
}

func main() {
	if err := initRedisClient(); err != nil {
		fmt.Printf("initRedisClient failed: %v\n", err)
		return
	}
	fmt.Println("initRedisClient started successfully")
	defer rdb.Close() // Close 关闭客户端,释放所有打开的资源。关闭客户端是很少见的,因为客户端是长期存在的,并在许多例程之间共享。

	hMGetDemo()
}

运行

Code/go/redis_demo via 🐹 v1.20.3 via 🅒 base 
➜ go run main.go 
initRedisClient started successfully
redis HMGet [lixia 14]
redis HGet value: 14

Code/go/redis_demo via 🐹 v1.20.3 via 🅒 base 
➜ 

ZSET 使用

package main

import (
	"context"
	"fmt"
	"github.com/redis/go-redis/v9"
	"time"
)

// 声明一个全局的 rdb 变量
var rdb *redis.Client

// 初始化连接
func initRedisClient() (err error) {
	// NewClient将客户端返回给Options指定的Redis Server。
	// Options保留设置以建立redis连接。
	rdb = redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // 没有密码,默认值
		DB:       0,  // 默认DB 0 连接到服务器后要选择的数据库。
		PoolSize: 20, // 最大套接字连接数。 默认情况下,每个可用CPU有10个连接,由runtime.GOMAXPROCS报告。
	})

	// Background返回一个非空的Context。它永远不会被取消,没有值,也没有截止日期。
	// 它通常由main函数、初始化和测试使用,并作为传入请求的顶级上下文
	ctx := context.Background()

	_, err = rdb.Ping(ctx).Result()
	if err != nil {
		return err
	}
	return nil
}


// zset Demo 操作 zset 示例
func zSetDemo() {
	// key
	zSetKey := "language_rank"
	// value Z表示有序集合的成员。
	languages := []redis.Z{
		{Score: 90.0, Member: "Golang"},
		{Score: 95.0, Member: "Python"},
		{Score: 97.0, Member: "Rust"},
		{Score: 99.0, Member: "C/C++"},
		{Score: 88.0, Member: "Java"},
	}
	// WithTimeout返回WithDeadline(parent, time.Now(). add (timeout))。
	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel()

	// ZAdd Redis `ZADD key score member [score member ...]` command.
	num, err := rdb.ZAdd(ctx, zSetKey, languages...).Result()
	if err != nil {
		fmt.Printf("zadd failed, err:%v\n", err)
		return
	}
	fmt.Printf("zadd successful num: %v\n", num)

	// ZIncrBy 给某一个元素添加分数值 把Golang的分数加 10
	newScore, err := rdb.ZIncrBy(ctx, zSetKey, 10.0, "Golang").Result()
	if err != nil {
		fmt.Printf("ZIncrBy failed, err:%v\n", err)
		return
	}
	fmt.Printf("ZIncrBy success Golang's score is %f now.\n", newScore)

	// 取分数最高的3个  适用于 排行榜、充值榜...
	// ZRevRangeWithScores according to the Redis documentation, if member does not exist
	// in the sorted set or key does not exist, it will return a redis.Nil error.
	ret, err := rdb.ZRevRangeWithScores(ctx, zSetKey, 0, 2).Result()
	if err != nil {
		fmt.Printf("zRevRangeWithScores failed, err: %v\n", err)
		return
	}
	for _, z := range ret {
		fmt.Printf("z.Member: %v, z.Score: %v\n", z.Member, z.Score)
	}

	// 取95~100分的
	op := &redis.ZRangeBy{
		Min: "95",
		Max: "100",
	}
	ret, err = rdb.ZRangeByScoreWithScores(ctx, zSetKey, op).Result()
	if err != nil {
		fmt.Printf("zrangebyscore failed, err:%v\n", err)
		return
	}
	fmt.Printf("zrangebyscore returned %v\n", ret)
	for _, z := range ret {
		fmt.Printf("ZRangeByScoreWithScores success Member: %v, Score: %v\n", z.Member, z.Score)
	}
}

func main() {
	if err := initRedisClient(); err != nil {
		fmt.Printf("initRedisClient failed: %v\n", err)
		return
	}
	fmt.Println("initRedisClient started successfully")
	defer rdb.Close() // Close 关闭客户端,释放所有打开的资源。关闭客户端是很少见的,因为客户端是长期存在的,并在许多例程之间共享。

	zSetDemo()
}

运行

Code/go/redis_demo via 🐹 v1.20.3 via 🅒 base 
➜ go run main.go
initRedisClient started successfully
zadd successful num: 5
ZIncrBy success Golang's score is 100.000000 now.
z.Member: Golang, z.Score: 100
z.Member: C/C++, z.Score: 99
z.Member: Rust, z.Score: 97
zrangebyscore returned [{95 Python} {97 Rust} {99 C/C++} {100 Golang}]
ZRangeByScoreWithScores success Member: Python, Score: 95
ZRangeByScoreWithScores success Member: Rust, Score: 97
ZRangeByScoreWithScores success Member: C/C++, Score: 99
ZRangeByScoreWithScores success Member: Golang, Score: 100

Code/go/redis_demo via 🐹 v1.20.3 via 🅒 base 
➜ 

Redis 查询

127.0.0.1:6379> ZREVRANGE language_rank 0 2
1) "Golang"
2) "C/C++"
3) "Rust"
127.0.0.1:6379> ZREVRANGE language_rank 0 2 withscores
1) "Golang"
2) "100"
3) "C/C++"
4) "99"
5) "Rust"
6) "97"
127.0.0.1:6379> ZRANDMEMBER language_rank
"Rust"
127.0.0.1:6379> ZRANGE language_rank 0 2
1) "Java"
2) "Python"
3) "Rust"
127.0.0.1:6379> ZRANGE language_rank 0 2 withscores
1) "Java"
2) "88"
3) "Python"
4) "95"
5) "Rust"
6) "97"
127.0.0.1:6379> ZINCRBY language_rank 1 "Python"
"96"
127.0.0.1:6379> ZRANGE language_rank 0 2 withscores
1) "Java"
2) "88"
3) "Python"
4) "96"
5) "Rust"
6) "97"
127.0.0.1:6379>

Scan 根据前缀查询 Key

package main

import (
	"context"
	"fmt"
	"github.com/redis/go-redis/v9"
	"time"
)

// 声明一个全局的 rdb 变量
var rdb *redis.Client

// 初始化连接
func initRedisClient() (err error) {
	// NewClient将客户端返回给Options指定的Redis Server。
	// Options保留设置以建立redis连接。
	rdb = redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // 没有密码,默认值
		DB:       0,  // 默认DB 0 连接到服务器后要选择的数据库。
		PoolSize: 20, // 最大套接字连接数。 默认情况下,每个可用CPU有10个连接,由runtime.GOMAXPROCS报告。
	})

	// Background返回一个非空的Context。它永远不会被取消,没有值,也没有截止日期。
	// 它通常由main函数、初始化和测试使用,并作为传入请求的顶级上下文
	ctx := context.Background()

	_, err = rdb.Ping(ctx).Result()
	if err != nil {
		return err
	}
	return nil
}

func scanKeyDemo(match string) {
	// WithTimeout返回WithDeadline(parent, time.Now(). add (timeout))。
	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel()
	// 根据前缀查询 Key
	iter := rdb.Scan(ctx, 0, match, 0).Iterator()

	for iter.Next(ctx) {
		fmt.Printf("key value: %v\n", iter.Val())
	}

	if err := iter.Err(); err != nil {
		fmt.Printf("rdb scan failed, err: %v\n", err)
		return
	}
}

func main() {
	if err := initRedisClient(); err != nil {
		fmt.Printf("initRedisClient failed: %v\n", err)
		return
	}
	fmt.Println("initRedisClient started successfully")
	defer rdb.Close() // Close 关闭客户端,释放所有打开的资源。关闭客户端是很少见的,因为客户端是长期存在的,并在许多例程之间共享。

	scanKeyDemo("l*")
}

运行

Code/go/redis_demo via 🐹 v1.20.3 via 🅒 base 
➜ go run main.go
initRedisClient started successfully
key value: language_rank

Code/go/redis_demo via 🐹 v1.20.3 via 🅒 base 
➜ 
posted @ 2023-06-16 12:58  寻月隐君  阅读(265)  评论(0编辑  收藏  举报