news 2026/4/22 10:50:30

从“点赞”到“私信”:手把手设计一个高可用的站内信系统(含数据库表结构)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从“点赞”到“私信”:手把手设计一个高可用的站内信系统(含数据库表结构)

从“点赞”到“私信”:手把手设计一个高可用的站内信系统

当用户在你的平台上点赞了一篇帖子,或是收到一条私信时,如何确保通知能实时、可靠地送达?站内信系统作为用户互动的核心枢纽,直接影响着产品的用户体验和留存率。本文将带你从产品视角出发,设计一套涵盖公告、提醒、私信等多种通知类型的完整解决方案。

1. 站内信系统的核心要素

任何优秀的站内信系统都需要解决三个基本问题:通知分类实时性保障用户体验控制。我们先从最基础的通知类型划分开始。

1.1 通知类型的三层架构

现代社区平台的通知通常分为三个层级:

  1. 系统公告:平台向全体或特定用户群发送的广播消息
  2. 行为触发通知:由用户互动(如点赞、评论)产生的个性化提醒
  3. 私信对话:用户之间的点对点通信

每种类型在存储结构、推送逻辑和用户权限控制上都有显著差异。下面是一个典型的类型对比表:

类型触发方式存储要求实时性要求用户控制权
系统公告管理员手动触发永久存储中等可退订类别
行为通知用户行为自动触发短期存储(30天)可关闭特定类型
私信用户主动发送永久存储极高可屏蔽特定用户

1.2 用户行为的事件化建模

将用户互动抽象为标准化事件是设计灵活通知系统的关键。每个事件应包含以下核心属性:

CREATE TABLE `notify_event` ( `id` bigint NOT NULL AUTO_INCREMENT, `actor_id` bigint NOT NULL COMMENT '触发用户ID', `action` varchar(32) NOT NULL COMMENT '行为类型:like/comment/share', `object_type` varchar(32) NOT NULL COMMENT '对象类型:post/user/comment', `object_id` bigint NOT NULL COMMENT '对象ID', `target_user_id` bigint NOT NULL COMMENT '目标用户ID', `created_at` datetime NOT NULL, PRIMARY KEY (`id`), KEY `idx_target_user` (`target_user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

提示:使用utf8mb4字符集确保支持emoji等特殊符号

2. 实时推送的技术实现

当事件产生后,如何高效地将其转化为用户可见的通知?这需要一套可靠的消息管道。

2.1 消息管道的三层缓冲

为避免高并发下的系统过载,建议采用三级缓冲架构:

  1. 应用层队列:使用RabbitMQ或Kafka接收原始事件
  2. 内存缓存:Redis存储待推送通知
  3. 持久化存储:MySQL最终落库
# 伪代码示例:事件处理流程 def process_event(event): # 1. 写入消息队列 mq.publish('notify_queue', event) # 2. 消费者处理 if user_is_online(event.target_user_id): # 实时推送 ws_server.push(event.target_user_id, render_notification(event)) else: # 存入待推送列表 redis.sadd(f"pending:{event.target_user_id}", event.id) # 3. 持久化存储 db.insert('notifications', user_id=event.target_user_id, content=render_content(event), is_read=False)

2.2 WebSocket的优化实践

保持长连接的稳定性是实时系统的难点,以下是几个关键优化点:

  • 心跳机制:客户端每50秒发送ping帧
  • 断线重连:采用指数退避算法(1s, 2s, 4s...)
  • 多路复用:单个连接承载所有通知类型

前端实现示例:

class NotificationSocket { constructor() { this.reconnectDelay = 1000; this.connect(); } connect() { this.ws = new WebSocket('wss://api.example.com/notify'); this.ws.onopen = () => { this.reconnectDelay = 1000; // 重置重连延迟 this.startHeartbeat(); }; this.ws.onmessage = (event) => { this.handleNotification(JSON.parse(event.data)); }; this.ws.onclose = () => { setTimeout(() => this.connect(), this.reconnectDelay); this.reconnectDelay = Math.min(this.reconnectDelay * 2, 30000); }; } startHeartbeat() { this.heartbeatInterval = setInterval(() => { this.ws.send('ping'); }, 50000); } }

3. 用户偏好与权限控制

不是所有用户都希望收到所有类型的通知,完善的权限体系必不可少。

3.1 通知设置的数据模型

用户应该能够:

  • 全局关闭特定类型的通知
  • 屏蔽特定用户的私信
  • 设置免打扰时段
CREATE TABLE `notification_settings` ( `user_id` bigint NOT NULL, `channel` varchar(32) NOT NULL COMMENT 'push/email/sms', `type` varchar(32) NOT NULL COMMENT 'like/comment/mention', `enabled` tinyint(1) NOT NULL DEFAULT '1', PRIMARY KEY (`user_id`,`channel`,`type`) ); CREATE TABLE `user_blocks` ( `user_id` bigint NOT NULL, `blocked_user_id` bigint NOT NULL, PRIMARY KEY (`user_id`,`blocked_user_id`) );

3.2 权限校验的拦截逻辑

在消息进入管道前进行权限检查:

def should_deliver_notification(sender_id, receiver_id, action): # 检查全局设置 setting = db.get_settings(receiver_id, action) if not setting.enabled: return False # 检查屏蔽关系 if db.exists('user_blocks', user_id=receiver_id, blocked_user_id=sender_id): return False # 检查免打扰时段 if is_quiet_time(receiver_id): return False return True

4. 性能优化与扩展策略

随着用户量增长,系统需要应对新的挑战。

4.1 分库分表策略

通知数据通常按用户ID进行分片:

notifications_0 notifications_1 ... notifications_9

分片路由规则:

public String determineTableShard(long userId) { int shard = (int) (userId % 10); return "notifications_" + shard; }

4.2 冷热数据分离

  • 热数据:最近3天的未读通知,存储在Redis
  • 温数据:30天内的通知,MySQL主库
  • 冷数据:历史归档,MySQL从库或对象存储

4.3 推送降级策略

当系统压力过大时,可依次降级:

  1. 关闭WebSocket实时推送,改为轮询
  2. 合并同类通知(如"3个新点赞")
  3. 延迟非关键通知(如每周摘要)

5. 监控与故障排查

完善的监控体系能帮助快速定位问题。

5.1 关键监控指标

指标报警阈值检查项
WebSocket连接数> 80% 最大负载是否需要扩容
未送达消息堆积量> 10,000消费者是否异常
通知延迟P99 > 3s队列处理能力

5.2 常见问题排查指南

问题:用户反映收不到点赞通知

排查步骤

  1. 检查事件是否生成

    SELECT * FROM notify_event WHERE action='like' AND target_user_id=?;
  2. 检查用户设置

    SELECT * FROM notification_settings WHERE user_id=? AND type='like';
  3. 检查推送日志

    grep "user_id=123" /var/log/push-service.log

问题:WebSocket连接频繁断开

检查项

  • Nginx超时配置

    proxy_read_timeout 600s; proxy_connect_timeout 60s;
  • 客户端心跳是否正常

  • 防火墙会话超时设置

在实际项目中,我们曾遇到因TCP keepalive设置不当导致的连接中断问题。通过调整内核参数解决:

# 调整TCP keepalive参数 sysctl -w net.ipv4.tcp_keepalive_time=300 sysctl -w net.ipv4.tcp_keepalive_intvl=60 sysctl -w net.ipv4.tcp_keepalive_probes=5
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/22 10:49:52

如何通过TTS-Backup彻底解决桌游模拟器数据丢失问题

如何通过TTS-Backup彻底解决桌游模拟器数据丢失问题 【免费下载链接】tts-backup Backup Tabletop Simulator saves and assets into comprehensive Zip files. 项目地址: https://gitcode.com/gh_mirrors/tt/tts-backup Tabletop Simulator(TTS)…

作者头像 李华
网站建设 2026/4/22 10:47:25

游戏设计规划日志

一、底层玩法设计1. 上手极快,深度足够:1分钟学会基础操作,几十小时仍有探索乐趣;兼顾新手与老玩家。2. 剥离重度数值内卷:弱化战力碾压、装备碾压;成长靠探索、收集、解锁、玩法熟练度,不靠氪金…

作者头像 李华
网站建设 2026/4/22 10:42:25

告别网络依赖:手把手教你打包Docker 20.10.7和Compose V2离线安装包(适配麒麟/aarch64)

麒麟系统aarch64架构离线部署Docker全栈指南:从资源封装到企业级实践 在国产化替代浪潮中,麒麟操作系统搭配ARM架构服务器已成为许多关键领域的基础设施选择。当网络访问受限或需要批量部署时,离线安装包的标准化封装能力直接决定了运维效率。…

作者头像 李华
网站建设 2026/4/22 10:40:25

【Solidworks】草图进阶:几何约束、智能编辑与精准标注实战解析

1. 草图几何关系的深度应用 在Solidworks中,几何关系是草图绘制的灵魂所在。很多初学者容易忽视这些看似简单的约束工具,但实际上它们能大幅提升设计效率和准确性。我刚开始用Solidworks时,经常遇到草图变形的问题,后来才发现是没…

作者头像 李华