文章目录
- 概要
- 问题解析
- 思考问题
- 数据基础
- 读取
- 写入
- 总结
概要
说起IM程序我们都不陌生,本篇文章我们就为如何实现一个IM做一个简单的整体方案设计以及基本的数据结构
问题解析
我们先不上一大堆牛逼哄哄的中间件。
我们先从实现角度,来讲讲设计思路。
从整体来看客户端主要做两件事 ,接受消息,发送消息。
从接受消息的角度来说,客户端主要也只做两件事 ‘被动收到推送消息(PUSH)’与‘主动拉取消息(PULL)’
我们简单的分析下,这三件事的基本操作是什么?
发送消息:将用户讯息传输到服务器
推送消息:‘实时’将用户信息传输到服务器
拉取数据:将用户历史数据传输给用户
思考问题
其实我们思考下上文所说的三个操作,除了推送消息是服务器需要判断用户当前是否在线,进行操作。发送消息,拉取数据。其实就是往数据库一写一查的事。
在发送消息的时候,我们将用户数据存入数据库。‘此时判断目标用户是否在线,如果是则进行推送。‘(当然通知订阅的方式也是可以的,这只是举一个例子)。等到用户进行查询的时候将其查出。
问题很简单,甚至初学者都能上手。
问题就是当这张表在高并发的时候,会有大量的写以及大量的读。以及随着时间,数据表不断增长,甚至短时间内就疯狂增长。此时应该如何处理?
解决方案有很多,因此我们就有了各式各样的中间件。本篇文章的中心思想也不是要教大家如何用这写组件。
我们只需要打好基础,后续随着业务驱动,来决定技术的走向。
数据基础
讲到IM数据结构,很简单最简单不超过五个字段
字段 |
---|
id |
senderId |
readerId |
content |
sendTime |
isSend |
最重要的就是这个id,我们需要他能做到,有序,唯一。
读取
为什么,我们想想如何读取用户的未读信息?总共三种
- 读取所有未读的消息然后进行分页( AND isSend= 2 ORDER BY sendTime desc limit 500)
- 根据传入的最后接收时间获取( AND sendTime >“” ORDER BY sendTime desc limit 500)
- 根据传入的最后ID进行获取(AND id > “XXX” ORDER BY id desc limit 500)
第一种方案,强依赖isSend=2,查询效率十分缓慢
第二种方案,根据sendTime 可能因为时间重复会发生数据丢失,并且增加后续业务开发的设计难度。
只有第三种方案是设计成本最低,并且在查询性能之上最高效,拓展能力也是最好。可以最大层面的满足后续业务需求。
写入
ID方案的主要设计复杂难度在于‘写入’。
因为我们在做其他业务的时候,我们都知道,一旦涉及有序且唯一。我们就很难逃开分布式锁。但是分布式锁+唯一则会严重影响效率。因为只要并发够多,最算你数据插入速度再快。也会形成严重的排队问题。
但是其实我们无需悲观,这时候我们就需要想想。
虽然我们表面说是要有序,先进来的请求他的id不应该小于后进来的。
但是真的需要做的那么精准吗?
其实不需要的,其实并不需要。在一秒之内同时进来的数据,用户是无感的。因此随先生成id又有什么问题呢?
数据库内的数据他的id真的需要实现从上到下升序吗
其实也不需要,只要id能表明他的有序性,谁先进个又有何分别呢?(当然了,你晚个一两秒,在读取时形成数据丢失就不好了)
因此,我们只需要依赖雪花算法,在形成id的那一刻能表明请求的先后顺序便可。
总结
到此,我们就形成了一个最基本的IM设计方案。
在后续开发中,我们遇上数据表大,我们可以使用分库分表。插入数据效率慢,我们可以使用插入缓冲等等。
我们只需要在我们的基础上不断添砖加瓦就好