Redis Set实战:Set在实际项目中的应用

Redis Set 实战:Set 在实际项目中的应用

Redis,作为一款高性能的键值存储数据库,以其丰富的数据结构和卓越的性能,在各种互联网应用中扮演着重要的角色。其中,Set(集合)作为一种独特的数据结构,以其无序性、唯一性等特点,在实际项目中有着广泛的应用场景。本文将深入探讨 Redis Set 的特性,并通过多个实际案例,详细解析 Set 在项目中的应用,帮助读者更好地理解和掌握 Set 的使用技巧。

一、Redis Set 数据结构详解

在深入探讨 Set 的应用之前,我们首先需要了解 Set 的基本特性和常用命令。

1.1 Set 的特性

Redis Set 是一个无序的、不重复的字符串集合。这意味着:

  • 无序性: Set 中的元素没有固定的顺序,每次获取到的元素顺序都可能不同。
  • 唯一性: Set 中不允许存在重复的元素,如果尝试添加重复元素,Redis 会自动忽略。
  • 基于字符串: Set 中的元素都是字符串类型。

1.2 Set 的常用命令

Redis 提供了一系列操作 Set 的命令,以下是一些常用的命令:

  • SADD key member [member ...]: 向集合 key 中添加一个或多个成员。
  • SMEMBERS key: 返回集合 key 中的所有成员。
  • SISMEMBER key member: 判断 member 元素是否是集合 key 的成员。
  • SCARD key: 获取集合 key 的成员数。
  • SREM key member [member ...]: 移除集合 key 中的一个或多个成员。
  • SPOP key [count]: 随机移除并返回集合中的一个或多个元素。
  • SRANDMEMBER key [count]:随机获取集合中的一个或多个元素。
  • SMOVE source destination member: 将 member 元素从 source 集合移动到 destination 集合。
  • SINTER key [key ...]: 返回所有给定集合的交集。
  • SUNION key [key ...]: 返回所有给定集合的并集。
  • SDIFF key [key ...]: 返回第一个集合与其他集合的差集。
  • SINTERSTORE destination key [key ...]: 将所有给定集合的交集存储在 destination 集合中。
  • SUNIONSTORE destination key [key ...]: 将所有给定集合的并集存储在 destination 集合中。
  • SDIFFSTORE destination key [key ...]: 将第一个集合与其他集合的差集存储在 destination 集合中。

二、Redis Set 实战案例

了解了 Set 的基本特性和常用命令后,我们来看一些 Set 在实际项目中的应用案例。

2.1 案例一:社交应用中的好友关系

在社交应用中,好友关系是一个典型的 Set 应用场景。我们可以使用 Set 来存储每个用户的好友列表。

  • 用户 ID 作为 Key: 每个用户的 ID 作为 Set 的 key。
  • 好友 ID 作为 Member: 该用户的好友 ID 作为 Set 的 member。

例如:

SADD user:1 2 // 用户1 添加好友2
SADD user:1 3 // 用户1 添加好友3
SADD user:2 1 // 用户2 添加好友1
SADD user:2 4 // 用户2 添加好友4

常用操作:

  • 获取用户的所有好友: SMEMBERS user:1 (返回 2, 3)
  • 判断两个用户是否是好友: SISMEMBER user:1 2 (返回 1,表示是好友)
  • 共同好友: SINTER user:1 user:2 (返回 1,表示共同好友是用户1)
  • 可能认识的人 (好友的好友):
    1. 获取用户1的所有好友: SMEMBERS user:1 (返回2, 3)
    2. 获取好友2的所有好友:SMEMBERS user:2 (返回 1,4)
    3. 获取好友3的所有好友:SMEMBERS user:3
    4. 取所有好友的好友的并集:SUNION user:2 user:3
    5. 去除用户1已经认识的好友: SDIFF (SUNION user:2 user:3的结果集) user:1
    6. 去除用户1自身。
  • 删除好友: SREM user:1 2 (用户1 删除好友2)

优势:

  • 高效查找: 使用 SISMEMBER 命令可以快速判断两个用户是否是好友,时间复杂度为 O(1)。
  • 方便的集合运算: 利用 SINTERSUNIONSDIFF 等命令可以轻松实现共同好友、可能认识的人等功能。

2.2 案例二:电商应用中的商品标签

在电商应用中,商品通常会有多个标签,例如“新品”、“热卖”、“促销”等。我们可以使用 Set 来存储商品的标签。

  • 商品 ID 作为 Key: 每个商品的 ID 作为 Set 的 key。
  • 标签作为 Member: 该商品的标签作为 Set 的 member。

例如:

SADD product:1001 "新品"
SADD product:1001 "热卖"
SADD product:1002 "促销"
SADD product:1002 "包邮"

常用操作:

  • 获取商品的所有标签: SMEMBERS product:1001 (返回 "新品", "热卖")
  • 查找具有特定标签的商品:
    • 将每个tag作为一个key,每个商品的id作为member
    • 例如:SADD tag:新品 product:1001
    • 查找具有新品标签的商品:SMEMBERS tag:新品
  • 统计具有特定标签的商品数量: SCARD tag:新品
  • 添加/删除标签: SADD product:1001 "限时抢购" / SREM product:1001 "热卖"

优势:

  • 灵活的标签管理: 可以方便地为商品添加、删除标签。
  • 高效的标签检索: 可以快速查找具有特定标签的商品。

2.3 案例三:网站 UV 统计 (独立访客)

UV(Unique Visitor)是指独立访客,即访问网站的不同 IP 地址的数量。我们可以使用 Set 来统计网站的 UV。

  • 日期作为 Key: 每天的日期作为 Set 的 key,例如 "2023-10-26"。
  • IP 地址作为 Member: 访问网站的 IP 地址作为 Set 的 member。

例如:

SADD 2023-10-26 192.168.1.1
SADD 2023-10-26 192.168.1.2
SADD 2023-10-26 192.168.1.1 // 重复 IP,自动忽略

常用操作:

  • 统计当天的 UV: SCARD 2023-10-26
  • 因为set的特性, 重复的member会被忽略, 所以可以保证统计的UV是唯一的

优势:

  • 自动去重: Set 的唯一性保证了每个 IP 地址只会被统计一次。
  • 高效统计: SCARD 命令可以快速获取集合的成员数,即 UV 数。

优化:

  • 如果网站的UV非常大,每天都使用一个Set来存储IP地址,会消耗大量的内存.
  • 可以考虑使用Redis的HyperLogLog数据结构来统计UV, 它可以以极小的内存占用, 提供近似的UV统计结果.

2.4 案例四:抽奖系统

Set 的随机性非常适合用于实现抽奖系统。我们可以将所有参与抽奖的用户 ID 放入一个 Set 中,然后使用 SRANDMEMBERSPOP 命令随机抽取中奖用户。

  • 奖池Key:奖品名称+时间 作为key, 例如luckyDraw:iphone15:20231027
  • 用户 ID 作为 Member: 所有参与抽奖的用户 ID 作为 Set 的 member。

例如:

SADD luckyDraw:iphone15:20231027 1001
SADD luckyDraw:iphone15:20231027 1002
SADD luckyDraw:iphone15:20231027 1003
...

常用操作:

  • 抽取一名幸运用户: SPOP luckyDraw:iphone15:20231027 (随机返回并移除一个用户 ID)
  • 抽取多名幸运用户: SPOP luckyDraw:iphone15:20231027 3 (随机返回并移除 3 个用户 ID)
  • SRANDMEMBER luckyDraw:iphone15:20231027 3 (随机返回3个用户ID, 但是不移除)
  • 如果抽奖活动需要放回抽奖(即一个用户可以中奖多次), 则使用SRANDMEMBER命令

优势:

  • 公平性: Set 的随机性保证了抽奖的公平性。
  • 高效性: SRANDMEMBERSPOP 命令可以快速抽取中奖用户。
  • 防止重复中奖: 如果需要防止用户重复中奖(不放回抽奖), 使用SPOP命令可以确保每个用户只会被抽取一次.

2.5 案例五:排行榜 (Top N)

虽然 Redis 的 Sorted Set 更适合用于实现排行榜,但在某些简单的场景下,Set 也可以用于实现 Top N 排行榜。例如,我们可以使用 Set 来记录最近 N 天活跃的用户。

  • 活跃用户 Key: 例如 active_users
  • 用户 ID 作为 Member: 活跃用户的 ID 作为 Set 的 member。

例如:

```
// 每天将活跃用户添加到 Set 中
SADD active_users 1001
SADD active_users 1002
...

// 当 Set 中的用户数量超过 N 时,移除最早活跃的用户
// 假设 N = 1000
if (SCARD active_users > 1000) {
// 获取最早活跃的用户 (这里需要额外的逻辑来记录用户的活跃时间)
// 假设我们使用一个 Sorted Set 来记录用户的活跃时间
// ZADD active_users_timestamp timestamp user_id
// 获取最早活跃的 100 个用户
user_ids = ZRANGE active_users_timestamp 0 99
// 从 Set 中移除这些用户
for user_id in user_ids:
SREM active_users user_id
ZREM active_users_timestamp user_id

}
```

优势:

  • 简单: 在只需要记录最近 N 个元素,而不需要排序的场景下,Set 比 Sorted Set 更简单。

局限性:

  • 无序: Set 本身是无序的,因此无法直接获取 Top N 的排序结果。
  • 需要额外逻辑: 需要额外的逻辑来维护 Set 的大小,例如移除最早活跃的用户。

注意: 对于需要排序的排行榜,强烈建议使用 Sorted Set。

2.6 案例六: 点赞、签到等

  • 点赞:

    • 每个文章/评论 使用一个Set
    • Key: article:{article_id}:likes 或者 comment:{comment_id}:likes
    • Member: 点赞用户的 ID
    • 操作:
      • 点赞:SADD article:123:likes user:456
      • 取消点赞:SREM article:123:likes user:456
      • 统计点赞数:SCARD article:123:likes
      • 判断用户是否点赞:SISMEMBER article:123:likes user:456
  • 签到:

    • 每个用户每天的签到记录使用一个 Set
    • Key: user:{user_id}:sign:{year}:{month}
    • Member: 签到的日期 (例如 "26" 表示 26 号)
    • 操作:
      • 签到:SADD user:1001:sign:2023:10 26
      • 统计当月签到次数:SCARD user:1001:sign:2023:10
      • 判断某天是否签到:SISMEMBER user:1001:sign:2023:10 26

三、总结

Redis Set 作为一种简单而强大的数据结构,在实际项目中有着广泛的应用。本文通过多个案例,详细介绍了 Set 在社交应用、电商应用、UV 统计、抽奖系统、排行榜等场景下的应用。

在选择使用 Set 时,需要考虑以下几点:

  • 是否需要去重: 如果需要存储不重复的元素,Set 是一个很好的选择。
  • 是否需要排序: 如果需要排序,应该使用 Sorted Set。
  • 是否需要集合运算: 如果需要进行交集、并集、差集等操作,Set 非常合适。

合理利用 Redis Set 的特性,可以有效地简化开发,提高系统性能。希望本文能够帮助读者更好地理解和掌握 Redis Set 的使用技巧,并在实际项目中发挥其价值。

THE END