评论与互动系统:评论树、@提及、点赞转发与互动计数一致性

全面讲解微型博客的评论与互动系统:评论树/平铺/热评模型、@提及与回复通知、点赞/转发/收藏/关注等互动类型、互动计数的最终一致性与防并发问题、互动聚合 feed 与消息通知、垃圾评论与互动刷量防护,以及互动数据的分层存储设计。

评论与互动是微型博客的「社交引擎」:没有评论与点赞,短内容只是一块公告板。但互动系统远比表面复杂——评论树怎么存、点赞计数怎么不丢、转发与回复如何触发通知、刷量与垃圾评论如何拦截,每一个问题都牵扯存储、并发与一致性。

本文系统讲解互动系统的五个核心模块:评论模型、互动类型与计数、互动通知、防滥用、分层存储,并给出从零到生产的工程路径。

一、评论模型

1.1 三种评论形态

形态结构适用存储
平铺一维列表简单场景单表 + 时间排序
树形父子嵌套讨论氛围邻接表 / 路径枚举
热评+平铺热评置顶+其余平铺高流量双查询组合

1.2 树形评论的存储

-- 邻接表:parent_id 关联
CREATE TABLE comments (
  id BIGINT PRIMARY KEY,
  post_id BIGINT NOT NULL,
  user_id BIGINT NOT NULL,
  parent_id BIGINT DEFAULT NULL,      -- 回复对象
  root_id BIGINT DEFAULT NULL,        -- 顶层评论 id(用于聚合)
  content TEXT NOT NULL,
  depth INT DEFAULT 0,
  created_at TIMESTAMPTZ DEFAULT now(),
  status SMALLINT DEFAULT 1           -- 1正常 2删除 3审核中
);
CREATE INDEX idx_comments_post ON comments(post_id, root_id, created_at DESC);

1.3 树形查询

-- 查询某帖全部评论(按 root + created_at 排序,聚合成树)
SELECT * FROM comments
WHERE post_id = :post AND status = 1
ORDER BY root_id, created_at;
-- 应用层组装为树;或直接平铺渲染(Flat 展示 + 缩进)

-- 子评论计数
UPDATE posts
SET comment_count = comment_count + 1
WHERE id = :post;

一句话总结:评论模型的核心是「父子关系怎么存 + 怎么排序展示」——邻接表 + root_id 聚合,能在平铺渲染与树形组装之间自由切换。

二、@提及与回复

2.1 提及解析

用户在评论/帖文中输入 @alice
  解析流程:文本扫描 → 匹配 @ + 用户名 → 解析出 user_id 列表
  存储:评论内容保留原文 + 附带 mention 数组
  通知:向被提及者发送通知
// Go 实现 @ 提及解析
func parseMentions(content string, userRepo UserRepo) ([]uint64, error) {
    re := regexp.MustCompile(`@([a-zA-Z0-9_]{2,30})`)
    ids := make([]uint64, 0, 2)
    for _, m := range re.FindAllStringSubmatch(content, -1) {
        uid, err := userRepo.ResolveName(m[1])
        if err == nil {
            ids = append(ids, uid)
        }
    }
    return ids, nil
}

2.2 回复与通知

A 回复 B 的评论
  → 生成评论(parent_id = B 的评论)
  → 向 B 推送「有人回复了你」
  → 若 @ 了 C,同时通知 C
  → 通知聚合:同一帖多次回复可聚合成一条

2.3 热评置顶

热评算法(简化):
  heat = 点赞数 × α + 回复数 × β - 时间衰减 γ
  常见实现:Reddit 式热度 = 支持率与时间的组合
  热评缓存:posts:id:hot_comments(TTL 5 分钟)

一句话总结:@提及把「社交内容」变成「社交网络」,回复通知与热评置顶则是让讨论「被看见」的两个放大器。

三、互动类型与计数

3.1 互动类型

类型语义是否计数是否通知对方
点赞认可是可聚合提醒
转发二次传播是可聚合提醒
收藏自我留存是否
关注关系建立否可提醒
回复讨论是是

3.2 互动计数的并发问题

直接 UPDATE posts SET like_count = like_count + 1 在高并发下有丢失更新风险。解法:

// 方案一:Redis 原子自增 + 异步落库
redis.Incr(ctx, "post:123:like")
// 异步任务每 N 秒把增量写回 PostgreSQL

// 方案二:数据库原子更新(单行 UPDATE 天然原子)
UPDATE posts SET like_count = like_count + 1 WHERE id = :post;

// 方案三:计数与明细分离
// 明细表记录谁点了赞;计数表只存累加值,定期重算

3.3 幂等:防止重复点赞

用户重复点击点赞 → 应只记一次
  解法:明细表 UNIQUE(user_id, post_id)
  点赞 = INSERT ON CONFLICT DO NOTHING
  取消 = DELETE
  计数 = 明细表 count() 或缓存

3.4 计数一致性兜底

高并发下缓存与 DB 可能偏差
  兜底:每日/每小时重算
  SELECT post_id, count(*) FROM like_details GROUP BY post_id
  写回计数表 → 收敛

一句话总结:互动计数的敌人是「并发丢失更新」与「重复操作」——Redis 原子自增扛峰值、明细表 UNIQUE 保幂等、定期重算收敛偏差,三层配合才能稳。

四、互动聚合与通知

4.1 互动 Feed

把「你的帖文被点赞/转发/回复」聚合成一条流
  LikeEvent(post=你的帖, by=张三)
  RepostEvent(post=你的帖, by=李四)
  → 聚合为「张三、李四等 5 人赞了你的帖」
  → 展示在通知中心

4.2 聚合规则

同类型同对象 N 分钟内 → 合并一条
  例:5 分钟内 5 个赞 → 「张三等 5 人赞了你」
跨类型分开 → 各自独立通知条目
  例:赞 1 条 + 回复 1 条 → 两条通知

4.3 通知存储

CREATE TABLE notifications (
  id BIGINT PRIMARY KEY,
  user_id BIGINT NOT NULL,          -- 接收者
  actor_ids BIGINT[] DEFAULT '{}',  -- 触发者(聚合)
  type SMALLINT,                    -- 1赞 2转发 3回复 4@ 5关注
  post_id BIGINT,
  comment_id BIGINT,
  agg_count INT DEFAULT 1,
  is_read BOOLEAN DEFAULT FALSE,
  created_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX idx_notif_user ON notifications(user_id, is_read, created_at DESC);

一句话总结:互动通知的价值在「聚合」——把同对象同类别的 N 条互动折叠成一条有代表性的通知,既减少打扰又保留信息量。

五、防滥用与垃圾互动

5.1 刷量攻击面

- 机器账号批量点赞/转发(刷热度)
- 垃圾评论(广告、引流)
- 恶意 @ 骚扰
- 评论刷屏(低频词重复)

5.2 防护手段

手段机制
速率限制每用户每接口限流(点赞/评论频率)
风控规则新号权重低、异常行为标记
内容过滤关键词 + 模型识别垃圾评论
图算法关联账号集群检测(同一设备/同 IP)
人工/仲裁举报 → 审核 → 处理

5.3 计数防刷

热度计算时剔除可疑互动
  权重化:新号/低信誉账号互动权重 × 0.1
  时间窗:短期爆发式互动降权
  结果:热度分不受刷量明显影响

一句话总结:防滥用的本质是「识别机器与异常」——限流挡频率、风控挡账号、过滤挡内容、权重挡刷量,四层协同才挡得住规模化攻击。

六、分层存储与性能

6.1 互动数据分层

热数据(Redis):
  - 互动计数缓存(like/repost/comment count)
  - 热评列表(posts:id:hot_comments TTL 5min)
  - 用户是否已赞(post:123:liked:{uid})

持久数据(PostgreSQL):
  - 评论明细、点赞明细、转发明细、收藏明细
  - 通知表
  - 计数表(定时重算落库)

冷数据(归档):
  - 超过 N 月的互动明细 → 分区/归档

6.2 高并发写入路径

点赞请求
  → Redis INCR 计数(毫秒级响应)
  → 明细写入队列(异步落库)
  → 通知聚合(异步)
  → 定时任务重算计数并落库

6.3 读写分离

读取评论/互动列表 → 走只读副本
写入互动 → 走主库
计数展示 → 走 Redis 缓存,可短暂延迟

一句话总结:互动系统是典型的「读多写少 + 强展示弱一致」场景——Redis 扛读与瞬时计数、队列削峰异步落库、重算收敛,分层之后并发不再是瓶颈。

七、总结

微型博客互动系统的建设路径可以概括为:先建模、再计数、后聚合、终防护。评论模型用「邻接表 + root_id」支撑树形与平铺双展示;互动计数用「Redis 自增 + 明细 UNIQUE + 定期重算」保证不丢不重;通知用聚合规则把打扰降到最低;防滥用用限流、风控、过滤、权重四层挡住刷量。

互动是微型博客的「生命力」——每一次点赞让创作者得到正反馈,每一次回复让讨论形成社区,每一次转发让内容流动起来。把互动系统做扎实,短内容平台才真正「活」起来。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「miniblog」更多文章

  1. 速率限制与防滥用:令牌桶、滑动窗口与分布式限流
  2. 通知系统:通知类型、聚合去重、多端同步与推送架构
  3. 用户鉴权与会话:密码哈希、JWT vs Session、OAuth2 与刷新令牌