在工作中总会有遇到过使用Redis的场景,除了最简单的JWT案例之外,Redis还有很多丰富的应用落地案例,这个周末特意整理了一批经典的Redis使用案例。
电商购物车模型
电商网站中的购物车就是一个经典的可以使用Redis来进行实现的案例,(这里我只是说可以使用,不排除有些电商平台的购物车是用其他分布式缓存组件实现的)。
这里我截取了某东的购物车界面:
其实可以发现,这里的很多操作细节正好可以借助Redis的map结构来进行实现。
添加商品
hset cart:1001 10088 1
增加数量
hincrby cart:1001 10088 1
商品总数
hlen cart:1001
删除商品
hdel cart:1001 10088
获取购物车所有商品
hgetall cart:1001
购物车缓存是否建议存储商品的详情信息?
不建议,如果后期商品的图片,价格发生了变动,还需要考虑维护redis中存储的那部分数据反而会增重后期数据维护的复杂性。
如何填充购物车内商品的其他详情信息?
可以先从redis中提取出来对应的商品id,然后进行一个批量Rpc或者Http查询,填充剩余的那些字段。这个过程中可以对已经下架或者已经失去优惠价格的商品做特殊过滤处理。
使用Hash结构的优势:
相对于直接将整个购物车对象转换为json格式直接存储到redis中能够减少redis内存空间的消耗。
不足:
不能单独指定某个key的过期时间
redis集群架构下不适合大规模使用,因为会影响主从同步之间的性能消耗。
微信公众号推送
这里我以公众号为案例并不是说微信官方这部分实现真的采用了Redis技术,而是想说明如果采用Redis作为技术选型该如何进行实现类似的场景。
例如说一篇公众号的内部推文,每个公众号都会定时发布一些推文信息,这些推文的标题等基础信息会在一个统一的列表页面中展示,例如下边所示:
实际上在实现这样类似业务场景的时候,我们可以将这类信息存储到Redis中减少DB的访问频率。
例如A公众号被三个用户订阅了,三个用户分别是user1,user2,user3
那么当有新文章进行发送的消息时候可以采用类似下方的操作来进行实现:
LPUSH msg:A-id:article A
查阅公众号最新消息记录的时候可以直接从List集合中查询,对于历史记录的分页查询可以通过LRange指令实现:
LRange msg:A-id:article 0 5
除了公众号历史推文界面,外部还有这种模式的推荐界面,再来看看外边的页面,如下:
这里属于公众号领域的一级模块,只展示每个公众号最近的发布文章标题和时间,并且按照先发先置顶的规则进行展示。
这里可以采用ZSet集合实现按照发布时间戳的倒序排序,并且Set集合可以保证公众号排序的唯一性。
zset有序列表,显而易见意思就是一个有序且是不重复上的数据结构,它类似于Java中的sortset和hashmap的结合体,但是在redis中是通过两种底层数据结构实现的。一种是ziplist压缩列表,另一种就是redis中最经典的数据结构skipList跳跃表。
公众号发布时候写入redis的一个ZSet集合,例如9998,9995,9996,9997这四个id的公众号号主在不同时间戳更新了最新的推文,那么在redis端应该接收到类似下边这样的数据。
> zadd article:topic:userId:10001 1622968092454 9998
1
> zadd article:topic:userId:10001 1622968092453 9995
1
> zadd article:topic:userId:10001 1622968092452 9997
1
> zadd article:topic:userId:10001 1622968092420 9996
1
当用户打开手机查看当天新发布的消息的时候,按照发布时间戳进行倒序排序
查询:
> zrevrange article:topic:userId:10001 0 5
9998
9995
9997
9996
对于微信这种超级高并发的场景,对于上述的两种页面渲染都不建议直接访问到redis层面,可以考虑加入本地缓存进行过度从而减少网络层面的数据传输消耗。
本地缓存的更新可以结合一些分布式协调类型的中间件进行辅助实现,例如zk,nacos等。
抽奖活动案例
前几年有个火起来的小程序叫做抽奖助手,挺多的号主在做一些抽奖活动的时候都喜欢让读者们分享推文,然后参加抽奖活动,类似如下界面:
这里的抽奖功能其实也是可以使用Redis进行实现的:
点击参与抽奖加入集合
SADD key {userlD}
查看参与抽奖所有用户
SMEMBERS key
抽取count名中奖者
SRANDMEMBER key [count] / SPOP key [count]
而且Redis自身内部对于参加抽奖的人群还能通过随机抽取的功能直接生成一组随机中奖人群,非常便捷。
朋友圈评论点赞案例
在一些社交App或电商App中,我们经常会看到评论这类型的互动功能,用户可以针对自己感兴趣的内容模块进行点赞,回复等互动。
假如点赞这块的功能也采用redis来进行实现,那么可以参考使用set结构:
点赞
SADD like:{消息ID} {用户ID}
取消点赞
SREM like:{消息ID} {用户ID}
检查用户是否点过赞
SISMEMBER like:{消息ID} {用户ID}
获取点赞的用户列表
SMEMBERS like:{消息ID}
获取点赞用户数
SCARD like:{消息ID}
对于回复的评论其实可以采用之前我们提及到的List结构来进行实现:
发表评论
> rpush review:10001 这是评论内容
1
> rpush review:10001 这是评论内容2
2
> rpush review:10001 这是评论内容3
3
> rpush review:10001 这是评论内容4
4
查看评论
> lrange review:10001 0 5
这是评论内容
这是评论内容2
这是评论内容3
这是评论内容4
互相关注用户
社交软件中还有一类比较典型的应用场景,例如说互相关注案例:A用户和B用户共同关注的人群等等。
这类型的场景也比较适合采用Set结构进行设计,例如说:
userA关注的人:
userASet-> {userD, userB, userC}
userB关注的人:
userBSet--> {userD, userA, userB , userG
筛选出双方都共同关注的人:
SINTER userASet userBSet--> {userD, userB}
希望认识对方的关注的朋友:(注意过滤掉自己)
SDIFF userASet userBSet->(userG,userA}
不过这类计算不是太推荐高频率地在redis内部使用,原因是redis做这类型的计算会比较复杂,再高并发场景下可能会产生堵塞情况发生,所以如果希望使用的话,可以加入一些本地缓存来进行优化。(注意是高并发下,一般场景还是支持使用的)
热搜榜单
最常见的案例,如微博的热搜榜单,每天总是能刷新出各类有趣的热点,
这部分的热点数据排序主要参考网民们在微博搜索的热点话题频率。当用户点击了某个话题的时候,后台就需要将其记录到数据库中,并且记录其点击频率。
Zset集合操作实现排行榜
点击新闻
ZINCRBY hotNews:20190819 1 守护香港
展示当日排行前十
ZREVRANGE hotNews:20190819 0 10 WITHSCORES
七日搜索榜单计算
ZUNIONSTORE hotNews:20190813-20190819 7
hotNews:20190813 hotNews:20190814... hotNews:20190819
展示七日排行前十
ZREVRANGE hotNews:20190813-20190819 0 10 WITHSCORES
简易版本的搜索引擎
在大多数的电商平台中经常会看到类似于下方的搜索功能,这类搜索条件非常复杂,在早期没有出现es的情况下,有些互联网公司会采用redis内部的set结构取进行设计,从而实现一个简单版本的搜索引擎。(不过现在数据量增大后,大多数都是采用es做查询了)
例如:
希望查询安卓型号,intel的cpu,8g内存的手机:
预备好一些关键字信息到redis
SADD brand:huawei P30
SADD brand:xiaomi mi-6X
SADD brand:iPhone iphone8
SADD os:android P30 mi-6X
SADD cpu:brand:intel P30 mi-6X
SADD ram:8G P30 mi-6X iphone8
多条件查询
SINTER os:android cpu:brand:intel ram:8G -> {P30,mi-6X}
小结
其实在互联网圈内还有很多关于Redis实践的案例,如:
微博、微信、陌陌<附近的人>
微信<摇一摇><抢红包>
滴滴打车、摩拜单车<附近的车>
美团和饿了么<附近的餐馆>
搜索自动补全
布隆过滤器
这部分内容点将在下一篇文章中和大家分享。