我们一块来分析下Redis 的不同数据结构在微服务场景下的具体应用:
1. String (字符串)
- 特点: 最基本的数据类型,二进制安全,可以存储任何类型的数据(文本、序列化对象、图片等),最大 512MB。支持原子性的递增/递减操作。
- 微服务适用场景:
- 分布式缓存: 最常见的用途。缓存数据库查询结果、API 响应、渲染后的 HTML 片段等。
- 示例:
UserService
查询用户信息后,将用户 ID 作为 Key,用户信息(序列化后的 JSON 或其他格式)作为 Value 存入 Redis String,并设置 TTL。GET user:123
->{"name": "Alice", "email": "..."}
。
- 示例:
- 分布式会话: 存储 Session ID 到用户会话数据的映射。
- 示例: 用户登录后,
AuthService
生成一个 Session ID,将 Session ID 作为 Key,用户的登录状态、权限等信息作为 Value 存入 Redis String。GET session:xyzabc
->{"userId": 123, "role": "admin", "loginTime": ...}
。
- 示例: 用户登录后,
- 计数器/限流器: 利用
INCR
,DECR
,INCRBY
等原子操作实现。- 示例:
APIGateway
对某个接口进行限流,使用INCR api:/orders:user:123
,并结合EXPIRE
设置时间窗口。当计数值超过阈值时拒绝请求。 - 示例:
ContentService
统计文章阅读数:INCR article:readcount:456
。
- 示例:
- 分布式锁 (简单实现): 利用
SET key value NX EX seconds
命令实现简单的互斥锁。- 示例:
InventoryService
在扣减库存前尝试获取商品锁:SET product:lock:sku123 my_request_id NX EX 30
。
- 示例:
- 分布式缓存: 最常见的用途。缓存数据库查询结果、API 响应、渲染后的 HTML 片段等。
2. Hash (哈希)
- 特点: 存储一个 Key 到多个 Field-Value 对的映射,类似于编程语言中的 Map 或 Dictionary。适合存储结构化对象。
- 微服务适用场景:
- 缓存结构化对象: 存储对象的部分或全部属性,便于单独更新或获取某个字段,节省网络带宽和序列化开销。
- 示例:
UserService
缓存用户信息,Key 为user:123
,Fields 包括name
,email
,age
等。HGET user:123 name
->"Alice"
;HSET user:123 age 31
只更新年龄。
- 示例:
- 购物车: 存储用户的购物车信息。
- 示例:
CartService
使用 Hash 存储购物车,Key 为cart:user:123
,Field 为商品 ID (product:sku456
),Value 为商品数量 (3
)。HINCRBY cart:user:123 product:sku456 1
实现添加商品数量。
- 示例:
- 缓存结构化对象: 存储对象的部分或全部属性,便于单独更新或获取某个字段,节省网络带宽和序列化开销。
3. List (列表)
- 特点: 有序的字符串列表,按照插入顺序排序。可以在列表头部或尾部添加/移除元素。支持阻塞式弹出操作。
- 微服务适用场景:
- 轻量级消息队列/任务队列: 实现先进先出 (FIFO) 或后进先出 (LIFO) 的简单队列。
- 示例:
OrderService
创建订单后,使用LPUSH order:notify:queue {"orderId": 789, "type": "email"}
将通知任务推入队列。NotificationService
使用BRPOP order:notify:queue 10
阻塞式地获取并处理任务。
- 示例:
- 最新消息/动态 Feed: 存储用户最近的操作或接收到的消息列表(通常需要配合
LTRIM
来限制列表长度)。- 示例:
TimelineService
用户发布新帖子后,LPUSH user:timeline:123 post:abc
,并用LTRIM user:timeline:123 0 999
保留最新的 1000 条。
- 示例:
- 轻量级消息队列/任务队列: 实现先进先出 (FIFO) 或后进先出 (LIFO) 的简单队列。
4. Set (集合)
- 特点: 无序的、唯一的字符串集合。支持高效的成员检查、添加、删除操作,以及集合间的交集、并集、差集运算。
- 微服务适用场景:
- 标签系统/分类: 存储对象(如文章、商品)关联的标签。
- 示例:
ProductService
存储商品的标签:SADD product:tags:sku123 "electronics" "audio" "bluetooth"
。可以方便地通过SISMEMBER
检查商品是否有某标签,或通过SINTER
查找同时具有多个标签的商品。
- 示例:
- 共同好友/关注关系: 存储用户的关注列表或好友列表,并利用集合运算找到共同关系。
- 示例:
SocialService
存储用户关注的人:SADD user:following:alice "bob" "charlie"
;SADD user:following:bob "alice" "charlie" "david"
.SINTER user:following:alice user:following:bob
->{"charlie"}
(共同关注)。
- 示例:
- 唯一访客统计 (简单场景): 记录访问某页面或功能的独立用户 ID。
- 示例:
AnalyticsService
记录访问某页面的独立访客:SADD page:visitors:/home "user:123" "user:456"
。SCARD page:visitors:/home
获取独立访客数。
- 示例:
- 标签系统/分类: 存储对象(如文章、商品)关联的标签。
5. Sorted Set (ZSet / 有序集合)
- 特点: Set 的升级版,每个成员都关联一个 double 类型的分数 (score),并根据分数进行排序。成员唯一,但分数可以重复。支持按分数范围或排名范围获取成员。
- 微服务适用场景:
- 排行榜: 根据分数(如积分、销售额、时间戳)对成员进行排序。
- 示例:
GameService
存储玩家积分榜:ZADD game:leaderboard 1500 "player1" 1200 "player2"
。ZREVRANGE game:leaderboard 0 9 WITHSCORES
获取排名前 10 的玩家及其分数。
- 示例:
- 延迟队列/定时任务: 将任务的执行时间戳作为 score,利用
ZRANGEBYSCORE
定期查询到期的任务。- 示例:
OrderService
创建订单后 30 分钟未支付自动取消:ZADD order:pending:cancel <current_timestamp + 30_minutes> "order:789"
。后台任务定时ZRANGEBYSCORE order:pending:cancel 0 <current_timestamp>
获取到期订单进行处理。
- 示例:
- 带权重的任务队列: score 可以代表任务优先级。
- 排行榜: 根据分数(如积分、销售额、时间戳)对成员进行排序。
6. Stream (流)
- 特点: Redis 5.0 引入的强大的 append-only 日志结构。支持消费组 (Consumer Groups),允许多个消费者并行处理同一个流的不同部分,并提供消息确认 (ACK) 机制,比 List 做消息队列更可靠、功能更丰富。
- 微服务适用场景:
- 可靠的消息队列/事件总线: 用于服务间的异步通信、事件溯源(轻量级)、日志收集等。
- 示例:
OrderService
在订单状态变更时,XADD order:events * status "paid" orderId "789" userId "123"
将事件添加到流中。InventoryService
和ShippingService
可以分别创建消费组,独立地从流中读取并处理paid
事件,并通过XACK
确认处理完成。
- 示例:
- 可靠的消息队列/事件总线: 用于服务间的异步通信、事件溯源(轻量级)、日志收集等。
7. HyperLogLog (HLL)
- 特点: 概率数据结构,用于对集合的基数(唯一元素的数量)进行估算,占用内存极小 (固定约 12KB),但结果有微小误差 (标准误差约 0.81%)。
- 微服务适用场景:
- 大规模唯一计数: 统计海量数据中的独立元素数量,如网站的 UV (Unique Visitors)、独立 IP 数、搜索词的独立用户数等,对精度要求不是 100% 严格的场景。
- 示例:
AnalyticsService
统计每日独立访客:对每个访问的用户 ID 执行PFADD daily_visitors:2023-10-27 "user_id_abc"
。最后通过PFCOUNT daily_visitors:2023-10-27
获得估算的 UV 值。
- 示例:
- 大规模唯一计数: 统计海量数据中的独立元素数量,如网站的 UV (Unique Visitors)、独立 IP 数、搜索词的独立用户数等,对精度要求不是 100% 严格的场景。
8. Geospatial (GEO)
- 特点: 基于 Sorted Set 实现,专门用于存储地理空间坐标(经度、纬度)并进行相关查询。
- 微服务适用场景:
- 位置服务: 查找附近的人/地点、计算两点间距离等。
- 示例:
LocationService
或RideSharingService
存储司机的位置:GEOADD drivers:available 116.40 39.90 "driver123"
。用户请求打车时,GEORADIUS drivers:available <user_longitude> <user_latitude> 5 km WITHDIST
查找 5 公里内的可用司机及其距离。
- 示例:
- 位置服务: 查找附近的人/地点、计算两点间距离等。
总结:
选择哪种 Redis 数据结构取决于微服务具体要解决的问题。通过比较每种数据结构的特性和优势,可以更高效的实现微服务中的缓存、通信和特定业务逻辑。我们要合理利用这些结构让Redis 在微服务架构中发挥它的价值。