在群聊系统中保存和展示用户的图片、视频或音频数据时,通常需要将元数据和文件分开存储 。
其中元数据存储在 MySQL 集群,文件数据存储在分布式对象存储集群中 。
5.1 交互流程消息发送和接收的时序图如下所示:
![听说你会架构设计?来,弄一个群聊系统](http://img.jiangsulong.com/231108/1I00263N-5.jpg)
文章插图
- 用户A在群中发送一条带有图片、视频或音频的消息 。
- 移动客户端应用将消息内容和媒体文件上传到服务器后端 。
- 服务器后端接收到消息和媒体文件后 , 将消息内容存储到 Message 表中,同时将媒体文件存储到分布式文件存储集群中 。在 Message 表里,不仅记录了媒体文件的 MediAID,以便关联消息和媒体;还记录了缩略图、视频封面图等等 。
- 服务器后端会向所有群成员广播这条消息 。移动客户端应用接收到消息后,会根据消息类型(文本、图片、视频、音频)加载对应的展示方式 。
- 当用户点击查看图片、视频或音频缩略图时,客户端应用会根据 MediaID 到对象存储集群中获取对应的媒体文件路径,并将其展示给用户 。
5.2 消息存储和展示除了上述建群功能中提到的用户表和群组表以外 , 存储元数据还需要以下表结构:
- Message表: 用于存储消息,每个消息都有一个唯一的 MessageID,消息类型(文本、图片、视频、音频),消息内容(文字、图片缩略图、视频封面图等),发送者 UserID、接收群 GroupID、发送时间等字段 。
- Media表: 存储用户上传的图片、视频、音频等媒体数据 。每个媒体文件都有一个唯一的 MediaID , 文件路径、上传者 UserID、上传时间等字段 。
- MessageState表: 用于存储用户消息状态,包括 MessageID、用户 ID、是否已读等 。在消息推送时,通过这张表计算未读数 , 统一推送给用户,并在离线用户的手机上展示一个小数字代表消息未读数 。
我:MessageState 表记录了用户的未读消息数,想要获取用户的消息未读数时 , 只需要客户端调用一下接口查询即可获取,这个接口将每个群的未读个数加起来,统一返回给客户端 , 然后借助手机的 SDK 推送功能加载到用户手机上 。
面试官:就这么简单吗,可以优化一下不?
我:(内心 OS,性能确实很差 , 就等着你问呢)是的,我们需要优化一下 , 首先 MySQL 查询 select count 类型的语句时,都会触发全表扫描,所以每次加载消息未读数都很慢 。
为了查询性能考虑,我们可以将用户的消息数量存入 Redis,并实时记录一个未读数值 。并且,当未读数大于 99 时,就将未读数值置为 100 且不再增加 。
当推送用户消息时,只要未读数为 100,就将推送消息数设置为 99+ , 以此来提升存储的性能和交互的效率 。
面试官:嗯,目前几乎所有的消息推送功能都是这么设计的 。那你再说一下 10 亿用户的群聊系统应该如何在高并发,海量数据下保证高性能和高可用吧!
我:我想到了几个点,比如采用集群部署、消息队列、多线程、缓存等 。
集群部署:可扩展在群聊系统中,我们用到了分布式可扩展的思想,无论是长连接服务、消息推送服务 , 还是数据库以及分布式文件存储服务,都是集群部署 。
一方面防止单体故障,另一方面可以根据业务来进行弹性伸缩 , 提升了系统的高可用性 。
消息队列:异步、削峰在消息推送时,由于消息量和用户量很多,所以我们将消息放到消息队列(比如 Kafka)中异步进行消费和推送,来进行流量削峰,防止数据太多将服务打崩 。
多线程在消息写入和消费时,可以多线程操作,一方面节省了硬件开销,不至于部署太多机器 。另一方面提升了效率,毕竟多个流水线工作肯定比单打独斗更快 。
其它优化缓存前面已经说到了,除了建群时记录 code,加群时记录群成员数,我们还可以缓存群聊里最近一段时间的消息,防止每个用户都去 DB 拉取一遍数据,这提升了消息查阅的效率 。
推荐阅读
- 一文读懂Android架构演进历程
- Instagram 早期技术架构,你了解了吗?
- 五百万和杨颖你会选择哪个?
- 减肥多吃这3种食物,少吃这3种食物,你会很快瘦下来
- 空气炸锅致癌是真的吗 听说空气炸锅致癌是真的吗
- 醋泡黑豆的作用,听说陈醋泡黑豆不错黑豆有大圆粒的还有小长形的是用哪种黑豆
- 普洱大叶种与小叶种,你会区分吗?
- 听说你会架构设计?来,弄一个微信群聊系统
- 时尚中短发、展现绝美女人气质,你会爱上哪款呢
- 当听说汪峰拥有30亿资产,葛荟婕的回应亮了,真是人间清醒