设计支持 50 万 QPS 的站内未读消息系统

news2024/9/23 18:26:10
引言

在现代互联网应用中,站内消息系统是许多平台不可或缺的功能之一,尤其是对于社交网络、电商、金融等需要大量用户交互的系统来说,消息通知功能更是关键。在高并发场景下,一个设计良好的消息系统不仅需要处理大量用户的未读消息,还需要保障消息的及时性和准确性。当我们设计一个需要处理每秒 50 万次查询(QPS)的站内未读消息系统时,必须对系统的架构、存储、缓存、并发控制等多方面进行优化,确保系统能够在高负载下平稳运行。

本文将详细讨论如何设计一个高并发、高可用、低延迟的站内未读消息系统,并通过一系列优化手段确保系统能够支撑 50 万 QPS 的查询需求。文章将结合实际开发中的技术手段,给出架构设计、存储方案、缓存方案、消息队列、并发控制等具体的技术实现和优化策略。


第一部分:系统架构设计

在设计一个高并发的站内未读消息系统时,系统的架构设计是关键。一个合理的架构能够在极高的并发场景下保持系统的稳定性,并确保用户的查询请求得到快速响应。

1.1 架构总览

支持 50 万 QPS 的站内消息系统需要实现以下功能:

  • 高并发处理:系统能够同时处理数十万用户的未读消息查询请求。
  • 消息实时性:用户可以及时接收到站内消息。
  • 消息一致性:保证用户未读消息的准确性,防止消息遗漏或重复。
  • 高可用性:系统在宕机或故障时能够快速恢复。
  • 水平扩展能力:能够根据用户量的增长进行横向扩展。

在高并发场景下,系统通常采用分布式架构,将消息存储、消息队列、缓存、服务分层等功能分布在不同的节点上,以减少单点故障,提高系统的扩展性。

1.2 架构组件
  • 消息存储层:负责存储用户的站内消息以及未读消息的状态。可以选择高性能的 NoSQL 数据库,如 Redis、Cassandra,或者支持分布式存储的关系型数据库,如 MySQL 集群。
  • 缓存层:为了减少数据库的查询压力,未读消息信息会被缓存到 Redis 等缓存中,缓存层是整个系统优化的重要环节。
  • 消息队列:通过使用消息队列(如 Kafka、RabbitMQ),消息的推送和消费可以解耦,提升系统的吞吐量。
  • 负载均衡与 API 网关:通过负载均衡器将用户请求分发到不同的服务实例,API 网关负责请求的路由和认证。
  • 用户通知服务:负责处理未读消息的逻辑,计算并返回用户未读消息数量。
  • 定时任务与批量更新服务:负责清理过期消息、定时同步缓存和数据库中的消息数据,确保数据的一致性。

第二部分:消息存储方案

2.1 数据库的选择

在设计支持 50 万 QPS 的系统时,选择合适的数据库是确保系统性能的关键。常见的数据库类型包括:

  • 关系型数据库(如 MySQL):具有强一致性和事务支持,但在高并发场景下性能可能不够理想。
  • NoSQL 数据库(如 Redis、Cassandra、HBase):更适合高并发、高吞吐量的场景。
2.2 数据分区与分库分表

为了应对海量用户消息的存储需求,必须对数据库进行分区处理。常见的分库分表策略有:

  1. 用户 ID 分片:通过用户 ID 的哈希值对数据进行分片,不同用户的消息会存储在不同的数据库或表中。

  2. 消息时间分片:根据消息的创建时间,将不同时间段的消息存储在不同的表中,减少单表的数据量,提升查询性能。

MySQL 分表示例:

CREATE TABLE user_messages_202301 (
    user_id BIGINT,
    message_id BIGINT,
    message_content TEXT,
    create_time TIMESTAMP,
    read_status TINYINT,
    PRIMARY KEY (user_id, message_id)
) PARTITION BY HASH(user_id) PARTITIONS 10;

在这个示例中,用户消息表 user_messages_202301 按照用户 ID 进行了哈希分片,数据分散到多个分区中。

2.3 Redis 作为未读消息存储方案

Redis 是一个高性能的内存数据库,适合存储高频访问的未读消息数据。我们可以使用 Redis 的数据结构(如 HASHSETZSET)来存储用户的未读消息。

Redis 存储用户未读消息示例:

# 使用 Redis HASH 存储用户的未读消息数
HSET user:10001 unread_messages 5

每个用户的未读消息数量存储在 Redis 中的一个 HASH 表中,user:10001 是用户的标识,unread_messages 是该用户的未读消息数量。


第三部分:缓存方案

3.1 缓存的重要性

为了应对高并发请求,减少对数据库的直接查询,缓存层是必不可少的。缓存可以显著减少数据库的查询压力,提高系统的响应速度。Redis 和 Memcached 是两种常用的缓存系统。

3.2 缓存设计策略
  1. 缓存用户未读消息数量:用户未读消息的数量是最常查询的数据,我们可以将其缓存在 Redis 中,减少数据库查询。

  2. 缓存过期策略:为了保持缓存和数据库的一致性,可以设置缓存的过期时间。当缓存过期时,重新从数据库加载数据并更新缓存。

  3. 主动更新策略:当用户的未读消息状态发生变化时(例如用户阅读了消息),主动更新缓存,而不是等待缓存过期。

3.3 缓存穿透、缓存击穿、缓存雪崩的应对

在高并发场景下,缓存可能面临一些问题:

  • 缓存穿透:用户查询不存在的数据,导致请求直接打到数据库。可以使用布隆过滤器来解决。
  • 缓存击穿:某些热点数据在缓存过期后,大量请求同时查询数据库。可以使用分布式锁来防止并发访问。
  • 缓存雪崩:大量缓存同时失效,导致数据库压力骤增。可以设置不同的过期时间,避免集中失效。
3.4 Redis 缓存方案示例
@Service
public class UnreadMessageService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // 查询用户未读消息数
    public int getUnreadMessageCount(Long userId) {
        String cacheKey = "user:" + userId + ":unread_messages";
        Integer unreadCount = (Integer) redisTemplate.opsForValue().get(cacheKey);
        if (unreadCount == null) {
            // 缓存不存在,从数据库加载
            unreadCount = queryUnreadMessageCountFromDB(userId);
            redisTemplate.opsForValue().set(cacheKey, unreadCount, 1, TimeUnit.HOURS);
        }
        return unreadCount;
    }

    // 模拟从数据库查询未读消息数
    private int queryUnreadMessageCountFromDB(Long userId) {
        // 查询数据库逻辑
        return 5;
    }
}

在这个示例中,我们使用 Redis 缓存用户的未读消息数,如果缓存中没有数据,则从数据库加载并更新缓存。


第四部分:消息队列与异步处理

4.1 消息队列的作用

在高并发系统中,消息队列可以帮助解耦消息生产和消息消费,减少数据库的直接压力。通过异步处理,系统可以在后台处理消息的存储和推送,保证前端用户请求的及时响应。

4.2 Kafka 在站内消息系统中的应用

Kafka 是一种高吞吐量的分布式消息队列系统,适合处理大规模的消息流。在未读消息系统中,我们可以使用 Kafka 进行消息的异步处理,例如:

  • 当用户收到新消息时,系统将消息存入 Kafka 队列。
  • 消费者从 Kafka 中读取消息并进行处理(如存储、通知推送等)。

Kafka 生产者示例:

public class MessageProducer {

    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;

    public void sendMessage(String userId, String message) {
        kafkaTemplate.send("user-messages", userId, message);
    }
}

Kafka 消费者示例:

@

KafkaListener(topics = "user-messages", groupId = "message_group")
public void listen(ConsumerRecord<String, String> record) {
    String userId = record.key();
    String message = record.value();
    // 处理消息,例如存储到数据库或推送给用户
}

通过 Kafka,将消息的生产和消费分开处理,减少系统的同步压力,提升系统的响应速度。

4.3 异步处理的优势

通过异步处理,系统可以在高并发情况下平滑应对大规模的消息流,不会因为前端的消息请求而阻塞数据库或缓存系统。Kafka、RabbitMQ 等消息队列都可以作为异步处理的基础设施。


第五部分:并发控制与限流

5.1 高并发下的限流策略

在高并发场景下,如果不加以控制,瞬时大量请求可能会压垮系统。因此,需要设计限流机制来保护系统。常见的限流方式有:

  • 漏桶算法:通过固定速率处理请求,避免突发请求过多。
  • 令牌桶算法:允许突发流量,但控制请求的速率。
5.2 限流与熔断器设计

为了确保系统在高并发下的稳定性,可以引入熔断器和限流器。熔断器的作用是在系统压力过大或某个服务异常时,快速返回失败,避免进一步加重系统负担。

使用 Resilience4j 实现限流器和熔断器:

public class MessageService {

    private final RateLimiter rateLimiter = RateLimiter.ofDefaults("messageRateLimiter");
    private final CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("messageCircuitBreaker");

    public String getMessages(String userId) {
        return RateLimiter.decorateSupplier(rateLimiter, () -> {
            return CircuitBreaker.decorateSupplier(circuitBreaker, () -> {
                // 查询未读消息的逻辑
                return "User messages";
            }).get();
        }).get();
    }
}

在这个示例中,我们使用 Resilience4j 实现了限流器和熔断器,确保系统在高并发下能够平稳运行。


第六部分:定时任务与批量更新

6.1 定时任务的作用

为了保持缓存与数据库数据的一致性,以及清理过期的消息数据,我们可以通过定时任务来批量更新消息状态。通过批量处理,可以减少对数据库的频繁更新操作,提升系统的整体性能。

6.2 使用 Quartz 实现定时任务
@Component
public class MessageCleanupTask {

    @Scheduled(cron = "0 0 * * * ?")
    public void cleanUpExpiredMessages() {
        // 清理过期消息的逻辑
        System.out.println("Running message cleanup task...");
    }
}

定时任务可以定期清理用户的过期消息,或者同步数据库与缓存中的未读消息状态,确保系统数据的一致性和准确性。


第七部分:监控与故障恢复

7.1 监控系统的重要性

对于高并发系统,实时监控系统的健康状况至关重要。通过监控系统的 QPS、数据库查询时间、缓存命中率、消息队列积压情况等指标,可以及时发现问题并进行优化。

7.2 使用 Prometheus 和 Grafana 进行监控

Prometheus 是一个开源的监控工具,可以对系统的各项指标进行采集。通过 Grafana 展示监控数据,运维人员可以及时发现系统中的瓶颈并进行调整。


结论

设计一个支持 50 万 QPS 的站内未读消息系统需要从系统架构、消息存储、缓存设计、并发控制、消息队列、定时任务、监控等多个方面进行优化。通过合理设计数据存储结构,使用缓存减少数据库压力,使用消息队列进行异步处理,并引入限流和熔断器来保证系统的稳定性,可以有效提高系统的并发处理能力。

在实际的项目中,开发人员需要根据具体的业务需求和系统特性,灵活选择和组合这些优化策略,以确保系统在高并发下的性能和可靠性。

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

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

相关文章

医院信息化运维监控:确保医疗系统的稳定与安全

在当今数字化时代&#xff0c;医院的信息化水平直接关系到医疗服务的效率和质量。随着医疗信息化的不断推进&#xff0c;医院对信息化运维监控的需求也日益增强。特别是IT软硬件资源监控和机房动环监控&#xff0c;它们在保障医院信息系统稳定运行中发挥着至关重要的作用。 首先…

在windows上使用vs code调试Rust程序

视频参考&#xff1a;https://www.youtube.com/watch?vTlfGs7ExC0A 前置条件 需要安装的软件&#xff1a; rustvs codeMinGW 或者其它能在 Windows 平台上运行 gdb、gcc 和 g 的软件。 需要安装的插件&#xff1a; rust-analyzer CodeLLDB 然后&#xff0c;在 vs code 中…

iPhone锁屏密码忘了怎么解锁?轻松解锁攻略来了

在日常生活中&#xff0c;智能手机已成为我们不可或缺的伙伴。其中&#xff0c;iPhone以其出色的性能和优雅的设计&#xff0c;赢得了全球用户的喜爱。然而&#xff0c;即便是最忠实的iPhone用户&#xff0c;也可能会遇到一些棘手的问题&#xff0c;比如忘记了锁屏密码。面对这…

韦唯湾区升明月演绎经典 震撼演绎传递爱与力量

9月22日晚&#xff0c;《“湾区升明月”2024大湾区电影音乐晚会》为全球观众送上了一场融合电影艺术与音乐魅力的艺术狂欢&#xff0c;晚会中不光有对不同代际经典影视金曲、经典歌曲的全新演绎&#xff0c;更是汇集了来自海峡两岸暨港澳地区的电影人、音乐人、奥运健儿代表。中…

【AI画图】stable-diffusion-webui学习之一《安装部署》

简介 Stable Diffusion是2022年发布的深度学习文本到图像生成模型&#xff0c;它是一种潜在扩散模型&#xff0c;它由创业公司Stability AI与多个学术研究者和非营利组织合作开发。目前的SD的源代码和模型都已经开源&#xff0c;在Github上由AUTOMATIC1111维护了一个完整的项目…

git报错:无法读取远程分支 ‘origin‘ does not appear to be a git repository

问题分析 push上传的时候本地分支和远程分支断开连接 所以重新链接即可 排查问题 1. 查看是否有分支&#xff0c;检查分支是否正确 git branch -v 2. 查看连接是否断开&#xff0c;断开无内容展示 查看远程仓库详细信息&#xff0c;可看到仓库地址 git remote -v 解决…

k8s中pod的创建过程和阶段状态

管理k8s集群 kubectl k8s中有两种用户 一种是登录的 一种是/sbin/nologin linux可以用密码登录&#xff0c;也可以用证书登录 k8s只能用证书登录 谁拿到这个证书&#xff0c;谁就可以管理集群 在k8s中&#xff0c;所有节点都被网络组件calico设置了路由和通信 所以pod的ip是可以…

如何在拥挤的应用商店中脱颖而出

在现在移动应用程序已成为我们日常生活中必不可少的一部分&#xff0c;从通信和娱乐到财务和健康管理&#xff0c;为各种问题提供解决方案。然而&#xff0c;Google Play 和 App Store 等应用商店的竞争每年都变得更加激烈。数以百万计的应用程序争夺用户的注意力&#xff0c;仅…

车间里应用到了不同品牌的PLC,这还能进行无线通讯吗?

自第一台PLC在GM公司汽车生产线上首次应用成功以来&#xff0c;PLC凭借其方便性、可靠性以及低廉的价格得到了广泛的应用。在现代化工厂中&#xff0c;除厂级PLC系统外&#xff0c;还存在很多独立的子系统。比如&#xff0c;各个生产车间的PLC系统、或同一生产车间的不同生产流…

长方形+ 下三角形的图形 css

<div class"transform">42.48%</div>//转化.transform {position: relative;width: 70px;height: 26px;background-color: #dcdfe6; /* 长方形的颜色 */display: flex;justify-content: center;align-items: center;font-family: PingFangTC-Medium;font…

安装Java(配置Windows环境)( ̄︶ ̄)↗

一、下载Java安装包 1.1、首先&#xff0c;需要访问Oracle的官方网站&#xff08;Oracle官网&#xff09;&#xff0c;在网站上找到Java SE&#xff08;标准版&#xff09;的下载页面。 1.2、根据自己的操作系统和需求选择合适的JDK&#xff08;Java Development Kit&#xf…

软件功能测试需进行哪些测试?第三方软件测评机构有哪些测试方法?

在信息化社会迅速发展的今天&#xff0c;软件功能测试在软件开发生命周期中占据着不可或缺的地位。软件功能测试是评估软件系统是否符合预期功能和用户需求的过程。其重要性体现在提升软件质量、确保用户满意度以及降低维护成本等方面。 软件功能测试是对软件应用程序进行的一…

828华为云征文 | 云服务器Flexus X实例,搭建GitLab远程仓库

828华为云征文 | 云服务器Flexus X实例&#xff0c;搭建GitLab远程仓库 搭建GitLab 1、购买华为云 Flexus X 实例 Flexus云服务器X实例-华为云 (huaweicloud.com) 2、docker安装 yum install -y docker-ce3、验证 Docker 是否安装成功&#xff0c;启动docker服务并设置开机自启…

开关柜设备红外检测数据集

开关柜设备红外检测数据集 包含以下2个数据文件&#xff1a; /train&#xff1a;训练集 /valid&#xff1a;验证集 /test&#xff1a;测试集 README.txt&#xff1a;数据说明 【数据说明】检测目标以Pascal VOC格式进行标注&#xff0c;对每个图像进行以下预处理&#xff0c;统…

国内比较有名的国学大师改名字专家颜廷利:量子纠缠与良知救善

国内比较有名的国学大师改名字专家颜廷利&#xff1a;量子纠缠与良知救善 在探索宇宙的奥秘和人类行为的伦理之间&#xff0c;存在着一种看似无形却实际牢不可破的联系。这种联系&#xff0c;在科学的视角下&#xff0c;可被视作量子纠缠的一种哲学体现&#xff1b;在人文的探…

【机器学习】TensorFlow编程基础

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 TensorFlow编程基础张量(Tensor)计算图(Computational Graph)会话(Session)基本…

解决Echarts:宽度100%,渲染的宽度却是100px

为什么我们宽度设置了100%&#xff0c;结果变为了100px&#xff1f; 源码这里没有获取到clientWidth&#xff0c;会将设置的width:100%转换称100px 解决办法&#xff1a; <div ref"numberPieRef"></div>let numberPieRef ref(null); let myChart nu…

鸿蒙OpenHarmony【轻量系统内核(异常调测)】子系统开发

异常调测 基本概念 OpenHarmony LiteOS-M提供异常接管调测手段&#xff0c;帮助开发者定位分析问题。异常接管是操作系统对运行期间发生的异常情况进行处理的一系列动作&#xff0c;例如打印异常发生时异常类型、发生异常时的系统状态、当前函数的调用栈信息、CPU现场信息、任…

SD教程:一键将真人照片转成插画风格头像,秒变二次元动漫主角~

大家好&#xff0c;我是灵魂画师向阳 如今AI技术日益成熟&#xff0c;今天给大家分享一个用AI绘画工具StableDiffusion制作真实头像转插画的教程&#xff0c;废话不多说&#xff0c;上操作。 本期教程我们将以SD为作图工具&#xff0c;如果你还没有安装使用过AI绘画工具Stable…

非root安装Augustus报错解决

git clone https://github.com/Gaius-Augustus/Augustus.git cd Augustus make augustus报错&#xff1a; 解决&#xff1a; wget -O boost_1_76_0.tar.gz https://sourceforge.net/projects/boost/files/boost/1.76.0/boost_1_76_0.tar.gz/downloadtar xzf boost_1_76_0.tar.…