Laravel 5.5:百万粉丝场景下的通知与子通知实现咨询
嘿,这个通知方案设计得挺靠谱的——用主通知+子通知的结构避免数据冗余,再配合队列异步处理,完美解决用户发帖子时的批量通知性能问题!我来给你把实现细节拆解清楚:
核心架构思路
咱们的核心逻辑是主通知存公共数据,子通知存用户关联状态:
- 当用户发帖子时,先在
notifications表存一条包含帖子核心数据的记录,不用给每个粉丝都存一遍帖子内容,大幅减少冗余 - 然后给每个粉丝在
subnotifications表建一条关联记录,用read_at字段标记是否已读,同时通过notification_id关联主通知,保证数据一致性 - 全程用队列异步处理,避免阻塞用户发布帖子的请求,提升前端响应速度
数据库表结构设计
先把两张表的字段明确下来,你可以根据自己的业务调整字段类型:
notifications表(主通知表)
CREATE TABLE notifications ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL COMMENT '发布帖子的用户ID', post_data JSON NOT NULL COMMENT '帖子核心数据,比如ID、标题、摘要等', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) );
subnotifications表(子通知表)
CREATE TABLE subnotifications ( id INT PRIMARY KEY AUTO_INCREMENT, notification_id INT NOT NULL COMMENT '关联主通知ID', fan_id INT NOT NULL COMMENT '粉丝用户ID', read_at DATETIME DEFAULT NULL COMMENT '已读时间,NULL表示未读', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (notification_id) REFERENCES notifications(id), FOREIGN KEY (fan_id) REFERENCES users(id), UNIQUE KEY idx_notification_fan (notification_id, fan_id) -- 避免给同一个粉丝重复发同一条通知 );
这里加个唯一索引idx_notification_fan很关键,防止因为队列重试或者其他异常导致给同一个粉丝生成多条重复的子通知。
队列异步处理流程
整个流程要和用户发布帖子的请求解耦,步骤大概是这样:
- 用户在前端点击“发布帖子”,后端先把帖子数据存入
posts表,立刻返回发布成功的响应给用户 - 后端异步触发一个队列任务,把发布帖子的用户ID和帖子ID传入任务
- 队列 worker 执行任务时:
- 查询该用户的粉丝列表(这里你说的是10位,实际按业务逻辑取)
- 插入一条主通知记录到
notifications表 - 批量生成子通知数据,插入到
subnotifications表
- 后续粉丝查看通知时,通过
subnotifications表的read_at字段判断是否已读,点击后更新该字段为当前时间
关键代码示例(以Laravel框架为例)
假设你用Laravel,咱们写一个队列任务类:
namespace App\Jobs; use App\Models\User; use App\Models\Post; use App\Models\Notification; use App\Models\SubNotification; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; class SendPostFanNotifications implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; protected $user; protected $post; // 任务失败重试次数,根据业务调整 public $tries = 3; public function __construct(User $user, Post $post) { $this->user = $user; $this->post = $post; } public function handle() { // 1. 获取当前用户的粉丝列表(这里取10个,实际可根据业务分页/过滤) $fans = $this->user->fans() ->select('id') ->take(10) ->get(); if ($fans->isEmpty()) { return; // 没有粉丝就不用发通知了 } // 2. 创建主通知记录 $notification = Notification::create([ 'user_id' => $this->user->id, 'post_data' => json_encode([ 'post_id' => $this->post->id, 'title' => $this->post->title, 'preview' => mb_substr($this->post->content, 0, 100) // 存个摘要就行,不用全量内容 ]) ]); // 3. 批量生成子通知数据,批量插入提升性能 $subNotificationBatch = $fans->map(function ($fan) use ($notification) { return [ 'notification_id' => $notification->id, 'fan_id' => $fan->id, 'read_at' => null ]; })->toArray(); SubNotification::insert($subNotificationBatch); } }
然后在发布帖子的控制器里触发这个任务:
public function store(Request $request) { // 先验证并保存帖子 $post = auth()->user()->posts()->create($request->validated()); // 异步触发通知任务,不阻塞当前请求 SendPostFanNotifications::dispatch(auth()->user(), $post); return response()->json(['message' => '帖子发布成功'], 201); }
额外优化建议
- 索引优化:给
subnotifications表的fan_id加索引,这样查询某个粉丝的所有通知时会更快;notification_id已经是外键,默认会有索引,但可以确认下 - 大粉丝量处理:如果用户粉丝很多(比如上万),不要一次性全查出来,要分批处理(比如每次处理1000个),避免内存溢出
- 队列监控:配置队列失败监控,比如用Laravel Horizon,能及时发现任务失败的情况,避免通知漏发
- 数据清理:定期清理已读且超过一定时间的旧通知,减少数据库存储压力
内容的提问来源于stack exchange,提问作者Wonka
相关产品推荐
相关产品推荐

