36未读系统(未读数)

838 阅读4分钟

1.什么是未读数

  • 未读数的场景有:

    • 当有人 @你、评论你、给你的博文点赞或者给你发送私信的时候,你会收到相应的未读提醒;

    • 系统通知的功能,也就是系统会给全部用户发送消息,通知用户有新的版本或者有一些好玩的运营活动\

    • 浏览信息流(微博的功能)的时候,如果长时间没有刷新页面,那么信息流上方就会提示你在这段时间有多少条信息没有看\

  • 针对场景1,可以根据用户使用传统的计数功能,因为是用户维度,不需要批量更新

  • 针对场景2,不适用传统的计数系统

2.未读数应该如何设计

如果将系统未读数循环用户进行+1

//伪代码
List<Long> userIds = getAllUserIds(); for(Long id : userIds) {   incrUnreadCount(id); }

缺点:

缺点1:获取全量用户是耗时操作(需要全表扫描)

  • 缺点

    • 全表扫描,不仅会对数据库造成很大压力,查询用户数据时间很长
    • 分库分表的业务逻辑会更复杂
  • 优化

    • 一次性间所有用户id,并存储在本地文件中,读取文件并轮训发送给这些用户
  • 即使这样仍然会存在不同用户造成的消息延迟,仍然是不可接受的

缺点2:只需针对活跃用户即可,不需要全量通知,造成资源浪费

3.未读系统设计

相同的部分:所有的系统消息在一个列表中,对所有用户共享

不同的部分:不同用户看到的消息时不同,每个人的未读计数不同(存储每条未读信息的id?)

实现细节(数组固定,移动下标即可):

  • 用户访问系统通知页面需要设置未读数为 0,我们需要将用户最近看过的通知 ID 设置为最新的一条系统通知 ID(每次点击,默认用户浏览所有系统通知)\

  • 最近已读id为空,默认新用户,返回未读数-0

  • 非活跃的用户,把最近看过的通知id清空

优点:比较通用的方案,既节省内存,有今年减少获取未读的延迟

\

红点的设计与系统通知类似:

  • 为每个用户存储一个上次点击的时间戳,每次点击更新时间戳

  • 同时记录一个全局时间戳

  • 是否展示红点逻辑

    • 用户时间戳小于全局时间戳,说明落后最新版本,展示红点
    • 反之不展示红点

\

4.如何为信息流的未读设计方案

  • 造成复杂的原因:

    • 信息流是基于关注关系的,未读数也是基于关注关系的,大v增加一条微博,不能给所有关注者未读+1,对实时要求更高\

    • 新的信息时轮训获得的(预加载,增加用户体验)

    • 不能共享存储,每个人都是独一无二的

  • 实现

    • 记录每个用户发布的博文数
    • 每个用户关注的用户中记录博文数的快照(Snapshot),当用户点击后,将未读置为0
    • 每次将未读的消息捞出来,给用户回放
  • 缺陷

    • 关系变更后(删除和新增关注),数据更新不及时
    • 缓存满了会剔除数据(业务保证,对实时要求没有那么高,监控剔除行为)

\

5.课程小结

  • 思考

    • 缓存是提升系统性能和抵抗大并发的神器
    • 设计的关键困难点想办法,深入理解业务需求(不是为了业务而业务,抓住本质需求)
    • 分析业务场景,权衡利弊
  • 小结

    • 评论未读、@未读、赞未读等一对一关系的未读数可以使用上节课讲到的通用计数方案来解决\

    • 在系统通知未读、全量用户打点等存在有限的共享存储的场景下,可以通过记录用户上次操作的时间或者偏移量,来实现未读方案(主要还是快照+偏移量)

    • 信息流未读方案最为复杂,采用的是记录用户博文数快照的方式\