XDM,要优化一套站内信,请问有什么可执行落地的方案。

2021-11-20 09:17:38 +08:00
 xiayushengfan
我初步的实现用 MYSQL ,一张站内信模板表,一张用户表,一张模板用户关系表。
但是现在用户表的数据量有点大,大概在百万条。
如果给多次所有的用户发站内信,关系表很容易数据量破亿。
请问有什么其他方法能解决这个问题。
有两个需求:
1.最好不要用 MYSQL 的分库分表,想用新技术代替
2.百万用户的日常活跃也就几千,可能阅读人数就几百这样子。
3.关系表中,有对应的用户是否阅读。
3104 次点击
所在节点    程序员
13 条回复
oott123
2021-11-20 10:11:46 +08:00
还这样,但是只存已读用户,没已读的就当未读
zpf124
2021-11-20 10:17:41 +08:00
我先说个我之前设计的实现,不一定好,而且当时我们的在线用户量级很小都不担心这个问题。

一张用户表,一张站内信表,一张站内信已读表。

站内信表中有 sendUserId 字段 魔法值 0 代表发送给所有人,其他值代表只发给某人。
因为我们数据量不大,所以消息是直接 join 查的,order 排序未读的展示在前。
zpf124
2021-11-20 10:23:29 +08:00
本来当时打算有个消息发送组的概念,消息分为发给个人还是发给某个组, 然后组里面有个魔法值是群发。
结果这个功能对于我们本事不是很重要,而且人手要优先干其他的,所以这里就简化了,有给某个组群发的需求,就 foreach ,创建多条信息发送给指定的人。
zpf124
2021-11-20 10:33:56 +08:00
最后说说你的需求
1 、数据肯定要落库的,虽然这个数据重要性不高丢了就丢了,但发消息偶尔会出现收不到肯定最起码业务领导会不爽。
所以如果想要性能那就用搜索引擎 es 或者其他非关系数据库如 mongoDB ,数据读取层面还可以加 redis 。

2 ,3 、阅读很少那就把阅读单独建表, 就像我们那种, 阅读表包含消息 id 、用户 id 、是否已读、已读时间、创建时间啥的。
kaiki
2021-11-20 10:41:12 +08:00
@zpf124
群发这块是不是直接发送一个空邮件或只有标题的邮件,邮件数据链接到一个群发表中取邮件内容效率会高点?
感觉发邮件和发文章也没什么太大的区别,就是用户能不能查看而已,既然是给所有人的,当文章来发会不会好一点。
xiayushengfan
2021-11-20 11:17:14 +08:00
@zpf124 我是打算是 mongoDB 去重新做这一块。主要 mongoDB 不怎么熟悉。
xiayushengfan
2021-11-20 11:20:05 +08:00
@kaiki
是偶尔群发,偶尔按照组别发送,但是这个组别又有很多。
一个用户可能有十几种属性或者是标签,然后标签组合,这样子类别就很多了。
所以不太想用 3 楼说的发送组的概念
zpf124
2021-11-20 11:38:03 +08:00
@kaiki 最后我们是发多条了,没有组的概念,这种情况下按照你说的方式确实能优化一些存储。

针对后面的问题,我们的文章表有 n 多字段,对于消息来说都是完全没用的,而且文章没有记录多少人已读的。
最后我们的站内信不是用户给用户发送的,基本都是系统通知,"你订阅的 某个资源更新了,url " 类似这种,所以我们和楼主的也不完全相似,因为我们的消息大多数是分组的,个别是全站群发,没有用户间私信。
zpf124
2021-11-20 11:42:36 +08:00
@xiayushengfan mongodb ,我们用的也不是很有心得,我们还是按照关系型数据库的用法或者 k-v 缓存的用法在用。

比如我们如果用 mongodb 存站内信的话,就是一个集合是所有消息,或者一个集合是一个月的消息之类的维度,每个文档包含文档的全部内容包括所有的接受用户 id ,以及已读状态, 查询的时候 直接查当前用户 id 的文档数据。
CrazyMonkeyV
2021-11-20 15:25:35 +08:00
这个我以前设计过,我们的情况类似,邮件会分类:
1 、全部用户邮件 先生成一条数据,然后再用户登录的时候,给他发一个邮件。
2 、特定类型用户邮件 先生成一条数据,然后再用户登录的时候,检测是否满足条件,满足给他发一个邮件。
上面这 2 类邮件,还有开始和结束时间(也就是有效期),有效期外登录则不发了
3 、指定用户右键 直接发
这样的话,日活不多的系统,邮件不会太多,mysql 基本没问题
CrazyMonkeyV
2021-11-20 15:28:17 +08:00
其实主要的概念就是把群发改为了给活跃用户发,减少数据量。
lesismal
2021-11-20 16:21:35 +08:00
### 选型
因为楼主希望不要用 mysql ,所以选择 mongodb ,能支持的数据量足够大,并且 nosql 方便扩展

### 信件存储方案(按类型分别处理,广播类信件去重)
1. 系统发给用户,多个用户收到的是相同内容:只插入一条到 letter 集合中
2. 系统发给用户,多个用户收到的是不同内容:一个用户一条插入到 letter 集合中,类似用户发给用户
3. 用户发给用户:每个一条插入到 letter 集合中

### mongodb 集合( collection )和 文档( document )设计
mongodb 的 collection 相当于 mysql 的 table
collection 里的 document 相当于 table 的一条数据 collection 设计:
1. collection name: letter
document struct:
{
"_id": xxx, //mongodb 自带的就行
typ: system/p2p/...,
time: xxx,
from: userid:name, //用户之间的会有这个字段,系统发的看功能设计是否需要
content: xxx,
...
}

2. collection name: letter_list
document struct:
{
userid: xxx,
letters: [
{
id: letter_id_xxx, // 根据信件 id 去 letter 里查
time: xxx,
readed: 0,
},
......
],
}

优化:上面的 1 、2 中的 document 是为了方便展示,直接是用展开的结构字段,实际使用中,应该把各个字段合并编码、减少字段数量、从而减少相应的计算消耗和存储空间占用等成本,比如冒号分隔符分割多个字段,取出后 splitN 得到各个字段内容
letter 可以优化成:
{
"_id": xxx, //mongodb 自带的就行
info: "type:time:from:content", // 例如: "0:1637396101::您的超级 VIP 已经开通!", "1:1637396101:用户 B:您的回复太棒了,非常感谢!"
// content 应该放在最后,以面 content 中有冒号时放在中间 splitN 无法正常解析
}

letter_list 可以优化成:
{
"_id": xxx, //mongodb 自带的就行
info: "id:time:readed", // 如:"aaaa:1637396101:0"
}

### 其他
具体细节以及 mongo 的一些优化,请根据自己实际情况进行
xiayushengfan
2021-11-22 09:32:12 +08:00
@lesismal 谢谢老哥,给的全面了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/816703

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX