V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
mikej
V2EX  ›  问与答

一个数据库的查询场景

  •  
  •   mikej · 2014-07-16 15:12:43 +08:00 · 3349 次点击
    这是一个创建于 3809 天前的主题,其中的信息可能已经有所发展或是发生改变。
    有两张表,一张是用户消息表,另外一张记录了消息的状态(已读未读,已删未删),现在在查询消息的时候,如果对应的状态是“已删”,那么这条消息就不显示。但我每次在用户消息表中取的数据条数的固定的,假设取10条,但有3条是“已删”,结果显示7条,用户点击下一页的时候,数据条数就有可能是2,3,7,4,3...

    现在用的数据库是hbase,不能联表查询,那么如何才能保证每页的数据都是10条?
    20 条回复    2014-07-17 18:24:12 +08:00
    caofugui
        1
    caofugui  
       2014-07-16 15:50:43 +08:00
    不能联表查询你还分两张表?要么换数据库,要么在消息表加上状态字段
    mikej
        2
    mikej  
    OP
       2014-07-16 16:00:31 +08:00
    @caofugui 因为消息分为公共消息和私人消息,私人消息有状态字段,由于用户量很大,为避免消息重复,公共消息的状态就存在另外一张表中。

    之所以用hbase,就是考虑到数量量太大,目前每天净增50万条,mysql有点顶不住
    hcy
        3
    hcy  
       2014-07-16 16:28:13 +08:00
    多次取,筛掉已删,再拼成10条的集合返回给前台,或者每次多取一点比如100条,筛掉已删,再切成多个10条的集合。多余的都缓存起来等下次翻页的时候再来拼接。
    mikej
        4
    mikej  
    OP
       2014-07-16 16:42:09 +08:00
    @hcy 如果每次取10条,那就要用递归?考虑到极端情况(比如前100条都被删除了),这样效率就很低啊,如果每次都多取一点数据,也同样会有这种情况啊。
    lu18887
        5
    lu18887  
       2014-07-16 16:53:23 +08:00
    需求有点古怪啊,不能联表查询的数据库还分表,这不是以己之短攻彼之长?
    私人消息加上状态字段,公共消息的状态为什么就要存在另外一个表中呢?我没看明白。
    mikej
        6
    mikej  
    OP
       2014-07-16 17:16:35 +08:00
    @lu18887 确实是很蛋疼的需求,纠结了好久。。

    公共消息的状态放在另外一张表中是 因为每个用户量太大,如果给公共消息加状态的话,只能是给每个用户都加一条数据。这样数据量就更大了。。
    wangdaimishu
        7
    wangdaimishu  
       2014-07-16 17:22:04 +08:00
    @mikej 用户ID为0的就表示公共消息,然后单独弄一个表存储公共消息的阅读状态,大概就是

    uid
    message_id
    这样的映射关系,读过公共消息的,就写一条映射记录。

    不过有个问题,就是当你想做“只看未读消息”或者“只看已读消息”时就很纠结了。

    以前用这个方法实现过类似的需求,基本达到了要求,就是代码实现上别扭点。
    mikej
        8
    mikej  
    OP
       2014-07-16 18:01:52 +08:00
    @wangdaimishu 专门写了一个方法去判断 已读未读 已删未删 这4种状态,然后然后在过滤这些消息。

    目前用递归的方法解决了 每次取10条 的问题,就是担心以后公共消息量大了后,查询效率会很低。。。

    你之前是怎么解决的,也是递归去查吗?
    wangdaimishu
        9
    wangdaimishu  
       2014-07-16 18:07:09 +08:00
    @mikej 原来你把消息的状态单独存了一个表。。。这么蛋疼的设计,说说为啥这么设计?
    mikej
        10
    mikej  
    OP
       2014-07-16 18:34:43 +08:00
    @wangdaimishu 不对啊,看到你上面的回复不也是这样做的吗?只有公共消息的状态单独存了一个表,私人消息没有啊。

    看来还是我没表达清楚啊,实际上是要实现一个消息中心的功能,每个用户都可以查看私人消息和公共消息,公共消息在message表里就一条数据,但每个用户都能读取它,状态就不一样,只能单独存了一个表了。

    如果不这样做,就要给每个用户添加一条公共消息,但网站用户1000万+,问题就在这儿。。
    wangdaimishu
        11
    wangdaimishu  
       2014-07-16 18:59:12 +08:00
    @mikej 我没有把状态跟消息分成两个表,直接在消息表里加了个字段 status ,然后0 1 2 这种表示 未读 已读,删除不做软删除,直接就delete了。

    只是我将 公共消息、私有消息,统一放在一个表里,然后做一个公共消息的已读映射。
    wangdaimishu
        12
    wangdaimishu  
       2014-07-16 19:01:25 +08:00
    @mikej 这样的话你可以看我在7楼的回复,那种设计就是为了解决公共消息太多的。实际上每次你发公共消息只会产生一条,就是UID=0的。
    hcy
        13
    hcy  
       2014-07-16 19:28:02 +08:00
    看能不能变通下, 前台不要把2种消息放一起显示。后台先直接查询消息状态表,可以先筛掉状态为“已删”的消息id,之后可以分页显示公共消息。私人消息我按你的说法理解为和状态表没有关联,这个分页显示也好弄吧。
    mikej
        14
    mikej  
    OP
       2014-07-16 20:00:39 +08:00
    @wangdaimishu 我也是你这么去做的,当用户删除公共消息的时候,就在状态表里面做一个已删映射,就向
    @hcy 所说,私人消息和状态表无关。
    viowan
        15
    viowan  
       2014-07-16 22:02:11 +08:00
    每次十条是指公告消息和私人消息加一起一共十条吗?
    可以试试UNION ALL 然后limit 看看能否解决?
    jarlyyn
        16
    jarlyyn  
       2014-07-17 00:14:46 +08:00
    看了下感觉是给每个用户都发了条公共消息么……
    感觉如果是我做的话会这样:
    1.公共记录之给最终用户显示最后10条(考虑过用户一年没登录后的感受么……)
    2.记录用户最后一次查看公共记录的时间。
    3.查看公共记录是必须查看一个列表(这样只需要记录最后查看时间)或者记录最近查看记录。
    这样需要check的只有公共记录的最后更新时间,以及用户最后查看公共记录的时间就可以了。
    mikej
        17
    mikej  
    OP
       2014-07-17 10:14:18 +08:00
    @viowan 是单查询10条公共消息,公共消息的状态在另外一张表中,私人消息则有自己的状态字段。

    现在线上跑的是mysql,用联表查询实现的,但hbase没有这么灵活的查询方式。
    mikej
        18
    mikej  
    OP
       2014-07-17 10:18:28 +08:00
    @jarlyyn 你说的确实是一个办法。
    viowan
        19
    viowan  
       2014-07-17 11:18:39 +08:00
    @mikej
    按找我的理解的话,现在数据库中是有两个表
    一个公共消息对应用户的阅读状态表A
    uid
    msgid
    readtype
    一个是消息表B,里面保存了所有消息(私人消息及公共消息)
    uid (uid为0表示公共消息)
    msgid
    readtype
    content
    ctime
    etc...
    由于没有使用过hbase不知道能不能做以下这种查询:
    当前查找的用户cuid
    select *
    from (
    select *
    from B
    where uid = cuid
    order by ctime desc
    limit 0,10
    union
    select *
    from B
    where msgid in(
    select msgid
    from A
    where uid = cuid
    limit 0,10
    )
    ) as Temp
    order by Temp,ctime desc
    limit 0,10
    代码就不一定对了,不过我的思路是这样
    mikej
        20
    mikej  
    OP
       2014-07-17 18:24:12 +08:00
    @viowan 感谢你的回复!目前线上也是用类似的方法去解决的。不过hbase没有关系型数据库这种灵活的查询方式。只能想其他办法了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5337 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 40ms · UTC 08:46 · PVG 16:46 · LAX 00:46 · JFK 03:46
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.