You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Django:使用PostgreSQL ArrayField存储点赞的方案探讨

关于点赞系统模型设计的疑问

背景与需求

我有两个模型:Post和Comment,两者均可被User点赞。核心需求如下:

  • 需在每个Post或Comment附近显示总点赞数;
  • 每个User需拥有展示其所有点赞内容的页面。

最直观的方案是使用多对多(m2m)字段,但担心未来会引发诸多问题。因此提出以下替代方案:

  1. 为Post和Comment模型添加字段:users_liked_ids = ArrayField(models.IntegerField());
  2. 为User模型添加字段:posts_liked_ids = ArrayField(models.IntegerField())、comments_liked_ids = ArrayField(models.IntegerField())。

用户点赞时执行两个操作:

  • 将用户ID添加至对应Post/Comment的users_liked_ids字段;
  • 将Post/Comment的ID添加至对应用户的posts_liked_ids/comments_liked_ids字段。

现提出以下问题:

  1. 该方案是否可行?
  2. 此方案下查询“当前用户是否已点赞某篇Post/Comment”的效率如何?
  3. 将点赞数据存储在单独表的ArrayField中是否比存在被点赞模型中更好?
  4. 是否应该继续使用直观的多对多字段方案?

回答

1. 该方案是否可行?

短期来看确实能跑通,满足你当前的两个核心需求:统计点赞数直接取数组长度,展示用户点赞内容可以从用户的数组字段里取ID查询。但这个方案藏着不少隐患:最突出的是数据一致性问题——点赞时必须同时更新两个地方的数组,一旦中间出现异常(比如事务失败、代码逻辑遗漏),就会出现“用户以为点赞成功但内容的点赞列表没记录”或者反过来的情况,后期排查修复会非常头疼。另外,当数组长度变大(比如热门帖子有上万点赞),存储和查询的开销会直线上升。

2. 查询当前用户是否已点赞的效率如何?

效率会随着数据量增长急剧下滑。因为ArrayField的包含查询(比如检查用户ID是否在users_liked_ids里)本质是全数组扫描,数据库无法给数组内的单个元素建索引。点赞数少的时候可能没感觉,但如果是有几千上万个点赞的热门内容,每次查询都要遍历整个数组,延迟会变得很高。反过来,查用户是否点赞了某篇内容,从用户的posts_liked_ids里检索也是同样的问题——用户点赞的内容越多,扫描的数组越长,速度越慢。

3. 将点赞数据存储在单独表的ArrayField中是否比存在被点赞模型中更好?

并没有本质改善。不管是单独建表存user_id+target_ids数组,还是target_id+user_ids数组,依然逃不开数组扫描的性能问题,还多了一层表关联的开销。而且数据一致性的问题依然存在——你还是要维护用户侧和内容侧的关联,只是换了个存储位置而已。

4. 是否应该继续使用直观的多对多字段方案?

非常建议回到多对多方案(或专门的中间表方案),Django的m2m字段本质就是自动生成中间表,这才是更合理的选择,原因如下:

  • 数据一致性:通过数据库事务和外键约束,能保证点赞关系的完整性,不会出现两边数据不一致的情况;
  • 查询效率:可以给中间表的user_id+target_id(加上内容类型区分)建联合索引,不管是查用户是否点赞某内容、统计内容点赞数,还是查询用户所有点赞内容,都能通过索引快速定位,数据量再大也能保持高效;
  • 扩展性:未来如果要给点赞加额外属性(比如点赞时间、是否取消点赞),中间表可以直接加字段,而ArrayField方案几乎没法扩展;
  • 维护成本:Django的ORM对多对多关系有完善封装,查询、更新操作都很简洁,后期维护比手动维护两个数组轻松太多。

你之前担心的“未来问题”,大多可以通过合理设计中间表和索引避免。比如担心性能就建联合索引;要区分Post和Comment,可以用Django的ContentType框架在中间表里加content_type字段,实现通用的点赞关系,不用给两个模型分别建m2m字段。


内容的提问来源于stack exchange,提问作者MaxCore

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:59:03