一个数据库的查询场景

2014-07-16 15:12:43 +08:00
 mikej
有两张表,一张是用户消息表,另外一张记录了消息的状态(已读未读,已删未删),现在在查询消息的时候,如果对应的状态是“已删”,那么这条消息就不显示。但我每次在用户消息表中取的数据条数的固定的,假设取10条,但有3条是“已删”,结果显示7条,用户点击下一页的时候,数据条数就有可能是2,3,7,4,3...

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

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

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

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

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

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

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

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

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

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

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

现在线上跑的是mysql,用联表查询实现的,但hbase没有这么灵活的查询方式。
mikej
2014-07-17 10:18:28 +08:00
@jarlyyn 你说的确实是一个办法。
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
2014-07-17 18:24:12 +08:00
@viowan 感谢你的回复!目前线上也是用类似的方法去解决的。不过hbase没有关系型数据库这种灵活的查询方式。只能想其他办法了。

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

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

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

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

© 2021 V2EX