【代码笔记】高并发场景下问题解决思路

高并发指的是在单位时间内,瞬时流量激增,系统需要同时处理大量并行的请求或操作。这种情况通常出现在面向大量用户或服务的分布式系统中,尤其是当用户请求高度集中时,比如促销活动、秒杀活动、注册抢课、热点事件、定时任务调度等。

在高并发发生时,系统可能存在以下问题:
1.系统性能维度

  • 性能瓶颈:高并发可能导致系统资源(如CPU、内存、磁盘I/O、网络带宽)达到瓶颈,影响整体性能。
  • 响应延迟:系统处理请求的响应时间可能因并发量增加而延长,影响用户体验。
  • 系统过载:超出系统设计容量的并发请求可能导致系统过载,甚至宕机。
  • 容错性差:在高并发下,系统的容错性受到考验,单点故障可能导致整个服务不可用。

2.用户行为维度

  • 不可预测性:用户行为在高并发期间可能变得难以预测,导致难以准确评估系统负载。
  • 用户操作冲突:大量用户同时进行操作可能导致冲突,如抢票、抢单等场景。
  • 用户体验下降:由于系统响应变慢,用户体验可能显著下降。

3.数据处理维度

  • 数据不一致:在高并发写入时,缺乏合适的锁机制可能导致数据不一致。
  • 事务管理困难:高并发环境下,保持事务的ACID属性变得更加困难。
  • 数据库压力:高并发可能导致数据库连接数过多,查询和事务处理速度下降。

二.策略

为了应对高并发带来的压力,在高并发场景下,系统设计和优化可以从以下几个维度进行调整:

1. 架构设计维度

  • 服务拆分:将单体应用拆分成多个微服务,实现服务的独立扩展和维护。
  • 负载均衡:使用硬件或软件负载均衡器,如Nginx或HAProxy,分配网络流量和请求。
  • 无状态设计:确保应用服务器无状态,可以水平扩展。无状态设计是构建可伸缩、高可用系统的重要原则,特别是在高并发场景下。在无状态设计中,服务器不会存储任何关于客户端请求的信息,每个请求都是独立的,不依赖于之前的任何请求。

2. 数据库与存储优化维度

  • 数据库优化:对数据库进行定期的维护,如优化索引、更新统计信息。
  • 缓存应用:使用缓存减少数据库访问,如Redis进行热点数据缓存。
  • 存储优化:使用SSD代替HDD,提高I/O效率;考虑使用分布式存储系统。

3. 缓存策略维度

  • 多级缓存:实现应用层、服务层和数据库层的多级缓存机制。
  • 缓存淘汰策略:合理配置缓存淘汰策略,如LRU(最近最少使用)。
  • 热点数据优化:对频繁访问的数据进行特殊缓存处理。

4. 代码与应用优化维度

  • 异步处理:将非实时性的任务异步化,使用消息队列如Kafka或RabbitMQ。
  • 代码审查:定期进行代码审查,优化代码逻辑和结构。
  • 资源池:使用线程池、数据库连接池等资源池技术,提高资源利用效率。

5. 运维与监控维度

  • 实时监控:部署实时监控系统,如Prometheus或Zabbix,监控系统性能指标。
  • 日志管理:集中管理日志,使用ELK(Elasticsearch, Logstash, Kibana)堆栈进行日志分析。
  • 自动扩缩容:结合云服务提供的自动扩缩容功能,根据流量自动调整资源。

通过上述维度的策略实施,可以显著提升系统在高并发环境下的性能和稳定性。然而,每个系统的具体场景和需求都有所不同,因此在实施优化时需要根据实际情况进行定制化的调整。

三.例子

在大学抢课场景,课程的人数限制为30个学生,系统面临的主要问题包括:

  1. 高并发处理:在抢课开始时,系统可能会收到大量并发请求,需要设计以承受这种瞬时流量。
  2. 数据一致性:确保在高并发下,课程的选课名额不会超卖。
  3. 公平性:确保所有学生在抢课开始时都有机会选到课程。
  4. 系统稳定性:在高负载下,系统需要保持稳定,避免宕机。

领域模型:

  • Course:代表一门课程,包含课程ID、课程名称、剩余名额等属性。
  • Student:代表学生,包含学生ID、姓名等属性。
  • Enrollment:代表选课记录,包含学生ID、课程ID、选课时间等信息。

实现逻辑:

  1. 初始化课程:在系统中预先定义好每门课程的信息,包括课程ID、课程名称、容量等。
  2. 发布课程:将课程信息发布到选课系统中,学生可以查看到可选用课的列表。
  3. 学生选课:学生发送选课请求到系统。
  4. 获取分布式锁:系统尝试获取对应课程的分布式锁,确保同时只有一个请求能修改名额。
  5. 检查名额:检查Redis中该课程的剩余名额是否大于0。
  6. 更新名额:如果名额足够,更新Redis中该课程的剩余名额,并记录选课信息到数据库。
  7. 释放锁:完成名额更新后,释放分布式锁。
  8. 返回结果:向学生返回选课成功或失败的结果。

Demo

以下是使用Spring Boot和Redis实现大学抢课逻辑的示例代码

CourseController.java - REST 控制器用于处理课程注册请求:

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/courses") // 定义API的基础路由
public class CourseController {

    @Autowired
    private CourseService courseService; // 注入课程服务类

    @PostMapping("/{courseId}/enroll") // 定义POST请求,用于抢课操作
    public ResponseEntity<?> enrollStudent(@PathVariable("courseId") String courseId, // 课程ID作为路径参数
                                           @RequestParam("studentId") String studentId) { // 学生ID作为请求参数
        boolean result = courseService.enroll(courseId, studentId); // 调用服务类的方法进行抢课
        if (result) {
            return ResponseEntity.ok("Enrollment successful!"); // 如果成功,返回成功响应
        } else {
            return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body("Course is full."); // 如果失败,返回服务不可用响应
        }
    }
}

CourseService.java - 服务类使用Redis进行分布式锁控制:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;

@Service
public class CourseService {

    @Autowired
    private StringRedisTemplate redisTemplate; // 注入Redis字符串模板类
    @Autowired
    private EnrollmentRepository enrollmentRepository; // 注入选课记录的持久层接口

    private static final String LOCK_SCRIPT = // 定义Lua脚本用于获取锁
        "if redis.call('set', KEYS[1], ARGV[1], 'NX', 'PX', ARGV[2]) == 1 then return 1 else return 0 end";

    public boolean enroll(String courseId, String studentId) {
        String lockKey = "course:" + courseId + ":lock"; // 定义锁的key
        String studentKey = "course:" + courseId + ":student:" + studentId; // 定义学生的key

        // 使用Redis的Lua脚本原子地尝试获取锁,使用随机值和1000ms超时
        Boolean acquiredLock = redisTemplate.execute(new DefaultRedisScript(LOCK_SCRIPT),
                                                        Collections.singletonList(lockKey),
                                                        studentId,
                                                        String.valueOf(1000L));

        if (Boolean.TRUE.equals(acquiredLock)) {
            try {
                // 检查学生是否已经选过这门课程
                if (redisTemplate.opsForSet().isMember(studentKey, studentId)) {
                    return false;
                }

                // 检查剩余座位数
                Integer remainingSeats = redisTemplate.opsForValue().increment("course:" + courseId + ":seats", -1);
                if (remainingSeats >= 0) {
                    // 选课成功,将学生添加到选课集合中
                    redisTemplate.opsForSet().add(studentKey, studentId);
                    // 保存选课记录
                    Enrollment enrollment = new Enrollment(studentId, courseId);
                    enrollmentRepository.save(enrollment);
                    return true;
                } else {
                    // 恢复座位数,因为课程已满
                    redisTemplate.opsForValue().increment("course:" + courseId + ":seats", 1);
                    return false;
                }
            } finally {
                // 总是在finally块中释放锁,以防止锁泄露
                redisTemplate.delete(lockKey);
            }
        } else {
            return false;
        }
    }
}

EnrollmentRepository.java - 持久层接口用于管理选课记录:

import org.springframework.data.jpa.repository.JpaRepository;

public interface EnrollmentRepository extends JpaRepository<Enrollment, Long> {
    // JPA/JDBC方法用于管理选课记录
}

CourseService中,我们使用Lua脚本来尝试获取课程的锁。如果锁被成功获取(acquiredLocktrue),我们进一步检查学生是否已经选过这门课程。如果没有,我们减少座位数,并且如果座位仍然可用,我们将学生添加到选课集合中并保存选课记录。如果课程已满或者学生已经选过这门课程,我们释放锁并返回false

请注意,上述代码知识一个思路演示,在生产系统中,还需要处理各种边缘情况和潜在的异常。可能还需要适当配置StringRedisTemplateEnrollmentRepository,包括在Spring Boot应用程序中设置必要的依赖和注解。
此外,用于锁定和跟踪学生请求的Redis键需要精心设计,以避免冲突,并确保它们可以被轻松管理和在不再使用后清理。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AScript纯本地离线文字识别插件

目的 AScript是一款可以模拟鼠标和键盘操作的自动化工具。它可以帮助用户自动完成一些重复的、繁琐的任务&#xff0c;节省大量人工操作的时间。但按键精灵是不包含图色功能&#xff0c;无法识别屏幕上的图像&#xff0c;根据图像的变化自动执行相应的操作。本篇文章主要讲解下…

15 华三华为链路聚合综述

1 链路聚合简介 以太网链路聚合通过将多条以太网物理链路捆绑在一起形成一条以太网逻辑链路&#xff0c;实现增加链路带宽的目的&#xff0c;同时这些捆绑在一起的链路通过相互动态备份&#xff0c;可以有效地提高链路的可靠性。 2 成员端口的状态 聚合组内的成员端口具有以下…

深入理解Docker容器镜像

深入理解Docker容器镜像 1 容器是什么&#xff1a;特殊的进程 容器其实是一种沙盒技术。顾名思义&#xff0c;沙盒就是能够像一个集装箱一样&#xff0c;把你的应用“装”起来的技术。这样&#xff0c;应用与应用之间&#xff0c;就因为有了边界而不至于相互干扰&#xff1b;而…

聊天室项目思路

发起群聊&#xff1a; 从好友表选取人发送到服务器&#xff0c;服务器随机生成不重复的群号&#xff0c;存储在数据库&#xff0c;同时建立中间表&#xff0c;处理用户与群聊的关系 申请入群&#xff1a; 输入群号&#xff0c;发消息给服务器&#xff0c;服务器查询是否存在…

如何使用openEuler 22.03 配置mail.rc给邮箱发送邮件

目录 需求环境总体步骤梳理详细步骤1. 安装mailx软件包&#xff08;centos默认安装&#xff0c;openEuler不默认安装&#xff09;2. 检查是否能ping得到smtp服务器3. 在qq邮箱开启smtp设置4. 修改/etc/mail.rc文件5. 测试 可能遇到的问题 需求 希望检查每日的备份和系统运行记…

[MRCTF2020]Ez_bypass1 and [网鼎杯 2020 青龙组]AreUSerialz1()php语言基础学习,以及序列化概念的基本了解

1.[MRCTF2020]Ez_bypass1 &#xff08;1&#xff09;打开环境后它是一串很长并且看起来非常混乱的代码&#xff0c;看不懂那咱就先不管&#xff0c;直接查看源码 &#xff08;2&#xff09;看了之后可以发现它涉及到很多东西 首先就是要进行一个仔细的代码审计&#xff0c;分…

代码随想录算法训练营第六十三天|84.柱状图中最大的矩形

代码随想录算法训练营第六十三天|84.柱状图中最大的矩形 84.柱状图中最大的矩形 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大面积。 示例 1: 输入&…

调试代码问题汇总

1.最常见的就是数据库密码不对。根据调试视频将你的数据库密码设置正确&#xff0c;数据库密码是数字的优先直接连如果不成功可以加个双引号或者单引号。 提示&#xff1a;java.sql.SQLException: Access denied for user rootlocalhost (using password: YES) 2.原本配置好的…

我觉得POC应该贴近实际

今天我看到一位老师给我一份测试数据。 这是三个国产数据库。算是分布式的。其中有两个和我比较熟悉&#xff0c;但是这个数据看上去并不好。看上去第一个黄色的数据库数据是这里最好的了。但是即使如此&#xff0c;我相信大部分做数据库的人都知道。MySQL和PostgreSQL平时拿出…

【算法基础实验】排序-最小优先队列MinPQ

优先队列 理论知识 MinPQ&#xff08;最小优先队列&#xff09;是一种常见的数据结构&#xff0c;用于有效管理一组元素&#xff0c;其中最小元素可以快速被检索和删除。这种数据结构广泛应用于各种算法中&#xff0c;包括图算法&#xff08;如 Dijkstra 的最短路径算法和 Pr…

RISCV 外部GCC 工具链安装@FreeBSD15

在交叉编译的时候&#xff0c;可以使用FreeBSD15默认的工具链&#xff1a;LLVM 也可以使用GCC工具链&#xff0c;GCC可以使用现成pkg包安装&#xff0c;也可以编译安装。 LLVM的特点是高移植性和高效&#xff0c;但学习成本高。GCC的特点是成熟稳定&#xff0c;但优化能力有限…

【系统架构师】-案例篇(五)企业应用系统集成与ESB

在航空业中&#xff0c;Ramp Coordination是指飞机从降落到起飞过程中所需要进行的各种业务活动的协调过程。通常每个航班都有一位员工负责Ramp Coordination&#xff0c;称之为RampCoordinator。由Ramp Coordinator协调的业务活动包括检查机位环境、卸货和装货等。 由于航班类…

2024C题生物质和煤共热解问题的研究 详细思路

背景 随着全球能源需求的不断增长和对可再生能源的追求&#xff0c;生物质和煤共热解作为一种潜在的能源转化技术备受关注。生物质是指可再生能源&#xff0c;源自植物和动物的有机物质&#xff0c;而煤则是一种化石燃料。** 在共热解过程中&#xff0c;生物质和煤在高温和缺氧…

数据库调优-SQL语句优化

2. SQL语句优化 sql 复制代码 # 请问这两条SQL语句有什么区别呢&#xff1f;你来猜一猜那条SQL语句执行查询效果更好&#xff01; select id from sys_goods where goods_name华为 HUAWEI 麦芒7 魅海蓝 6G64G 全网通; ​ select id from sys_goods where goods_id14967325985…

【科研】常用的实验结果评价指标(1) —— R2(R-square)是什么?

常用的实验结果评价指标&#xff08;1&#xff09; —— R2(R-square)&#xff0c;可能为负数吗&#xff1f;&#xff01; 提示&#xff1a;先说概念&#xff0c;后续再陆续上代码 文章目录 常用的实验结果评价指标&#xff08;1&#xff09; —— R2(R-square)&#xff0c;可能…

【电路笔记】-无源高通滤波器

无源高通滤波器 文章目录 无源高通滤波器1、概述2、一阶高通滤波器的频率响应3、高通滤波器示例4、二阶高通滤波器5、RC 差异化因素高通滤波器与低通滤波器电路完全相反,因为这两个组件已互换,滤波器输出信号现在从电阻器两端获取。 1、概述 由于低通滤波器只允许低于其截止…

Python中的多进程、多线程、协程

Python中的多线程、多进程、协程 一、概述 1. 多线程Thread &#xff08;threading&#xff09;&#xff1a; 优点&#xff1a;同一个进程中可以启动多个线程&#xff0c;充分利用IO时&#xff0c;cpu进行等待的时间缺点&#xff1a;相对于进程&#xff0c;多线程只能并发执…

Python写了for i in range(10)却只打印一遍?

题目&#xff1a;定义一个两个参数的重复打印函数&#xff0c;第一个参数指定要打印的字符串&#xff0c;第二个参数指定要重复打印的次数&#xff0c;在主程序中调用该函数&#xff0c;打印10遍你的学号姓名。 为什么调用函数后结果只打印了一遍? 看了题目感觉就很诡异&#…

AS-VJ900实时视频拼接系统产品介绍:两画面视频拼接方法和操作

目录 一、实时视频拼接系统介绍 &#xff08;一&#xff09;实时视频拼接的定义 &#xff08;二&#xff09;无缝拼接 &#xff08;三&#xff09;AS-VJ900功能介绍 1、功能 2、拼接界面介绍 二、拼接前的准备 &#xff08;一&#xff09;摄像机选择 &#xff08;二&a…

169.招式拆解 II(unordered_map)

刷算法题&#xff1a; 第一遍&#xff1a;1.看5分钟&#xff0c;没思路看题解 2.通过题解改进自己的解法&#xff0c;并且要写每行的注释以及自己的思路。 3.思考自己做到了题解的哪一步&#xff0c;下次怎么才能做对(总结方法) 4.整理到自己的自媒体平台。 5.再刷重复的类…