Redis 集合(Set)
Redis 集合是一种无序的字符串集合,集合中的每个元素都是唯一的,不允许重复。集合适用于需要快速查找和去重的场景,如标签系统、好友列表、推荐系统等。
场景示例
-
标签系统
- 示例:
SADD tags "golang" SADD tags "redis" SADD tags "database" SMEMBERS tags
- 说明:使用
SADD
添加标签,使用SMEMBERS
获取所有标签。
- 示例:
-
好友列表
- 示例:
SADD friends:user1 "user2" SADD friends:user1 "user3" SADD friends:user2 "user1" SADD friends:user2 "user3" SINTER friends:user1 friends:user2
- 说明:使用
SADD
添加好友,使用SINTER
获取共同好友。
- 示例:
-
推荐系统
- 示例:
SADD viewed:product1 "user1" SADD viewed:product1 "user2" SADD viewed:product2 "user2" SADD viewed:product2 "user3" SUNION viewed:product1 viewed:product2
- 说明:使用
SADD
记录查看产品的用户,使用SUNION
获取所有查看过某些产品的用户。
- 示例:
-
去重操作
- 示例:
SADD unique:emails "user1@example.com" SADD unique:emails "user2@example.com" SADD unique:emails "user1@example.com" SMEMBERS unique:emails
- 说明:使用
SADD
添加邮件地址,使用SMEMBERS
获取唯一的邮件地址列表。
- 示例:
-
随机获取元素
- 示例:
SADD colors "red" "green" "blue" SRANDMEMBER colors
- 说明:使用
SADD
添加颜色,使用SRANDMEMBER
随机获取一个颜色。
- 示例:
底层实现
Redis 集合的底层实现主要基于两种数据结构:intset
(整数集合)和 hashtable
(哈希表)。
-
整数集合(intset)
- 当集合中的所有元素都是整数且数量较少时,Redis 使用整数集合存储集合。整数集合是一种紧凑的编码方式,节省内存。
- 结构:
typedef struct intset { uint32_t encoding; // 编码方式 uint32_t length; // 集合中元素的数量 int8_t contents[]; // 元素数组 } intset;
- 优点:内存占用小,适合存储小规模整数集合。
-
哈希表(hashtable)
- 当集合中的元素类型复杂或数量较多时,Redis 使用哈希表存储集合。哈希表提供快速的查找、插入和删除操作。
- 结构:
typedef struct dictht { dictEntry **table; // 哈希表数组 unsigned long size; // 哈希表大小 unsigned long sizemask; // 哈希表大小掩码,用于计算索引 unsigned long used; // 已使用节点数量 } dictht; typedef struct dictEntry { void *key; // 键 void *val; // 值(在集合中为 NULL) struct dictEntry *next; // 链表指针,解决冲突 } dictEntry;
- 优点:高效处理大规模数据,查找速度快。
Redis 会根据集合的元素类型和数量,动态选择最合适的数据结构进行存储,从而在性能和内存使用之间取得平衡。
Go 中使用 Redis 集合
在 Go 语言中,可以使用 go-redis
库来操作 Redis 集合。下面是一些基本操作示例。
-
连接 Redis
package main import ( "github.com/go-redis/redis/v8" "context" "fmt" ) var ctx = context.Background() func main() { rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "", // no password set DB: 0, // use default DB }) // 示例代码调用 example(rdb) } func example(rdb *redis.Client) { // 示例操作 }
-
添加元素
func example(rdb *redis.Client) { err := rdb.SAdd(ctx, "tags", "golang").Err() if err != nil { panic(err) } err = rdb.SAdd(ctx, "tags", "redis").Err() if err != nil { panic(err) } }
-
获取所有元素
func example(rdb *redis.Client) { members, err := rdb.SMembers(ctx, "tags").Result() if err != nil { panic(err) } fmt.Println("Tags:", members) }
-
检查元素是否存在
func example(rdb *redis.Client) { exists, err := rdb.SIsMember(ctx, "tags", "golang").Result() if err != nil { panic(err) } fmt.Println("Is 'golang' a member of tags?", exists) }
-
删除元素
func example(rdb *redis.Client) { err := rdb.SRem(ctx, "tags", "redis").Err() if err != nil { panic(err) } members, err := rdb.SMembers(ctx, "tags").Result() if err != nil { panic(err) } fmt.Println("Tags after removal:", members) }
-
随机获取元素
func example(rdb *redis.Client) { member, err := rdb.SRandMember(ctx, "tags").Result() if err != nil { panic(err) } fmt.Println("Random tag:", member) }
通过这些示例,可以在 Go 语言中方便地操作 Redis 集合,实现各种常见场景的需求。