Web 消息中心怎么做 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
xx19941215

Web 消息中心怎么做

  •  
  •   xx19941215 Jun 30, 2018 5860 views
    This topic created in 2860 days ago, the information mentioned may be changed or developed.

    公司一个新任务 消息中心,有没有大佬指点小弟下。具体需求就是比如运营在后台发一篇通知,现在的几百万用户的消息中心会出现一条未读消息,然后可以标记已读还有删除功能。还有各种其他的消息,比如支付提醒、收藏的店铺上新提醒。这个要怎么设计,感觉 mysql 顶不住啊?

    14 replies    2019-02-24 17:52:45 +08:00
    xiaoxinshiwo
        1
    xiaoxinshiwo  
       Jun 30, 2018
    mq,发布 /订阅
    mysql 顶得住,主要是你的提醒查询 sql 要快
    最简单就是 ajax 轮询(次不推荐)
    其次是 comet 长连接(不推荐)
    最后就是 webSocket(推荐)
    iac
        2
    iac  
       Jun 30, 2018
    一个信息表,一个信息查看记录表
    misaka19000
        3
    misaka19000  
       Jun 30, 2018
    想用 mysql 的话直接用一张表存用户与消息的关系,之后查询的时候根据用户来查询与其相关的消息就行了,消息的已读未读状态用一个字段来表示,几千万条数据 mysql 还是能顶住的,关键是你这个业务场景的并发量大概是怎么样的?
    misaka19000
        4
    misaka19000  
       Jun 30, 2018
    @xiaoxinshiwo #1 我觉得对实时性要求没那么高的话完全可以在用户刷新页面的时候再显示消息内容,没必要实时显示
    wuxi317
        5
    wuxi317  
       Jun 30, 2018 via Android   1
    消息主表,用户自己的消息表,用户最后一次查询或者登录的时间。定向给用户的消息就直接落表,全局消息在用户登录或查询接口里按照 msg 创建时间>最后查询/登录时间的,查到了,就 copy 到用户自己的消息表中,标注为系统消息。这样可以标注已读未读了。
    vebuqi
        6
    vebuqi  
       Jun 30, 2018   3
    从场景上来看,建议把消息分成两种,全站消息和个人消息。
    全站消息一张表,所有用户个人消息一张表
    消息操作记录如已读,删除等用另一张表

    运营推新文章,往全站表里塞一条
    支付提醒,店铺上新提醒属个人消息,记到个人表

    塞完后
    在线用户通过 WebSocket 推送提醒,用户侧出红点
    离线用户,在登录时候,去查一次返回就可以了

    用户收到提醒,点开消息中心时候去查消息(一定不能收到 WebSocket 推送就去查消息)
    用户操作消息,加入到消息操作记录表里
    abcbuzhiming
        7
    abcbuzhiming  
       Jun 30, 2018
    @misaka19000 它这个场景上 MySQL 还真有些问题,如果它说的几百万用户是真的。那么只要群发 10 次,用户与信息的关系表的记录会达到几千万,群发 100 次的时候这张表的数据会达到几亿这个级别。到这个级别的时候 MySQL 的查询速度也会大大的变慢,即使加了索引改善也不会很大,换别的关系数据库也不会有啥改善的。要么系统设计成按时间分割表,比如每个月(群发特别频繁的时候估计每周都得新建)新建一张用户与信息关系表,查询的时候 union 操作。要么就只有上大数据了。
    另外这个场景还可能存在的一个问题就是几百万用的消息到达时间的差别拉的太长,单库群发一次,毕竟要一口气插入几百万条用户与信息关系数据,也是要时间的
    abcbuzhiming
        8
    abcbuzhiming  
       Jun 30, 2018
    @wuxi317 你这个想法补充,我一直以来没想到怎么弄的问题就是在群发的时候需要插入很多条用户-信息关系记录的问题。你这个解决办法把插入过程延后,并分散到每个客户端登录的时候由他们来做。
    但是你这个方法有个弱点,如果这条群发消息是用户在线的时候发的,那用户如果没有退出重新登录行为,就永远收不到这条消息了,所以这个方法似乎有点问题,我建议还是把“ copy 到用户自己的消息表中”这个行为放在客户端轮询时都有新消息的过程中做
    wuxi317
        9
    wuxi317  
       Jun 30, 2018 via Android
    @abcbuzhiming 有提到了一个消息查询接口,看实时性需求。如果有实效要求,那么是可以使用消息查询接口的。但是在消息查询接口中,时间查询条件就需要很好的去处理,如果使用 mysql 来记录每一个用户最后一次查询时间,那这个量级就非常巨大了。
    xx19941215
        10
    xx19941215  
    OP
       Jun 30, 2018
    @abcbuzhiming @iac @misaka19000 @vebuqi @wuxi317 @xiaoxinshiwo 小弟先消化一下各位的回答 非常感谢
    zpf124
        11
    zpf124  
       Jun 30, 2018   1
    我们之前的做法是:

    消息组表,消息表,用户消息已读表。

    消息组:id 0 表示全站消息,所有人都加入这个组,其他组 根据当时的消息分类,代码后台自动创建。
    消息表: 存储消息内容,关联消息组表,
    已读表: 存储已读记录 关联消息 id 与用户 id。

    通过这样,我们把 读取压力改成了写入压力... 不过我们用户量不是非常大,而且信息重要性还不高,不是每个人都有兴趣去标记已读的.... 如果后来真有这个压力了,到时候再用 redis 做写入缓存。

    另外 因为我们需求的原因,我们没有给单个用户发消息的情况,都是群发:“你好你关注的 xx 有了 xx 的新动态”(当然如果关注这个订阅的只有一个人那就是单发了),
    用户也没有删除消息的选项,不过如果做删除的话我们直接在已读表上改造一下就可以逻辑删除了。
    XYxe
        12
    XYxe  
       Jun 30, 2018 via Android
    mingyun
        13
    mingyun  
       Jun 30, 2018
    @XYxe 这篇文章很详细
    xx19941215
        14
    xx19941215  
    OP
       Feb 24, 2019
    @wuxi317 我是用老哥这个方法弄的
    About     Help     Advertise     Blog     API     FAQ     Solana     3404 Online   Highest 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 47ms UTC 10:54 PVG 18:54 LAX 03:54 JFK 06:54
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86