推送消息存储策略

news2024/10/21 19:18:36

在程序开发中,我们经常碰到要给全部用户或指定用户发送消息,这种场景我们要如何设计呢?

发送消息给所有用户,并且考虑到数据量可能会很大,通常有几种策略可以帮助你更好地管理和存储这些消息数据。以下是一些建议和实现方法: 

1. 消息存储策略

a. 消息表设计

你可以设计一个消息表来存储所有的消息,而不是为每个用户单独存储消息。这样可以避免重复数据,并且方便管理。

CREATE TABLE messages (
    id INT AUTO_INCREMENT PRIMARY KEY,
    content TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    message_type ENUM('public', 'private') DEFAULT 'public' -- 消息类型
);
b. 用户消息关系表

使用一个关系表来存储用户和消息的关系。这样可以让每个用户都能访问到他们接收到的消息。

CREATE TABLE user_messages (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    message_id INT NOT NULL,
    read_status ENUM('unread', 'read') DEFAULT 'unread',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id),
    FOREIGN KEY (message_id) REFERENCES messages(id)
);

 

2. 发送消息给所有用户的逻辑

在发送消息时,你可以将消息插入到 messages 表中,然后为所有用户在 user_messages 表中插入相应的记录。

// app/controller/MessageController.php
public function sendMessageToAll(Request $request)
{
    $content = $request->post('content'); // 消息内容

    // 验证参数
    if (empty($content)) {
        return json(['status' => 'error', 'message' => '消息内容不能为空']);
    }

    // 创建消息记录
    $message = new Message();
    $message->content = $content;
    $message->created_at = date('Y-m-d H:i:s');

    // 保存消息
    if ($message->save()) {
        // 获取消息ID
        $messageId = $message->id;

        // 获取所有用户ID
        $userIds = User::pluck('id')->toArray(); // 假设 User 模型存在

        // 为每个用户插入记录
        foreach ($userIds as $userId) {
            $userMessage = new UserMessage();
            $userMessage->user_id = $userId;
            $userMessage->message_id = $messageId;
            $userMessage->save();
        }

        return json(['status' => 'success', 'message' => '消息发送成功']);
    } else {
        return json(['status' => 'error', 'message' => '消息发送失败']);
    }
}

3. 消息检索与分页

为了避免一次性加载大量消息,可以使用分页查询来获取用户的消息。

public function getMessages(Request $request)
{
    $userId = $request->post('user_id'); // 当前用户ID
    $page = $request->post('page', 1); // 当前页码
    $limit = $request->post('limit', 10); // 每页条数

    // 获取用户接收到的消息
    $messages = UserMessage::with('message') // 假设有定义关系
        ->where('user_id', $userId)
        ->paginate($limit, false, ['page' => $page]);

    return json(['status' => 'success', 'data' => $messages]);
}

 

4. 数据归档与清理

考虑到数据量可能会很大,你可以定期进行数据归档和清理:

  • 归档旧消息:将旧消息转移到归档表中,减少主表数据量。
  • 定期清理:根据业务需求,定期删除过期的消息记录。

5. 使用消息队列

如果消息发送量非常大,可以考虑使用消息队列(如 RabbitMQ、Redis 等)来处理消息的发送。这种方式可以将消息发送的负载分散到多个工作进程中,提高性能。

6. 总结几个凝问

a. 用户消息关系表数据量会不会很大?

是的,用户消息关系表(user_messages)的数据量可能会变得非常庞大,尤其是在以下情况下:

  1. 用户数量多:如果系统中有大量用户,每个用户都接收到多条消息,关系表的条目数量会迅速增加。

  2. 消息发送频率高:如果系统频繁发送消息(例如,通知、公告等),每次发送都会为每个接收用户在关系表中插入一条记录。

  3. 历史消息存储:如果你打算保留所有用户的历史消息记录,关系表将不断增长。

b. 数据归档要如何操作?

数据量大了我们可以归档。当然还有分区表、清理过期数据等等很多方式。在这只介绍数据归档:

数据归档是管理数据库中大量历史数据的一种有效策略。以下是一些常见的归档操作步骤和方法:

1. 设计归档表

首先,你需要设计一个归档表来存储旧数据。这个表的结构通常与原始表相似,但可以根据需要进行调整。

CREATE TABLE archived_user_messages (
    id INT PRIMARY KEY,
    user_id INT NOT NULL,
    message_id INT NOT NULL,
    read_status ENUM('unread', 'read') DEFAULT 'unread',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    archived_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id),
    FOREIGN KEY (message_id) REFERENCES messages(id)
);

 2. 确定归档策略

在归档之前,你需要确定归档的策略,包括:

  • 归档的时间阈值:例如,归档超过 6 个月的消息。
  • 归档的条件:可以根据消息的状态、类型等进行归档。

3. 归档数据

使用 SQL 语句将符合条件的数据从主表中移动到归档表。可以使用 INSERT INTO ... SELECT 语句进行数据复制,然后使用 DELETE 语句删除原始数据。

-- 1. 插入数据到归档表
INSERT INTO archived_user_messages (user_id, message_id, read_status, created_at)
SELECT user_id, message_id, read_status, created_at
FROM user_messages
WHERE created_at < NOW() - INTERVAL 6 MONTH;

-- 2. 删除原始表中的数据
DELETE FROM user_messages
WHERE created_at < NOW() - INTERVAL 6 MONTH;

4. 定期归档

为了保持数据库的性能,建议定期执行归档操作。可以使用以下方法:

  • 定时任务:使用操作系统的定时任务(如 cron)或数据库的调度功能(如 MySQL 的事件调度器)来定期执行归档脚本。

5. 备份归档数据

在归档操作之前,确保对原始数据进行备份,以防止意外数据丢失。可以定期备份归档表的数据。

 c. 归档后,ID还是会越来越大

在数据库中,ID 通常是自增的,归档操作不会影响 ID 的增长,因为每次插入新记录时,ID 会继续递增。因此,即使归档了旧数据,ID 仍然会保持增长。这可能会导致 ID 的使用效率不高,尤其是在数据量非常大的情况下。

解决方案

如果你想控制 ID 的增长,或者想要更有效地管理 ID,可以考虑以下几种方法:

1. 使用 UUID
  • UUID(通用唯一标识符):使用 UUID 作为主键而不是自增整数。UUID 是随机生成的,理论上不会重复,可以避免 ID 的连续增长问题。 
CREATE TABLE user_messages (
    id UUID PRIMARY KEY DEFAULT (UUID()),
    user_id INT NOT NULL,
    message_id INT NOT NULL,
    read_status ENUM('unread', 'read') DEFAULT 'unread',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

 

2. 重置 ID
  • 重置 ID:在某些情况下,可以考虑在归档后重置 ID,但这通常不推荐,因为可能会导致数据一致性问题和外键约束的复杂性。
3. 使用分区表
  • 分区表:将数据分区存储,根据时间或用户 ID 等进行分区。这样可以使每个分区的数据量更小,尽管 ID 仍然会增长,但查询和管理会更高效。
4. 监控和清理
  • 监控 ID 使用情况:定期监控 ID 的使用情况,确保 ID 的增长在可控范围内。如果 ID 过大,可以考虑归档或清理不再需要的数据。
5. 数据库设计优化
  • 优化数据模型:重新审视数据模型,确保只存储必要的数据,避免不必要的 ID 增长。

总结

虽然 ID 的自增特性是数据库设计中的常见做法,但在处理大量数据时,可能需要考虑其他方案来管理 ID 的增长。使用 UUID 或分区表等方法可以帮助有效管理数据,同时保持系统的性能。如果你有特定的需求或问题,请告诉我!

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2220238.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Next.js 项目实战系列】07-分配 Issue 给用户

原文链接 CSDN 的排版/样式可能有问题&#xff0c;去我的博客查看原文系列吧&#xff0c;觉得有用的话&#xff0c;给我的库点个star&#xff0c;关注一下吧 上一篇【Next.js 项目实战系列】06-身份验证 分配 Issue 给用户 本节代码链接 Select Button​ # /app/issues/[i…

【Java】正则表达式详解

目录 引言 一、基本概念 1.1 元字符 1.2 预定义字符类 1.3 边界匹配符 1.4 数量标识符 1.5 捕获与非捕获分组 二、Java中的正则表达式支持 三、正则表达式的使用示例 3.1 匹配字符串 3.2 替换字符串 3.3 分割字符串 3.4 使用Pattern和Matcher 3.5 捕获组和后向…

【电商项目】1分布式基础篇

1 项目简介 1.2 项目架构图 1.2.1 项目微服务架构图 1.2.2 微服务划分图 2 分布式基础概念 3 Linux系统环境搭建 查看网络IP和网关 linux网络环境配置 补充P123&#xff08;修改linux网络设置&开启root密码访问&#xff09; 设置主机名和hosts映射 主机名解析过程分析&…

金九银十互联网大厂Java高频面试题(2024最新含答案)

2024 年的互联网行业竞争越来越严峻&#xff0c;面试也是越来越难&#xff0c;一直以来我都想整理一套完美的面试宝典&#xff0c;奈何难抽出时间&#xff0c;这套 1200道的 Java 面试手册我整理了整整 1 个月&#xff0c;上传到 Git 上目前 star 数达到了 30K 这套互联网 Jav…

冲击美团!已成功 OC

这是一位训练营学员的美团面经&#xff0c;目前已经 OC 。 在此之前他已经拿到了不少公司的offer&#xff0c;但是都达不到他的预期&#xff0c;美团给的待遇就非常不错&#xff0c;大厂不愧是大厂&#xff0c;就是不知道工作强度如何。 他经历了一共三场面试&#xff0c;一面…

实现省略号查看详情样式

1.期望实现效果 2.目前实现效果 3.实现代码 1.wxml<view class"desc-text"><view class"show-more">查看详情 >></view><!-- <rich-text nodes"{{富文本接口数据内容 }}"></rich-text> --><text&…

python反爬

1.无限debug无法f12 关闭掉 Deactivate Breakpoints

【闲谈程序设计例三则:抛弃传统单步进初级阶段,用推导归纳出来的规律写代码,进入进阶阶段,人类自性的高级活动。】2024-10-21

闲谈程序设计三则&#xff1a;抛弃传统单步进&#xff0c;用推导归纳出来的规律写代码。 本论坛常见新学提问都是一些入门级别的问题&#xff0c;近来AI活跃抢答&#xff0c;然而&#xff0c;对于有些问题AI可以说是答非所问&#xff0c;令人哭笑不得&#xff0c;而AI能回答的…

MacOS安装BurpSuite

文章目录 一、下载地址二、下载注册机三、安装教程四、启动burpsuit五、免责声明 一、下载地址 https://portswigger-cdn.net/burp/releases/download?productpro&version2024.7.1&typeMacOsx二、下载注册机 https://github.com/NepoloHebo/BurpSuite-BurpLoaderKey…

B站协议登录到实现各种功能完整代码(专栏总结)

B站协议登录、点赞、收藏、转发实现及代码 关注、动态转发实现动态抽奖实现及代码 直播预约抽奖实现及代码 本文为本专栏的总结文章 一、扫码登录 请求获取二维码包&#xff0c;得到二维码链接和qrcode_key参数之后&#xff0c;利用qrcode_key循环GET请求登录状态包即可&#x…

【word】页眉横线无法取消

小伙伴们日常想在页眉里加横线&#xff0c;直接双击页眉&#xff0c;然后在页眉横线里选择自己喜欢的横线样式就可以了。 但今天我遇到的这个比较奇特&#xff0c;有些页有这个横线&#xff0c;有些页没有&#xff0c;就很奇怪。 最后排查完&#xff0c;发现是只有标题2的页…

WPF入门_02依赖属性

1、依赖属性主要有以下三个优点 1)依赖属性加入了属性变化通知、限制、验证等功能。这样可以使我们更方便地实现应用,同时大大减少了代码量 2)节约内存:在WinForm中,每个UI控件的属性都赋予了初始值,这样每个相同的控件在内存中都会保存一份初始值。而WPF依赖属性很好地…

《向量数据库指南》揭秘:Mlivus Cloud如何赋能GraphRAG应用

嘿,各位向量数据库和AI领域的探索者们,我是你们的老朋友,大禹智库的向量数据库高级研究员王帅旭,也是《向量数据库指南》的作者。今天,咱们来聊聊一个既前沿又实用的话题——检索增强生成(Retrieval Augmented Generation,简称RAG)及其面临的挑战,特别是如何用Mlivus …

前端/node.js锁定依赖版本、锁定依赖的依赖的版本

一、知识前提 version&#xff1a;必须依赖某个具体的版本。如&#xff1a;vue的3.2.0&#xff0c;表示必须安装3.2.0版本。>version&#xff1a;必须大于某个版本。>version&#xff1a;大于或等于某个版本。<version&#xff1a;必须小于某个版本。<version&…

多线程——单例模式

目录 前言 一、设计模式 二、饿汉模式 三、懒汉模式 1.单线程版 2.多线程版 结尾 前言 前面的几篇文章中介绍了多线程编程的基础知识&#xff0c;在本篇文章开始&#xff0c;就会利用前面的多线程编程知识来编写一些代码案例&#xff0c;从而使大家可以更好的理解运用多…

扩散模型对抗蒸馏:ADD 和 Latent-ADD

扩散模型对抗蒸馏&#xff1a;ADD 和 Latent-ADD ADD&#xff08;Adversarial Diffusion Distillation&#xff09;和 Latent-ADD 是 StabilityAI 公司提出的一系列针对 Stable Diffusion 的扩散模型对抗蒸馏方法&#xff0c;通过对抗训练和蒸馏训练来提高扩散模型的采样速度&…

python基于图片内容识别的微信自动发送信息(对其中逻辑修改一些可以改为自动化回复)

1.内容基于python日常生活问题帮助 2.主要框架 import time from datetime import datetimeimport pyperclip import win32api import win32con import os import refrom Image_Content_Text_Recognition import ICTR from screenshot import img 上面是逻辑部分主要框架 i…

【开源免费】基于SpringBoot+Vue.JS在线视频教育平台(JAVA毕业设计)

本文项目编号 T 027 &#xff0c;文末自助获取源码 \color{red}{T027&#xff0c;文末自助获取源码} T027&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 新…

解析 Vue 模板的本质:从语法糖到渲染过程

大家耳熟能详的表述如下&#xff1a;Vue 模板的本质其实是一种 声明式渲染 的形式&#xff0c;它在开发过程中提供了将组件的结构与逻辑分离的便利。 也就是说&#xff0c;模板 template 的存在只是为了让我们以更直观的方式描述界面的结构&#xff0c;然而在运行时&#xff0…

Android Framework AMS(09)service组件分析-3(bindService和unbindService关键流程分析)

该系列文章总纲链接&#xff1a;专题总纲目录 Android Framework 总纲 本章关键点总结 & 说明&#xff1a; 说明&#xff1a;上上一章节主要解读应用层service组件启动的2种方式startService和bindService&#xff0c;以及从APP层到AMS调用之间的打通。上一章节我们关注了s…