基本使用
为了在Go中连接Redis并进行增删改查操作,通常我们使用go-redis这个库。以下是详细的步骤和代码示例:
1. 安装 go-redis:
使用以下命令安装:
go get -u github.com/go-redis/redis/v8
2. 连接到Redis:
首先,我们需要建立与Redis的连接。
package main
import (
"context"
"fmt"
"log"
"github.com/go-redis/redis/v8"
)
var ctx = context.Background()
func main() {
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379", // Redis地址
Password: "", // Redis密码,没有则留空
DB: 0, // 使用的DB编号
})
//关闭连接
defer rdb.Close()
// 检查连接
pong, err := rdb.Ping(ctx).Result()
if err != nil {
log.Fatalf("Could not connect to Redis: %v", err)
}
fmt.Println(pong)
}
3. 增删改查操作:
下面的示例将展示如何在Redis中进行增删改查操作:
package DB
import (
"context"
"fmt"
"log"
"github.com/go-redis/redis/v8"
)
var ctx = context.Background()
func Redis() {
rdb := Connect()
//Set(rdb)
//Get(rdb)
Delete(rdb)
}
func Connect() *redis.Client {
rdb := redis.NewClient(&redis.Options{
Addr: "120.53.94.106:6379", // Redis地址
Password: "nrec1234", // Redis密码,没有则留空
DB: 0, // 使用的DB编号
})
// 检查连接
pong, err := rdb.Ping(ctx).Result()
if err != nil {
log.Fatalf("Could not connect to Redis: %v", err)
}
fmt.Println(pong)
return rdb
}
// Set 添加或更新
func Set(rdb *redis.Client) {
err := rdb.Set(ctx, "key", "value", 0).Err()
if err != nil {
log.Fatalf("Failed to set key: %v", err)
}
}
func Get(rdb *redis.Client) {
val, err := rdb.Get(ctx, "key").Result()
if err != nil {
log.Fatalf("Failed to get key: %v", err)
}
fmt.Println("key:", val)
}
func Delete(rdb *redis.Client) {
err := rdb.Del(ctx, "key").Err()
if err != nil {
log.Fatalf("Failed to delete key: %v", err)
}
//确认是否成功删除
val, err := rdb.Get(ctx, "key").Result()
if err == redis.Nil { //代表操作成功,但是key不存在
fmt.Println("key does not exist")
} else if err != nil { //代表操作失败
log.Fatalf("Failed to get key: %v", err)
} else { //操作成功,且key存在
fmt.Println("key:", val)
}
}
这个示例首先设置一个值,然后获取它,接着更新它,并最后删除它。通过这些基本的操作,你可以轻松地与Redis进行交互。
库方法
redis.Client
redis.Client 提供了大量的方法和属性来与 Redis 数据库进行交互。以下是一些常用的方法和属性:
名称 | 函数签名 | 描述 |
---|---|---|
Ping | Ping(ctx context.Context) *StatusCmd | 发送 PING 命令,通常用于测试连接是否正常。 |
Get | Get(ctx context.Context, key string) *StringCmd | 获取指定 key 的值。 |
Set | Set(ctx context.Context, key string, value interface{}, expiration time.Duration) *StatusCmd | 设置指定 key 的值,并可指定一个过期时间。 |
Del | Del(ctx context.Context, keys ...string) *IntCmd | 删除一个或多个 keys。 |
Exists | Exists(ctx context.Context, keys ...string) *IntCmd | 检查一个或多个 keys 是否存在。 |
Expire | Expire(ctx context.Context, key string, expiration time.Duration) *BoolCmd | 为给定 key 设置过期时间。 |
TTL | TTL(ctx context.Context, key string) *DurationCmd | 获取给定 key 的剩余生存时间。 |
RPush | RPush(ctx context.Context, key string, values ...interface{}) *IntCmd | 将一个或多个值插入到列表的尾部(最右边)。 |
LPush | LPush(ctx context.Context, key string, values ...interface{}) *IntCmd | 将一个或多个值插入到列表的头部(最左边)。 |
RPop | RPop(ctx context.Context, key string) *StringCmd | 移除并返回列表的最后一个元素。 |
LPop | LPop(ctx context.Context, key string) *StringCmd | 移除并返回列表的第一个元素。 |
LLen | LLen(ctx context.Context, key string) *IntCmd | 返回列表的长度。 |
HSet | HSet(ctx context.Context, key string, values ...interface{}) *IntCmd | 为哈希表中的字段设置值。 |
HGet | HGet(ctx context.Context, key string, field string) *StringCmd | 获取存储在哈希表中指定字段的值。 |
HGetAll | HGetAll(ctx context.Context, key string) *StringStringMapCmd | 获取在哈希表中指定 key 的所有字段和值。 |
ZAdd | ZAdd(ctx context.Context, key string, members ...*Z) *IntCmd | 将一个或多个成员元素及其分数值加入到有序集当中。 |
ZRange | ZRange(ctx context.Context, key string, start, stop int64) *StringSliceCmd | 通过索引区间返回有序集合成指定区间内的成员。 |
Close | Close() error | 关闭连接池以及与Redis服务器的所有连接。 |
Options | Options() *Options | 返回客户端的配置选项。 |
Pipeline | () | 返回一个redis.Pipeliner对象 |
这只是 redis.Client 中方法和属性的一部分,实际上它还提供了许多其他功能。如果需要具体某一功能或更多细节,建议直接查看 go-redis 的官方文档或源代码。
当然可以,以下是给出的代码示例:
package main
import (
"fmt"
"time"
"github.com/go-redis/redis/v8"
"golang.org/x/net/context"
)
func main() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
DB: 0,
})
// 发送 PING 命令,通常用于测试连接是否正常。
res, err := rdb.Ping(ctx).Result()
fmt.Println(res, err) // PONG <nil>
// 获取指定 key 的值。
rdb.Set(ctx, "mykey", "myvalue", 0)
val, err := rdb.Get(ctx, "mykey").Result()
fmt.Println(val) // myvalue
// 设置指定 key 的值,并可指定一个过期时间。
rdb.Set(ctx, "key", "value", time.Second)
time.Sleep(2 * time.Second)
val, err = rdb.Get(ctx, "key").Result()
if err == redis.Nil {
fmt.Println("key does not exist") // key does not exist
} else if err != nil {
fmt.Println("Get failed", err)
} else {
fmt.Println("key", val)
}
// 删除一个或多个 keys。
rdb.Del(ctx, "key")
val, err = rdb.Get(ctx, "key").Result()
if err == redis.Nil {
fmt.Println("key does not exist") // key does not exist
}
// 检查一个或多个 keys 是否存在。
count := rdb.Exists(ctx, "key", "mykey").Val()
fmt.Println(count) // 1
// 为给定 key 设置过期时间。
rdb.Expire(ctx, "mykey", time.Second*10)
// 获取给定 key 的剩余生存时间。
duration := rdb.TTL(ctx, "mykey").Val()
fmt.Println(duration) // 9.9998765s
// 将一个或多个值插入到列表的尾部(最右边)。
rdb.RPush(ctx, "list", "value1", "value2", "value3")
// 将一个或多个值插入到列表的头部(最左边)。
rdb.LPush(ctx, "list", "value0")
// 移除并返回列表的最后一个元素。
lastVal := rdb.RPop(ctx).Val()
fmt.Println(lastVal) // value3
// 移除并返回列表的第一个元素。
firstVal := rdb.LPop(ctx).Val()
fmt.Println(firstVal) // value0
// 返回列表的长度。
length := rdb.LLen(ctx, "list").Val()
fmt.Println(length) // 2
// 为哈希表中的字段设置值。
rdb.HSet(ctx, "myhash", "field", "value")
// 获取存储在哈希表中指定字段的值。
hashVal := rdb.HGet(ctx, "myhash", "field").Val()
fmt.Println(hashVal) // value
// 获取在哈希表中指定 key 的所有字段和值。
hashAll := rdb.HGetAll(ctx, "myhash").Val()
fmt.Println(hashAll) // map[field:value]
// 将一个或多个成员元素及其分数值加入到有序集当中。
rdb.ZAdd(ctx, "myzset", &redis.Z{Score: 1, Member: "one"}, &redis.Z{Score: 2, Member: "two"})
// 通过索引区间返回有序集合成指定区间内的成员。
rangeRes := rdb.ZRange(ctx, "myzset", 0, -1).Val()
fmt.Println(rangeRes) // [one two]
// 返回客户端的配置选项。
opts := rdb.Options()
fmt.Println(opts.Addr) // localhost:6379
rdb.Close()
}
请注意:这只是一个简单示例,没有进行详细的错误处理。在实际应用中,应始终处理可能的错误并确保资源(如数据库连接)正确关闭。
TxPipelined
是的,TxPipelined 方法提供了一种方式来在事务中执行多个 Redis 命令。这基于 Redis 的事务特性,使用了 MULTI 和 EXEC 命令来包裹一个事务块。与此同时,它也结合了 pipelining 的特性,允许客户端在等待服务器回应之前发送多个命令。
基本介绍:
在 Redis 中,事务提供了一种将多个命令作为一个整体执行的方式。这意味着,要么所有的命令都成功执行,要么一个都不执行。但需要注意的是,Redis 的事务不像传统的 RDBMS 事务,它们不支持回滚。如果事务块中的某个命令失败,后续的命令仍然会执行。
TxPipelined 结合了 pipelining 和事务的特性,允许您批量执行多个命令,而不用等待每个命令的回应。
代码示例:
package main
import (
"fmt"
"log"
"github.com/go-redis/redis/v8"
"golang.org/x/net/context"
)
func main() {
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379", // Redis地址
Password: "", // Redis密码,没有则留空
DB: 0, // 使用的DB编号
})
defer rdb.Close()
ctx := context.Background()
// 使用 TxPipelined 执行事务
_, err := rdb.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
// 以下命令将会被打包在一个事务中执行
pipe.Set(ctx, "key1", "value1", 0)
pipe.Set(ctx, "key2", "value2", 0)
pipe.Incr(ctx, "counter")
return nil
})
if err != nil {
log.Fatalf("TxPipelined failed: %v", err)
}
val1, err := rdb.Get(ctx, "key1").Result()
val2, err := rdb.Get(ctx, "key2").Result()
count, err := rdb.Get(ctx, "counter").Result()
fmt.Println("key1:", val1)
fmt.Println("key2:", val2)
fmt.Println("counter:", count)
}
以上代码中,我们使用 TxPipelined 方法在一个事务中执行了三个命令。如果这三个命令中的任何一个失败,其他的仍然会执行,但在大多数情况下,您应该期望它们都成功执行。
需要注意的是,与传统的 RDBMS 不同,Redis 的事务不提供回滚功能。
redis.Pipeliner
redis.Pipeliner 是 Go Redis 客户端的一个接口,用于支持 pipelining 功能。Pipelining 允许你一次性发送多个命令到 Redis 服务器,而不需要等待每个命令的响应。这可以提高性能,因为它减少了每次命令和响应之间的网络往返时间。
以下是 redis.Pipeliner 的一些常用方法和属性:
名称 | 函数签名 | 描述 |
---|---|---|
Get | Get(ctx context.Context, key string) *StringCmd | 获取一个 key 的值。 |
Set | Set(ctx context.Context, key string, value interface{}, expiration time.Duration) *StatusCmd | 设置一个 key 的值,可以带有一个可选的过期时间。 |
Incr | Incr(ctx context.Context, key string) *IntCmd | 将 key 的值增加 1。 |
Decr | Decr(ctx context.Context, key string) *IntCmd | 将 key 的值减少 1。 |
MGet | MGet(ctx context.Context, keys ...string) *SliceCmd | 获取多个 keys 的值。 |
MSet | MSet(ctx context.Context, values ...interface{}) *StatusCmd | 设置多个 keys 的值。 |
Del | Del(ctx context.Context, keys ...string) *IntCmd | 删除一个或多个 keys。 |
LPush | LPush(ctx context.Context, key string, values ...interface{}) *IntCmd | 将一个或多个值插入到列表的左侧(头部)。 |
RPush | RPush(ctx context.Context, key string, values ...interface{}) *IntCmd | 将一个或多个值插入到列表的右侧(尾部)。 |
Exec | Exec(ctx context.Context) ([]Cmder, error) | 执行所有缓存的命令并返回结果。 |
请注意,redis.Pipeliner 有许多其他方法,上面列出的只是一部分常用方法。如果你想了解更多方法或详细信息,建议查看官方文档或源代码。
当然可以。以下是每一项的代码示例,以及它们对应的中文描述注释:
package main
import (
"fmt"
"time"
"github.com/go-redis/redis/v8"
"golang.org/x/net/context"
)
func main() {
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
defer rdb.Close()
ctx := context.Background()
// 执行所有缓存的命令并返回结果。
pipeliner := rdb.Pipeline()
pipeliner.Set(ctx, "pipeKey", "pipeValue", 0)
pipeliner.Get(ctx, "pipeKey")
_, err = pipeliner.Exec(ctx)
if err != nil {
fmt.Println(err)
} else {
pipeGet := pipeliner.Get(ctx, "pipeKey")
fmt.Println(pipeGet.Val()) // 输出结果
}
}
请注意,上述代码示例主要展示了每个操作的基本用法。在实际使用中,还需要处理各种错误和返回值的细节。
存储其他数据类型
Redis 支持多种数据类型,包括:
- Strings(字符串) :
err := client.Set(ctx, "key", "value", 0).Err()
if err != nil {
panic(err)
}
val, err := client.Get(ctx, "key").Result()
if err != nil {
panic(err)
}
fmt.Println("key:", val)
- Lists(列表) :
client.LPush(ctx, "listKey", "value1", "value2", "value3")
val, err := client.RPop(ctx, "listKey").Result()
if err != nil {
panic(err)
}
fmt.Println("Popped from list:", val)
- Sets(集合) :
client.SAdd(ctx, "setKey", "member1", "member2", "member3")
members, err := client.SMembers(ctx, "setKey").Result()
if err != nil {
panic(err)
}
fmt.Println("Set members:", members)
- Sorted Sets(有序集合) :
client.ZAdd(ctx, "zsetKey", &redis.Z{
Score: 1,
Member: "member1",
}, &redis.Z{
Score: 2,
Member: "member2",
})
rank, err := client.ZRank(ctx, "zsetKey", "member1").Result()
if err != nil {
panic(err)
}
fmt.Println("Rank of member1:", rank)
- Hashes(哈希) :
client.HSet(ctx, "hashKey", "field1", "value1", "field2", "value2")
val, err := client.HGet(ctx, "hashKey", "field1").Result()
if err != nil {
panic(err)
}
fmt.Println("hashKey field1:", val)
- Bitmaps(不会以常规的键值对的方式进行存储和查询,但 Redis 提供了特定的命令来在位层面上操作 bitmaps)。
- HyperLogLogs(也不会以常规的键值对方式进行存储和查询,但 Redis 提供了特定的命令来使用 HyperLogLog 进行唯一值的计数)。
- Streams(是 Redis 5.0 版本引入的新数据类型,用于实现消息队列的功能)。
以上是 Redis 支持的主要数据类型的简单示例。为了执行上述示例,您需要使用适当的 Redis 客户端库(例如 go-redis),并确保已经建立了与 Redis 服务器的连接。
列表(双向链表)
Redis 中的列表数据类型允许你在列表的头部或尾部添加、弹出元素。列表中的元素可以重复。以下是 Redis 中操作列表的一些基本方法:
名称 | 函数签名 | 描述 |
---|---|---|
添加 | LPush(key string, values ...interface{}) | 在列表的头部添加一个或多个元素。 |
RPush(key string, values ...interface{}) | 在列表的尾部添加一个或多个元素。 | |
弹出 | LPop(key string) | 从列表的头部弹出一个元素,并返回该元素。 |
RPop(key string) | 从列表的尾部弹出一个元素,并返回该元素。 | |
获取 | LRange(ctx context.Context, key string, start, stop int64) | 获取列表指定范围内的所有元素。(确认需要ctx参数) |
设置 | LSet(key string, index int64, value interface{}) | 设置列表中指定索引的元素值。 |
删除 | LRem(key string, count int64, value interface{}) | 根据指定的 count 值,从头到尾或从尾到头删除等于 value 的元素。 count > 0 时从头到尾查找,count < 0 时从尾到头查找,count = 0 时删除所有等于 value 的元素。 |
长度 | LLen(key string) | 获取列表的长度。 |
索引获取 | LIndex(key string, index int64) | 获取列表中指定索引的元素。 |
截取 | LTrim(key string, start, stop int64) | 修剪列表,使其只保留指定范围内的元素。 |
这些函数签名是基于 Go 语言的 go-redis 客户端库。当使用这个库时,通常还需要传递一个上下文参数 (ctx) 到每个函数中。
在 Redis 中,列表是一个双向链表,这意味着可以在其头部和尾部快速地添加或移除元素。
- 头部 (Left) :列表的第一个元素。由于 Redis 的列表是双向链表,所以在头部添加或移除元素是一个 O(1) 操作。
- 尾部 (Right) :列表的最后一个元素。同样地,由于 Redis 的列表实现方式,所以在尾部添加或移除元素也是一个 O(1) 操作。
关于命令前缀:
- "L" 代表 "Left",即列表的头部。
- "R" 代表 "Right",即列表的尾部。
这种命名约定为什么采用 "L" 和 "R" 而不是 "H"(Head)和 "T"(Tail)呢?这实际上是由于 Redis 的开发者选择的一种命名方式,可能是因为 "Left" 和 "Right" 在某种程度上更直观地表示了操作的方向,而不是操作的位置。而且 "Left" 和 "Right" 这样的命名方式在其他数据结构,如双端队列,中也很常见。
当你处理 Redis 列表时,通常使用 "L" 命令对头部进行操作,使用 "R" 命令对尾部进行操作,从而根据具体的应用场景来选择最合适的操作。
好的,以下是基于 go-redis 客户端库的代码示例:
func SetList(rdb *redis.Client) {
// 在列表的头部添加一个或多个元素。
//会从左侧先推入world 再推入hello
err := rdb.LPush(ctx, "mylist", "world", "hello").Err()
if err != nil {
panic(err)
}
// 输出: hello world
// 在列表的尾部添加一个或多个元素。
err = rdb.RPush(ctx, "mylist", "foo").Err()
if err != nil {
panic(err)
}
// 输出: hello world foo
// 从列表的头部弹出一个元素,并返回该元素。弹出,会在列表中删除该元素
val, err := rdb.LPop(ctx, "mylist").Result()
if err != nil {
panic(err)
}
fmt.Println(val) // 输出: hello
// 从列表的尾部弹出一个元素,并返回该元素。弹出,会在列表中删除该元素
val, err = rdb.RPop(ctx, "mylist").Result()
if err != nil {
panic(err)
}
fmt.Println(val) // 输出: foo
// 获取列表指定范围内的所有元素。
vals, err := rdb.LRange(ctx, "mylist", 0, -1).Result()
if err != nil {
panic(err)
}
fmt.Println(vals) // 输出: [world]
// 设置列表中指定索引的元素值。会更改该索引位置的值
err = rdb.LSet(ctx, "mylist", 0, "earth").Err()
if err != nil {
panic(err)
}
// 输出: [earth]
// 根据指定的 count 值,从头到尾或从尾到头删除等于 value 的元素。0代表删除所有匹配的元素
err = rdb.LRem(ctx, "mylist", 0, "earth").Err()
if err != nil {
panic(err)
}
// 输出: []
// 获取列表的长度。
length, err := rdb.LLen(ctx, "mylist").Result()
if err != nil {
panic(err)
}
fmt.Println(length) // 输出: 0
// 获取列表中指定索引的元素。
val, err = rdb.LIndex(ctx, "mylist", 0).Result()
if err == redis.Nil {
fmt.Println("element not found") // 输出: element not found
} else if err != nil {
panic(err)
} else {
fmt.Println(val)
}
// 修剪列表,使其只保留指定范围内的元素。
err = rdb.LTrim(ctx, "mylist", 0, 1).Err()
if err != nil {
panic(err)
}
// 输出: []
//可以在创建连接时声明
rdb.Close()
}
此示例假定 Redis 正在本地运行,地址是默认的 localhost:6379,并且没有密码保护。确保调整为你的环境设置。
LREM
在 Redis 的 LREM 命令中,第二个参数是一个整数,表示要删除的匹配元素的数量。其行为如下:
- 如果这个整数是 0,那么它将删除列表中所有匹配的元素。
- 如果这个整数是正数,那么它将从头部开始删除相应数量的匹配元素。
- 如果这个整数是负数,那么它将从尾部开始删除相应数量的匹配元素。
所以,对于你提供的代码:
err = rdb.LRem(ctx, "mylist", 0, "earth").Err()
0 意味着它将从 mylist 中删除所有值为 "earth" 的元素,而不仅仅是一个或几个。
LRange
rdb.LRange(ctx, key, 0, -1) 是从 Redis 中获取列表数据的方法,让我们逐个参数地了解它:
- ctx: 这是上下文对象,通常用于控制取消、超时等操作。在大多数情况下,您可以使用 context.Background() 或 context.TODO() 作为此参数。
- key: 这是 Redis 列表的 key 名称,它指定您想要访问的列表。
- 0: 这是开始索引。在 Redis 列表中,索引从 0 开始,表示列表的第一个元素。所以,这里的 0 意味着从列表的开始位置获取数据。
- -1: 这是结束索引。在 Redis 列表中,-1 是一个特殊的索引,表示列表的最后一个元素。所以,-1 意味着一直获取到列表的末尾。
因此,当您执行 rdb.LRange(ctx, key, 0, -1) 时,您实际上是获取 Redis 列表中的所有元素,从第一个元素到最后一个元素。
更进一步地,您可以使用其他索引值来获取列表的特定部分:
- rdb.LRange(ctx, key, 1, 2) 会获取列表中从第二个到第三个元素的部分。
- rdb.LRange(ctx, key, -3, -1) 会获取列表中的最后三个元素。
这些索引为您提供了获取列表中任何子集的能力。
对不起,您是对的。在新版本的 go-redis 库中,许多函数,如 LRange,已经不再直接接受 context.Context 作为第一个参数。而是通过后面的方法,如 WithContext,来传递上下文。
传统的方法原型确实如下:
LRange(key string, start, stop int64) *StringSliceCmd
当你使用这个方法时,它会返回一个 *StringSliceCmd 类型,你可以通过 .Val() 来获取值或 .Err() 来获取可能发生的错误。
例如:
result := rdb.LRange("mylist", 0, -1)
values := result.Val() // 获取值
err := result.Err() // 获取错误
如果您需要传递一个 context.Context 到 Redis 命令,您可以这样做:
ctx := context.Background()
rdb.WithContext(ctx).LRange("mylist", 0, -1)
对于 WithContext,它返回一个新的客户端对象,该对象使用给定的上下文执行命令。
再次为我的误导向您致以诚挚的歉意,感谢您的纠正。
LInsertBefore、LInsertAfter
Redis 列表的数据结构并不提供直接在中间位置插入或删除元素的原子操作。但你可以通过以下的命令来实现:
- LINSERT:在指定的元素前或后插入一个元素。
- 为删除指定下标的元素,你需要采用组合命令,如先使用LRANGE获取整个列表,然后在应用程序中删除该元素,再使用DEL和RPUSH或LPUSH重建整个列表。
下面是具体的代码示例:
- 在指定元素前或后插入元素
// 在元素 "world" 之前插入 "hello"
err := rdb.LInsertBefore(ctx, "mylist", "world", "hello").Err()
if err != nil {
panic(err)
}
// 在元素 "world" 之后插入 "there"
err = rdb.LInsertAfter(ctx, "mylist", "world", "there").Err()
if err != nil {
panic(err)
}
在Redis中,如果列表里有多个相同的元素,LInsertBefore 和 LInsertAfter 会寻找第一个匹配的元素来执行插入操作。也就是说,它不会对所有匹配的元素进行插入,只会对第一个找到的匹配元素进行操作。
举例来说,假设我们有一个列表 mylist,其值为 [a, b, c, b, d]:
- 如果我们执行 LInsertBefore 在 b 之前插入 x,那么列表会变成 [a, x, b, c, b, d]。
- 如果我们执行 LInsertAfter 在 b 之后插入 x,那么列表会变成 [a, b, x, c, b, d]。
以下是一个Go代码示例,展示了这种行为:
// ListLInsert 列表中插入
func ListLInsert(rdb *redis.Client) {
// 先清空并创建一个列表
rdb.Del(ctx, "mylist")
rdb.RPush(ctx, "mylist", "a", "b", "c", "b", "d")
// 在第一个 "b" 之前插入 "x"
rdb.LInsertBefore(ctx, "mylist", "b", "x")
list, _ := rdb.LRange(ctx, "mylist", 0, -1).Result()
fmt.Println(list) // 输出:[a x b c b d]
// 在第一个 "b" 之后插入 "y"
rdb.LInsertAfter(ctx, "mylist", "b", "y")
list, _ = rdb.LRange(ctx, "mylist", 0, -1).Result()
fmt.Println(list) // 输出:[a x b y c b d]
}
所以,如果你希望对其他匹配的元素执行插入操作,需要使用其他方法,如循环进行检查和插入。但这在大多数场景中可能并不是通常所需的。
中间位置的删除
- 删除指定下标的元素
// ListRemove 中间位置的删除
func ListRemove(rdb *redis.Client) {
key := "mylist"
index := 2
val, err := rdb.LRange(ctx, key, 0, -1).Result()
if err != nil {
panic(err)
}
// 确保索引有效
if index < 0 || index >= len(val) {
fmt.Errorf("index out of range")
}
// 移除指定索引的元素
val = append(val[:index], val[index+1:]...)
// 重新构建列表
_, err = rdb.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
pipe.Del(ctx, key)
pipe.RPush(ctx, key, val)
return nil
})
}
注意:上述删除指定下标元素的方法涉及到多个操作,并非原子性,可能会在高并发环境下遇到问题。如果确保原子性是关键需求,可能需要额外的逻辑或使用其他的数据结构或技术来实现。
集合(不重复)
集合在 Redis 中是一个无序的、唯一的数据结构,提供了常见的集合操作。以下是 Redis 中关于集合操作的一些常用方法和它们的描述:
名称 | 函数签名 | 描述 |
---|---|---|
添加 | SAdd(ctx, key, members ...interface{}) | 向集合添加一个或多个成员。 |
获取成员 | SMembers(ctx, key) | 返回集合中的所有成员。 |
判断成员 | SIsMember(ctx, key, member) | 判断 member 是否是集合的成员。 |
删除成员 | SRem(ctx, key, members ...interface{}) | 移除集合中一个或多个成员。 |
集合长度 | SCard(ctx, key) | 获取集合的成员数。 |
集合差集 | SDiff(ctx, keys ...string) | 返回给定所有集合的差集。 |
差集并存 | SDiffStore(ctx, destination, keys ...string) | 返回给定所有集合的差集并存储在 destination 中。 |
集合交集 | SInter(ctx, keys ...string) | 返回给定所有集合的交集。 |
交集并存 | SInterStore(ctx, destination, keys ...string) | 返回给定所有集合的交集并存储在 destination 中。 |
移动成员 | SMove(ctx, source, destination, member) | 将 member 从 source 集合移动到 destination 集合。 |
随机移除 | SPop(ctx, key) | 随机移除并返回集合中的一个成员。 |
随机元素 | SRandMember(ctx, key, countOpt ...int64) | 随机返回集合中的一个或多个成员。 |
集合并集 | SUnion(ctx, keys ...string) | 返回所有给定集合的并集。 |
并集并存 | SUnionStore(ctx, destination, keys ...string) | 返回所有给定集合的并集并存储在 destination 中。 |
这些操作在 Go 的 go-redis 库中都对应有相关的方法可以直接使用。
以下是使用 go-redis 库进行集合操作的一些常用方法及其描述:
func set(rdb *redis.Client) {
// SAdd: 向集合添加一个或多个成员。
// 注释: 返回被成功添加的新元素的数量
added, _ := rdb.SAdd(ctx, "myset", "member1", "member2").Result()
fmt.Println(added)
// SMembers: 返回集合中的所有成员。
members, _ := rdb.SMembers(ctx, "myset").Result()
fmt.Println(members)
// SIsMember: 判断 member 是否是集合的成员。
// 注释: 如果member是集合的成员,返回1,否则返回0
isMember, _ := rdb.SIsMember(ctx, "myset", "member1").Result()
fmt.Println(isMember)
// SRem: 移除集合中一个或多个成员。
removed, _ := rdb.SRem(ctx, "myset", "member1").Result()
fmt.Println(removed)
// SCard: 获取集合的成员数。
card, _ := rdb.SCard(ctx, "myset").Result()
fmt.Println(card)
// SDiff: 返回给定所有集合的差集。otherset不存在所以是空,不会报错
diff, _ := rdb.SDiff(ctx, "myset", "otherset").Result()
fmt.Println(diff)
// SDiffStore: 返回给定所有集合的差集并存储在 destination 中。会在redis中创建一个新集合
diffCount, _ := rdb.SDiffStore(ctx, "resultset", "myset", "otherset").Result()
fmt.Println(diffCount)
// SInter: 返回给定所有集合的交集。
inter, _ := rdb.SInter(ctx, "myset", "otherset").Result()
fmt.Println(inter)
// SInterStore: 返回给定所有集合的交集并存储在 destination 中。如果交集是空,会清空resultset这个key
interCount, _ := rdb.SInterStore(ctx, "resultset", "myset", "otherset").Result()
fmt.Println(interCount)
// SMove: 将 member 从 source 集合移动到 destination 集合。source集合会被删除
moved, _ := rdb.SMove(ctx, "myset", "otherset", "member2").Result()
fmt.Println(moved)
// SPop: 随机移除并返回集合中的一个成员。不存在返回空字符串
popped, _ := rdb.SPop(ctx, "myset").Result()
fmt.Println(popped)
// SRandMember: 随机返回集合中的一个或多个成员。
randMembers, _ := rdb.SRandMember(ctx, "myset").Result()
fmt.Println(randMembers)
// SUnion: 返回所有给定集合的并集。
union, _ := rdb.SUnion(ctx, "myset", "otherset").Result()
fmt.Println(union)
// SUnionStore: 返回所有给定集合的并集并存储在 destination 中。不会删除原有集合
unionCount, _ := rdb.SUnionStore(ctx, "resultset", "myset", "otherset").Result()
fmt.Println(unionCount)
}
请注意:这只是一个示例代码块,用于展示如何使用 go-redis 中的各种集合操作。在实际使用时,你应该合理处理任何可能出现的错误。
client.SAdd
client.SAdd(ctx, "setKey", "member1", "member2", "member3") 会向 Redis 中添加一个集合(Set)数据结构。
具体操作如下:
- 使用 key "setKey"。
- 向这个集合中添加三个成员:"member1"、"member2"、和 "member3"。
在 Redis 中,集合是一个无序的、不含重复成员的集。这意味着,无论你添加多少次相同的成员,它在集合中只会存在一次。
执行这个命令后,你可以使用 Redis 的 SMEMBERS 命令来检查 "setKey" 下的所有成员:
SMEMBERS setKey
输出应该会是:
1) "member1"
2) "member2"
3) "member3"
请注意,由于集合是无序的,返回的成员的顺序可能会与插入的顺序不同。
Sorted Sets(有序集合)
有序集合(Sorted Sets)是一种具有权重的集合,允许你按照元素的权重值排序,就是给每个member设一个权重值score。以下是使用 go-redis 库进行Sorted Set操作的常用方法:
名称 | 函数签名 | 描述 |
---|---|---|
ZAdd | ZAdd(ctx context.Context, key string, members ...*Z) *IntCmd | 向有序集合添加一个或多个成员。 |
ZCard | ZCard(ctx context.Context, key string) *IntCmd | 获取有序集合的成员数。 |
ZCount | ZCount(ctx context.Context, key, min, max string) *IntCmd | 计算在给定分数区间内的成员数量。 |
ZIncrBy | ZIncrBy(ctx context.Context, key string, increment float64, member string) *FloatCmd | 通过指定的增量递增成员的分数。 |
ZInterStore | ZInterStore(ctx context.Context, destination string, store *ZStore) *IntCmd | 计算给定的一个或多个有序集合的交集并将结果集存储在新的有序集合中。 |
ZPopMax | ZPopMax(ctx context.Context, key string, count ...int64) *ZSliceCmd | 移除并返回有序集合中的最大成员。 |
ZPopMin | ZPopMin(ctx context.Context, key string, count ...int64) *ZSliceCmd | 移除并返回有序集合中的最小成员。 |
ZRange | ZRange(ctx context.Context, key string, start, stop int64) *StringSliceCmd | 通过索引区间返回有序集合成指定区间内的成员。 |
ZRangeByScore | ZRangeByScore(ctx context.Context, key string, opt *ZRangeBy) *StringSliceCmd | 通过分数返回有序集内指定区间内的成员。 |
ZRank | ZRank(ctx context.Context, key, member string) *IntCmd | 返回有序集合中指定成员的索引。 |
ZRem | ZRem(ctx context.Context, key string, members ...interface{}) *IntCmd | 移除有序集合中的一个或多个成员。 |
ZRemRangeByRank | ZRemRangeByRank(ctx context.Context, key string, start, stop int64) *IntCmd | 移除有序集合中给定的索引区间的所有成员。 |
ZRemRangeByScore | ZRemRangeByScore(ctx context.Context, key, min, max string) *IntCmd | 移除有序集合中给定的分数区间的所有成员。 |
ZRevRange | ZRevRange(ctx context.Context, key string, start, stop int64) *StringSliceCmd | 返回有序集中指定区间内的成员,通过索引,分数从高到底。 |
ZRevRangeByScore | ZRevRangeByScore(ctx context.Context, key string, opt *ZRangeBy) *StringSliceCmd | 返回有序集中指定分数区间内的成员,分数从高到底排序。 |
ZScore | ZScore(ctx context.Context, key, member string) *FloatCmd | 返回有序集合中指定成员的分数。 |
ZUnionStore | ZUnionStore(ctx context.Context, dest string, store *ZStore) *IntCmd | 计算给定的一个或多个有序集合的并集并存储在新的有序集合中。 |
请注意,go-redis库还提供了更多的操作方法,这里只是列举了一部分常用的方法。
func sortedSet(rdb *redis.Client) {
// 向有序集合添加一个或多个成员。
addRes1 := rdb.ZAdd(ctx, "zset", &redis.Z{Score: 1, Member: "one"})
fmt.Println("ZAdd (one):", addRes1.Val()) // 输出:1
// ZAdd不会给这个key重新赋值 不会把上面的 key值 覆盖掉。但是在数据库中显示[(b'one', 1.0), (b'two', 2.0)],
// 应该是宝塔面板数据显示问题
addRes2 := rdb.ZAdd(ctx, "zset", &redis.Z{Score: 2, Member: "two"}, &redis.Z{Score: 3, Member: "three"})
fmt.Println("ZAdd (two & three):", addRes2.Val()) // 输出:2
// 获取有序集合的成员数。
card := rdb.ZCard(ctx, "zset")
//为什么是3个,看存储的明明是[(b'one', 1.0), (b'two', 2.0)] 两个值
fmt.Println("ZCard:", card.Val()) // 输出:3
// 计算在给定区间内的成员数量。1-2区间内肯定有两个
count := rdb.ZCount(ctx, "zset", "1", "2")
fmt.Println("ZCount:", count.Val()) // 输出:2
// 通过指定的增量递增成员的分数。将one成员的分数+1
incr := rdb.ZIncrBy(ctx, "zset", 1, "one")
fmt.Println("ZIncrBy:", incr.Val()) // 输出:2
// 通过索引区间返回有序集合成指定区间内的成员。-1应该表示到结尾
rangeRes := rdb.ZRange(ctx, "zset", 0, -1)
fmt.Println("ZRange:", rangeRes.Val()) // 输出:["one", "two", "three"]
// 返回有序集合中指定成员的索引。
rank := rdb.ZRank(ctx, "zset", "one")
fmt.Println("ZRank:", rank.Val()) // 输出:0
// 移除有序集合中的一个或多个成员。
rem := rdb.ZRem(ctx, "zset", "one")
fmt.Println("ZRem:", rem.Val()) // 输出:1
// 移除有序集合中给定的索引区间的所有成员。
remRangeRank := rdb.ZRemRangeByRank(ctx, "zset", 0, 1)
fmt.Println("ZRemRangeByRank:", remRangeRank.Val()) // 输出:2
// 移除有序集合中给定的分数区间的所有成员。
remRangeScore := rdb.ZRemRangeByScore(ctx, "zset", "1", "2")
fmt.Println("ZRemRangeByScore:", remRangeScore.Val()) // 输出:2
// 返回有序集中指定区间内的成员,通过索引,分数从高到底。
revRange := rdb.ZRevRange(ctx, "zset", 0, -1)
fmt.Println("ZRevRange:", revRange.Val()) // 输出:["three", "two", "one"]
// 返回有序集中指定分数区间内的成员,分数从高到底排序。
revRangeScore := rdb.ZRevRangeByScore(ctx, "zset", &redis.ZRangeBy{Min: "1", Max: "2"})
fmt.Println("ZRevRangeByScore:", revRangeScore.Val()) // 输出:["two", "one"]
// 返回有序集合中指定成员的分数。
score := rdb.ZScore(ctx, "zset", "one")
fmt.Println("ZScore:", score.Val()) // 输出:2
}
ZAdd
在 Redis 的有序集合(sorted set)数据结构中,ZAdd 用于将一个或多个成员及其分数添加到有序集合中。如果指定的成员已经存在于有序集合中,那么只会更新这个成员的分数,其他未指定的成员不会受到影响。如果有序集合不存在,则会新建一个。如果成员不存在,则会添加该成员。
例如:
- 当您首次调用 ZAdd 为键 "zset" 添加成员 "one" 时,有序集合会是这样的:{one: 1}。
- 如果您再次调用 ZAdd 为键 "zset" 添加成员 "two" 和 "three",有序集合将变为:{one: 1, two: 2, three: 3}。
- 如果您再次对成员 "one" 调用 ZAdd 并为其提供新的分数,例如 5,有序集合将更新为:{one: 5, two: 2, three: 3}。
所以,ZAdd 不会重置整个有序集合,它只是添加或更新指定的成员。
ZCount
ZCount 方法是用来计算有序集合(sorted set)中指定分数区间内的成员数量。它返回分数值在 min 和 max 之间的元素的数量(包括 min 和 max)。
函数签名:
ZCount(key string, min, max string) *IntCmd
例如,假设我们有一个名为 zset 的有序集合,其内容如下:
one: 1
two: 2
three: 3
four: 4
five: 5
如果我们想要计算分数在 2 和 4 之间(包括2和4)的成员数量,可以这样使用:
count := rdb.ZCount(ctx, "zset", "2", "4").Val()
fmt.Println(count) // 输出:3
上述代码输出的 3 是因为成员 two, three, 和 four 的分数都在 2 和 4 之间。
Hashes(哈希)
当使用 Go 语言与 Redis 进行交互操作哈希(Hashes)时,常用的库是 go-redis/redis。以下是该库中关于哈希操作的一些常用方法的介绍:
名称 | 函数签名 | 描述 |
---|---|---|
HSet | HSet(key string, values ...interface{}) *IntCmd | 设置哈希字段的值 |
HGet | HGet(key string, field string) *StringCmd | 获取存储在哈希字段的值 |
HExists | HExists(key string, field string) *BoolCmd | 检查哈希字段是否存在 |
HDel | HDel(key string, fields ...string) *IntCmd | 删除一个或多个哈希字段 |
HGetAll | HGetAll(key string) *StringStringMapCmd | 获取存储在哈希的所有字段和值 |
HIncrBy | HIncrBy(key string, field string, incr int64) *IntCmd | 为哈希字段的整数值增加给定的整数 |
HIncrByFloat | HIncrByFloat(key string, field string, incr float64) *FloatCmd | 为哈希字段的浮点数值增加给定的浮点数 |
HKeys | HKeys(key string) *StringSliceCmd | 获取哈希的所有字段 |
HLen | HLen(key string) *IntCmd | 获取哈希的字段数量 |
HMGet | HMGet(key string, fields ...string) *SliceCmd | 获取所有给定字段的值 |
HMSet | HMSet(key string, fields map[string]interface{}) *StatusCmd | 同时设置一个或多个字段值 |
HSetNX | HSetNX(key string, field string, value interface{}) *BoolCmd | 仅当字段不存在时,设置哈希字段的值 |
HVals | HVals(key string) *StringSliceCmd | 获取哈希的所有值 |
以上是 go-redis/redis 库中关于哈希操作的常用方法。你可以根据需要选择适当的方法来进行相关的哈希操作。
func hash(rdb *redis.Client) {
// 设置哈希字段的值
rdb.HSet(ctx, "hashkey", "field1", "value1") // 输出: 1
// 获取存储在哈希字段的值
fmt.Println(rdb.HGet(ctx, "hashkey", "field1")) // 输出: value1
// 检查哈希字段是否存在
fmt.Println(rdb.HExists(ctx, "hashkey", "field1")) // 输出: true
// 删除一个或多个哈希字段
rdb.HDel(ctx, "hashkey", "field1") // 输出: 1
// 获取存储在哈希的所有字段和值
fmt.Println(rdb.HGetAll(ctx, "hashkey")) // 输出: map[field1:value1]
// 为哈希字段的整数值增加给定的整数
rdb.HSet(ctx, "hashkey", "intfield", "5")
rdb.HIncrBy(ctx, "hashkey", "intfield", 5) // 输出: 10
// 为哈希字段的浮点数值增加给定的浮点数
rdb.HSet(ctx, "hashkey", "floatfield", "5.5")
rdb.HIncrByFloat(ctx, "hashkey", "floatfield", 1.5) // 输出: 7.0
// 获取哈希的所有字段
fmt.Println(rdb.HKeys(ctx, "hashkey")) // 输出: [field1 intfield floatfield]
// 获取哈希的字段数量
fmt.Println(rdb.HLen(ctx, "hashkey")) // 输出: 3
// 获取所有给定字段的值
fmt.Println(rdb.HMGet(ctx, "hashkey", "field1", "intfield")) // 输出: [value1 10]
// 同时设置一个或多个字段值
rdb.HMSet(ctx, "hashkey", map[string]interface{}{"newfield1": "newvalue1", "newfield2": "newvalue2"})
// 仅当字段不存在时,设置哈希字段的值
rdb.HSetNX(ctx, "hashkey", "field1", "newvalue1") // 输出: false
// 获取哈希字段值的长度
fmt.Println(rdb.HLen(ctx, "field1")) // 输出: 6
// 获取哈希的所有值
fmt.Println(rdb.HVals(ctx, "hashkey")) // 输出: [value1 10 7.0 newvalue1 newvalue2]
}