【需求设计】从一个小需求感受Redis的独特魅力

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 从一个小小的需求看Redis在实际项目是如何使用的

分享一个简单的小需求应该怎么设计实现以及有关Redis的使用

Redis在实际应用中使用的非常广泛,本篇文章就从一个简单的需求说起,为你讲述一个需求是如何从头到尾开始做的,又是如何一步步完善的。之前写过一篇《如何实现页面广告随时上下线、过期自动下线及到时自动上线》,也涉及到了Redis在项目中的实际应用,有兴趣的可以看一下。

需求

设定,现在我们有一个APP,产品新提出一个叫“程序员树洞”的功能,具体功能就不说了,其中这个功能有一点需要做的是在使用该功能时,如果是首次进入会展示一个协议页面,用户需要勾选后点确定才能进入功能,此后再进该功能,不再显示协议页直接进入该功能。

评论中有小伙伴说这个功能完全可以是客户端来做,我想说的是,这个功能和客户端获取手机权限不同,这是真实存在的业务,如一些和金融相关的场景必须后端来记录,而且这还有多端的情况,并不是只有客户端需要展示,也会有PC端,那么就不适合客户端来记录了,就需要后端来实现了。好了,暂且不讨论这个需求的合理性了,本文的重点是讨论Redis在实际项目中的应用。

如下图所示,
image.png

原型图

需求分析

需求就是这么的简单,我们来分析一下。
1、用户点击该功能时前端需要知道该给用户显示哪个页面,这一步需要请求后端接口,后台告诉前端这个用户有没有同意过协议。
2、用户勾选协议点确定,后端需要记录这步操作(记录用户已经同意协议),这一步需在点确定时前端请求后端接口。

概要设计

前面需求分析里说了,后端需要告诉前端当前用户有没有同意过协议,所以后端需要把这个信息记录下来,最好是记录到数据库保存,那就需要一张表来记录同意过协议的用户。表结构大致是:id,客户号,插入时间。

详细设计

1、记录客户是否已同意过协议并提供查询功能(查询是否同意过协议)
2、没有同意过的和同意过的用户信息怎么存储?
3、如何高效的查询是否同意过?
4、怎么保证高并发下服务的可用性,数据库的可用性?

功能实现

经过一系列的分析讨论过后,发现后端需要提供两个接口:
1、hasAgree(),查询该用户是否已同意协议
2、recordAgree(),记录用户已同意协议

第一版 Just DB

很容易嘛!不就是CRUD吗,小意思。用户进来先查数据库有没有记录,没有返回用户没有同意过协议,前端给用户展示协议页,否则展示功能页;用户点同意后,后台记录用户已点了同意协议,记录到库。一个查询一个插入,5分钟搞定嘛。
image.png

208a5abf0eb24614b04a79b51090679e.png

直接甩代码
image.png

第一版代码如上,我觉得刚入门的程序员都能够写出来。如果用户量不大,该功能的点击量不大的话,这么做还是勉强说得过去。为什么说勉强说得过去?因为存在隐患。你看啊,如果每次点击都会去查库,假如你们公司的运营宣传的很好,到上线这个功能后很多很多人都来用,瞬时 涌入大并发或者有人恶意攻击仿造高并发,那么就会造成瞬时大量请求过来都去查库,很可能数据库顶不住就挂了。就算数据库没挂,每次查库也都是浪费啊。所以这是个隐患,或者潜在的危险,那么第二版我们就去解决这个问题。

第二版 引入Redis缓存

考虑到每次查库很浪费,那我们使用缓存好不好?
进来先查缓存有没有对应的数据,缓存里有就直接返回,没有则查库,库里有就存缓存。这样redis就分担了一部分数据库的压力。
image.png

代码呈上
image.png

这一版好一点了,部分请求分摊到redis了,减轻了数据库的压力,访问性能也提高了。

第三版 解决缓存穿透

随着客户量的增加,点击这个功能的次数、频率越来越高,假如有人频繁点击该功能,弹出协议后,退出,再点,再退出…就是不点确定
c99f15eaee394b4d805113d4b9be0105.png

这样会有啥问题?

这样的话频繁查询该用户是否同意过协议这个接口就会首先看缓存中没有,然后查数据库中也没有,这样绕过了缓存每次都会走数据库,这类请求量多了也是个问题,这就是缓存穿透。所以第三版,我们来解决缓存穿透的问题。
image.png

解决缓存穿透:
因为是数据库和缓存都没有,我们可以让数据库没有的也存到redis。需要改变redis的数据类型,由set改为hash,目的是记录状态值。
image.png

可以看到,我们的这个key-field-value没有设置过期时间,因为可以认为这个key是一个热点key,对于热点key我们的处理方式是,永久有效或过期时间尽量长一点。

第四版 缓存预热防止缓存击穿

另一个关于缓存的问题,那就是缓存击穿。

何为缓存击穿?
假如该功能在前期宣传力度比较大,或预计该功能上线后点击量比较大的话,那么在功能上线后很可能就会一瞬间大量用户来点击这个功能,因为我们前面的逻辑是首次进入该功能的用户展示协议页,我们的后台处理虽然加了redis缓存,但是新上的功能所有用户都没有点过,那么redis里就没有缓存,是不是所有用户的请求都落到数据库了?一旦瞬间流量非常大,数据库安全性就存在隐患,有被搞垮的可能。
image.png

这个问题就是可以理解为缓存击穿。(实际的缓存击穿是某个key在缓存里不存在或是失效后,某一瞬间很多请求都来访问这个key,都判定为redis里没有这个key,就都去查库。)

理解成缓存击穿是没问题,因为我们是对同一个key进行读写的。

所以怎么解决呢?我们可以在该功能上线前,提前将需要做缓存的数据放入redis,即缓存预热

如何预热?
将所有用户的信息都放到redis
举个栗子(也许不是最佳的),我们使用Redis的hash数据结构,key-field-value。key我们可以固定一个字符串如coderTreeHole_Agreement_Check,field我们可以用客户号(唯一),value是个标志位,用0代表没同意过协议,1代表同意过。一般在电商大促前都会对热点key进行预热,不然真的扛不住。
image.png

and,用户量很大的时候redis里的coderTreeHole_Agreement_Check这个key是不是很大?在redis集群部署模式下,这个key是不是都放在一个节点上?why?

redis3.0上加入了cluster模式,实现的redis的分布式存储,也就是说每台redis节点上存储不同的内容。在redis的每一个节点上,都有这么两个东西,一个是插槽(slot),它的的取值范围是:0-16383。还有一个就是cluster,可以理解为是一个集群管理的插件。当我们的存取的key到达的时候,redis会根据crc16的算法得出一个结果,然后把结果对16384求余数,这样每个key都会对应一个编号在0-16383之间的哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作。

看了上面这段话,明白了吧。那对于这个大key而且是热点key的请求,是不是都落到某一个redis节点上了?大key会带来很多问题,篇幅原因以后再来细说,跑题了。。。

针对这个需求,你还有什么方法防止缓存击穿?

第五版 消息队列削峰填谷

可以看到我们上面的设计其实都是在解决读的问题,而对数据库的写操作都是实时进行操作的。

当用户点了同意,前端就调后台的recordAgree方法将该记录记录到数据库,即这条记录是立马插入到数据库的。

如果刚上线这个功能,大量用户同时点这个功能,并发量大的话,请求走到后台,那么写库的操作就非常多,数据库连接数突然激增,数据库会顶不住吧。

所以为避免流量集中落到数据库,此时我们可以使用消息队列MQ。将插入操作的请求发往消息队列,使插入操作以一定的速率到数据库执行,使得对数据库的请求数尽量平滑,消息发给消息队列立即返回给前端成功,不用等待插库完成,这样用户响应速度更快了,体验更好了。用MQ实现了异步解耦,削峰填谷。
image.png

到这你是不是忍不住说很完美,设计的真赞~~
54fbbe581b124e99a63b5ea22ac44781.png

另外MQ的使用注意的点还是非常多的,如:消息队列的消息重复消费问题,可靠性投递,顺序问题,事务消息等。

总结

对于这个需求设计到哪种程度取决于你的用户量和并发量,如果是像双十一那样,肯定是要用消息队列的,那一般小的例如,用户量1千万,日活10万,请求最集中的也就是中午9-12点,下午13-17点吧,差不多8个小时,平均一个小时1.25万,用户都来点这个功能的话,每分钟208,每秒3.5,算不上高并发,数据库完全扛得住。

总结一下,这个需求我们用到的知识点(敲黑板),redis 数据缓存,redis 缓存穿透,缓存击穿,热点 key 问题,redis 大 key 问题(没具体讲),消息队列异步解耦等。

画图码字不易,如果觉得我写的还可以,记得点赞鼓励一下哦,如果觉得有问题欢迎指正。
原创不易,需要您的鼓励和支持,欢迎关注公众号“编程大道”,第一时间收看技术干货推送~

转载请附原文链接~

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
存储 缓存 NoSQL
Redis点赞业务的设计与实现(Redis键值设计)
案例分享Redis点赞业务实现!
1308 2
Redis点赞业务的设计与实现(Redis键值设计)
|
存储 缓存 负载均衡
Redis cluster去中心化设计的思考与总结
Redis cluster去中心化设计的思考与总结
223 0
|
存储 缓存 NoSQL
详细设计- Redis 设计|学习笔记
快速学习详细设计- Redis 设计
99 0
详细设计- Redis 设计|学习笔记
|
NoSQL Redis
【Redis系列】为啥Redis Cluster设计成16384个槽?
这意味着它们包含原始形式的节点的插槽配置,该节点使用2K的空间和16384个slot,但使用65535的插槽会使用令人望而却步的 8K 的空间。所以16384是在正确的范围内,以确保每个 master 有足够的插槽,最多 1000 个 maters,但这个数量足够小,可以轻松地将插槽配置作为原始位图传播。在小集群中,位图将难以压缩,因为当 N 小时,位图将设置的槽位/N 位占很大比例的位。集群节点越多,心跳包的消息体内携带的数据越多。集群规模较小的场景下,每个分片负责大量的slot,很难压缩。
172 0
【Redis系列】为啥Redis Cluster设计成16384个槽?
|
存储 缓存 运维
|
NoSQL Unix Linux
通过Redis学习事件驱动设计
通过Redis学习事件驱动设计
134 0
通过Redis学习事件驱动设计
|
存储 NoSQL 算法
阿里面试这样问:redis 为什么把简单的字符串设计成 SDS?
阿里面试这样问:redis 为什么把简单的字符串设计成 SDS?
154 0
阿里面试这样问:redis 为什么把简单的字符串设计成 SDS?
|
NoSQL Unix Redis
Redis 中的持久化技术《Redis设计与实现》
本篇将介绍 Redis 中的持久化技术,主要有两种: RDB持久化 和 AOF持久化
121 0
Redis 中的持久化技术《Redis设计与实现》
|
存储 JSON NoSQL
Spring之借助Redis设计一个简单访问计数器
为什么要做一个访问计数?之前的个人博客用得是卜算子做站点访问计数,用起来挺好,但出现较多次的响应很慢,再其次就是个人博客实在是访问太少,数据不好看😢... 前面一篇博文简单介绍了Spring中的RedisTemplate的配置与使用,那么这篇算是一个简单的应用case了,主要基于Redis的计数器来实现统计
328 0
Spring之借助Redis设计一个简单访问计数器
|
缓存 NoSQL Java
Redis 缓存设计
本文介绍Redis 缓存设计:穿透优化、无底洞优化、雪崩优化、热点key 重建优化

热门文章

最新文章