一个数据库的查询场景 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
mikej
V2EX    问与答

一个数据库的查询场景

  •  
  •   mikej 2014-07-16 15:12:43 +08:00 3642 次点击
    这是一个创建于 4117 天前的主题,其中的信息可能已经有所发展或是发生改变。
    有两张表,一张是用户消息表,另外一张记录了消息的状态(已读未读,已删未删),现在在查询消息的时候,如果对应的状态是“已删”,那么这条消息就不显示。但我每次在用户消息表中取的数据条数的固定的,假设取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     Solana     3116 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 11:37 PVG 19:37 LAX 04:37 JFK 07:37
    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