掘金 后端 ( ) • 2024-04-16 17:11

theme: channing-cyan

大家好,我是小趴菜,最近有一个需求,就是之后有新功能上线,那么用户在登录以后需要给个弹窗,主要是为了提醒用户,平台新增了这么一个功能,当然用户弹了一次之后,后续就不用再弹了

当然后续有新功能上线依然是需要弹的,需求就是这样,其实也蛮简单的对不对???

接下来我们就一步一步的来设计一下,该如何实现这个功能

实现流程

做什么需求都是需要把流程搞明白的,不然后面麻烦就大了,

  • 1:用户登录,判断这个用户对应的这个新功能对应的弹窗是否有弹过
  • 2:如果有弹过,就返回给前端一个状态值,就不需要弹了,比如 {"功能ID":1,"是否弹过":1}
  • 3:如果没有弹过,那么返回前端一个{"功能ID":1,"是否弹过":0}
  • 4:前端拿到这个json数据,判断是否弹过,然后再根据新功能的ID来弹出对应的弹框就行了

数据库存储方案

首先我们会想到,在数据库建一张新功能的表,然后再建一张用户与新功能之间的关联关系表

新功能表:obility
id:新功能的主键
obility_name:新功能的名称
obility_desc:新功能的描述


用户与新功能的关联关系表:obility_user
id:主键
user_id:用户ID
obility_id:新功能的id
is_open:是否弹过 0:没有,1:弹过了

那么判断的流程是这样的

  • 1:拿到这个登录的用户的user_id
  • 2:去数据库查ubility_user表,找到这个用户所有的is_open=0的记录
  • 3:如果查询不为空,那么这些就是这次用户需要弹的弹窗

现在用户登录进来,首先拿到这个登录用户的ID,然后去obility_user表中查询出这个用户有哪些新功能的弹窗没有弹出来过

select * from obility_user where user_id = 1 and is_open = 0;

我们拿到这个用户所有没有弹过的新功能,然后进行拼接一起返回给前端就行了,好了,这个功能其实到这也就实现的差不多了

但是这种方案会不会存在什么弊端呢???

弊端一:数据量问题

其实在用户量小,并且你的新功能确定不会很多的时候,这种方式确实是可以实现的,因为数据量不是很大,后续加个索引查询也没什么问题

如果现在你的用户量很大,比如说有1000万,这时候加一个新功能,那么你这张obility_user表就会有1000万的数据,那如果有10个新功能,那就是1亿的数据,这时候单表肯定满足不了我们的业务了,所以你会采用分表来做,这时候的就要引入分库分表的中间件了,总的来说实现太复杂了

弊端二:数据同步问题

而且这种方案其实还有一个弊端,就是我们查询的时候,这个时候这个新功能与用户的关联关系记录是已经存在表内的,也就是说我们在发布一个新功能的时候就要把所有用户跟这个新功能的关联关系都要同步到obility_user这张表中,不然用户登录的时候是查不到,那么也就不会有弹窗了

所以你还要保证这个新功能与所有用户的关联关系记录都成功保存到obility_user这张表中,当然少量的数据丢失也没什么影响,那这时候你是不是还要再做个数据同步的功能??

还有数据同步是需要时间的,如果你有几千万的数据,对吧,所有记录维护好需要10分钟时间,这时候有个用户登录进来了,但是这个新功能与这个用户的关联关系还没保存到数据库中,这时候就不会弹这个弹窗,然后这个用户发现有这个新功能就使用了,后续用户再登录的时候,这时候这个新功能与这个用户的关联关系保存到数据库了,这时候就会弹出这个弹窗,对于用户来说就有点体验不好了

数据库存储方案-优化

之前的方案中我们发现要实现起来太麻烦了,比如数据量大需要分库分表,还要做数据同步,还有数据延迟的问题

之前查询的是ubility_user表,然后拿到这个用户所有 is_open这个字段等于0的数据

select * from obility_user where user_id = 1 and is_open = 0;

这时候我们换种查询流程

  • 1:获取登录用户的user_id
  • 2:还是去查ubility_user这张表,查询出这个用户与哪些新功能已经存在关联记录了
  • 3:与ubility表做对比,判断这个用户还有哪些新功能不存在这个关联记录的
  • 4:那么不存在关联记录的这些新功能就是这次用户要弹的弹窗了
  • 5:用户弹窗之后,在ubility_user表插入这几条关联关系的记录

相比于之前的方案,这时候我们发布一个新功能就不需要提前做好用户与新功能的关联关系了,我们把这个流程放到登录的时候判断了,那么也就不会存在数据延迟的问题了

而且这个方案可以在一定程度上减少我们ubility_user这张表的数据量,试想一下,系统里面有100万的用户,难道这100万的用户真的都是真实用户吗?难道就没有一些所谓的僵尸用户???

其实这个所谓的僵尸用户是不会登录我们系统的,这些用户不登陆,那么ubility_user这张表就不会存他们的关联关系了

但是依然没有解决数据量的问题,即使除了那些僵尸用户,真实用户还是有很多,后续你的新功能多了,ubility_user这张表的数据量还是有很多,还是避免不了要分表

使用redis来存储

仔细想想,其实我们只需要知道用户在登录的时候这个新功能的弹窗有没有弹出来过,无非就是弹过,没弹过两种,那么redis中的bitmap数据类型就很符合我们的业务了,我们一个新功能就建立一个新的bitmap,KEY呢就是这个新任务的ID,每个用户对应着一个bit位,如果这个用户对应的这个任务上的值是0,那么就没有弹过,弹窗之后把这个对应的用户的值改成1就可以了

这样后续即使有多个新任务也就是多几个bitmap而已,我们也不需要使用数据库来存储,也不用考虑后续数据量多了还要分库分表了,简单多了

而且即使redis有些时候丢了一点数据也无所谓,大不了就再弹一次嘛,对于用户来说其实影响也不大的