V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Simle100
V2EX  ›  Java

群发消息已读未读如何设计才能降低数据库的压力

  •  
  •   Simle100 · 2021-03-11 11:40:11 +08:00 · 4972 次点击
    这是一个创建于 1350 天前的主题,其中的信息可能已经有所发展或是发生改变。

    目前涉及 1.消息表 A,2.消息发送表 B A->B 为 1 对多的关系。消息的发送使用了中间件,但统计已读未读存在性能压力。 现在的场景是,经常给 10 万用户发一条消息,然后查看哪些用户读了消息,哪些用户没有读消息。这么做 B 表的数据量会非常大,导致查询的效率非常底下,请问该如何设计,才能减少数据库压力? 目前的思路:分表。

    19 条回复    2021-03-12 13:39:12 +08:00
    rqrq
        1
    rqrq  
       2021-03-11 11:53:00 +08:00   ❤️ 1
    >>然后查看哪些用户读了消息
    要不再建一张表,只记录查看了消息的用户
    gBurnX
        2
    gBurnX  
       2021-03-11 12:04:40 +08:00   ❤️ 2
    1.表 B 的设计思路,其实是偷懒的做法,这种思路方便一次性查出 8 辈子的消息记录,缺点就是性能差。

    2.科学的高性能的设计思路应该是,发一次消息,建一个表。这样每个表里就只有十万条消息了。

    3.每隔一段时间清理这些表,如果历史数据需要保存,可以直接 dump 到文件,或者放置在内存占用少的数据库实例中。
    tabris17
        3
    tabris17  
       2021-03-11 12:05:26 +08:00   ❤️ 1
    已读和未读消息分两个表存
    FucUrFrd
        4
    FucUrFrd  
       2021-03-11 13:57:43 +08:00 via Android   ❤️ 2
    消息已读存 hbase,msgid:reader id
    jacobsun
        5
    jacobsun  
       2021-03-11 14:06:37 +08:00   ❤️ 1
    用 redis 记录发送的消息, 两个集合搞定. 一个记录发送的用户 ID 一个记录已读的用户 ID 然后去差集就好
    已读未读的状态在前端的消息列表控制
    Simle100
        6
    Simle100  
    OP
       2021-03-11 14:40:45 +08:00
    大佬们有没有做过这样的需求?
    teddy2725
        7
    teddy2725  
       2021-03-11 14:52:17 +08:00
    把已读事件写到一个流里面,用 flink 做实时统计
    wennew
        8
    wennew  
       2021-03-11 14:59:38 +08:00
    我用的 redisSET 记录消息的用户状态,然后定时备份到 mysql,缺点很多,但是只要 redis 不挂勉强能应付
    soulzz
        9
    soulzz  
       2021-03-11 15:23:46 +08:00
    放客户端 sql 库啊
    soulzz
        10
    soulzz  
       2021-03-11 15:41:38 +08:00
    服务端可以这样做:消息发送后保存消息 id 和发送时的时间戳在内存中
    半小时内在内存中用布隆过滤器处理客户端发过来的数据区分已读未读,写一个定时
    对超过半小时的消息 id 入库处理(或者用消息队列异步合并写)。

    客户端的已读未读就放在客户端的库一条条处理
    labulaka521
        11
    labulaka521  
       2021-03-11 15:46:13 +08:00
    插个眼 目前也有类似的需求
    iceteacover
        12
    iceteacover  
       2021-03-11 15:53:49 +08:00
    mark,感觉不同数量级的消息 会有不同的设计实践。
    sujin190
        13
    sujin190  
       2021-03-11 15:54:27 +08:00
    @jacobsun #5
    @wennew #8 set 操作比直接用 hashmap 麻烦吧,而且直接写数据库就是了,redis 就是个缓存,统计已读有问题不是写已读数据有问题

    其实直接发的时候都把发送用户 ID 己到 redis 消息 ID 对应的 hashmap 里就是了啊,读了就从里边删了,看哪些用户没读直接读整个 hashmap 就是了,设置一个合理过期时间也都不用清理,读不到再从数据库加载就是了,发消息和读消息都先写数据库再写 redis 就是了
    sujin190
        14
    sujin190  
       2021-03-11 15:57:56 +08:00
    如果你觉得直接用 hashmap 太耗内存,那么就在发消息式给个再当前消息内部有效的自增 ID,redis 这边就能用 bitmap 来存是否已读了啊,查的时候直接取出哪些位置没读,然后结合消息 ID 就直接能查除用户了,这个应该式最省内存也不是很消耗性能的方式了吧
    x537196
        15
    x537196  
       2021-03-11 16:16:18 +08:00
    做了一套类似的,不过没有明细需求,只统计发送数、已读数
    wennew
        16
    wennew  
       2021-03-11 18:00:11 +08:00
    @sujin190 hashmap 加过期时间还是变成 redis 了呀, 而且这东西确实可以用 nosql 冗余存储,mysql 这种确实不好使啊
    sujin190
        17
    sujin190  
       2021-03-11 18:18:01 +08:00
    @wennew #16 我的意思用 redis 的 set 数据结构比用 redis 的 hashmap 数据结构操作复杂,都是 redis 。。
    aguesuka
        18
    aguesuka  
       2021-03-12 11:05:14 +08:00 via Android
    才十万,直接存数据库建好索引分区肯定没问题。
    Simle100
        19
    Simle100  
    OP
       2021-03-12 13:39:12 +08:00
    @aguesuka 群发一条就 10 万,要是每天 100 个用户群发一条,一天就有 1000 万了,你看看一个月就有多少数据量了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1400 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 17:27 · PVG 01:27 · LAX 09:27 · JFK 12:27
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.