根据 tag 找用户,怎么设计数据库会比较好呢

2015-01-16 20:35:35 +08:00
 fffonion

每个用户有数量不定的tag,比如长得帅,没朋友等;tag的数量可能随时会增加
需求是想找出所有有没朋友的tag的用户,或者可能想找所有同时有长得帅和没朋友tag的用户,应该怎么设计数据库呢?

目前想到的两种:
第一种是按tag存,每个tag下存有这个tag的用户的id的列表,有用户添加标签之后就去追加这个列表(这样是不是比较适合用mongodb?)
第二种是存一个表,字段是用户id和用户tag,每个用户的每个tag就存一条记录,然后给tag字段加索引,然后select fileid from table where tag = 想查询的tag;

大家觉得哪种更有优势,或者有更好的设计方法呢?

2127 次点击
所在节点    数据库
34 条回复
yeyuliu
2015-01-16 21:05:30 +08:00
第一种啊。redis 比较合适。如果不打算redis 落地的话。结合第二种做原始数据的存储。
a2z
2015-01-16 21:06:33 +08:00
这个不是mongodb专门干的活么
fffonion
2015-01-16 21:36:23 +08:00
@yeyuliu get√

@a2z mongo的话第一种有更好的解决方案嘛?对它不太熟
a2z
2015-01-16 21:40:45 +08:00
@fffonion

每个用户加一个field 叫tags,比如 "tags":["长得帅","没朋友"]
fffonion
2015-01-16 21:46:43 +08:00
@a2z 就是说除了tag对应哪些用户再加个反向的数据是吧
Archangel_SDY
2015-01-16 21:50:53 +08:00
用第二种设计数据库,用第一种做缓存扔 Redis.
fffonion
2015-01-16 22:12:13 +08:00
@Archangel_SDY good看来还是redis大法好
kmvan
2015-01-16 22:31:06 +08:00
memcache 能否做到?
如果能,是key = uid,还是key = tagId 呢?
willwen
2015-01-16 22:31:58 +08:00
還不如用ardb來存,另外用什麼存也根本不重要。

用RDBMS的話,就是雙表(users, tags)。如果是pg,就直接在users裡加個數組字段指向tags。如果是mysql就連第三個表,存tag->user的關係。

這種以後要分析還是查詢都方便。
caixiexin
2015-01-16 23:00:07 +08:00
@willwen +1 目前做了一个跟lz很像的需求,mysql下就是按照三个表的方式实现的。tag,user,tage_user
zado
2015-01-16 23:16:27 +08:00
我想到一种方法,用nosql,把所有所有名字+tga以及所有tga+名字两两组合成key,以后就按前缀来搜索。例如:
"小明"[长的帅]
"小明”[没朋友]
"小明”[没有钱]
[没朋友]"小明"
[没有钱]"小明"
[长得帅]"小明"
搜"小明",能得到他的所有tga,搜[没朋友],能得到所有没朋友的人。
a2z
2015-01-16 23:37:08 +08:00
@zado
你这样用户和tag多了后表行数直接指数增长了……
letv
2015-01-16 23:42:01 +08:00
@a2z 那应该怎么设计呢?
zado
2015-01-16 23:47:43 +08:00
@a2z nosql没有表行数限制,再大的表也能装下,同时查找也是非常迅速的。
willwen
2015-01-16 23:47:59 +08:00
@letv postgresql的做法是最優雅的,也最貼近實際表現形式。直接存tag_id到uset裡,保持係數級增長。
zado
2015-01-16 23:59:22 +08:00
@a2z 假设有100万用户,每个用户有100个tga,那么也才2亿个key(100万 * 100 + 100*100万),况且不是每个用户都有100个tga的。
fffonion
2015-01-17 01:43:18 +08:00
@willwen postgre没接触过,在user里存tagid可以通过tag找到用户吗?实际上是在底层实现了类似上面的第二种方法吧(猜的

@zado 这样也不错就是看上去有点不舒服lol
typcn
2015-01-17 02:10:01 +08:00
@kmvan memcache 比 redis 功能少的多,而且还比 redis 慢 10% - 20%
aliao0019
2015-01-17 02:59:53 +08:00
用 pg 的话不应该用户和 tag 是多对多么,关系放到第三个双主键的表里面 user_id tag_id 两个字段;如果只是在用户下存了 tag_ids 数组那通过 tag 查用户的时候还要把 tag_ids 拿出来手动筛;mongo 的话既在 user 下存 tag_ids 又在 tag 下存 user_ids ,写入时候更新两份,怎么样?
puncsky
2015-01-17 03:36:41 +08:00
如果规模很大,先明确不要用join操作。一般情况下“写”比“读”慢40倍左右,而“读”在一般的互联网产品中,比如twitter,读写比是100:1到1000:1。这里的情况假设也是读远高于写(用户经常看到tag但是很少改tag)。这里我们应该着重于对“写”优化。

你的第一种做法,userIdsByTags,是对“求某一tag对应所有用户"很好的读优化,搜索是O(1)。增是O(1),删改是O(k) k是这个tag里面的用户数。相应的tradeoff是userId有很多冗余存在这些lists里面,不方便“求某一用户所有的tag”,搜索是O(m*k),m是tag的个数,k是tag的平均长度。当然我们可以再冗余一点,加一个tagsByUserIds的表,这样就方便读了。不方便的是写的时候要维护两张表。


你的第二种做法,(_id, userId, tag)的schema,给tag加index,对于“求某一tag对应所有用户",额外的空间B tree换来的搜索时间复杂度O(logn),(如果错误请指正),冗余是两个fields要存很多重复的值。如果同时给userId加index,“求某一用户所有的tag”也是O(logn). 这时候写可能是O(logn + logk)或者O(logn + k)。

所以,
假设时间比空间更宝贵,“读”、“增"要比"改"、"删"多,用第一种。
如果”改“、”删“很频繁,可以考虑用第二种。

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

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

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

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

© 2021 V2EX